From 7b4d690ca97b302c138a298896d4cdc38f625459 Mon Sep 17 00:00:00 2001 From: Janis Saldabols Date: Fri, 31 Jul 2026 13:56:34 +0300 Subject: [PATCH 1/2] ILLDEV-465 Add update-metadata action --- broker/app/app.go | 7 +- broker/patron_request/api/api-handler.go | 93 +-- broker/patron_request/api/api-handler_test.go | 174 ------ broker/patron_request/service/action.go | 203 ++++++- .../service/action_mapping_test.go | 3 +- broker/patron_request/service/action_test.go | 547 +++++++++++++++--- .../service/statemodel_capabilities.go | 7 + .../patron_request/api/api-handler_test.go | 2 +- misc/state-models.yaml | 14 +- 9 files changed, 685 insertions(+), 365 deletions(-) diff --git a/broker/app/app.go b/broker/app/app.go index a442e67c..854a0ca7 100644 --- a/broker/app/app.go +++ b/broker/app/app.go @@ -190,10 +190,10 @@ func Init(ctx context.Context) (Context, error) { iso18626Handler := handler.CreateIso18626Handler(eventBus, eventRepo, illRepo, dirAdapter) lmsCreator := lms.NewLmsCreator(illRepo, dirAdapter) lookupAdapterCreator := catalog.NewLookupAdapterCreator(AVAILABILITY_ADAPTER, METAPROXY_URL) - prActionService := prservice.CreatePatronRequestActionService(prRepo, illRepo, eventBus, &iso18626Handler, lmsCreator, email.NewEmailService()) + lookupAdapterFactory := service.NewLookupAdapterFactory(illRepo, dirAdapter, CONSORTIUM_SYMBOL, lookupAdapterEnv, lookupAdapterCreator) + prActionService := prservice.CreatePatronRequestActionService(prRepo, illRepo, eventBus, &iso18626Handler, lmsCreator, email.NewEmailService(), lookupAdapterFactory, dirAdapter) prMessageHandler.SetAutoActionRunner(prActionService) iso18626Client := client.CreateIso18626Client(eventBus, illRepo, prMessageHandler, MAX_MESSAGE_SIZE, delay) - lookupAdapterFactory := service.NewLookupAdapterFactory(illRepo, dirAdapter, CONSORTIUM_SYMBOL, lookupAdapterEnv, lookupAdapterCreator) supplierLocator := service.CreateSupplierLocator(eventBus, illRepo, dirAdapter, lookupAdapterFactory) workflowManager := service.CreateWorkflowManager(eventBus, illRepo, service.WorkflowConfig{}) tenantResolver := tenant.NewResolver().WithIllRepo(illRepo).WithLookupAdapter(dirAdapter).WithTenantToSymbol(TENANT_TO_SYMBOL) @@ -201,9 +201,6 @@ func Init(ctx context.Context) (Context, error) { prApiHandler := prapi.NewPrApiHandler(prRepo, eventBus, eventRepo, tenantResolver, &iso18626Handler, API_PAGE_SIZE) prApiHandler.SetAutoActionRunner(prActionService) prApiHandler.SetActionTaskProcessor(prActionService) - prApiHandler.SetIllRepo(illRepo) - prApiHandler.SetDirectoryLookupAdapter(dirAdapter) - prApiHandler.SetLookupAdapterFactory(lookupAdapterFactory) sseBroker := api.NewSseBroker(appCtx, tenantResolver) psApiHandler := psapi.NewPsApiHandler(psRepo, prRepo, tenantResolver) diff --git a/broker/patron_request/api/api-handler.go b/broker/patron_request/api/api-handler.go index cff60f9e..eff06f14 100644 --- a/broker/patron_request/api/api-handler.go +++ b/broker/patron_request/api/api-handler.go @@ -13,20 +13,15 @@ import ( "github.com/go-playground/validator/v10" "github.com/google/uuid" "github.com/indexdata/cql-go/cqlbuilder" - "github.com/indexdata/crosslink/broker/adapter" "github.com/indexdata/crosslink/broker/api" - "github.com/indexdata/crosslink/broker/catalog" "github.com/indexdata/crosslink/broker/common" "github.com/indexdata/crosslink/broker/events" "github.com/indexdata/crosslink/broker/handler" - "github.com/indexdata/crosslink/broker/ill_db" "github.com/indexdata/crosslink/broker/oapi" pr_db "github.com/indexdata/crosslink/broker/patron_request/db" "github.com/indexdata/crosslink/broker/patron_request/proapi" prservice "github.com/indexdata/crosslink/broker/patron_request/service" - "github.com/indexdata/crosslink/broker/service" "github.com/indexdata/crosslink/broker/tenant" - dirapi "github.com/indexdata/crosslink/directory/api" "github.com/indexdata/crosslink/iso18626" "github.com/indexdata/go-utils/utils" "github.com/jackc/pgerrcode" @@ -44,18 +39,15 @@ var brokerSymbol = utils.GetEnv("BROKER_SYMBOL", "ISIL:BROKER") var errInvalidPatronRequest = errors.New("invalid patron request") type PatronRequestApiHandler struct { - limitDefault int32 - prRepo pr_db.PrRepo - eventBus events.EventBus - eventRepo events.EventRepo - actionMappingService prservice.ActionMappingService - autoActionRunner prservice.AutoActionRunner - actionTaskProcessor ActionTaskProcessor - tenantResolver *tenant.TenantResolver - notificationSender prservice.PatronRequestNotificationService - lookupAdapterFactory *service.LookupAdapterFactory - illRepo ill_db.IllRepo - directoryLookupAdapter adapter.DirectoryLookupAdapter + limitDefault int32 + prRepo pr_db.PrRepo + eventBus events.EventBus + eventRepo events.EventRepo + actionMappingService prservice.ActionMappingService + autoActionRunner prservice.AutoActionRunner + actionTaskProcessor ActionTaskProcessor + tenantResolver *tenant.TenantResolver + notificationSender prservice.PatronRequestNotificationService } func NewPrApiHandler(prRepo pr_db.PrRepo, eventBus events.EventBus, @@ -79,18 +71,6 @@ func (a *PatronRequestApiHandler) SetActionTaskProcessor(actionTaskProcessor Act a.actionTaskProcessor = actionTaskProcessor } -func (a *PatronRequestApiHandler) SetLookupAdapterFactory(lookupAdapterFactory *service.LookupAdapterFactory) { - a.lookupAdapterFactory = lookupAdapterFactory -} - -func (a *PatronRequestApiHandler) SetIllRepo(illRepo ill_db.IllRepo) { - a.illRepo = illRepo -} - -func (a *PatronRequestApiHandler) SetDirectoryLookupAdapter(directoryLookupAdapter adapter.DirectoryLookupAdapter) { - a.directoryLookupAdapter = directoryLookupAdapter -} - func decodeRequiredBody[T any](r *http.Request, dst *T) error { if r.Body == nil || r.Body == http.NoBody { return errors.New("body is required") @@ -288,38 +268,6 @@ func AddOwnerRestriction(queryBuilder *cqlbuilder.QueryBuilder, symbol string, s return queryBuilder, err } -func (a *PatronRequestApiHandler) metadataUpdate(ctx common.ExtendedContext, illRequest *iso18626.Request, requesterPeer ill_db.Peer) error { - if a.lookupAdapterFactory == nil { - return nil - } - lookupAdapter, configPeer, err := a.lookupAdapterFactory.GetAdapterRequester(ctx, requesterPeer) - if err != nil { - return fmt.Errorf("failed to get lookup adapter: %w", err) - } - if lookupAdapter == nil { - return nil - } - - mode := dirapi.None - if configPeer.CatalogConfig != nil && configPeer.CatalogConfig.MetadataUpdateMode != nil { - mode = *configPeer.CatalogConfig.MetadataUpdateMode - } - if mode == dirapi.None { - return nil - } - lookupParams := catalog.LookupParamsFromBibliographicInfo(illRequest.BibliographicInfo, illRequest.ServiceInfo) - - lookupResult, err := lookupAdapter.Lookup(lookupParams) - if err != nil { - return fmt.Errorf("failed to perform lookup for patron request: %w", err) - } - metadata, err := lookupResult.GetMetadata() - if err != nil { - return fmt.Errorf("failed to get metadata for patron request: %w", err) - } - return catalog.MetadataRequestUpdate(&illRequest.BibliographicInfo, metadata, lookupParams, mode) -} - func (a *PatronRequestApiHandler) PostPatronRequests(w http.ResponseWriter, r *http.Request, params proapi.PostPatronRequestsParams) { logParams := map[string]string{"method": "PostPatronRequests"} ctx := common.CreateExtCtxWithArgs(r.Context(), &common.LoggerArgs{Other: logParams}) @@ -373,29 +321,6 @@ func (a *PatronRequestApiHandler) PostPatronRequests(w http.ResponseWriter, r *h return } - if a.illRepo != nil && a.directoryLookupAdapter != nil { - peers, _, peerErr := a.illRepo.GetCachedPeersBySymbols(ctx, []string{symbol}, a.directoryLookupAdapter) - if peerErr != nil { - api.AddInternalError(ctx, w, peerErr) - return - } - if len(peers) == 0 { - api.AddInternalError(ctx, w, fmt.Errorf("no peer found for requester symbol %q", symbol)) - return - } - if len(peers) > 1 { - ctx.Logger().Warn("multiple peers found for requester symbol, using first peer", "symbol", symbol, "peerCount", len(peers)) - } - requesterPeer := peers[0] - if requesterPeer.Vendor == string(dirapi.CrossLink) { - err := a.metadataUpdate(ctx, &illRequest, requesterPeer) - if err != nil { - api.AddInternalError(ctx, w, err) - return - } - } - } - dbreq := buildDbPatronRequest(&newPr, params.XOkapiTenant, creationTime, requesterReqId, illRequest, borrowerInitialState, stateModelName) pr, err := a.prRepo.CreatePatronRequest(ctx, pr_db.CreatePatronRequestParams(dbreq)) if err != nil { diff --git a/broker/patron_request/api/api-handler_test.go b/broker/patron_request/api/api-handler_test.go index b0006cd6..8af125d5 100644 --- a/broker/patron_request/api/api-handler_test.go +++ b/broker/patron_request/api/api-handler_test.go @@ -15,18 +15,14 @@ import ( "github.com/google/uuid" "github.com/indexdata/cql-go/pgcql" - "github.com/indexdata/crosslink/broker/catalog" "github.com/indexdata/crosslink/broker/common" "github.com/indexdata/crosslink/broker/events" "github.com/indexdata/crosslink/broker/handler" - "github.com/indexdata/crosslink/broker/ill_db" pr_db "github.com/indexdata/crosslink/broker/patron_request/db" "github.com/indexdata/crosslink/broker/patron_request/proapi" prservice "github.com/indexdata/crosslink/broker/patron_request/service" - "github.com/indexdata/crosslink/broker/service" "github.com/indexdata/crosslink/broker/tenant" "github.com/indexdata/crosslink/broker/test/mocks" - dirapi "github.com/indexdata/crosslink/directory/api" "github.com/indexdata/crosslink/iso18626" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" @@ -1248,176 +1244,6 @@ func (m *MockActionTaskProcessorExclusiveError) ProcessInvokeActionTask(ctx comm }, nil } -// --- metadataUpdate tests --- - -// mockLookupCreator controls what GetAdapter returns when no globalLookupAdapter is pre-set. -type mockLookupCreator struct { - adapter catalog.LookupAdapter - err error -} - -func (m *mockLookupCreator) GetAdapter(peer ill_db.Peer) (catalog.LookupAdapter, error) { - return m.adapter, m.err -} - -// peerWithMetadataMode builds a Peer whose CustomData carries the given MetadataUpdateMode. -// Pass nil to leave CatalogConfig absent entirely. -func peerWithMetadataMode(mode *dirapi.MetadataUpdateMode) ill_db.Peer { - var cc *dirapi.CatalogConfig - if mode != nil { - cc = &dirapi.CatalogConfig{MetadataUpdateMode: mode} - } - return ill_db.Peer{ - CustomData: dirapi.Entry{Name: "test-peer", CatalogConfig: cc}, - } -} - -// lookupFactoryWithAdapter creates a LookupAdapterFactory that returns the given adapter directly. -func lookupFactoryWithAdapter(adapter catalog.LookupAdapter) *service.LookupAdapterFactory { - return service.NewLookupAdapterFactory(nil, nil, "", adapter, nil) -} - -func TestMetadataUpdateNoFactory(t *testing.T) { - h := PatronRequestApiHandler{} - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) - err := h.metadataUpdate(ctx, &iso18626.Request{}, ill_db.Peer{}) - assert.NoError(t, err) -} - -func TestMetadataUpdateAdapterInitError(t *testing.T) { - creator := &mockLookupCreator{err: errors.New("adapter init failed")} - factory := service.NewLookupAdapterFactory(nil, nil, "", nil, creator) - h := PatronRequestApiHandler{} - h.SetLookupAdapterFactory(factory) - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) - err := h.metadataUpdate(ctx, &iso18626.Request{}, ill_db.Peer{}) - assert.ErrorContains(t, err, "failed to get lookup adapter") -} - -func TestMetadataUpdateNilLookupAdapter(t *testing.T) { - creator := &mockLookupCreator{} // returns nil adapter, nil error - factory := service.NewLookupAdapterFactory(nil, nil, "", nil, creator) - h := PatronRequestApiHandler{} - h.SetLookupAdapterFactory(factory) - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) - err := h.metadataUpdate(ctx, &iso18626.Request{}, ill_db.Peer{}) - assert.NoError(t, err) -} - -func TestMetadataUpdateNoCatalogConfig(t *testing.T) { - factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{}) - h := PatronRequestApiHandler{} - h.SetLookupAdapterFactory(factory) - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) - peer := peerWithMetadataMode(nil) // CatalogConfig absent → mode stays None - err := h.metadataUpdate(ctx, &iso18626.Request{}, peer) - assert.NoError(t, err) -} - -func TestMetadataUpdateModeNone(t *testing.T) { - mode := dirapi.None - factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{}) - h := PatronRequestApiHandler{} - h.SetLookupAdapterFactory(factory) - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) - err := h.metadataUpdate(ctx, &iso18626.Request{}, peerWithMetadataMode(&mode)) - assert.NoError(t, err) -} - -func TestMetadataUpdateMetadataLookupError(t *testing.T) { - mode := dirapi.Merge - factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Err: errors.New("lookup failed")}) - h := PatronRequestApiHandler{} - h.SetLookupAdapterFactory(factory) - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) - err := h.metadataUpdate(ctx, &iso18626.Request{}, peerWithMetadataMode(&mode)) - assert.ErrorContains(t, err, "failed to perform lookup for patron request") -} - -func TestMetadataUpdateMergePopulatesEmptyFields(t *testing.T) { - mode := dirapi.Merge - meta := catalog.Metadata{Title: "Catalog Title", Author: "Jane Doe"} - factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Metadata: meta}) - h := PatronRequestApiHandler{} - h.SetLookupAdapterFactory(factory) - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) - req := &iso18626.Request{} // empty bib info - err := h.metadataUpdate(ctx, req, peerWithMetadataMode(&mode)) - assert.NoError(t, err) - assert.Equal(t, "Catalog Title", req.BibliographicInfo.Title) - assert.Equal(t, "Jane Doe", req.BibliographicInfo.Author) -} - -func TestMetadataUpdateMergePreservesExistingFields(t *testing.T) { - mode := dirapi.Merge - meta := catalog.Metadata{Title: "Catalog Title"} - factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Metadata: meta}) - h := PatronRequestApiHandler{} - h.SetLookupAdapterFactory(factory) - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) - req := &iso18626.Request{ - BibliographicInfo: iso18626.BibliographicInfo{Title: "Existing Title"}, - } - err := h.metadataUpdate(ctx, req, peerWithMetadataMode(&mode)) - assert.NoError(t, err) - assert.Equal(t, "Existing Title", req.BibliographicInfo.Title) // not overwritten -} - -func TestMetadataUpdateAutoModeWithIdentifierReplaces(t *testing.T) { - mode := dirapi.Auto - meta := catalog.Metadata{Title: "Catalog Title", Author: "Catalog Author", Isbn: "1234567890"} - factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Metadata: meta}) - h := PatronRequestApiHandler{} - h.SetLookupAdapterFactory(factory) - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) - req := &iso18626.Request{ - BibliographicInfo: iso18626.BibliographicInfo{ - Title: "Old Title", - SupplierUniqueRecordId: "record-123", // non-empty → Auto resolves to Replace - BibliographicItemId: []iso18626.BibliographicItemId{ - { - BibliographicItemIdentifier: "0987654321", - BibliographicItemIdentifierCode: iso18626.TypeSchemeValuePair{Text: "ISBN"}, - }, - }, - }, - } - err := h.metadataUpdate(ctx, req, peerWithMetadataMode(&mode)) - assert.NoError(t, err) - assert.Equal(t, "Catalog Title", req.BibliographicInfo.Title) // replaced - assert.Equal(t, "Catalog Author", req.BibliographicInfo.Author) // replaced - assert.Equal(t, "1234567890", req.BibliographicInfo.BibliographicItemId[0].BibliographicItemIdentifier) // replaced - assert.Equal(t, "ISBN", req.BibliographicInfo.BibliographicItemId[0].BibliographicItemIdentifierCode.Text) // replaced -} - -func TestMetadataUpdateAutoModeWithoutIdentifierMerges(t *testing.T) { - mode := dirapi.Auto - meta := catalog.Metadata{Title: "Catalog Title", Author: "Catalog Author", Isbn: "1234567890", Issn: "4321-4321"} - factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Metadata: meta}) - h := PatronRequestApiHandler{} - h.SetLookupAdapterFactory(factory) - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) - req := &iso18626.Request{ - BibliographicInfo: iso18626.BibliographicInfo{ - Title: "Patron Title", // no SupplierUniqueRecordId → Auto resolves to Merge - BibliographicItemId: []iso18626.BibliographicItemId{ - { - BibliographicItemIdentifier: "0987654321", - BibliographicItemIdentifierCode: iso18626.TypeSchemeValuePair{Text: "ISBN"}, - }, - }, - }, - } - err := h.metadataUpdate(ctx, req, peerWithMetadataMode(&mode)) - assert.NoError(t, err) - assert.Equal(t, "Patron Title", req.BibliographicInfo.Title) // preserved (Merge) - assert.Equal(t, "Catalog Author", req.BibliographicInfo.Author) // filled in (was empty) - assert.Equal(t, "0987654321", req.BibliographicInfo.BibliographicItemId[0].BibliographicItemIdentifier) // kept - assert.Equal(t, "ISBN", req.BibliographicInfo.BibliographicItemId[0].BibliographicItemIdentifierCode.Text) // kept - assert.Equal(t, "4321-4321", req.BibliographicInfo.BibliographicItemId[1].BibliographicItemIdentifier) // added (not present) - assert.Equal(t, "ISSN", req.BibliographicInfo.BibliographicItemId[1].BibliographicItemIdentifierCode.Text) // added (not present) -} - // --- PutPatronRequestsId tests --- // illRepoNoTx returns pgx.ErrNoRows for GetIllTransactionByRequesterRequestId, diff --git a/broker/patron_request/service/action.go b/broker/patron_request/service/action.go index 2c172a53..d289ac82 100644 --- a/broker/patron_request/service/action.go +++ b/broker/patron_request/service/action.go @@ -11,6 +11,8 @@ import ( "time" "github.com/google/uuid" + "github.com/indexdata/crosslink/broker/adapter" + "github.com/indexdata/crosslink/broker/catalog" "github.com/indexdata/crosslink/broker/common" "github.com/indexdata/crosslink/broker/email" "github.com/indexdata/crosslink/broker/events" @@ -19,7 +21,9 @@ import ( "github.com/indexdata/crosslink/broker/lms" pr_db "github.com/indexdata/crosslink/broker/patron_request/db" "github.com/indexdata/crosslink/broker/patron_request/proapi" + "github.com/indexdata/crosslink/broker/service" "github.com/indexdata/crosslink/broker/shim" + dirapi "github.com/indexdata/crosslink/directory/api" "github.com/indexdata/crosslink/iso18626" "github.com/indexdata/go-utils/utils" "github.com/jackc/pgx/v5/pgtype" @@ -29,12 +33,14 @@ const COMP = "pr_action_service" type PatronRequestActionService struct { PatronRequestMessageSender - prRepo pr_db.PrRepo - illRepo ill_db.IllRepo - eventBus events.EventBus - lmsCreator lms.LmsCreator - actionMappingService ActionMappingService - emailService email.EmailService + prRepo pr_db.PrRepo + illRepo ill_db.IllRepo + eventBus events.EventBus + lmsCreator lms.LmsCreator + actionMappingService ActionMappingService + emailService email.EmailService + directoryLookupAdapter adapter.DirectoryLookupAdapter + lookupAdapterFactory *service.LookupAdapterFactory } type actionExecutionResult struct { @@ -44,6 +50,27 @@ type actionExecutionResult struct { retryPr pr_db.PatronRequest } +type actionDecisionDetailMetadataUpdate struct { + Type string `json:"type"` + Outcome string `json:"outcome"` + Mode string `json:"mode"` + EffectiveMode string `json:"effectiveMode"` + LookupParams catalog.LookupParams `json:"lookupParams"` + Source actionDecisionDetailMetadataSource `json:"source"` + Changes []actionDecisionDetailMetadataChange `json:"changes"` +} + +type actionDecisionDetailMetadataSource struct { + AdapterType string `json:"adapterType"` + ConfigurationPeerID string `json:"configurationPeerId"` +} + +type actionDecisionDetailMetadataChange struct { + Field string `json:"field"` + PreviousValue string `json:"previousValue"` + NewValue string `json:"newValue"` +} + type autoActionFailure struct { action pr_db.PatronRequestAction msg string @@ -64,7 +91,9 @@ type actionParams struct { AutoActionParams *proapi.ModelAction_Params `json:"autoActionParams,omitempty"` } -func CreatePatronRequestActionService(prRepo pr_db.PrRepo, illRepo ill_db.IllRepo, eventBus events.EventBus, iso18626Handler handler.Iso18626HandlerInterface, lmsCreator lms.LmsCreator, emailService email.EmailService) *PatronRequestActionService { +func CreatePatronRequestActionService(prRepo pr_db.PrRepo, illRepo ill_db.IllRepo, eventBus events.EventBus, + iso18626Handler handler.Iso18626HandlerInterface, lmsCreator lms.LmsCreator, emailService email.EmailService, + lookupAdapterFactory *service.LookupAdapterFactory, directoryLookupAdapter adapter.DirectoryLookupAdapter) *PatronRequestActionService { return &PatronRequestActionService{ PatronRequestMessageSender: PatronRequestMessageSender{iso18626Handler: iso18626Handler, logErrorAndReturnResult: logActionErrorAndReturnResult}, prRepo: prRepo, @@ -73,6 +102,8 @@ func CreatePatronRequestActionService(prRepo pr_db.PrRepo, illRepo ill_db.IllRep lmsCreator: lmsCreator, actionMappingService: ActionMappingService{SMService: &StateModelService{}}, emailService: emailService, + lookupAdapterFactory: lookupAdapterFactory, + directoryLookupAdapter: directoryLookupAdapter, } } @@ -347,6 +378,8 @@ func (a *PatronRequestActionService) handleBorrowingAction(ctx common.ExtendedCo switch action { case BorrowerActionValidate: return a.validateBorrowingRequest(ctx, pr, lmsAdapter, illRequest) + case BorrowerActionUpdateMetadata: + return a.updateMetadataBorrowingRequest(ctx, pr, lmsAdapter, illRequest) case BorrowerActionSendRequest: return a.sendBorrowingRequest(ctx, pr, illRequest) case BorrowerActionReceive: @@ -453,14 +486,168 @@ func (a *PatronRequestActionService) validateBorrowingRequest(ctx common.Extende // perhaps it would be better to have both original and canonical id stored? pr.Patron = pgtype.Text{String: userId, Valid: true} + return actionExecutionResult{status: events.EventStatusSuccess, pr: pr} +} + +func (a *PatronRequestActionService) updateMetadataBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) actionExecutionResult { + peers, _, peerErr := a.illRepo.GetCachedPeersBySymbols(ctx, []string{pr.RequesterSymbol.String}, a.directoryLookupAdapter) + if peerErr != nil { + status, result := logActionErrorAndReturnResult(ctx, "failed to get requester peer", peerErr) + return actionExecutionResult{status: status, result: result, pr: pr} + } + if len(peers) == 0 { + status, result := logActionErrorAndReturnResult(ctx, "failed to get requester peer", fmt.Errorf("no peer found for requester symbol %q", pr.RequesterSymbol.String)) + return actionExecutionResult{status: status, result: result, pr: pr} + } + if len(peers) > 1 { + ctx.Logger().Warn("multiple peers found for requester symbol, using first peer", "symbol", pr.RequesterSymbol.String, "peerCount", len(peers)) + } + requesterPeer := peers[0] + var decisionDetails []actionDecisionDetailMetadataUpdate + if requesterPeer.Vendor == string(dirapi.CrossLink) { + detail, err := a.metadataUpdateWithDetails(ctx, &illRequest, requesterPeer) + if err != nil { + status, result := logActionErrorAndReturnResult(ctx, "metadata update failed", err) + return actionExecutionResult{status: status, result: result, pr: pr} + } + if detail != nil { + decisionDetails = append(decisionDetails, *detail) + } + } + res := actionExecutionResult{status: events.EventStatusSuccess, pr: pr} - if illRequest.BibliographicInfo.SupplierUniqueRecordId == "" { + if len(decisionDetails) > 0 || illRequest.BibliographicInfo.SupplierUniqueRecordId == "" { res.result = &events.EventResult{} + if len(decisionDetails) > 0 { + res.result.CustomData = map[string]any{"decisionDetails": decisionDetails} + } + } + if illRequest.BibliographicInfo.SupplierUniqueRecordId == "" { res.result.ActionResult = &events.ActionResult{Outcome: ActionOutcomeReview} } return res } +func (a *PatronRequestActionService) metadataUpdate(ctx common.ExtendedContext, illRequest *iso18626.Request, requesterPeer ill_db.Peer) error { + _, err := a.metadataUpdateWithDetails(ctx, illRequest, requesterPeer) + return err +} + +func (a *PatronRequestActionService) metadataUpdateWithDetails(ctx common.ExtendedContext, illRequest *iso18626.Request, requesterPeer ill_db.Peer) (*actionDecisionDetailMetadataUpdate, error) { + if a.lookupAdapterFactory == nil { + return nil, nil + } + lookupAdapter, configPeer, err := a.lookupAdapterFactory.GetAdapterRequester(ctx, requesterPeer) + if err != nil { + return nil, fmt.Errorf("failed to get lookup adapter: %w", err) + } + if lookupAdapter == nil { + return nil, nil + } + + mode := dirapi.None + if configPeer.CatalogConfig != nil && configPeer.CatalogConfig.MetadataUpdateMode != nil { + mode = *configPeer.CatalogConfig.MetadataUpdateMode + } + if mode == dirapi.None { + return nil, nil + } + lookupParams := catalog.LookupParamsFromBibliographicInfo(illRequest.BibliographicInfo, illRequest.ServiceInfo) + + lookupResult, err := lookupAdapter.Lookup(lookupParams) + if err != nil { + return nil, fmt.Errorf("failed to perform lookup for patron request: %w", err) + } + metadata, err := lookupResult.GetMetadata() + if err != nil { + return nil, fmt.Errorf("failed to get metadata for patron request: %w", err) + } + before := cloneBibliographicInfo(illRequest.BibliographicInfo) + err = catalog.MetadataRequestUpdate(&illRequest.BibliographicInfo, metadata, lookupParams, mode) + if err != nil { + return nil, err + } + changes := metadataUpdateChanges(before, illRequest.BibliographicInfo) + return &actionDecisionDetailMetadataUpdate{ + Type: "metadata-update", + Outcome: metadataUpdateOutcome(changes), + Mode: string(mode), + EffectiveMode: string(effectiveMetadataUpdateMode(mode, lookupParams)), + LookupParams: lookupParams, + Source: metadataUpdateSource(configPeer), + Changes: changes, + }, nil +} + +func cloneBibliographicInfo(info iso18626.BibliographicInfo) iso18626.BibliographicInfo { + info.BibliographicItemId = append([]iso18626.BibliographicItemId(nil), info.BibliographicItemId...) + return info +} + +func effectiveMetadataUpdateMode(mode dirapi.MetadataUpdateMode, params catalog.LookupParams) dirapi.MetadataUpdateMode { + if mode != dirapi.Auto { + return mode + } + if params.Identifier != "" { + return dirapi.Replace + } + return dirapi.Merge +} + +func metadataUpdateOutcome(changes []actionDecisionDetailMetadataChange) string { + if len(changes) == 0 { + return "unchanged" + } + return "updated" +} + +func metadataUpdateSource(configPeer dirapi.Entry) actionDecisionDetailMetadataSource { + source := actionDecisionDetailMetadataSource{} + if configPeer.Id != nil { + source.ConfigurationPeerID = configPeer.Id.String() + } + if configPeer.CatalogConfig != nil { + switch { + case configPeer.CatalogConfig.Sru != nil: + source.AdapterType = "sru" + case configPeer.CatalogConfig.Zoom != nil: + source.AdapterType = "zoom" + } + } + return source +} + +func metadataUpdateChanges(before iso18626.BibliographicInfo, after iso18626.BibliographicInfo) []actionDecisionDetailMetadataChange { + changes := make([]actionDecisionDetailMetadataChange, 0) + addMetadataStringChange := func(field string, previousValue string, newValue string) { + if previousValue == newValue { + return + } + changes = append(changes, actionDecisionDetailMetadataChange{ + Field: field, + PreviousValue: previousValue, + NewValue: newValue, + }) + } + addMetadataStringChange("title", before.Title, after.Title) + addMetadataStringChange("subtitle", before.Subtitle, after.Subtitle) + addMetadataStringChange("author", before.Author, after.Author) + addMetadataStringChange("supplierUniqueRecordId", before.SupplierUniqueRecordId, after.SupplierUniqueRecordId) + addMetadataStringChange("edition", before.Edition, after.Edition) + addMetadataStringChange("isbn", bibliographicItemValue(before.BibliographicItemId, "ISBN"), bibliographicItemValue(after.BibliographicItemId, "ISBN")) + addMetadataStringChange("issn", bibliographicItemValue(before.BibliographicItemId, "ISSN"), bibliographicItemValue(after.BibliographicItemId, "ISSN")) + return changes +} + +func bibliographicItemValue(items []iso18626.BibliographicItemId, code string) string { + for _, id := range items { + if strings.TrimSpace(id.BibliographicItemIdentifierCode.Text) == code { + return id.BibliographicItemIdentifier + } + } + return "" +} + func deepCopyISO18626Request(request iso18626.Request) (iso18626.Request, error) { requestJSON, err := json.Marshal(request) if err != nil { diff --git a/broker/patron_request/service/action_mapping_test.go b/broker/patron_request/service/action_mapping_test.go index 8ed80172..11bdd635 100644 --- a/broker/patron_request/service/action_mapping_test.go +++ b/broker/patron_request/service/action_mapping_test.go @@ -15,7 +15,8 @@ import ( func TestNewReturnableActionMapping(t *testing.T) { borrowerStateActionMapping := map[pr_db.PatronRequestState][]PatronRequestAction{ BorrowerStateNew: {{actionName: BorrowerActionValidate, auto: true}}, - BorrowerStateValidated: {{actionName: BorrowerActionSendRequest, auto: true}}, + BorrowerStateValidated: {{actionName: BorrowerActionUpdateMetadata, auto: true}}, + BorrowerStateMetadataUpdated: {{actionName: BorrowerActionSendRequest, auto: true}}, BorrowerStateNeedsReview: {{actionName: BorrowerActionSendRequest}}, BorrowerStateSupplierLocated: {{actionName: BorrowerActionCancelRequest}}, BorrowerStateConditionPending: {{actionName: BorrowerActionAcceptCondition}, {actionName: BorrowerActionRejectCondition}}, diff --git a/broker/patron_request/service/action_test.go b/broker/patron_request/service/action_test.go index 13f16062..4fbf8948 100644 --- a/broker/patron_request/service/action_test.go +++ b/broker/patron_request/service/action_test.go @@ -10,6 +10,9 @@ import ( "testing" "time" + "github.com/google/uuid" + "github.com/indexdata/crosslink/broker/adapter" + "github.com/indexdata/crosslink/broker/catalog" "github.com/indexdata/crosslink/broker/common" "github.com/indexdata/crosslink/broker/events" "github.com/indexdata/crosslink/broker/handler" @@ -18,6 +21,7 @@ import ( "github.com/indexdata/crosslink/broker/ncipclient" pr_db "github.com/indexdata/crosslink/broker/patron_request/db" "github.com/indexdata/crosslink/broker/patron_request/proapi" + "github.com/indexdata/crosslink/broker/service" "github.com/indexdata/crosslink/broker/shim" dirapi "github.com/indexdata/crosslink/directory/api" "github.com/indexdata/crosslink/iso18626" @@ -33,7 +37,7 @@ var actionValidate = BorrowerActionValidate func TestInvokeAction(t *testing.T) { mockEventBus := new(MockEventBus) - prAction := CreatePatronRequestActionService(*new(pr_db.PrRepo), new(IllRepoMock), mockEventBus, new(handler.Iso18626Handler), nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(*new(pr_db.PrRepo), new(IllRepoMock), mockEventBus, new(handler.Iso18626Handler), nil, new(EmailSenderMock), nil, nil) event := events.Event{ ID: "action-1", } @@ -45,7 +49,7 @@ func TestInvokeAction(t *testing.T) { } func TestHandleInvokeActionNotSpecifiedAction(t *testing.T) { - prAction := CreatePatronRequestActionService(*new(pr_db.PrRepo), new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(*new(pr_db.PrRepo), new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock), nil, nil) status, resultData := prAction.handleInvokeAction(appCtx, events.Event{}) @@ -55,7 +59,7 @@ func TestHandleInvokeActionNotSpecifiedAction(t *testing.T) { func TestHandleInvokeActionNoPR(t *testing.T) { mockPrRepo := new(MockPrRepo) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock), nil, nil) mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{}, errors.New("not fund")) status, resultData := prAction.handleInvokeAction(appCtx, events.Event{PatronRequestID: patronRequestId, EventData: events.EventData{CommonEventData: events.CommonEventData{Action: &actionValidate}}}) @@ -68,7 +72,7 @@ func TestHandleInvokeActionNoPRSide(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:x").Return(lms.CreateLmsAdapterMockOK(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{State: BorrowerStateNew, Side: "helper", IllRequest: illRequest}, nil) @@ -80,7 +84,7 @@ func TestHandleInvokeActionNoPRSide(t *testing.T) { func TestHandleInvokeActionWhichIsNotAllowed(t *testing.T) { mockPrRepo := new(MockPrRepo) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock), nil, nil) mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{State: BorrowerStateValidated, Side: SideBorrowing}, nil) status, resultData := prAction.handleInvokeAction(appCtx, events.Event{PatronRequestID: patronRequestId, EventData: events.EventData{CommonEventData: events.CommonEventData{Action: &actionValidate}}}) @@ -91,7 +95,7 @@ func TestHandleInvokeActionWhichIsNotAllowed(t *testing.T) { func TestHandleInvokeActionNoLms(t *testing.T) { mockPrRepo := new(MockPrRepo) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:x"}, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}, IllRequest: illRequest}, nil) @@ -103,7 +107,7 @@ func TestHandleInvokeActionNoLms(t *testing.T) { func TestHandleInvokeActionTerminateOKNoLms(t *testing.T) { mockPrRepo := new(MockPrRepo) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock), nil, nil) action := TerminateAction mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ ID: patronRequestId, @@ -128,7 +132,7 @@ func TestHandleInvokeActionTerminateOKNoLms(t *testing.T) { func TestHandleInvokeActionTerminateRejectsTerminal(t *testing.T) { mockPrRepo := new(MockPrRepo) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock), nil, nil) action := TerminateAction mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ ID: patronRequestId, @@ -147,7 +151,7 @@ func TestHandleBorrowingActionMissingRequesterSymbol(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:x").Return(lms.CreateLmsAdapterMockOK(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}, IllRequest: illRequest}, nil) mockPrRepo.On("GetPatronRequestByIdForUpdate", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}, IllRequest: illRequest}, nil) @@ -159,19 +163,25 @@ func TestHandleBorrowingActionMissingRequesterSymbol(t *testing.T) { assert.True(t, mockPrRepo.savedPr.NeedsAttention) } -func TestHandleInvokeActionValidateNeedReview(t *testing.T) { +func TestHandleInvokeActionUpdateMetadataNeedReview(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) mockEventBus := new(MockEventBus) + illRepo := new(IllRepoMock) lmsCreator.On("GetAdapter", "ISIL:x").Return(createLmsAdapterMockLog(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + illRepo.On("GetCachedPeersBySymbols", []string{"ISIL:x"}, mock.Anything).Return([]ill_db.Peer{{Vendor: "other"}}, "", nil) + prAction := CreatePatronRequestActionService(mockPrRepo, illRepo, mockEventBus, new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} fakeEventID := "1234" - mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:x"}, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}}, nil) + pr := pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:x"}, State: BorrowerStateValidated, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}} + mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr, nil) + mockPrRepo.On("GetPatronRequestByIdForUpdate", patronRequestId).Return(pr, nil) + mockEventBus.On("CreateNoticeWithParent", fakeEventID).Return("", nil) + action := BorrowerActionUpdateMetadata - status, resultData := prAction.handleInvokeAction(appCtx, events.Event{ID: fakeEventID, PatronRequestID: patronRequestId, EventData: events.EventData{CommonEventData: events.CommonEventData{Action: &actionValidate}}}) + status, resultData := prAction.handleInvokeAction(appCtx, events.Event{ID: fakeEventID, PatronRequestID: patronRequestId, EventData: events.EventData{CommonEventData: events.CommonEventData{Action: &action}}}) assert.Equal(t, events.EventStatusSuccess, status) assert.NotNil(t, resultData) @@ -187,17 +197,22 @@ func TestHandleInvokeActionValidateSendRequest(t *testing.T) { mockIso18626Handler := new(MockIso18626Handler) lmsCreator.On("GetAdapter", "ISIL:x").Return(createLmsAdapterMockLog(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{BibliographicInfo: iso18626.BibliographicInfo{SupplierUniqueRecordId: "12312"}} fakeEventID := "1234" + //action := BorrowerActionUpdateMetadata initialPR := pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:x"}, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}} validatedPR := initialPR validatedPR.State = BorrowerStateValidated - sentPR := initialPR + updatePr := initialPR + updatePr.State = BorrowerStateMetadataUpdated + sentPR := updatePr sentPR.State = BorrowerStateSent + mockPrRepo.On("GetPatronRequestByIdForUpdate", patronRequestId).Return(sentPR, nil) mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(initialPR, nil).Once() mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(validatedPR, nil).Once() - mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(sentPR, nil).Once() + mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(updatePr, nil).Once() + mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(sentPR, nil) mockEventBus.On("CreateNoticeWithParent", fakeEventID).Return("", nil) status, resultData := prAction.handleInvokeAction(appCtx, events.Event{ID: fakeEventID, PatronRequestID: patronRequestId, EventData: events.EventData{CommonEventData: events.CommonEventData{Action: &actionValidate}}}) @@ -217,17 +232,21 @@ func TestHandleInvokeActionValidateSendRequestDuplicate(t *testing.T) { patronRequestId := "duplicate" lmsCreator.On("GetAdapter", "ISIL:x").Return(createLmsAdapterMockLog(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{BibliographicInfo: iso18626.BibliographicInfo{SupplierUniqueRecordId: "12312"}} fakeEventID := "1234" initialPR := pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:x"}, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}} validatedPR := initialPR validatedPR.State = BorrowerStateValidated + updatePr := initialPR + updatePr.State = BorrowerStateMetadataUpdated sentPR := initialPR sentPR.State = BorrowerStateSent + mockPrRepo.On("GetPatronRequestByIdForUpdate", patronRequestId).Return(sentPR, nil) mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(initialPR, nil).Once() mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(validatedPR, nil).Once() - mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(sentPR, nil).Once() + mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(updatePr, nil).Once() + mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(sentPR, nil) mockEventBus.On("CreateNoticeWithParent", fakeEventID).Return("", nil) status, resultData := prAction.handleInvokeAction(appCtx, events.Event{ID: fakeEventID, PatronRequestID: patronRequestId, EventData: events.EventData{CommonEventData: events.CommonEventData{Action: &actionValidate}}}) @@ -239,11 +258,161 @@ func TestHandleInvokeActionValidateSendRequestDuplicate(t *testing.T) { assert.Equal(t, ActionOutcomeDuplicate, mockPrRepo.savedPr.LastActionOutcome.String) } +func TestMetadataUpdateNoFactory(t *testing.T) { + s := PatronRequestActionService{} + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + err := s.metadataUpdate(ctx, &iso18626.Request{}, ill_db.Peer{}) + assert.NoError(t, err) +} + +func TestMetadataUpdateAdapterInitError(t *testing.T) { + creator := &mockLookupCreator{err: errors.New("adapter init failed")} + factory := service.NewLookupAdapterFactory(nil, nil, "", nil, creator) + s := PatronRequestActionService{ + lookupAdapterFactory: factory, + } + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + err := s.metadataUpdate(ctx, &iso18626.Request{}, ill_db.Peer{}) + assert.ErrorContains(t, err, "failed to get lookup adapter") +} + +func TestMetadataUpdateNilLookupAdapter(t *testing.T) { + creator := &mockLookupCreator{} // returns nil adapter, nil error + factory := service.NewLookupAdapterFactory(nil, nil, "", nil, creator) + s := PatronRequestActionService{ + lookupAdapterFactory: factory, + } + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + err := s.metadataUpdate(ctx, &iso18626.Request{}, ill_db.Peer{}) + assert.NoError(t, err) +} + +func TestMetadataUpdateNoCatalogConfig(t *testing.T) { + factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{}) + s := PatronRequestActionService{ + lookupAdapterFactory: factory, + } + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + peer := peerWithMetadataMode(nil) // CatalogConfig absent → mode stays None + err := s.metadataUpdate(ctx, &iso18626.Request{}, peer) + assert.NoError(t, err) +} + +func TestMetadataUpdateModeNone(t *testing.T) { + mode := dirapi.None + factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{}) + s := PatronRequestActionService{ + lookupAdapterFactory: factory, + } + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + err := s.metadataUpdate(ctx, &iso18626.Request{}, peerWithMetadataMode(&mode)) + assert.NoError(t, err) +} + +func TestMetadataUpdateMetadataLookupError(t *testing.T) { + mode := dirapi.Merge + factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Err: errors.New("lookup failed")}) + s := PatronRequestActionService{ + lookupAdapterFactory: factory, + } + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + err := s.metadataUpdate(ctx, &iso18626.Request{}, peerWithMetadataMode(&mode)) + assert.ErrorContains(t, err, "failed to perform lookup for patron request") +} + +func TestMetadataUpdateMergePopulatesEmptyFields(t *testing.T) { + mode := dirapi.Merge + meta := catalog.Metadata{Title: "Catalog Title", Author: "Jane Doe"} + factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Metadata: meta}) + s := PatronRequestActionService{ + lookupAdapterFactory: factory, + } + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + req := &iso18626.Request{} // empty bib info + err := s.metadataUpdate(ctx, req, peerWithMetadataMode(&mode)) + assert.NoError(t, err) + assert.Equal(t, "Catalog Title", req.BibliographicInfo.Title) + assert.Equal(t, "Jane Doe", req.BibliographicInfo.Author) +} + +func TestMetadataUpdateMergePreservesExistingFields(t *testing.T) { + mode := dirapi.Merge + meta := catalog.Metadata{Title: "Catalog Title"} + factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Metadata: meta}) + s := PatronRequestActionService{ + lookupAdapterFactory: factory, + } + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + req := &iso18626.Request{ + BibliographicInfo: iso18626.BibliographicInfo{Title: "Existing Title"}, + } + err := s.metadataUpdate(ctx, req, peerWithMetadataMode(&mode)) + assert.NoError(t, err) + assert.Equal(t, "Existing Title", req.BibliographicInfo.Title) // not overwritten +} + +func TestMetadataUpdateAutoModeWithIdentifierReplaces(t *testing.T) { + mode := dirapi.Auto + meta := catalog.Metadata{Title: "Catalog Title", Author: "Catalog Author", Isbn: "1234567890"} + factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Metadata: meta}) + s := PatronRequestActionService{ + lookupAdapterFactory: factory, + } + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + req := &iso18626.Request{ + BibliographicInfo: iso18626.BibliographicInfo{ + Title: "Old Title", + SupplierUniqueRecordId: "record-123", // non-empty → Auto resolves to Replace + BibliographicItemId: []iso18626.BibliographicItemId{ + { + BibliographicItemIdentifier: "0987654321", + BibliographicItemIdentifierCode: iso18626.TypeSchemeValuePair{Text: "ISBN"}, + }, + }, + }, + } + err := s.metadataUpdate(ctx, req, peerWithMetadataMode(&mode)) + assert.NoError(t, err) + assert.Equal(t, "Catalog Title", req.BibliographicInfo.Title) // replaced + assert.Equal(t, "Catalog Author", req.BibliographicInfo.Author) // replaced + assert.Equal(t, "1234567890", req.BibliographicInfo.BibliographicItemId[0].BibliographicItemIdentifier) // replaced + assert.Equal(t, "ISBN", req.BibliographicInfo.BibliographicItemId[0].BibliographicItemIdentifierCode.Text) // replaced +} + +func TestMetadataUpdateAutoModeWithoutIdentifierMerges(t *testing.T) { + mode := dirapi.Auto + meta := catalog.Metadata{Title: "Catalog Title", Author: "Catalog Author", Isbn: "1234567890", Issn: "4321-4321"} + factory := lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Metadata: meta}) + s := PatronRequestActionService{ + lookupAdapterFactory: factory, + } + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + req := &iso18626.Request{ + BibliographicInfo: iso18626.BibliographicInfo{ + Title: "Patron Title", // no SupplierUniqueRecordId → Auto resolves to Merge + BibliographicItemId: []iso18626.BibliographicItemId{ + { + BibliographicItemIdentifier: "0987654321", + BibliographicItemIdentifierCode: iso18626.TypeSchemeValuePair{Text: "ISBN"}, + }, + }, + }, + } + err := s.metadataUpdate(ctx, req, peerWithMetadataMode(&mode)) + assert.NoError(t, err) + assert.Equal(t, "Patron Title", req.BibliographicInfo.Title) // preserved (Merge) + assert.Equal(t, "Catalog Author", req.BibliographicInfo.Author) // filled in (was empty) + assert.Equal(t, "0987654321", req.BibliographicInfo.BibliographicItemId[0].BibliographicItemIdentifier) // kept + assert.Equal(t, "ISBN", req.BibliographicInfo.BibliographicItemId[0].BibliographicItemIdentifierCode.Text) // kept + assert.Equal(t, "4321-4321", req.BibliographicInfo.BibliographicItemId[1].BibliographicItemIdentifier) // added (not present) + assert.Equal(t, "ISSN", req.BibliographicInfo.BibliographicItemId[1].BibliographicItemIdentifierCode.Text) // added (not present) +} + func TestHandleInvokeActionValidateGetAdapterFailed(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:x").Return(lms.CreateLmsAdapterMockOK(), assert.AnError) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:x"}, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}, IllRequest: illRequest, NeedsAttention: true}, nil) @@ -258,7 +427,7 @@ func TestHandleInvokeActionValidateLookupFailed(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}}, nil) mockPrRepo.On("GetPatronRequestByIdForUpdate", patronRequestId).Return(pr_db.PatronRequest{RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:x"}, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}, IllRequest: illRequest}, nil) @@ -274,9 +443,9 @@ func TestHandleInvokeActionSendRequest(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} - mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: BorrowerStateValidated, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}}, nil) + mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: BorrowerStateMetadataUpdated, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}}, nil) action := BorrowerActionSendRequest status, resultData := prAction.handleInvokeAction(appCtx, events.Event{PatronRequestID: patronRequestId, EventData: events.EventData{CommonEventData: events.CommonEventData{Action: &action}}}) @@ -295,7 +464,7 @@ func TestHandleInvokeActionReceiveOK(t *testing.T) { mockEventBus := new(MockEventBus) emailMock := new(EmailSenderMock) emailMock.On("IsReadyToSend").Return(false) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, emailMock) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, emailMock, nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Once().Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateShipped, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateReceived, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) @@ -333,7 +502,7 @@ func TestHandleInvokeActionReceiveAcceptItemFailed(t *testing.T) { mockIso18626Handler := new(MockIso18626Handler) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} action := BorrowerActionReceive mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateShipped, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) @@ -351,7 +520,7 @@ func TestHandleInvokeActionReceiveNoItem(t *testing.T) { mockIso18626Handler := new(MockIso18626Handler) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(lms.CreateLmsAdapterMockOK(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateShipped, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{}, nil) @@ -369,7 +538,7 @@ func TestHandleInvokeActionReceiveItemLookupFailure(t *testing.T) { mockIso18626Handler := new(MockIso18626Handler) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(lms.CreateLmsAdapterMockOK(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateShipped, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{}, assert.AnError) @@ -386,7 +555,7 @@ func TestHandleInvokeActionCheckOutOK(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(lms.CreateLmsAdapterMockOK(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, Patron: pgtype.Text{Valid: true, String: "patron1"}, State: BorrowerStateReceived, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -403,7 +572,7 @@ func TestHandleInvokeActionCheckOutItemFails(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(lms.CreateLmsAdapterMockOK(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, Patron: pgtype.Text{Valid: true, String: "patron1"}, State: BorrowerStateReceived, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{}, assert.AnError) @@ -420,7 +589,7 @@ func TestHandleInvokeActionCheckOutFails(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateReceived, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -436,7 +605,7 @@ func TestHandleInvokeActionCheckInOK(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(lms.CreateLmsAdapterMockOK(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateCheckedOut, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -453,7 +622,7 @@ func TestHandleInvokeActionCheckInItemFails(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(lms.CreateLmsAdapterMockOK(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateCheckedOut, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{}, assert.AnError) @@ -470,7 +639,7 @@ func TestHandleInvokeActionCheckInFails(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateCheckedOut, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -487,7 +656,7 @@ func TestHandleInvokeActionShipReturnOK(t *testing.T) { mockIso18626Handler := new(MockIso18626Handler) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(lms.CreateLmsAdapterMockOK(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateCheckedIn, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -505,7 +674,7 @@ func TestHandleInvokeActionShipReturnItemFails(t *testing.T) { mockIso18626Handler := new(MockIso18626Handler) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(lms.CreateLmsAdapterMockOK(), nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateCheckedIn, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{}, assert.AnError) @@ -523,7 +692,7 @@ func TestHandleInvokeActionShipReturnFails(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateCheckedIn, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -540,7 +709,7 @@ func TestHandleInvokeActionCancelRequest(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: BorrowerStateWillSupply, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) action := BorrowerActionCancelRequest @@ -556,7 +725,7 @@ func TestHandleInvokeActionAcceptCondition(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateConditionPending, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) action := BorrowerActionAcceptCondition @@ -579,7 +748,7 @@ func TestHandleInvokeActionAcceptConditionMarksReceivedConditionNotificationsAcc lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateConditionPending, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}, NeedsAttention: true}, nil) action := BorrowerActionAcceptCondition @@ -603,7 +772,7 @@ func TestHandleInvokeActionRejectCondition(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateConditionPending, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) action := BorrowerActionRejectCondition @@ -626,7 +795,7 @@ func TestHandleInvokeActionRejectConditionMarksReceivedConditionNotificationsRej lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REC1").Return(createLmsAdapterMockFail(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateConditionPending, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) action := BorrowerActionRejectCondition @@ -646,7 +815,7 @@ func TestHandleInvokeActionRejectConditionMarksReceivedConditionNotificationsRej func TestSendBorrowingRequestInvalidSymbol(t *testing.T) { mockPrRepo := new(MockPrRepo) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, nil, new(EmailSenderMock), nil, nil) var request iso18626.Request result := prAction.sendBorrowingRequest(appCtx, pr_db.PatronRequest{State: BorrowerStateValidated, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "x"}}, request) @@ -657,7 +826,7 @@ func TestSendBorrowingRequestInvalidSymbol(t *testing.T) { func TestSendBorrowingRequestZeroValueIllRequest(t *testing.T) { mockPrRepo := new(MockPrRepo) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, nil, new(EmailSenderMock), nil, nil) result := prAction.sendBorrowingRequest(appCtx, pr_db.PatronRequest{ ID: patronRequestId, @@ -684,7 +853,7 @@ func TestSendBorrowingRequestZeroValueIllRequest(t *testing.T) { func TestSendBorrowingRequestPreservesIllRequestFields(t *testing.T) { mockPrRepo := new(MockPrRepo) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, nil, new(EmailSenderMock), nil, nil) requestType := iso18626.TypeRequestTypeNew illRequest := iso18626.Request{ @@ -755,7 +924,7 @@ func TestShipReturnBorrowingRequestMissingSupplierSymbol(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsAdapter := lms.CreateLmsAdapterMockOK() lmsCreator.On("GetAdapter", pgtype.Text{Valid: true, String: "ISIL:REC1"}).Return(lmsAdapter, nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) illRequest := iso18626.Request{} @@ -772,7 +941,7 @@ func TestShipReturnBorrowingRequestMissingRequesterSymbol(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsAdapter := lms.CreateLmsAdapterMockOK() lmsCreator.On("GetAdapter", pgtype.Text{}).Return(lmsAdapter, nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) var request iso18626.Request @@ -788,7 +957,7 @@ func TestShipReturnBorrowingRequestInvalidSupplierSymbol(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsAdapter := lms.CreateLmsAdapterMockOK() lmsCreator.On("GetAdapter", pgtype.Text{Valid: true, String: "ISIL:REC1"}).Return(lmsAdapter, nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) var request iso18626.Request @@ -804,7 +973,7 @@ func TestShipReturnBorrowingRequestInvalidRequesterSymbol(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsAdapter := lms.CreateLmsAdapterMockOK() lmsCreator.On("GetAdapter", pgtype.Text{Valid: true, String: "x"}).Return(lmsAdapter, nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) var request iso18626.Request @@ -817,7 +986,7 @@ func TestShipReturnBorrowingRequestInvalidRequesterSymbol(t *testing.T) { func TestHandleInvokeLenderActionNoSupplierSymbol(t *testing.T) { mockPrRepo := new(MockPrRepo) lmsCreator := new(MockLmsCreator) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateNew, Side: SideLending}, nil) mockPrRepo.On("GetPatronRequestByIdForUpdate", patronRequestId).Return(pr_db.PatronRequest{RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:x"}, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}, IllRequest: illRequest}, nil) @@ -833,7 +1002,7 @@ func TestHandleInvokeLenderActionNoLms(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), assert.AnError) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateNew, Side: SideLending, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) mockPrRepo.On("GetPatronRequestByIdForUpdate", patronRequestId).Return(pr_db.PatronRequest{RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:x"}, State: BorrowerStateNew, Side: SideBorrowing, Tenant: pgtype.Text{Valid: true, String: "testlib"}, IllRequest: illRequest}, nil) @@ -851,7 +1020,7 @@ func TestHandleInvokeLenderActionValidate(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(createLmsAdapterMockLog(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} initialPR := pr_db.PatronRequest{ @@ -894,7 +1063,7 @@ func TestHandleInvokeLenderActionValidateAutoActionError(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(createLmsAdapterMockLog(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} initialPR := pr_db.PatronRequest{ @@ -940,7 +1109,7 @@ func TestHandleInvokeLenderActionValidateAutoActionCreateTaskError(t *testing.T) lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(createLmsAdapterMockLog(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} initialPR := pr_db.PatronRequest{ @@ -985,7 +1154,7 @@ func TestHandleInvokeLenderActionWillSupplyUseIllTitleWhenRequestItemEmptyOK(t * lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) lmsAdapter.On("RequestItem", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("1", "2", "", nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{BibliographicInfo: iso18626.BibliographicInfo{Title: "title1"}} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) action := LenderActionWillSupply @@ -1014,7 +1183,7 @@ func TestHandleInvokeLenderActionWillSupplyUseRequestItemTitleWhenAvailableOK(t lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) lmsAdapter.On("RequestItem", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("1", "2", "title2", nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{BibliographicInfo: iso18626.BibliographicInfo{Title: "title1"}} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) action := LenderActionWillSupply @@ -1046,7 +1215,7 @@ func TestHandleInvokeLenderActionRejectCancel(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ ID: patronRequestId, @@ -1081,7 +1250,7 @@ func TestHandleInvokeLenderActionWillSupplyNcipFailed(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(createLmsAdapterMockFail(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) @@ -1101,7 +1270,7 @@ func TestHandleInvokeLenderActionWillSupplySaveItemFailed(t *testing.T) { lmsAdapter.On("CancelRequestItem", "req-1", "").Return(nil) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{Header: iso18626.Header{RequestingAgencyRequestId: "req-1"}} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.saveItemFail = true @@ -1184,7 +1353,7 @@ func TestHandleInvokeLenderActionCannotSupply(t *testing.T) { lmsAdapter.On("CancelRequestItem", "req-1", "").Return(nil) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{Header: iso18626.Header{RequestingAgencyRequestId: "req-1"}} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "item-1"}}, nil) @@ -1212,7 +1381,7 @@ func TestHandleInvokeLenderActionCannotSupplyWithReason(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{}, nil) @@ -1247,7 +1416,7 @@ func TestHandleInvokeLenderActionCannotSupplyCancelRequestItemFailed(t *testing. lmsAdapter.On("CancelRequestItem", "req-1", "").Return(errors.New("cancel failed")) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{Header: iso18626.Header{RequestingAgencyRequestId: "req-1"}} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "item-1"}}, nil) @@ -1270,7 +1439,7 @@ func TestHandleInvokeLenderActionAddConditionOK(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) action := LenderActionAddCondition @@ -1309,7 +1478,7 @@ func TestHandleInvokeLenderActionAskRetryMissingItemId(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) action := LenderActionAskRetry @@ -1331,7 +1500,7 @@ func TestHandleInvokeLenderActionAskRetryCost(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) action := LenderActionAskRetry @@ -1353,7 +1522,7 @@ func TestHandleInvokeLenderActionAskRetryMissingReasonRetry(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) action := LenderActionAskRetry @@ -1377,7 +1546,7 @@ func TestHandleInvokeLenderActionAskRetryFull(t *testing.T) { lmsAdapter.On("CancelRequestItem", "req-1", "").Return(nil) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{Header: iso18626.Header{RequestingAgencyRequestId: "req-1"}} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "item-1"}}, nil) @@ -1414,7 +1583,7 @@ func TestHandleInvokeLenderActionAskRetryCancelRequestItemFailed(t *testing.T) { lmsAdapter.On("CancelRequestItem", "req-1", "").Return(errors.New("cancel failed")) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{Header: iso18626.Header{RequestingAgencyRequestId: "req-1"}} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "item-1"}}, nil) @@ -1444,7 +1613,7 @@ func TestHandleInvokeBorrowerActionAcceptRetryAutoActionCreateTaskError(t *testi lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REQ1").Return(createLmsAdapterMockLog(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(MockIllRepo), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(MockIllRepo), mockEventBus, mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} initialPR := pr_db.PatronRequest{ ID: patronRequestId, @@ -1485,7 +1654,7 @@ func TestHandleInvokeLenderActionAddConditionMissingConditionAndCost(t *testing. lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) action := LenderActionAddCondition @@ -1509,7 +1678,7 @@ func TestHandleInvokeLenderActionAddConditionWithCurrency(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) action := LenderActionAddCondition @@ -1559,7 +1728,7 @@ func TestHandleInvokeLenderActionAddConditionMissingCurrency(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) action := LenderActionAddCondition @@ -1584,7 +1753,7 @@ func TestHandleInvokeLenderActionAddConditionTypeCost(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{IllRequest: illRequest, State: LenderStateValidated, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) action := LenderActionAddCondition @@ -1613,7 +1782,7 @@ func TestHandleInvokeLenderActionShipOK(t *testing.T) { lmsAdapter.On("CheckOutItem", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", nil) // no title lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateWillSupply, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{ @@ -1660,7 +1829,7 @@ func TestHandleInvokeLenderActionShipNewTitleOK(t *testing.T) { lmsAdapter.On("CheckOutItem", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("new title", nil) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateWillSupply, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{ @@ -1709,7 +1878,7 @@ func TestHandleInvokeLenderActionShipNewTitleFail(t *testing.T) { lmsAdapter.On("CheckOutItem", mock.Anything, "1234", mock.Anything, mock.Anything).Return("new title", nil) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateWillSupply, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -1731,7 +1900,7 @@ func TestHandleInvokeLenderActionShipGetItemsByIdFail(t *testing.T) { lmsAdapter := new(mockLmsAdapter) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateWillSupply, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{}, assert.AnError) @@ -1751,7 +1920,7 @@ func TestHandleInvokeLenderActionShipGetItemsByIdEmpty(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateWillSupply, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{}, nil) @@ -1771,7 +1940,7 @@ func TestHandleInvokeLenderActionShipLmsFailed(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(createLmsAdapterMockFail(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateWillSupply, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -1790,7 +1959,7 @@ func TestHandleInvokeLenderActionMarkReceivedOK(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateShippedReturn, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -1807,7 +1976,7 @@ func TestHandleInvokeLenderActionMarkReceivedNoItems(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateShippedReturn, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{}, nil) @@ -1824,7 +1993,7 @@ func TestHandleInvokeLenderActionMarkReceivedLmsFailed(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(createLmsAdapterMockFail(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateShippedReturn, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -1843,7 +2012,7 @@ func TestHandleInvokeLenderActionAcceptCancel(t *testing.T) { lmsAdapter.On("CancelRequestItem", "req-1", "").Return(nil) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{Header: iso18626.Header{RequestingAgencyRequestId: "req-1"}} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateCancelRequested, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1"), RequesterReqID: getDbText("req-1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "item-1"}}, nil) @@ -1871,7 +2040,7 @@ func TestHandleInvokeLenderActionAcceptCancelCancelRequestItemFailed(t *testing. lmsAdapter.On("CancelRequestItem", "req-1", "").Return(errors.New("cancel failed")) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lmsAdapter, nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{Header: iso18626.Header{RequestingAgencyRequestId: "req-1"}} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateCancelRequested, Side: SideLending, SupplierSymbol: getDbText("ISIL:SUP1"), RequesterSymbol: getDbText("ISIL:REQ1"), RequesterReqID: getDbText("req-1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "item-1"}}, nil) @@ -1893,7 +2062,7 @@ func TestHandleInvokeLenderActionAcceptCancelMissingRequesterSymbol(t *testing.T lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:SUP1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateCancelRequested, Side: SideLending, RequesterSymbol: pgtype.Text{Valid: false, String: ""}, SupplierSymbol: getDbText("ISIL:SUP1")}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{}, nil) @@ -1995,7 +2164,7 @@ func TestGetDirectoryEmailData(t *testing.T) { t.Run(tc.name, func(t *testing.T) { illRepoMock := new(IllRepoMock) illRepoMock.On("GetPeerBySymbol", tc.symbol).Return(tc.peer, tc.repoErr) - prAction := CreatePatronRequestActionService(*new(pr_db.PrRepo), illRepoMock, *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(*new(pr_db.PrRepo), illRepoMock, *new(events.EventBus), new(handler.Iso18626Handler), nil, new(EmailSenderMock), nil, nil) gotFrom, gotTo, err := prAction.getDirectoryEmailData(appCtx, tc.symbol, tc.toNeeded) @@ -2140,6 +2309,8 @@ func newActionServiceWithEmail(prRepo *MockPrRepo, emailSvc *EmailSenderMock) *P new(handler.Iso18626Handler), nil, emailSvc, + nil, + nil, ) } @@ -2482,6 +2653,8 @@ func TestSendEmailNotification(t *testing.T) { new(handler.Iso18626Handler), nil, emailSvc, + nil, + nil, ) res := svc.sendEmailNotification(appCtx, tc.pr, tc.params, tc.symbol) @@ -2516,7 +2689,7 @@ func TestHandleInvokeActionBorrowerActionSendNotification(t *testing.T) { FromEmail: ptr("from@mail.com"), }, }, nil) - prAction := CreatePatronRequestActionService(mockPrRepo, illMock, *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, emailMock) + prAction := CreatePatronRequestActionService(mockPrRepo, illMock, *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, emailMock, nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateReceived, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -2541,7 +2714,7 @@ func TestHandleInvokeActionBorrowerActionSendNotification_emailServiceNotReady(t emailMock := new(EmailSenderMock) emailMock.On("IsReadyToSend").Return(false) illMock := new(IllRepoMock) - prAction := CreatePatronRequestActionService(mockPrRepo, illMock, *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, emailMock) + prAction := CreatePatronRequestActionService(mockPrRepo, illMock, *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, emailMock, nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: BorrowerStateReceived, Side: SideBorrowing, RequesterSymbol: pgtype.Text{Valid: true, String: "ISIL:REC1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -2572,7 +2745,7 @@ func TestHandleInvokeActionLenderActionSendNotification(t *testing.T) { FromEmail: ptr("from@mail.com"), }, }, nil) - prAction := CreatePatronRequestActionService(mockPrRepo, illMock, *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, emailMock) + prAction := CreatePatronRequestActionService(mockPrRepo, illMock, *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, emailMock, nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateNew, Side: SideLending, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -2602,7 +2775,7 @@ func TestHandleInvokeActionLenderActionSendNotification_emailServiceNotReady(t * FromEmail: ptr("from@mail.com"), }, }, nil) - prAction := CreatePatronRequestActionService(mockPrRepo, illMock, *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, emailMock) + prAction := CreatePatronRequestActionService(mockPrRepo, illMock, *new(events.EventBus), new(handler.Iso18626Handler), lmsCreator, emailMock, nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ID: patronRequestId, IllRequest: illRequest, State: LenderStateNew, Side: SideLending, SupplierSymbol: pgtype.Text{Valid: true, String: "ISIL:SUP1"}}, nil) mockPrRepo.On("GetItemsByPrId", patronRequestId).Return([]pr_db.Item{{Barcode: "1234"}}, nil) @@ -2628,7 +2801,7 @@ func TestHandleInvokeBorrowerActionCancelLocalSupply(t *testing.T) { mockEventBus := new(MockEventBus) emailMock := new(EmailSenderMock) emailMock.On("IsReadyToSend").Return(false) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, emailMock) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), mockEventBus, mockIso18626Handler, lmsCreator, emailMock, nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Once().Return(pr_db.PatronRequest{ ID: patronRequestId, @@ -2670,7 +2843,7 @@ func TestHandleInvokeBorrowerActionCannotSupplyLocally(t *testing.T) { lmsCreator := new(MockLmsCreator) lmsCreator.On("GetAdapter", "ISIL:REQ1").Return(lms.CreateLmsAdapterMockOK(), nil) mockIso18626Handler := new(MockIso18626Handler) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) illRequest := iso18626.Request{} mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr_db.PatronRequest{ ID: patronRequestId, @@ -2744,7 +2917,7 @@ func TestHandleInvokeBorrowerActionFillLocally(t *testing.T) { } lmsCreator.On("GetAdapter", "ISIL:REQ1").Return(lmsAdapter, nil) mockPrRepo.On("GetPatronRequestById", patronRequestId).Return(pr, nil) - prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock)) + prAction := CreatePatronRequestActionService(mockPrRepo, new(IllRepoMock), *new(events.EventBus), mockIso18626Handler, lmsCreator, new(EmailSenderMock), nil, nil) action := BorrowerActionFillLocally status, resultData := prAction.handleInvokeAction(appCtx, events.Event{ @@ -2770,6 +2943,159 @@ func TestHandleInvokeBorrowerActionFillLocally(t *testing.T) { } } +func TestUpdateMetadataBorrowingRequestAddsDecisionDetails(t *testing.T) { + mode := dirapi.Auto + configPeerID := uuid.MustParse("00000000-0000-0000-0000-000000000456") + illRepo := new(IllRepoMock) + illRepo.On("GetCachedPeersBySymbols", []string{"ISIL:x"}, mock.Anything).Return([]ill_db.Peer{ + { + Vendor: string(dirapi.CrossLink), + CustomData: dirapi.Entry{ + Id: &configPeerID, + CatalogConfig: &dirapi.CatalogConfig{ + MetadataUpdateMode: &mode, + Sru: &dirapi.SruConfig{Address: "http://sru.example.test"}, + }, + }, + }, + }, "", nil) + svc := &PatronRequestActionService{ + illRepo: illRepo, + lookupAdapterFactory: lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Metadata: catalog.Metadata{ + Identifier: "catalog-record-456", + Title: "Canonical title", + Author: "Example Author", + Isbn: "9781234567890", + }}), + } + pr := pr_db.PatronRequest{ + ID: patronRequestId, + RequesterSymbol: pgtype.Text{String: "ISIL:x", Valid: true}, + } + illRequest := iso18626.Request{ + BibliographicInfo: iso18626.BibliographicInfo{ + Title: "Original title", + SupplierUniqueRecordId: "record-123", + BibliographicItemId: []iso18626.BibliographicItemId{ + { + BibliographicItemIdentifier: "9781234567890", + BibliographicItemIdentifierCode: iso18626.TypeSchemeValuePair{Text: "ISBN"}, + }, + }, + }, + ServiceInfo: &iso18626.ServiceInfo{ServiceType: iso18626.TypeServiceTypeLoan}, + } + + res := svc.updateMetadataBorrowingRequest(appCtx, pr, nil, illRequest) + + assert.Equal(t, events.EventStatusSuccess, res.status) + if assert.NotNil(t, res.result) { + details, ok := res.result.CustomData["decisionDetails"].([]actionDecisionDetailMetadataUpdate) + if assert.True(t, ok) && assert.Len(t, details, 1) { + assert.Equal(t, actionDecisionDetailMetadataUpdate{ + Type: "metadata-update", + Outcome: "updated", + Mode: "auto", + EffectiveMode: "replace", + LookupParams: catalog.LookupParams{ + Identifier: "record-123", + Isbn: "9781234567890", + Issn: "", + Title: "Original title", + ServiceType: "Loan", + }, + Source: actionDecisionDetailMetadataSource{ + AdapterType: "sru", + ConfigurationPeerID: "00000000-0000-0000-0000-000000000456", + }, + Changes: []actionDecisionDetailMetadataChange{ + {Field: "title", PreviousValue: "Original title", NewValue: "Canonical title"}, + {Field: "author", PreviousValue: "", NewValue: "Example Author"}, + {Field: "supplierUniqueRecordId", PreviousValue: "record-123", NewValue: "catalog-record-456"}, + }, + }, details[0]) + } + } + illRepo.AssertExpectations(t) +} + +func TestUpdateMetadataBorrowingRequestNegativeCases(t *testing.T) { + pr := pr_db.PatronRequest{ + ID: patronRequestId, + RequesterSymbol: pgtype.Text{String: "ISIL:x", Valid: true}, + } + + tests := []struct { + name string + setup func(*IllRepoMock) *PatronRequestActionService + wantMsg string + wantErrMsg string + }{ + { + name: "requester peer lookup fails", + setup: func(illRepo *IllRepoMock) *PatronRequestActionService { + illRepo.On("GetCachedPeersBySymbols", []string{"ISIL:x"}, mock.Anything).Return([]ill_db.Peer{}, "", errors.New("peer lookup failed")) + return &PatronRequestActionService{ + illRepo: illRepo, + } + }, + wantMsg: "failed to get requester peer", + wantErrMsg: "peer lookup failed", + }, + { + name: "requester peer not found", + setup: func(illRepo *IllRepoMock) *PatronRequestActionService { + illRepo.On("GetCachedPeersBySymbols", []string{"ISIL:x"}, mock.Anything).Return([]ill_db.Peer{}, "", nil) + return &PatronRequestActionService{ + illRepo: illRepo, + } + }, + wantMsg: "failed to get requester peer", + wantErrMsg: "no peer found for requester symbol \"ISIL:x\"", + }, + { + name: "metadata update fails for CrossLink requester peer", + setup: func(illRepo *IllRepoMock) *PatronRequestActionService { + mode := dirapi.Merge + illRepo.On("GetCachedPeersBySymbols", []string{"ISIL:x"}, mock.Anything).Return([]ill_db.Peer{ + { + Vendor: string(dirapi.CrossLink), + CustomData: dirapi.Entry{Name: "requester", CatalogConfig: &dirapi.CatalogConfig{MetadataUpdateMode: &mode}}, + }, + }, "", nil) + return &PatronRequestActionService{ + illRepo: illRepo, + lookupAdapterFactory: lookupFactoryWithAdapter(&catalog.MockLookupAdapter{Err: errors.New("lookup failed")}), + } + }, + wantMsg: "metadata update failed", + wantErrMsg: "failed to perform lookup for patron request: lookup failed", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + illRepo := new(IllRepoMock) + svc := tt.setup(illRepo) + + res := svc.updateMetadataBorrowingRequest(appCtx, pr, nil, iso18626.Request{}) + + assert.Equal(t, events.EventStatusError, res.status) + assert.Equal(t, pr, res.pr) + if assert.NotNil(t, res.result) { + if assert.NotNil(t, res.result.EventError) { + assert.Equal(t, tt.wantMsg, res.result.EventError.Message) + assert.Equal(t, tt.wantErrMsg, res.result.EventError.Cause) + } + if assert.NotNil(t, res.result.ActionResult) { + assert.Equal(t, ActionOutcomeFailure, res.result.ActionResult.Outcome) + } + } + illRepo.AssertExpectations(t) + }) + } +} + type MockEventBus struct { mock.Mock events.EventBus @@ -3313,3 +3639,42 @@ func (i *IllRepoMock) GetPeerBySymbol(ctx common.ExtendedContext, symbol string) args := i.Called(symbol) return args.Get(0).(ill_db.Peer), args.Error(1) } + +func (i *IllRepoMock) GetCachedPeersBySymbols(ctx common.ExtendedContext, symbols []string, directoryAdapter adapter.DirectoryLookupAdapter) ([]ill_db.Peer, string, error) { + for _, call := range i.ExpectedCalls { + if call.Method == "GetCachedPeersBySymbols" { + args := i.Called(symbols, directoryAdapter) + return args.Get(0).([]ill_db.Peer), args.String(1), args.Error(2) + } + } + return []ill_db.Peer{{Vendor: "other"}}, "", nil +} + +// --- metadataUpdate tests --- + +// mockLookupCreator controls what GetAdapter returns when no globalLookupAdapter is pre-set. +type mockLookupCreator struct { + adapter catalog.LookupAdapter + err error +} + +func (m *mockLookupCreator) GetAdapter(peer ill_db.Peer) (catalog.LookupAdapter, error) { + return m.adapter, m.err +} + +// lookupFactoryWithAdapter creates a LookupAdapterFactory that returns the given adapter directly. +func lookupFactoryWithAdapter(adapter catalog.LookupAdapter) *service.LookupAdapterFactory { + return service.NewLookupAdapterFactory(nil, nil, "", adapter, nil) +} + +// peerWithMetadataMode builds a Peer whose CustomData carries the given MetadataUpdateMode. +// Pass nil to leave CatalogConfig absent entirely. +func peerWithMetadataMode(mode *dirapi.MetadataUpdateMode) ill_db.Peer { + var cc *dirapi.CatalogConfig + if mode != nil { + cc = &dirapi.CatalogConfig{MetadataUpdateMode: mode} + } + return ill_db.Peer{ + CustomData: dirapi.Entry{Name: "test-peer", CatalogConfig: cc}, + } +} diff --git a/broker/patron_request/service/statemodel_capabilities.go b/broker/patron_request/service/statemodel_capabilities.go index 50b42bbd..65760a1f 100644 --- a/broker/patron_request/service/statemodel_capabilities.go +++ b/broker/patron_request/service/statemodel_capabilities.go @@ -25,6 +25,7 @@ const ( const ( BorrowerStateNew pr_db.PatronRequestState = "NEW" BorrowerStateValidated pr_db.PatronRequestState = "VALIDATED" + BorrowerStateMetadataUpdated pr_db.PatronRequestState = "METADATA_UPDATED" BorrowerStateNeedsReview pr_db.PatronRequestState = "NEEDS_REVIEW" BorrowerStateLocalSupply pr_db.PatronRequestState = "LOCAL_SUPPLY" BorrowerStateSent pr_db.PatronRequestState = "SENT" @@ -63,6 +64,7 @@ const ( const ( BorrowerActionValidate pr_db.PatronRequestAction = "validate" + BorrowerActionUpdateMetadata pr_db.PatronRequestAction = "update-metadata" BorrowerActionSendRequest pr_db.PatronRequestAction = "send-request" BorrowerActionCancelRequest pr_db.PatronRequestAction = "cancel-request" BorrowerActionAcceptCondition pr_db.PatronRequestAction = "accept-condition" @@ -116,6 +118,7 @@ func requesterBuiltInStates() []string { return uniqueSorted([]string{ string(BorrowerStateNew), string(BorrowerStateValidated), + string(BorrowerStateMetadataUpdated), string(BorrowerStateNeedsReview), string(BorrowerStateLocalSupply), string(BorrowerStateSent), @@ -164,6 +167,10 @@ func requesterBuiltInActions() []proapi.ActionCapability { Name: string(BorrowerActionValidate), Parameters: []string{}, }, + { + Name: string(BorrowerActionUpdateMetadata), + Parameters: []string{}, + }, { Name: string(BorrowerActionSendRequest), Parameters: []string{}, diff --git a/broker/test/patron_request/api/api-handler_test.go b/broker/test/patron_request/api/api-handler_test.go index aaf43039..c04f3dd3 100644 --- a/broker/test/patron_request/api/api-handler_test.go +++ b/broker/test/patron_request/api/api-handler_test.go @@ -416,7 +416,7 @@ func TestNeedsReviewAndUpdate(t *testing.T) { }), "timed out waiting for NEEDS_REVIEW state") assert.Equal(t, string(prservice.BorrowerStateNeedsReview), foundPr.State) if assert.NotNil(t, foundPr.LastAction) { - assert.Equal(t, string(prservice.BorrowerActionValidate), *foundPr.LastAction) + assert.Equal(t, string(prservice.BorrowerActionUpdateMetadata), *foundPr.LastAction) } if assert.NotNil(t, foundPr.LastActionOutcome) { assert.Equal(t, prservice.ActionOutcomeReview, *foundPr.LastActionOutcome) diff --git a/misc/state-models.yaml b/misc/state-models.yaml index dbf9d96d..fea6523a 100644 --- a/misc/state-models.yaml +++ b/misc/state-models.yaml @@ -19,13 +19,25 @@ stateModels: desc: "Validate the request (e.g. check patron via NCIP LookupUser, if enabled)" transitions: success: VALIDATED - review: NEEDS_REVIEW trigger: auto - name: VALIDATED display: Validated desc: Patron Request is valid side: REQUESTER + primaryAction: update-metadata + actions: + - name: update-metadata + desc: Update the request metadata + transitions: + success: METADATA_UPDATED + review: NEEDS_REVIEW + trigger: auto + + - name: METADATA_UPDATED + display: Metadata Updated + desc: Patron Request metadata has been updated + side: REQUESTER primaryAction: send-request actions: - name: send-request From a5eaefd45b838965caef3f4e4aa3d7c523df316a Mon Sep 17 00:00:00 2001 From: Janis Saldabols Date: Fri, 31 Jul 2026 14:40:05 +0300 Subject: [PATCH 2/2] ILLDEV-465 Fix parameter issue --- broker/patron_request/service/action.go | 6 ++++-- broker/patron_request/service/action_test.go | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/broker/patron_request/service/action.go b/broker/patron_request/service/action.go index d289ac82..f92cda59 100644 --- a/broker/patron_request/service/action.go +++ b/broker/patron_request/service/action.go @@ -379,7 +379,7 @@ func (a *PatronRequestActionService) handleBorrowingAction(ctx common.ExtendedCo case BorrowerActionValidate: return a.validateBorrowingRequest(ctx, pr, lmsAdapter, illRequest) case BorrowerActionUpdateMetadata: - return a.updateMetadataBorrowingRequest(ctx, pr, lmsAdapter, illRequest) + return a.updateMetadataBorrowingRequest(ctx, pr, illRequest) case BorrowerActionSendRequest: return a.sendBorrowingRequest(ctx, pr, illRequest) case BorrowerActionReceive: @@ -489,7 +489,7 @@ func (a *PatronRequestActionService) validateBorrowingRequest(ctx common.Extende return actionExecutionResult{status: events.EventStatusSuccess, pr: pr} } -func (a *PatronRequestActionService) updateMetadataBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) actionExecutionResult { +func (a *PatronRequestActionService) updateMetadataBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, illRequest iso18626.Request) actionExecutionResult { peers, _, peerErr := a.illRepo.GetCachedPeersBySymbols(ctx, []string{pr.RequesterSymbol.String}, a.directoryLookupAdapter) if peerErr != nil { status, result := logActionErrorAndReturnResult(ctx, "failed to get requester peer", peerErr) @@ -515,6 +515,8 @@ func (a *PatronRequestActionService) updateMetadataBorrowingRequest(ctx common.E } } + pr.IllRequest = illRequest + res := actionExecutionResult{status: events.EventStatusSuccess, pr: pr} if len(decisionDetails) > 0 || illRequest.BibliographicInfo.SupplierUniqueRecordId == "" { res.result = &events.EventResult{} diff --git a/broker/patron_request/service/action_test.go b/broker/patron_request/service/action_test.go index 4fbf8948..ff3481ed 100644 --- a/broker/patron_request/service/action_test.go +++ b/broker/patron_request/service/action_test.go @@ -2986,7 +2986,7 @@ func TestUpdateMetadataBorrowingRequestAddsDecisionDetails(t *testing.T) { ServiceInfo: &iso18626.ServiceInfo{ServiceType: iso18626.TypeServiceTypeLoan}, } - res := svc.updateMetadataBorrowingRequest(appCtx, pr, nil, illRequest) + res := svc.updateMetadataBorrowingRequest(appCtx, pr, illRequest) assert.Equal(t, events.EventStatusSuccess, res.status) if assert.NotNil(t, res.result) { @@ -3078,7 +3078,7 @@ func TestUpdateMetadataBorrowingRequestNegativeCases(t *testing.T) { illRepo := new(IllRepoMock) svc := tt.setup(illRepo) - res := svc.updateMetadataBorrowingRequest(appCtx, pr, nil, iso18626.Request{}) + res := svc.updateMetadataBorrowingRequest(appCtx, pr, iso18626.Request{}) assert.Equal(t, events.EventStatusError, res.status) assert.Equal(t, pr, res.pr)