-
Notifications
You must be signed in to change notification settings - Fork 0
CROSSLINK-191 NCIP supplier side #365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request implements NCIP support for supplier-side operations in the Crosslink ILL brokering service. The changes enable supplier libraries to validate their institutional patron accounts and perform LMS operations via NCIP when acting as lenders.
Key changes:
- Refactored LmsCreator interface to accept string symbols instead of pgtype.Text for cleaner API design
- Added InstitutionalPatron() method to LmsAdapter interface to support supplier-side validation
- Consolidated ILL request parsing and LMS adapter creation to reduce code duplication in action handlers
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| broker/patron_request/service/action.go | Refactored action handling to parse ILL requests and create LMS adapters early; added supplier-side NCIP calls for validate, will-supply, ship, and mark-received actions |
| broker/patron_request/service/action_test.go | Updated tests for new method signatures; added edge case tests for missing LMS creator, invalid ILL requests, and missing symbols; removed duplicate/redundant tests |
| broker/lms/lms_creator.go | Changed GetAdapter interface signature from pgtype.Text to string |
| broker/lms/lms_creator_impl.go | Simplified implementation by removing symbol validation (now handled by callers) |
| broker/lms/lms_creator_test.go | Updated tests to use string symbols instead of pgtype.Text |
| broker/lms/lms_adapter.go | Added InstitutionalPatron() method to interface |
| broker/lms/lms_adapter_ncip.go | Implemented InstitutionalPatron() with configurable institutional_patron field |
| broker/lms/lms_adapter_manual.go | Added stub InstitutionalPatron() implementation returning empty string |
| go.work.sum | Transitive dependency updates from Go workspace |
| var illRequest iso18626.Request | ||
| err = json.Unmarshal(pr.IllRequest, &illRequest) | ||
| if err != nil { | ||
| return events.LogErrorAndReturnResult(ctx, "failed to parse ILL request", err) | ||
| } |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ILL request parsing at lines 110-114 happens unconditionally for all actions, but not all actions require the ILL request data. Actions like BorrowerActionCancelRequest, BorrowerActionAcceptCondition, BorrowerActionRejectCondition, LenderActionCannotSupply, LenderActionAddCondition, and LenderActionMarkCancelled don't use the illRequest parameter. This means these actions will fail if pr.IllRequest contains invalid JSON, even though they don't need it. Consider moving the ILL request parsing to only happen when needed, or ensure pr.IllRequest is always valid JSON even for actions that don't require it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Premature optimization. The illRequest bytes are stored in the DB anyway, so this makes no difference for the user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 10 comments.
| return a.validateBorrowingRequest(ctx, pr, lmsAdapter) | ||
| case BorrowerActionSendRequest: | ||
| return a.sendBorrowingRequest(ctx, pr) | ||
| return a.sendBorrowingRequest(ctx, pr, illRequest) |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sendBorrowingRequest action is not receiving the lmsAdapter parameter even though it's retrieved in handleBorrowingAction. While this action may not currently need it, the inconsistency in parameter passing could lead to confusion. Consider either passing the adapter to all actions or restructuring to only retrieve it when needed.
| // TODO set these values properly | ||
| itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId | ||
| requestId := "" | ||
| borrowerBarcode := "" | ||
| externalReferenceValue := "" |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using empty strings as placeholders for required NCIP parameters (requestId, borrowerBarcode, externalReferenceValue) may cause the LMS CheckOutItem operation to fail or behave unexpectedly. These TODO comments indicate incomplete implementation that should be addressed before merging.
| // TODO set these values properly | |
| itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId | |
| requestId := "" | |
| borrowerBarcode := "" | |
| externalReferenceValue := "" | |
| itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId | |
| // Use non-empty values for required LMS parameters; fall back to itemId as a stable surrogate. | |
| requestId := itemId | |
| borrowerBarcode := itemId | |
| externalReferenceValue := itemId |
| // TODO set these values properly | ||
| itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using an empty itemId placeholder may cause the LMS CheckInItem operation to fail. This TODO comment indicates incomplete implementation that should be addressed before merging.
| // TODO set these values properly | |
| itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId | |
| itemId := strings.TrimSpace(illRequest.BibliographicInfo.SupplierUniqueRecordId) | |
| if itemId == "" { | |
| return events.LogErrorAndReturnResult(ctx, "LMS CheckInItem failed", errors.New("missing supplier unique record id for item check-in")) | |
| } |
| func TestHandleInvokeActionNoLms(t *testing.T) { | ||
| mockPrRepo := new(MockPrRepo) | ||
| prAction := CreatePatronRequestActionService(mockPrRepo, *new(events.EventBus), new(handler.Iso18626Handler), nil) | ||
| 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"}}, nil) | ||
|
|
||
| status, resultData := prAction.handleInvokeAction(appCtx, events.Event{PatronRequestID: patronRequestId, EventData: events.EventData{CommonEventData: events.CommonEventData{Action: &actionValidate}}}) | ||
|
|
||
| assert.Equal(t, events.EventStatusError, status) | ||
| assert.Equal(t, "LMS creator not configured", resultData.EventError.Message) | ||
| } |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test expects the error "LMS creator not configured" but doesn't provide an IllRequest field in the PatronRequest. According to the new code logic, it would fail earlier at line 107-114 with "failed to parse ILL request" before reaching the LMS creator check. The test should include a valid IllRequest field.
| if !a.actionMappingService.GetActionMapping(pr).IsActionAvailable(pr, action) { | ||
| return events.LogErrorAndReturnResult(ctx, "state "+string(pr.State)+" does not support action "+string(action), errors.New("invalid action")) | ||
| } | ||
| if a.lmsCreator == nil { |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The LMS creator validation happens for all actions and sides, but some actions like cancelBorrowingRequest, acceptConditionBorrowingRequest, and rejectConditionBorrowingRequest don't use the LMS adapter. This check should be moved to only apply when LMS operations are actually needed, or those actions should be handled before this validation.
| if a.lmsCreator == nil { | |
| requiresLMS := true | |
| switch action { | |
| case "cancelBorrowingRequest", "acceptConditionBorrowingRequest", "rejectConditionBorrowingRequest": | |
| requiresLMS = false | |
| } | |
| if requiresLMS && a.lmsCreator == nil { |
| lmsAdapter, err := a.lmsCreator.GetAdapter(ctx, pr.RequesterSymbol.String) | ||
| if err != nil { | ||
| return events.LogErrorAndReturnResult(ctx, "failed to create LMS adapter", err) | ||
| } |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The LMS adapter is retrieved for all borrowing actions, but BorrowerActionSendRequest doesn't use it. The lmsAdapter parameter should only be passed to actions that actually need it, or the GetAdapter call should be moved to the individual action handlers that require it.
| func (a *PatronRequestActionService) willSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { | ||
| // TODO set these values properly | ||
| itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId | ||
| requestId := "" | ||
| borrowerBarCode := "" | ||
| pickupLocation := "" | ||
| itemLocation := "" | ||
| err := lmsAdapter.RequestItem(requestId, itemId, borrowerBarCode, pickupLocation, itemLocation) | ||
| if err != nil { |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using empty strings as placeholders for required NCIP parameters (requestId, borrowerBarCode, pickupLocation, itemLocation) may cause the LMS RequestItem operation to fail or behave unexpectedly. These TODO comments indicate incomplete implementation that should be addressed before merging.
| func (a *PatronRequestActionService) willSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { | |
| // TODO set these values properly | |
| itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId | |
| requestId := "" | |
| borrowerBarCode := "" | |
| pickupLocation := "" | |
| itemLocation := "" | |
| err := lmsAdapter.RequestItem(requestId, itemId, borrowerBarCode, pickupLocation, itemLocation) | |
| if err != nil { | |
| type lmsRequestItemParams struct { | |
| RequestID string | |
| ItemID string | |
| BorrowerBarcode string | |
| PickupLocation string | |
| ItemLocation string | |
| } | |
| // buildLmsRequestItemParams constructs the parameters required for the LMS | |
| // RequestItem operation. It returns an error if the parameters cannot be | |
| // safely derived yet. | |
| func buildLmsRequestItemParams(pr pr_db.PatronRequest, illRequest iso18626.Request) (lmsRequestItemParams, error) { | |
| params := lmsRequestItemParams{ | |
| ItemID: illRequest.BibliographicInfo.SupplierUniqueRecordId, | |
| } | |
| if params.ItemID == "" { | |
| return params, errors.New("missing item ID for LMS RequestItem") | |
| } | |
| // TODO: Derive RequestID, BorrowerBarcode, PickupLocation and ItemLocation | |
| // from the patron request and/or ISO18626 request once the data mapping | |
| // is defined. For now, signal that this is not fully implemented instead | |
| // of calling the LMS with placeholder values. | |
| return params, errors.New("building LMS RequestItem parameters not fully implemented") | |
| } | |
| func (a *PatronRequestActionService) willSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { | |
| params, err := buildLmsRequestItemParams(pr, illRequest) | |
| if err != nil { | |
| return events.LogErrorAndReturnResult(ctx, "LMS RequestItem parameters invalid", err) | |
| } | |
| err = lmsAdapter.RequestItem(params.RequestID, params.ItemID, params.BorrowerBarcode, params.PickupLocation, params.ItemLocation) | |
| if err != nil { |
| institutionalPatron := lms.InstitutionalPatron() | ||
| _, err := lms.LookupUser(institutionalPatron) | ||
| if err != nil { | ||
| return events.LogErrorAndReturnResult(ctx, "LMS LookupUser failed", err) | ||
| } |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If InstitutionalPatron returns an empty string (as it does in LmsAdapterManual), this will call LookupUser with an empty string which may not be the intended behavior. Consider checking if institutionalPatron is empty and handling that case explicitly, or documenting that an empty string is valid for manual adapters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
https://index-data.atlassian.net/browse/CROSSLINK-191