From 47c090c3c17b8ca21b117b8fecd2bf0e20779320 Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Wed, 28 Jan 2026 17:26:32 -0500 Subject: [PATCH 01/11] Add schema/path for returnables statemodel. Add method to return as json --- broker/go.mod | 6 +- broker/go.sum | 10 ++ broker/patron_request/api/api-handler.go | 49 ++++++++ broker/patron_request/oapi/open-api.yaml | 117 ++++++++++++++++++ broker/patron_request/service/action.go | 58 +++++---- .../patron_request/service/action_mapping.go | 70 +++++++---- go.work | 2 +- httpclient/go.mod | 4 +- httpclient/go.sum | 6 +- illmock/go.mod | 13 +- illmock/go.sum | 17 +-- ncip/go.mod | 7 +- ncip/go.sum | 14 +-- 13 files changed, 278 insertions(+), 95 deletions(-) diff --git a/broker/go.mod b/broker/go.mod index 7aac6ce6..11099ddd 100644 --- a/broker/go.mod +++ b/broker/go.mod @@ -6,8 +6,6 @@ require ( github.com/indexdata/crosslink/httpclient v0.0.0 github.com/indexdata/crosslink/illmock v0.0.0 github.com/indexdata/crosslink/iso18626 v0.0.0 - github.com/indexdata/crosslink/marcxml v0.0.0 - github.com/indexdata/crosslink/sru v0.0.0 ) replace ( @@ -29,6 +27,7 @@ require ( github.com/jackc/pgx/v5 v5.8.0 github.com/lib/pq v1.10.9 github.com/oapi-codegen/oapi-codegen/v2 v2.5.1 + github.com/oapi-codegen/runtime v1.1.2 github.com/stretchr/testify v1.11.1 github.com/testcontainers/testcontainers-go v0.40.0 github.com/testcontainers/testcontainers-go/modules/postgres v0.40.0 @@ -38,6 +37,7 @@ require ( dario.cat/mergo v1.0.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect @@ -59,6 +59,8 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect github.com/go-openapi/swag v0.23.1 // indirect + github.com/indexdata/crosslink v0.0.0-20260121111122-3096ccb3a8ea // indirect + github.com/indexdata/crosslink/marcxml v0.0.0-20260122210555-37edcda908a8 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect diff --git a/broker/go.sum b/broker/go.sum index 95631f17..d0ce281f 100644 --- a/broker/go.sum +++ b/broker/go.sum @@ -6,6 +6,10 @@ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEK github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -74,6 +78,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DW github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030 h1:RP9TZZTGoBo2Jx04FEWaRmNhCX/Fk8VR04tX0FMRzik= github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030/go.mod h1:zmSHcE8JyK94EWZrV7VyjLr2QfRoj+EeEOttl9wm64U= +github.com/indexdata/crosslink v0.0.0-20260121111122-3096ccb3a8ea h1:xYeIBOOcz3gdVSCqGt2llJUQ9KXvXSSXtc8vhFVZxd4= +github.com/indexdata/crosslink v0.0.0-20260121111122-3096ccb3a8ea/go.mod h1:r0ibawAkcS/t9mThAE9T2riuuU+jd9B7qUKOkBDIoGk= github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72 h1:/ssOlpKK1EOeWMCOUrxCOCZP+0Z1LD58Xg1nrU01TAQ= github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72/go.mod h1:0sW6Szxv8GNU3LBtK6mgBKDEUnlovPfghiG9xi+i0R8= github.com/indexdata/xsd2goxsl v1.3.0 h1:LZGBORHnO6olHBtvc6hQEefymdRuiM77FgtW4pCek7g= @@ -90,6 +96,7 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -133,6 +140,8 @@ github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/oapi-codegen/oapi-codegen/v2 v2.5.1 h1:5vHNY1uuPBRBWqB2Dp0G7YB03phxLQZupZTIZaeorjc= github.com/oapi-codegen/oapi-codegen/v2 v2.5.1/go.mod h1:ro0npU1BWkcGpCgGD9QwPp44l5OIZ94tB3eabnT7DjQ= +github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= +github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= @@ -169,6 +178,7 @@ github.com/speakeasy-api/jsonpath v0.6.2 h1:Mys71yd6u8kuowNCR0gCVPlVAHCmKtoGXYoA github.com/speakeasy-api/jsonpath v0.6.2/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw= github.com/speakeasy-api/openapi-overlay v0.10.2 h1:VOdQ03eGKeiHnpb1boZCGm7x8Haj6gST0P3SGTX95GU= github.com/speakeasy-api/openapi-overlay v0.10.2/go.mod h1:n0iOU7AqKpNFfEt6tq7qYITC4f0yzVVdFw0S7hukemg= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= diff --git a/broker/patron_request/api/api-handler.go b/broker/patron_request/api/api-handler.go index b9d89e55..a6e8350c 100644 --- a/broker/patron_request/api/api-handler.go +++ b/broker/patron_request/api/api-handler.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "maps" "net/http" "sync" @@ -35,6 +36,54 @@ func NewApiHandler(prRepo pr_db.PrRepo, eventBus events.EventBus, tenant common. } } +func (a *PatronRequestApiHandler) GetStateModelModelsReturnables(w http.ResponseWriter, r *http.Request) { + //logParams := map[string]string{"method": "GetStateModelModelsReturnables"} + //ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{ + // Other: logParams, + //}) + var stateModel proapi.StateModel + stateModel.Desc = strPtr("Requester/Supplier workflow per ISO18626, including NCIP integration and ISO status events.") + stateModel.Name = strPtr("CrossLink Returnables State Model") + + actionMapping := prservice.NewReturnableActionMapping() + + borrowerMap := actionMapping.GetBorrowerActionsMap() + lenderMap := actionMapping.GetLenderActionsMap() + + allStates := make([]proapi.State, 0, len(borrowerMap)+len(lenderMap)) + + lenderStates := makeStateList(borrowerMap, proapi.SUPPLIER) + allStates = append(allStates, lenderStates...) + + borrowerStates := makeStateList(lenderMap, proapi.REQUESTER) + allStates = append(allStates, borrowerStates...) + + stateModel.States = &allStates + + writeJsonResponse(w, stateModel) +} + +func makeStateList(stateMap map[pr_db.PatronRequestState][]pr_db.PatronRequestAction, stateSide proapi.StateSide) []proapi.State { + stateList := make([]proapi.State, 0, len(stateMap)) + var state proapi.State + for k := range maps.Keys(stateMap) { + state.Name = string(k) + state.Side = stateSide + actionList := make([]proapi.Action, 0, len(stateMap[k])) + for _, prAction := range stateMap[k] { + var action proapi.Action + action.Name = string(prAction) + //Where do we get the transitions? + actionList = append(actionList, action) + } + } + return stateList +} + +func strPtr(s string) *string { + return &s +} + func (a *PatronRequestApiHandler) GetPatronRequests(w http.ResponseWriter, r *http.Request) { logParams := map[string]string{"method": "GetPatronRequests"} ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{ diff --git a/broker/patron_request/oapi/open-api.yaml b/broker/patron_request/oapi/open-api.yaml index 89a26a8d..cc7df50d 100644 --- a/broker/patron_request/oapi/open-api.yaml +++ b/broker/patron_request/oapi/open-api.yaml @@ -16,6 +16,106 @@ components: pattern: '^[_a-z][_a-z0-9]*$' schemas: + StateModel: + type: object + description: ReShare state model definition + additionalProperties: false + properties: + type: + type: string + const: StateModel + name: + type: string + description: Name of the state model + desc: + type: string + description: Description of the state model + version: + type: string + description: Version of the state model in SemVer + states: + type: array + description: A list of all allowed states + items: + $ref: '#/components/schemas/State' + + State: + title: State + type: object + description: Definition of a particular state + properties: + name: + type: string + description: Name of the state, in capital letters with underscores + display: + type: string + description: Human-readable state name + desc: + type: string + description: Description of the state, e.g., what situation is modelled by the state + side: + type: string + description: Indicates which side of the request the state belongs to + enum: + - REQUESTER + - SUPPLIER + actions: + type: array + description: List of all actions that may be performed on the request when in this state + items: + $ref: '#/components/schemas/Action' + events: + type: array + description: List of all events that may be triggered to the request when in this state + items: + $ref: '#/components/schemas/Event' + terminal: + type: boolean + description: Indicates if the state is terminal (meaning no actions or events are allowed in this state) + oneOf: + - anyOf: + - required: + - actions + - required: + - events + - required: + - terminal + required: + - name + - side + + Action: + type: object + title: Action + additionalProperties: false + properties: + name: + type: string + description: Description of the action + transitions: + type: array + description: List of all possible state transitions after performing the action. When no transitions are defined, the action is considered to be non-state-changing + items: + type: string + required: + - name + + Event: + type: object + title: Event + additionalProperties: false + properties: + name: + type: string + description: Description of the event + transitions: + type: array + description: List of all possible state transitions after performing the event. When no transitions are defined, the event is considered to be non-state-changing + items: + type: string + required: + - name + PatronRequest: type: object properties: @@ -110,6 +210,23 @@ components: - actionResult paths: + /state_model/models/returnables: + get: + summary: Retrieve the returnables state model + responses: + '200': + description: Successful retrieval of state model + content: + application/json: + schema: + $ref: '#/components/schemas/StateModel' + '500': + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /patron_requests: get: summary: Retrieve patron requests diff --git a/broker/patron_request/service/action.go b/broker/patron_request/service/action.go index e18f72de..ba32a2ea 100644 --- a/broker/patron_request/service/action.go +++ b/broker/patron_request/service/action.go @@ -69,7 +69,11 @@ const ( LenderActionMarkCancelled pr_db.PatronRequestAction = "mark-cancelled" ) -type PatronRequestActionService struct { +type PatronRequestActionService interface { + InvokeAction(common.ExtendedContext, events.Event) +} + +type ReturnablesPatronRequestActionService struct { prRepo pr_db.PrRepo eventBus events.EventBus iso18626Handler handler.Iso18626HandlerInterface @@ -77,8 +81,8 @@ type PatronRequestActionService struct { actionMappingService ActionMappingService } -func CreatePatronRequestActionService(prRepo pr_db.PrRepo, eventBus events.EventBus, iso18626Handler handler.Iso18626HandlerInterface, lmsCreator lms.LmsCreator) PatronRequestActionService { - return PatronRequestActionService{ +func CreatePatronRequestActionService(prRepo pr_db.PrRepo, eventBus events.EventBus, iso18626Handler handler.Iso18626HandlerInterface, lmsCreator lms.LmsCreator) *ReturnablesPatronRequestActionService { + return &ReturnablesPatronRequestActionService{ prRepo: prRepo, eventBus: eventBus, iso18626Handler: iso18626Handler, @@ -87,12 +91,12 @@ func CreatePatronRequestActionService(prRepo pr_db.PrRepo, eventBus events.Event } } -func (a *PatronRequestActionService) InvokeAction(ctx common.ExtendedContext, event events.Event) { +func (a *ReturnablesPatronRequestActionService) InvokeAction(ctx common.ExtendedContext, event events.Event) { ctx = ctx.WithArgs(ctx.LoggerArgs().WithComponent(COMP)) _, _ = a.eventBus.ProcessTask(ctx, event, a.handleInvokeAction) } -func (a *PatronRequestActionService) handleInvokeAction(ctx common.ExtendedContext, event events.Event) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) handleInvokeAction(ctx common.ExtendedContext, event events.Event) (events.EventStatus, *events.EventResult) { if event.EventData.Action == nil { return events.LogErrorAndReturnResult(ctx, "action not specified", errors.New("action not specified")) } @@ -122,7 +126,7 @@ func (a *PatronRequestActionService) handleInvokeAction(ctx common.ExtendedConte } } -func (a *PatronRequestActionService) handleBorrowingAction(ctx common.ExtendedContext, action pr_db.PatronRequestAction, pr pr_db.PatronRequest, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) handleBorrowingAction(ctx common.ExtendedContext, action pr_db.PatronRequestAction, pr pr_db.PatronRequest, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { if !pr.RequesterSymbol.Valid { return events.LogErrorAndReturnResult(ctx, "missing requester symbol", nil) } @@ -154,7 +158,7 @@ func (a *PatronRequestActionService) handleBorrowingAction(ctx common.ExtendedCo } } -func (a *PatronRequestActionService) handleLenderAction(ctx common.ExtendedContext, action pr_db.PatronRequestAction, pr pr_db.PatronRequest, illRequest iso18626.Request, actionParams map[string]interface{}) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) handleLenderAction(ctx common.ExtendedContext, action pr_db.PatronRequestAction, pr pr_db.PatronRequest, illRequest iso18626.Request, actionParams map[string]interface{}) (events.EventStatus, *events.EventResult) { if !pr.SupplierSymbol.Valid { return events.LogErrorAndReturnResult(ctx, "missing supplier symbol", nil) } @@ -182,7 +186,7 @@ func (a *PatronRequestActionService) handleLenderAction(ctx common.ExtendedConte } } -func (a *PatronRequestActionService) updateStateAndReturnResult(ctx common.ExtendedContext, pr pr_db.PatronRequest, state pr_db.PatronRequestState, result *events.EventResult) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) updateStateAndReturnResult(ctx common.ExtendedContext, pr pr_db.PatronRequest, state pr_db.PatronRequestState, result *events.EventResult) (events.EventStatus, *events.EventResult) { pr.State = state pr, err := a.prRepo.SavePatronRequest(ctx, pr_db.SavePatronRequestParams(pr)) if err != nil { @@ -190,7 +194,7 @@ func (a *PatronRequestActionService) updateStateAndReturnResult(ctx common.Exten } return events.EventStatusSuccess, result } -func (a *PatronRequestActionService) checkSupplyingResponseAndUpdateState(ctx common.ExtendedContext, pr pr_db.PatronRequest, state pr_db.PatronRequestState, result *events.EventResult, status events.EventStatus, eventResult *events.EventResult, httpStatus *int) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) checkSupplyingResponseAndUpdateState(ctx common.ExtendedContext, pr pr_db.PatronRequest, state pr_db.PatronRequestState, result *events.EventResult, status events.EventStatus, eventResult *events.EventResult, httpStatus *int) (events.EventStatus, *events.EventResult) { if httpStatus == nil { return status, eventResult } @@ -201,7 +205,7 @@ func (a *PatronRequestActionService) checkSupplyingResponseAndUpdateState(ctx co return a.updateStateAndReturnResult(ctx, pr, state, nil) } -func (a *PatronRequestActionService) validateBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) validateBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter) (events.EventStatus, *events.EventResult) { patron := "" if pr.Patron.Valid { patron = pr.Patron.String @@ -216,7 +220,7 @@ func (a *PatronRequestActionService) validateBorrowingRequest(ctx common.Extende return a.updateStateAndReturnResult(ctx, pr, BorrowerStateValidated, nil) } -func (a *PatronRequestActionService) sendBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, request iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) sendBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, request iso18626.Request) (events.EventStatus, *events.EventResult) { result := events.EventResult{} // pr.RequesterSymbol is validated earlier in handleBorrowingAction requesterSymbol := strings.SplitN(pr.RequesterSymbol.String, ":", 2) @@ -297,7 +301,7 @@ func isbnFromIllRequest(illRequest iso18626.Request) string { return isbn } -func (a *PatronRequestActionService) receiveBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) receiveBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { patron := "" if pr.Patron.Valid { patron = pr.Patron.String @@ -326,7 +330,7 @@ func (a *PatronRequestActionService) receiveBorrowingRequest(ctx common.Extended return a.updateStateAndReturnResult(ctx, pr, BorrowerStateReceived, &result) } -func (a *PatronRequestActionService) checkoutBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) checkoutBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { patron := "" if pr.Patron.Valid { patron = pr.Patron.String @@ -341,7 +345,7 @@ func (a *PatronRequestActionService) checkoutBorrowingRequest(ctx common.Extende return a.updateStateAndReturnResult(ctx, pr, BorrowerStateCheckedOut, nil) } -func (a *PatronRequestActionService) checkinBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) checkinBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId err := lmsAdapter.CheckInItem(itemId) if err != nil { @@ -350,7 +354,7 @@ func (a *PatronRequestActionService) checkinBorrowingRequest(ctx common.Extended return a.updateStateAndReturnResult(ctx, pr, BorrowerStateCheckedIn, nil) } -func (a *PatronRequestActionService) shipReturnBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) shipReturnBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId err := lmsAdapter.DeleteItem(itemId) if err != nil { @@ -368,7 +372,7 @@ func (a *PatronRequestActionService) shipReturnBorrowingRequest(ctx common.Exten return a.updateStateAndReturnResult(ctx, pr, BorrowerStateShippedReturned, &result) } -func (a *PatronRequestActionService) sendRequestingAgencyMessage(ctx common.ExtendedContext, pr pr_db.PatronRequest, result *events.EventResult, action iso18626.TypeAction) (events.EventStatus, *events.EventResult, *int) { +func (a *ReturnablesPatronRequestActionService) sendRequestingAgencyMessage(ctx common.ExtendedContext, pr pr_db.PatronRequest, result *events.EventResult, action iso18626.TypeAction) (events.EventStatus, *events.EventResult, *int) { if !pr.RequesterSymbol.Valid { status, eventResult := events.LogErrorAndReturnResult(ctx, "missing requester symbol", nil) return status, eventResult, nil @@ -414,7 +418,7 @@ func (a *PatronRequestActionService) sendRequestingAgencyMessage(ctx common.Exte return "", nil, &w.StatusCode } -func (a *PatronRequestActionService) cancelBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) cancelBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { result := events.EventResult{} status, eventResult, httpStatus := a.sendRequestingAgencyMessage(ctx, pr, &result, iso18626.TypeActionCancel) if httpStatus == nil { @@ -427,15 +431,15 @@ func (a *PatronRequestActionService) cancelBorrowingRequest(ctx common.ExtendedC return a.updateStateAndReturnResult(ctx, pr, BorrowerStateCancelPending, &result) } -func (a *PatronRequestActionService) acceptConditionBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) acceptConditionBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { return a.updateStateAndReturnResult(ctx, pr, BorrowerStateWillSupply, nil) } -func (a *PatronRequestActionService) rejectConditionBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) rejectConditionBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { return a.updateStateAndReturnResult(ctx, pr, BorrowerStateCancelPending, nil) } -func (a *PatronRequestActionService) validateLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lms lms.LmsAdapter) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) validateLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lms lms.LmsAdapter) (events.EventStatus, *events.EventResult) { institutionalPatron := lms.InstitutionalPatron(pr.RequesterSymbol.String) _, err := lms.LookupUser(institutionalPatron) if err != nil { @@ -444,7 +448,7 @@ func (a *PatronRequestActionService) validateLenderRequest(ctx common.ExtendedCo return a.updateStateAndReturnResult(ctx, pr, LenderStateValidated, nil) } -func (a *PatronRequestActionService) willSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) willSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId requestId := illRequest.Header.RequestingAgencyRequestId userId := lmsAdapter.InstitutionalPatron(pr.RequesterSymbol.String) @@ -460,13 +464,13 @@ func (a *PatronRequestActionService) willSupplyLenderRequest(ctx common.Extended return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateWillSupply, &result, status, eventResult, httpStatus) } -func (a *PatronRequestActionService) cannotSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) cannotSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { result := events.EventResult{} status, eventResult, httpStatus := a.sendSupplyingAgencyMessage(ctx, pr, &result, iso18626.MessageInfo{ReasonForMessage: iso18626.TypeReasonForMessageStatusChange}, iso18626.StatusInfo{Status: iso18626.TypeStatusUnfilled}) return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateUnfilled, &result, status, eventResult, httpStatus) } -func (a *PatronRequestActionService) addConditionsLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, actionParams map[string]interface{}) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) addConditionsLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, actionParams map[string]interface{}) (events.EventStatus, *events.EventResult) { result := events.EventResult{} status, eventResult, httpStatus := a.sendSupplyingAgencyMessage(ctx, pr, &result, iso18626.MessageInfo{ @@ -477,7 +481,7 @@ func (a *PatronRequestActionService) addConditionsLenderRequest(ctx common.Exten return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateConditionPending, &result, status, eventResult, httpStatus) } -func (a *PatronRequestActionService) shipLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) shipLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId requestId := illRequest.Header.RequestingAgencyRequestId userId := lmsAdapter.InstitutionalPatron(pr.RequesterSymbol.String) @@ -492,7 +496,7 @@ func (a *PatronRequestActionService) shipLenderRequest(ctx common.ExtendedContex return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateShipped, &result, status, eventResult, httpStatus) } -func (a *PatronRequestActionService) markReceivedLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) markReceivedLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId err := lmsAdapter.CheckInItem(itemId) if err != nil { @@ -503,13 +507,13 @@ func (a *PatronRequestActionService) markReceivedLenderRequest(ctx common.Extend return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateCompleted, &result, status, eventResult, httpStatus) } -func (a *PatronRequestActionService) markCancelledLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { +func (a *ReturnablesPatronRequestActionService) markCancelledLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { result := events.EventResult{} status, eventResult, httpStatus := a.sendSupplyingAgencyMessage(ctx, pr, &result, iso18626.MessageInfo{ReasonForMessage: iso18626.TypeReasonForMessageStatusChange}, iso18626.StatusInfo{Status: iso18626.TypeStatusCancelled}) return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateCancelled, &result, status, eventResult, httpStatus) } -func (a *PatronRequestActionService) sendSupplyingAgencyMessage(ctx common.ExtendedContext, pr pr_db.PatronRequest, result *events.EventResult, messageInfo iso18626.MessageInfo, statusInfo iso18626.StatusInfo) (events.EventStatus, *events.EventResult, *int) { +func (a *ReturnablesPatronRequestActionService) sendSupplyingAgencyMessage(ctx common.ExtendedContext, pr pr_db.PatronRequest, result *events.EventResult, messageInfo iso18626.MessageInfo, statusInfo iso18626.StatusInfo) (events.EventStatus, *events.EventResult, *int) { if !pr.RequesterSymbol.Valid { status, eventResult := events.LogErrorAndReturnResult(ctx, "missing requester symbol", nil) return status, eventResult, nil diff --git a/broker/patron_request/service/action_mapping.go b/broker/patron_request/service/action_mapping.go index 548db4e5..671a2c00 100644 --- a/broker/patron_request/service/action_mapping.go +++ b/broker/patron_request/service/action_mapping.go @@ -1,34 +1,16 @@ package prservice import ( - pr_db "github.com/indexdata/crosslink/broker/patron_request/db" "slices" + + pr_db "github.com/indexdata/crosslink/broker/patron_request/db" ) type ActionMapping interface { IsActionAvailable(pr pr_db.PatronRequest, action pr_db.PatronRequestAction) bool GetActionsForPatronRequest(pr pr_db.PatronRequest) []pr_db.PatronRequestAction -} - -var returnableBorrowerStateActionMapping = map[pr_db.PatronRequestState][]pr_db.PatronRequestAction{ - BorrowerStateNew: {BorrowerActionValidate}, - BorrowerStateValidated: {BorrowerActionSendRequest}, - BorrowerStateSupplierLocated: {BorrowerActionCancelRequest}, - BorrowerStateConditionPending: {BorrowerActionAcceptCondition, BorrowerActionRejectCondition}, - BorrowerStateWillSupply: {BorrowerActionCancelRequest}, - BorrowerStateShipped: {BorrowerActionReceive}, - BorrowerStateReceived: {BorrowerActionCheckOut}, - BorrowerStateCheckedOut: {BorrowerActionCheckIn}, - BorrowerStateCheckedIn: {BorrowerActionShipReturn}, -} -var returnableLenderStateActionMapping = map[pr_db.PatronRequestState][]pr_db.PatronRequestAction{ - LenderStateNew: {LenderActionValidate}, - LenderStateValidated: {LenderActionWillSupply, LenderActionCannotSupply, LenderActionAddCondition}, - LenderStateWillSupply: {LenderActionAddCondition, LenderActionCannotSupply, LenderActionShip}, - LenderStateConditionPending: {LenderActionCannotSupply}, - LenderStateConditionAccepted: {LenderActionShip, LenderActionCannotSupply}, - LenderStateShippedReturn: {LenderActionMarkReceived}, - LenderStateCancelRequested: {LenderActionMarkCancelled, LenderActionWillSupply}, + GetBorrowerActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction + GetLenderActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction } type ActionMappingService struct { @@ -39,20 +21,56 @@ func (r *ActionMappingService) GetActionMapping(pr pr_db.PatronRequest) ActionMa } type ReturnableActionMapping struct { + borrowerStateActionMapping map[pr_db.PatronRequestState][]pr_db.PatronRequestAction + lenderStateActionMapping map[pr_db.PatronRequestState][]pr_db.PatronRequestAction +} + +/* Constructor function to initialize the mappings for the returnables */ +func NewReturnableActionMapping() *ReturnableActionMapping { + r := new(ReturnableActionMapping) + r.borrowerStateActionMapping = map[pr_db.PatronRequestState][]pr_db.PatronRequestAction{ + BorrowerStateNew: {BorrowerActionValidate}, + BorrowerStateValidated: {BorrowerActionSendRequest}, + BorrowerStateSupplierLocated: {BorrowerActionCancelRequest}, + BorrowerStateConditionPending: {BorrowerActionAcceptCondition, BorrowerActionRejectCondition}, + BorrowerStateWillSupply: {BorrowerActionCancelRequest}, + BorrowerStateShipped: {BorrowerActionReceive}, + BorrowerStateReceived: {BorrowerActionCheckOut}, + BorrowerStateCheckedOut: {BorrowerActionCheckIn}, + BorrowerStateCheckedIn: {BorrowerActionShipReturn}, + } + r.lenderStateActionMapping = map[pr_db.PatronRequestState][]pr_db.PatronRequestAction{ + LenderStateNew: {LenderActionValidate}, + LenderStateValidated: {LenderActionWillSupply, LenderActionCannotSupply, LenderActionAddCondition}, + LenderStateWillSupply: {LenderActionAddCondition, LenderActionCannotSupply, LenderActionShip}, + LenderStateConditionPending: {LenderActionCannotSupply}, + LenderStateConditionAccepted: {LenderActionShip, LenderActionCannotSupply}, + LenderStateShippedReturn: {LenderActionMarkReceived}, + LenderStateCancelRequested: {LenderActionMarkCancelled, LenderActionWillSupply}, + } + return r +} + +func (r *ReturnableActionMapping) GetBorrowerActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction { + return r.borrowerStateActionMapping +} + +func (r *ReturnableActionMapping) GetLenderActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction { + return r.lenderStateActionMapping } func (r *ReturnableActionMapping) IsActionAvailable(pr pr_db.PatronRequest, action pr_db.PatronRequestAction) bool { if pr.Side == SideBorrowing { - return isActionAvailable(pr.State, action, returnableBorrowerStateActionMapping) + return isActionAvailable(pr.State, action, r.borrowerStateActionMapping) } else { - return isActionAvailable(pr.State, action, returnableLenderStateActionMapping) + return isActionAvailable(pr.State, action, r.lenderStateActionMapping) } } func (r *ReturnableActionMapping) GetActionsForPatronRequest(pr pr_db.PatronRequest) []pr_db.PatronRequestAction { if pr.Side == SideBorrowing { - return getActionsByStateFromMapping(pr.State, returnableBorrowerStateActionMapping) + return getActionsByStateFromMapping(pr.State, r.borrowerStateActionMapping) } else { - return getActionsByStateFromMapping(pr.State, returnableLenderStateActionMapping) + return getActionsByStateFromMapping(pr.State, r.lenderStateActionMapping) } } diff --git a/go.work b/go.work index f653d161..b2834096 100644 --- a/go.work +++ b/go.work @@ -3,11 +3,11 @@ go 1.25 use ( ./broker ./common + ./directory ./httpclient ./illmock ./iso18626 ./marcxml ./ncip ./sru - ./directory ) diff --git a/httpclient/go.mod b/httpclient/go.mod index 67f3f70c..e4aeacf7 100644 --- a/httpclient/go.mod +++ b/httpclient/go.mod @@ -5,9 +5,9 @@ go 1.25 require github.com/stretchr/testify v1.11.1 require ( - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/httpclient/go.sum b/httpclient/go.sum index 3aa9f6ad..d8e1aa73 100644 --- a/httpclient/go.sum +++ b/httpclient/go.sum @@ -1,6 +1,5 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -9,8 +8,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= diff --git a/illmock/go.mod b/illmock/go.mod index 6ee25f54..99a80289 100644 --- a/illmock/go.mod +++ b/illmock/go.mod @@ -5,33 +5,28 @@ go 1.25 require ( github.com/indexdata/crosslink/httpclient v0.0.0 github.com/indexdata/crosslink/iso18626 v0.0.0 - github.com/indexdata/crosslink/marcxml v0.0.0 - github.com/indexdata/crosslink/sru v0.0.0 github.com/indexdata/crosslink/ncip v0.0.0 - github.com/indexdata/crosslink/directory v0.0.0 ) replace ( + github.com/indexdata/crosslink/directory => ../directory github.com/indexdata/crosslink/httpclient => ../httpclient github.com/indexdata/crosslink/iso18626 => ../iso18626 github.com/indexdata/crosslink/marcxml => ../marcxml - github.com/indexdata/crosslink/sru => ../sru github.com/indexdata/crosslink/ncip => ../ncip - github.com/indexdata/crosslink/directory => ../directory + github.com/indexdata/crosslink/sru => ../sru ) require ( github.com/google/uuid v1.6.0 github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030 github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72 - github.com/oapi-codegen/runtime v1.1.2 github.com/stretchr/testify v1.11.1 ) require ( - github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/kr/text v0.2.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/illmock/go.sum b/illmock/go.sum index ff7efe0b..72ae1394 100644 --- a/illmock/go.sum +++ b/illmock/go.sum @@ -1,31 +1,18 @@ -github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= -github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= -github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030 h1:RP9TZZTGoBo2Jx04FEWaRmNhCX/Fk8VR04tX0FMRzik= github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030/go.mod h1:zmSHcE8JyK94EWZrV7VyjLr2QfRoj+EeEOttl9wm64U= github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72 h1:/ssOlpKK1EOeWMCOUrxCOCZP+0Z1LD58Xg1nrU01TAQ= github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72/go.mod h1:0sW6Szxv8GNU3LBtK6mgBKDEUnlovPfghiG9xi+i0R8= -github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= -github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/ncip/go.mod b/ncip/go.mod index 81ceb225..20202d25 100644 --- a/ncip/go.mod +++ b/ncip/go.mod @@ -8,7 +8,10 @@ require ( ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ncip/go.sum b/ncip/go.sum index c14175c1..bbdce919 100644 --- a/ncip/go.sum +++ b/ncip/go.sum @@ -1,12 +1,12 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/indexdata/go-utils v0.0.0-20250311160228-a87f76a1f868 h1:GKZL+YkrCuzL7N9lewA6uuJhjzgOkb4Az0F9+1xmbeI= -github.com/indexdata/go-utils v0.0.0-20250311160228-a87f76a1f868/go.mod h1:0sW6Szxv8GNU3LBtK6mgBKDEUnlovPfghiG9xi+i0R8= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72 h1:/ssOlpKK1EOeWMCOUrxCOCZP+0Z1LD58Xg1nrU01TAQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From ce0e815e21d10ea98a5ed7aa309fe8dc6680aa8d Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Wed, 28 Jan 2026 17:28:55 -0500 Subject: [PATCH 02/11] Assign result of actionList --- broker/patron_request/api/api-handler.go | 1 + 1 file changed, 1 insertion(+) diff --git a/broker/patron_request/api/api-handler.go b/broker/patron_request/api/api-handler.go index a6e8350c..55025412 100644 --- a/broker/patron_request/api/api-handler.go +++ b/broker/patron_request/api/api-handler.go @@ -76,6 +76,7 @@ func makeStateList(stateMap map[pr_db.PatronRequestState][]pr_db.PatronRequestAc //Where do we get the transitions? actionList = append(actionList, action) } + state.Actions = &actionList } return stateList } From 4f61e1b4027a9444ef722eb92d282c977d40b337 Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Fri, 30 Jan 2026 16:52:12 -0500 Subject: [PATCH 03/11] Marshall statemodel from JSON, get action mapping from statemodel --- broker/app/app.go | 2 +- broker/patron_request/api/api-handler.go | 21 +- broker/patron_request/service/action.go | 84 +-- .../patron_request/service/action_mapping.go | 49 +- .../service/action_mapping_test.go | 44 +- broker/patron_request/service/action_test.go | 7 + .../service/statemodels/returnables.json | 516 ++++++++++++++++++ 7 files changed, 652 insertions(+), 71 deletions(-) create mode 100644 broker/patron_request/service/statemodels/returnables.json diff --git a/broker/app/app.go b/broker/app/app.go index a0f40101..5858622e 100644 --- a/broker/app/app.go +++ b/broker/app/app.go @@ -166,7 +166,7 @@ func Init(ctx context.Context) (Context, error) { sseBroker := api.NewSseBroker(appCtx, common.NewTenant(TENANT_TO_SYMBOL)) - AddDefaultHandlers(eventBus, iso18626Client, supplierLocator, workflowManager, iso18626Handler, prActionService, prApiHandler, sseBroker) + AddDefaultHandlers(eventBus, iso18626Client, supplierLocator, workflowManager, iso18626Handler, *prActionService, prApiHandler, sseBroker) err = StartEventBus(ctx, eventBus) if err != nil { return Context{}, err diff --git a/broker/patron_request/api/api-handler.go b/broker/patron_request/api/api-handler.go index 55025412..bd4dc8a8 100644 --- a/broker/patron_request/api/api-handler.go +++ b/broker/patron_request/api/api-handler.go @@ -41,26 +41,9 @@ func (a *PatronRequestApiHandler) GetStateModelModelsReturnables(w http.Response //ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{ // Other: logParams, //}) - var stateModel proapi.StateModel - stateModel.Desc = strPtr("Requester/Supplier workflow per ISO18626, including NCIP integration and ISO status events.") - stateModel.Name = strPtr("CrossLink Returnables State Model") + stateModel, _ := prservice.LoadReturnablesStateModel() - actionMapping := prservice.NewReturnableActionMapping() - - borrowerMap := actionMapping.GetBorrowerActionsMap() - lenderMap := actionMapping.GetLenderActionsMap() - - allStates := make([]proapi.State, 0, len(borrowerMap)+len(lenderMap)) - - lenderStates := makeStateList(borrowerMap, proapi.SUPPLIER) - allStates = append(allStates, lenderStates...) - - borrowerStates := makeStateList(lenderMap, proapi.REQUESTER) - allStates = append(allStates, borrowerStates...) - - stateModel.States = &allStates - - writeJsonResponse(w, stateModel) + writeJsonResponse(w, *stateModel) } func makeStateList(stateMap map[pr_db.PatronRequestState][]pr_db.PatronRequestAction, stateSide proapi.StateSide) []proapi.State { diff --git a/broker/patron_request/service/action.go b/broker/patron_request/service/action.go index ba32a2ea..d412d69a 100644 --- a/broker/patron_request/service/action.go +++ b/broker/patron_request/service/action.go @@ -1,6 +1,7 @@ package prservice import ( + "embed" "encoding/json" "encoding/xml" "errors" @@ -12,6 +13,7 @@ import ( "github.com/indexdata/crosslink/broker/handler" "github.com/indexdata/crosslink/broker/lms" pr_db "github.com/indexdata/crosslink/broker/patron_request/db" + proapi "github.com/indexdata/crosslink/broker/patron_request/oapi" "github.com/indexdata/crosslink/iso18626" "github.com/jackc/pgx/v5/pgtype" ) @@ -69,11 +71,7 @@ const ( LenderActionMarkCancelled pr_db.PatronRequestAction = "mark-cancelled" ) -type PatronRequestActionService interface { - InvokeAction(common.ExtendedContext, events.Event) -} - -type ReturnablesPatronRequestActionService struct { +type PatronRequestActionService struct { prRepo pr_db.PrRepo eventBus events.EventBus iso18626Handler handler.Iso18626HandlerInterface @@ -81,8 +79,32 @@ type ReturnablesPatronRequestActionService struct { actionMappingService ActionMappingService } -func CreatePatronRequestActionService(prRepo pr_db.PrRepo, eventBus events.EventBus, iso18626Handler handler.Iso18626HandlerInterface, lmsCreator lms.LmsCreator) *ReturnablesPatronRequestActionService { - return &ReturnablesPatronRequestActionService{ +//go:embed statemodels +var modelFS embed.FS + +func LoadReturnablesStateModel() (*proapi.StateModel, error) { + path := "statemodels/returnables.json" + stateModel, err := loadStateModel(path) + if err != nil { + return nil, err + } + return stateModel, nil +} + +func loadStateModel(path string) (*proapi.StateModel, error) { + data, err := modelFS.ReadFile(path) + if err != nil { + return nil, err + } + + var stateModel proapi.StateModel + err = json.Unmarshal(data, &stateModel) + + return &stateModel, err +} + +func CreatePatronRequestActionService(prRepo pr_db.PrRepo, eventBus events.EventBus, iso18626Handler handler.Iso18626HandlerInterface, lmsCreator lms.LmsCreator) *PatronRequestActionService { + return &PatronRequestActionService{ prRepo: prRepo, eventBus: eventBus, iso18626Handler: iso18626Handler, @@ -91,12 +113,12 @@ func CreatePatronRequestActionService(prRepo pr_db.PrRepo, eventBus events.Event } } -func (a *ReturnablesPatronRequestActionService) InvokeAction(ctx common.ExtendedContext, event events.Event) { +func (a *PatronRequestActionService) InvokeAction(ctx common.ExtendedContext, event events.Event) { ctx = ctx.WithArgs(ctx.LoggerArgs().WithComponent(COMP)) _, _ = a.eventBus.ProcessTask(ctx, event, a.handleInvokeAction) } -func (a *ReturnablesPatronRequestActionService) handleInvokeAction(ctx common.ExtendedContext, event events.Event) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) handleInvokeAction(ctx common.ExtendedContext, event events.Event) (events.EventStatus, *events.EventResult) { if event.EventData.Action == nil { return events.LogErrorAndReturnResult(ctx, "action not specified", errors.New("action not specified")) } @@ -126,7 +148,7 @@ func (a *ReturnablesPatronRequestActionService) handleInvokeAction(ctx common.Ex } } -func (a *ReturnablesPatronRequestActionService) handleBorrowingAction(ctx common.ExtendedContext, action pr_db.PatronRequestAction, pr pr_db.PatronRequest, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) handleBorrowingAction(ctx common.ExtendedContext, action pr_db.PatronRequestAction, pr pr_db.PatronRequest, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { if !pr.RequesterSymbol.Valid { return events.LogErrorAndReturnResult(ctx, "missing requester symbol", nil) } @@ -158,7 +180,7 @@ func (a *ReturnablesPatronRequestActionService) handleBorrowingAction(ctx common } } -func (a *ReturnablesPatronRequestActionService) handleLenderAction(ctx common.ExtendedContext, action pr_db.PatronRequestAction, pr pr_db.PatronRequest, illRequest iso18626.Request, actionParams map[string]interface{}) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) handleLenderAction(ctx common.ExtendedContext, action pr_db.PatronRequestAction, pr pr_db.PatronRequest, illRequest iso18626.Request, actionParams map[string]interface{}) (events.EventStatus, *events.EventResult) { if !pr.SupplierSymbol.Valid { return events.LogErrorAndReturnResult(ctx, "missing supplier symbol", nil) } @@ -186,7 +208,7 @@ func (a *ReturnablesPatronRequestActionService) handleLenderAction(ctx common.Ex } } -func (a *ReturnablesPatronRequestActionService) updateStateAndReturnResult(ctx common.ExtendedContext, pr pr_db.PatronRequest, state pr_db.PatronRequestState, result *events.EventResult) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) updateStateAndReturnResult(ctx common.ExtendedContext, pr pr_db.PatronRequest, state pr_db.PatronRequestState, result *events.EventResult) (events.EventStatus, *events.EventResult) { pr.State = state pr, err := a.prRepo.SavePatronRequest(ctx, pr_db.SavePatronRequestParams(pr)) if err != nil { @@ -194,7 +216,7 @@ func (a *ReturnablesPatronRequestActionService) updateStateAndReturnResult(ctx c } return events.EventStatusSuccess, result } -func (a *ReturnablesPatronRequestActionService) checkSupplyingResponseAndUpdateState(ctx common.ExtendedContext, pr pr_db.PatronRequest, state pr_db.PatronRequestState, result *events.EventResult, status events.EventStatus, eventResult *events.EventResult, httpStatus *int) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) checkSupplyingResponseAndUpdateState(ctx common.ExtendedContext, pr pr_db.PatronRequest, state pr_db.PatronRequestState, result *events.EventResult, status events.EventStatus, eventResult *events.EventResult, httpStatus *int) (events.EventStatus, *events.EventResult) { if httpStatus == nil { return status, eventResult } @@ -205,7 +227,7 @@ func (a *ReturnablesPatronRequestActionService) checkSupplyingResponseAndUpdateS return a.updateStateAndReturnResult(ctx, pr, state, nil) } -func (a *ReturnablesPatronRequestActionService) validateBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) validateBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter) (events.EventStatus, *events.EventResult) { patron := "" if pr.Patron.Valid { patron = pr.Patron.String @@ -220,7 +242,7 @@ func (a *ReturnablesPatronRequestActionService) validateBorrowingRequest(ctx com return a.updateStateAndReturnResult(ctx, pr, BorrowerStateValidated, nil) } -func (a *ReturnablesPatronRequestActionService) sendBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, request iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) sendBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, request iso18626.Request) (events.EventStatus, *events.EventResult) { result := events.EventResult{} // pr.RequesterSymbol is validated earlier in handleBorrowingAction requesterSymbol := strings.SplitN(pr.RequesterSymbol.String, ":", 2) @@ -301,7 +323,7 @@ func isbnFromIllRequest(illRequest iso18626.Request) string { return isbn } -func (a *ReturnablesPatronRequestActionService) receiveBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) receiveBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { patron := "" if pr.Patron.Valid { patron = pr.Patron.String @@ -330,7 +352,7 @@ func (a *ReturnablesPatronRequestActionService) receiveBorrowingRequest(ctx comm return a.updateStateAndReturnResult(ctx, pr, BorrowerStateReceived, &result) } -func (a *ReturnablesPatronRequestActionService) checkoutBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) checkoutBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { patron := "" if pr.Patron.Valid { patron = pr.Patron.String @@ -345,7 +367,7 @@ func (a *ReturnablesPatronRequestActionService) checkoutBorrowingRequest(ctx com return a.updateStateAndReturnResult(ctx, pr, BorrowerStateCheckedOut, nil) } -func (a *ReturnablesPatronRequestActionService) checkinBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) checkinBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId err := lmsAdapter.CheckInItem(itemId) if err != nil { @@ -354,7 +376,7 @@ func (a *ReturnablesPatronRequestActionService) checkinBorrowingRequest(ctx comm return a.updateStateAndReturnResult(ctx, pr, BorrowerStateCheckedIn, nil) } -func (a *ReturnablesPatronRequestActionService) shipReturnBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) shipReturnBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId err := lmsAdapter.DeleteItem(itemId) if err != nil { @@ -372,7 +394,7 @@ func (a *ReturnablesPatronRequestActionService) shipReturnBorrowingRequest(ctx c return a.updateStateAndReturnResult(ctx, pr, BorrowerStateShippedReturned, &result) } -func (a *ReturnablesPatronRequestActionService) sendRequestingAgencyMessage(ctx common.ExtendedContext, pr pr_db.PatronRequest, result *events.EventResult, action iso18626.TypeAction) (events.EventStatus, *events.EventResult, *int) { +func (a *PatronRequestActionService) sendRequestingAgencyMessage(ctx common.ExtendedContext, pr pr_db.PatronRequest, result *events.EventResult, action iso18626.TypeAction) (events.EventStatus, *events.EventResult, *int) { if !pr.RequesterSymbol.Valid { status, eventResult := events.LogErrorAndReturnResult(ctx, "missing requester symbol", nil) return status, eventResult, nil @@ -418,7 +440,7 @@ func (a *ReturnablesPatronRequestActionService) sendRequestingAgencyMessage(ctx return "", nil, &w.StatusCode } -func (a *ReturnablesPatronRequestActionService) cancelBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) cancelBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { result := events.EventResult{} status, eventResult, httpStatus := a.sendRequestingAgencyMessage(ctx, pr, &result, iso18626.TypeActionCancel) if httpStatus == nil { @@ -431,15 +453,15 @@ func (a *ReturnablesPatronRequestActionService) cancelBorrowingRequest(ctx commo return a.updateStateAndReturnResult(ctx, pr, BorrowerStateCancelPending, &result) } -func (a *ReturnablesPatronRequestActionService) acceptConditionBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) acceptConditionBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { return a.updateStateAndReturnResult(ctx, pr, BorrowerStateWillSupply, nil) } -func (a *ReturnablesPatronRequestActionService) rejectConditionBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) rejectConditionBorrowingRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { return a.updateStateAndReturnResult(ctx, pr, BorrowerStateCancelPending, nil) } -func (a *ReturnablesPatronRequestActionService) validateLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lms lms.LmsAdapter) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) validateLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lms lms.LmsAdapter) (events.EventStatus, *events.EventResult) { institutionalPatron := lms.InstitutionalPatron(pr.RequesterSymbol.String) _, err := lms.LookupUser(institutionalPatron) if err != nil { @@ -448,7 +470,7 @@ func (a *ReturnablesPatronRequestActionService) validateLenderRequest(ctx common return a.updateStateAndReturnResult(ctx, pr, LenderStateValidated, nil) } -func (a *ReturnablesPatronRequestActionService) willSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) willSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId requestId := illRequest.Header.RequestingAgencyRequestId userId := lmsAdapter.InstitutionalPatron(pr.RequesterSymbol.String) @@ -464,13 +486,13 @@ func (a *ReturnablesPatronRequestActionService) willSupplyLenderRequest(ctx comm return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateWillSupply, &result, status, eventResult, httpStatus) } -func (a *ReturnablesPatronRequestActionService) cannotSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) cannotSupplyLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { result := events.EventResult{} status, eventResult, httpStatus := a.sendSupplyingAgencyMessage(ctx, pr, &result, iso18626.MessageInfo{ReasonForMessage: iso18626.TypeReasonForMessageStatusChange}, iso18626.StatusInfo{Status: iso18626.TypeStatusUnfilled}) return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateUnfilled, &result, status, eventResult, httpStatus) } -func (a *ReturnablesPatronRequestActionService) addConditionsLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, actionParams map[string]interface{}) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) addConditionsLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, actionParams map[string]interface{}) (events.EventStatus, *events.EventResult) { result := events.EventResult{} status, eventResult, httpStatus := a.sendSupplyingAgencyMessage(ctx, pr, &result, iso18626.MessageInfo{ @@ -481,7 +503,7 @@ func (a *ReturnablesPatronRequestActionService) addConditionsLenderRequest(ctx c return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateConditionPending, &result, status, eventResult, httpStatus) } -func (a *ReturnablesPatronRequestActionService) shipLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) shipLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId requestId := illRequest.Header.RequestingAgencyRequestId userId := lmsAdapter.InstitutionalPatron(pr.RequesterSymbol.String) @@ -496,7 +518,7 @@ func (a *ReturnablesPatronRequestActionService) shipLenderRequest(ctx common.Ext return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateShipped, &result, status, eventResult, httpStatus) } -func (a *ReturnablesPatronRequestActionService) markReceivedLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) markReceivedLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest, lmsAdapter lms.LmsAdapter, illRequest iso18626.Request) (events.EventStatus, *events.EventResult) { itemId := illRequest.BibliographicInfo.SupplierUniqueRecordId err := lmsAdapter.CheckInItem(itemId) if err != nil { @@ -507,13 +529,13 @@ func (a *ReturnablesPatronRequestActionService) markReceivedLenderRequest(ctx co return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateCompleted, &result, status, eventResult, httpStatus) } -func (a *ReturnablesPatronRequestActionService) markCancelledLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { +func (a *PatronRequestActionService) markCancelledLenderRequest(ctx common.ExtendedContext, pr pr_db.PatronRequest) (events.EventStatus, *events.EventResult) { result := events.EventResult{} status, eventResult, httpStatus := a.sendSupplyingAgencyMessage(ctx, pr, &result, iso18626.MessageInfo{ReasonForMessage: iso18626.TypeReasonForMessageStatusChange}, iso18626.StatusInfo{Status: iso18626.TypeStatusCancelled}) return a.checkSupplyingResponseAndUpdateState(ctx, pr, LenderStateCancelled, &result, status, eventResult, httpStatus) } -func (a *ReturnablesPatronRequestActionService) sendSupplyingAgencyMessage(ctx common.ExtendedContext, pr pr_db.PatronRequest, result *events.EventResult, messageInfo iso18626.MessageInfo, statusInfo iso18626.StatusInfo) (events.EventStatus, *events.EventResult, *int) { +func (a *PatronRequestActionService) sendSupplyingAgencyMessage(ctx common.ExtendedContext, pr pr_db.PatronRequest, result *events.EventResult, messageInfo iso18626.MessageInfo, statusInfo iso18626.StatusInfo) (events.EventStatus, *events.EventResult, *int) { if !pr.RequesterSymbol.Valid { status, eventResult := events.LogErrorAndReturnResult(ctx, "missing requester symbol", nil) return status, eventResult, nil diff --git a/broker/patron_request/service/action_mapping.go b/broker/patron_request/service/action_mapping.go index 671a2c00..41869e23 100644 --- a/broker/patron_request/service/action_mapping.go +++ b/broker/patron_request/service/action_mapping.go @@ -4,6 +4,7 @@ import ( "slices" pr_db "github.com/indexdata/crosslink/broker/patron_request/db" + proapi "github.com/indexdata/crosslink/broker/patron_request/oapi" ) type ActionMapping interface { @@ -17,7 +18,8 @@ type ActionMappingService struct { } func (r *ActionMappingService) GetActionMapping(pr pr_db.PatronRequest) ActionMapping { - return new(ReturnableActionMapping) + //At a future point, we will check the PatronRequest loan type to decide what kind of mapping service to return + return NewReturnableActionMapping() } type ReturnableActionMapping struct { @@ -28,26 +30,33 @@ type ReturnableActionMapping struct { /* Constructor function to initialize the mappings for the returnables */ func NewReturnableActionMapping() *ReturnableActionMapping { r := new(ReturnableActionMapping) - r.borrowerStateActionMapping = map[pr_db.PatronRequestState][]pr_db.PatronRequestAction{ - BorrowerStateNew: {BorrowerActionValidate}, - BorrowerStateValidated: {BorrowerActionSendRequest}, - BorrowerStateSupplierLocated: {BorrowerActionCancelRequest}, - BorrowerStateConditionPending: {BorrowerActionAcceptCondition, BorrowerActionRejectCondition}, - BorrowerStateWillSupply: {BorrowerActionCancelRequest}, - BorrowerStateShipped: {BorrowerActionReceive}, - BorrowerStateReceived: {BorrowerActionCheckOut}, - BorrowerStateCheckedOut: {BorrowerActionCheckIn}, - BorrowerStateCheckedIn: {BorrowerActionShipReturn}, + stateModel, err := LoadReturnablesStateModel() + + if err != nil { + return nil } - r.lenderStateActionMapping = map[pr_db.PatronRequestState][]pr_db.PatronRequestAction{ - LenderStateNew: {LenderActionValidate}, - LenderStateValidated: {LenderActionWillSupply, LenderActionCannotSupply, LenderActionAddCondition}, - LenderStateWillSupply: {LenderActionAddCondition, LenderActionCannotSupply, LenderActionShip}, - LenderStateConditionPending: {LenderActionCannotSupply}, - LenderStateConditionAccepted: {LenderActionShip, LenderActionCannotSupply}, - LenderStateShippedReturn: {LenderActionMarkReceived}, - LenderStateCancelRequested: {LenderActionMarkCancelled, LenderActionWillSupply}, + + borrowerMap := make(map[pr_db.PatronRequestState][]pr_db.PatronRequestAction) + lenderMap := make(map[pr_db.PatronRequestState][]pr_db.PatronRequestAction) + + for _, state := range *stateModel.States { + if state.Actions != nil { + nameList := make([]pr_db.PatronRequestAction, 0) + for _, action := range *state.Actions { + nameList = append(nameList, pr_db.PatronRequestAction(action.Name)) + } + if state.Side == proapi.REQUESTER { + borrowerMap[pr_db.PatronRequestState(state.Name)] = nameList + } else { + lenderMap[pr_db.PatronRequestState(state.Name)] = nameList + } + } + } + + r.borrowerStateActionMapping = borrowerMap + r.lenderStateActionMapping = lenderMap + return r } @@ -66,6 +75,7 @@ func (r *ReturnableActionMapping) IsActionAvailable(pr pr_db.PatronRequest, acti return isActionAvailable(pr.State, action, r.lenderStateActionMapping) } } + func (r *ReturnableActionMapping) GetActionsForPatronRequest(pr pr_db.PatronRequest) []pr_db.PatronRequestAction { if pr.Side == SideBorrowing { return getActionsByStateFromMapping(pr.State, r.borrowerStateActionMapping) @@ -77,6 +87,7 @@ func (r *ReturnableActionMapping) GetActionsForPatronRequest(pr pr_db.PatronRequ func isActionAvailable(state pr_db.PatronRequestState, action pr_db.PatronRequestAction, actionMapping map[pr_db.PatronRequestState][]pr_db.PatronRequestAction) bool { return slices.Contains(getActionsByStateFromMapping(state, actionMapping), action) } + func getActionsByStateFromMapping(state pr_db.PatronRequestState, actionMapping map[pr_db.PatronRequestState][]pr_db.PatronRequestAction) []pr_db.PatronRequestAction { if actions, ok := actionMapping[state]; ok { return actions diff --git a/broker/patron_request/service/action_mapping_test.go b/broker/patron_request/service/action_mapping_test.go index 838bfe32..eca74c33 100644 --- a/broker/patron_request/service/action_mapping_test.go +++ b/broker/patron_request/service/action_mapping_test.go @@ -1,11 +1,45 @@ package prservice import ( + "testing" + pr_db "github.com/indexdata/crosslink/broker/patron_request/db" "github.com/stretchr/testify/assert" - "testing" ) +func TestNewReturnableActionMapping(t *testing.T) { + borrowerStateActionMapping := map[pr_db.PatronRequestState][]pr_db.PatronRequestAction{ + BorrowerStateNew: {BorrowerActionValidate}, + BorrowerStateValidated: {BorrowerActionSendRequest}, + BorrowerStateSupplierLocated: {BorrowerActionCancelRequest}, + BorrowerStateConditionPending: {BorrowerActionAcceptCondition, BorrowerActionRejectCondition}, + BorrowerStateWillSupply: {BorrowerActionCancelRequest}, + BorrowerStateShipped: {BorrowerActionReceive}, + BorrowerStateReceived: {BorrowerActionCheckOut}, + BorrowerStateCheckedOut: {BorrowerActionCheckIn}, + BorrowerStateCheckedIn: {BorrowerActionShipReturn}, + } + + lenderStateActionMapping := map[pr_db.PatronRequestState][]pr_db.PatronRequestAction{ + LenderStateNew: {LenderActionValidate}, + LenderStateValidated: {LenderActionWillSupply, LenderActionCannotSupply, LenderActionAddCondition}, + LenderStateWillSupply: {LenderActionAddCondition, LenderActionCannotSupply, LenderActionShip}, + LenderStateConditionPending: {LenderActionCannotSupply}, + LenderStateConditionAccepted: {LenderActionShip, LenderActionCannotSupply}, + LenderStateShippedReturn: {LenderActionMarkReceived}, + LenderStateCancelRequested: {LenderActionMarkCancelled, LenderActionWillSupply}, + } + + returnableActionMapping := NewReturnableActionMapping() + + assert.NotNil(t, returnableActionMapping) + + mapCompare(t, returnableActionMapping.borrowerStateActionMapping, borrowerStateActionMapping) + + mapCompare(t, returnableActionMapping.lenderStateActionMapping, lenderStateActionMapping) + +} + var actionMappingService = ActionMappingService{} func TestIsActionAvailable(t *testing.T) { @@ -27,3 +61,11 @@ func TestGetActionsForPatronRequest(t *testing.T) { assert.Equal(t, []pr_db.PatronRequestAction{LenderActionAddCondition, LenderActionCannotSupply, LenderActionShip}, actionMappingService.GetActionMapping(pr_db.PatronRequest{}).GetActionsForPatronRequest(pr_db.PatronRequest{Side: SideLending, State: LenderStateWillSupply})) assert.Equal(t, []pr_db.PatronRequestAction{}, actionMappingService.GetActionMapping(pr_db.PatronRequest{}).GetActionsForPatronRequest(pr_db.PatronRequest{Side: SideLending, State: LenderStateShipped})) } + +func mapCompare(t *testing.T, map1 map[pr_db.PatronRequestState][]pr_db.PatronRequestAction, map2 map[pr_db.PatronRequestState][]pr_db.PatronRequestAction) { + for stateName := range map1 { + listOne := map1[stateName] + listTwo := map2[stateName] + assert.Equal(t, len(listOne), len(listTwo)) + } +} diff --git a/broker/patron_request/service/action_test.go b/broker/patron_request/service/action_test.go index d482c13f..956aa7dd 100644 --- a/broker/patron_request/service/action_test.go +++ b/broker/patron_request/service/action_test.go @@ -879,3 +879,10 @@ func TestCallNumberFromIllRequest(t *testing.T) { callNumber := callNumberFromIllRequest(r) assert.Equal(t, "QA76.73.G63 D37 2020", callNumber) } + +func TestLoadReturnableStateModel(t *testing.T) { + stateModel, err := LoadReturnablesStateModel() + assert.Nil(t, err) + assert.NotNil(t, stateModel) + +} diff --git a/broker/patron_request/service/statemodels/returnables.json b/broker/patron_request/service/statemodels/returnables.json new file mode 100644 index 00000000..7175f872 --- /dev/null +++ b/broker/patron_request/service/statemodels/returnables.json @@ -0,0 +1,516 @@ +{ + "type": "StateModel", + "name": "CrossLink Returnables State Model", + "desc": "Requester/Supplier workflow per ISO18626, including NCIP integration and ISO status events.", + "version": "1.0.0", + "states": [ + { + "name": "NEW", + "display": "New", + "desc": "Initial state after Patron Request is created (POST)", + "side": "REQUESTER", + "actions": [ + { + "name": "validate", + "desc": "Validate the request (e.g. check patron via NCIP LookupUser, if enabled)", + "transitions": [ + "VALIDATED" + ] + } + ] + }, + { + "name": "VALIDATED", + "display": "Validated", + "desc": "Patron Request is valid", + "side": "REQUESTER", + "actions": [ + { + "name": "send-request", + "desc": "Send ISO18626 request to the supplier or broker", + "transitions": [ + "SENT" + ] + } + ] + }, + { + "name": "SENT", + "display": "Sent", + "desc": "Request is sent", + "side": "REQUESTER", + "events": [ + { + "name": "expect-to-supply", + "desc": "Supplier indicates an intention to supply (ISO18626 ExpectToSupply)", + "transitions": [ + "SUPPLIER_LOCATED" + ] + }, + { + "name": "will-supply", + "desc": "Supplier will supply without conditions (ISO18626 WillSupply)", + "transitions": [ + "WILL_SUPPLY" + ] + }, + { + "name": "will-supply-conditional", + "desc": "Supplier will supply with conditions (ISO18626 WillSupply with conditions)", + "transitions": [ + "CONDITION_PENDING" + ] + }, + { + "name": "loaned", + "desc": "Supplier shipped the item (ISO18626 Loaned)", + "transitions": [ + "SHIPPED" + ] + }, + { + "name": "unfilled", + "desc": "Supplier cannot supply (ISO18626 Unfilled)", + "transitions": [ + "UNFILLED" + ] + } + ] + }, + { + "name": "SUPPLIER_LOCATED", + "display": "Supplier Located", + "desc": "After receiving ISO ExpectToSupply", + "side": "REQUESTER", + "actions": [ + { + "name": "cancel-request", + "desc": "Send ISO18626 Cancel request to supplier", + "transitions": [ + "CANCEL_PENDING" + ] + } + ], + "events": [ + { + "name": "will-supply", + "desc": "Supplier will supply without conditions (ISO18626 WillSupply)", + "transitions": [ + "WILL_SUPPLY" + ] + }, + { + "name": "will-supply-conditional", + "desc": "Supplier will supply with conditions (ISO18626 WillSupply with conditions)", + "transitions": [ + "CONDITION_PENDING" + ] + }, + { + "name": "unfilled", + "desc": "Supplier cannot supply (ISO18626 Unfilled)", + "transitions": [ + "UNFILLED" + ] + } + ] + }, + { + "name": "CONDITION_PENDING", + "display": "Condition Pending", + "desc": "Received supply conditions", + "side": "REQUESTER", + "actions": [ + { + "name": "accept-condition", + "desc": "Accept supplier conditions (ISO18626 Notification)", + "transitions": [ + "WILL_SUPPLY" + ] + }, + { + "name": "reject-condition", + "desc": "Reject supplier conditions (ISO18626 Cancel)", + "transitions": [ + "CANCEL_PENDING" + ] + } + ] + }, + { + "name": "WILL_SUPPLY", + "display": "Will Supply", + "desc": "Received ISO18626 WillSupply (without condition)", + "side": "REQUESTER", + "actions": [ + { + "name": "cancel-request", + "desc": "Send ISO18626 Cancel to supplier", + "transitions": [ + "CANCEL_PENDING" + ] + } + ], + "events": [ + { + "name": "loaned", + "desc": "Supplier shipped the item (ISO18626 Loaned)", + "transitions": [ + "SHIPPED" + ] + }, + { + "name": "unfilled", + "desc": "Supplier cannot supply (ISO18626 Unfilled)", + "transitions": [ + "UNFILLED" + ] + } + ] + }, + { + "name": "SHIPPED", + "display": "Shipped", + "desc": "After receiving ISO18626 Loaned", + "side": "REQUESTER", + "actions": [ + { + "name": "receive", + "desc": "Receive and accept item in local ILS (via NCIP AcceptItem if enabled); send ISO18626 Received", + "transitions": [ + "RECEIVED" + ] + } + ] + }, + { + "name": "RECEIVED", + "display": "Received", + "desc": "Item received and accepted into local ILS.", + "side": "REQUESTER", + "actions": [ + { + "name": "check-out", + "desc": "Check out item to patron (NCIP CheckOutItem)", + "transitions": [ + "CHECKED_OUT" + ] + } + ] + }, + { + "name": "CHECKED_OUT", + "display": "Checked Out", + "desc": "Item is checked out to patron", + "side": "REQUESTER", + "actions": [ + { + "name": "check-in", + "desc": "Check the item back-in (NCIP CheckInItem)", + "transitions": [ + "CHECKED_IN" + ] + } + ] + }, + { + "name": "CHECKED_IN", + "display": "Checked In", + "desc": "Item is checked back in to the local ILS", + "side": "REQUESTER", + "actions": [ + { + "name": "ship-return", + "desc": "Send ISO18626 ShippedReturn and delete the temporary item (NCIP DeleteItem)", + "transitions": [ + "SHIPPED_RETURNED" + ] + } + ] + }, + { + "name": "SHIPPED_RETURNED", + "display": "Shipped Returned", + "desc": "Item is in return shipment", + "side": "REQUESTER", + "events": [ + { + "name": "completed", + "desc": "Supplier/broker signals loan/copy completion", + "transitions": [ + "COMPLETED" + ] + } + ] + }, + { + "name": "CANCEL_PENDING", + "display": "Cancel Pending", + "desc": "ISO18626 Cancel action is sent to supplier/broker", + "side": "REQUESTER", + "events": [ + { + "name": "cancel-accepted", + "desc": "Supplier accepts cancellation", + "transitions": [ + "CANCELLED" + ] + } + ] + }, + { + "name": "COMPLETED", + "display": "Completed", + "desc": "Received LoanCompleted or CopyCompleted from the supplier/broker", + "side": "REQUESTER", + "terminal": true + }, + { + "name": "CANCELLED", + "display": "Cancelled", + "desc": "Cancel response is received", + "side": "REQUESTER", + "terminal": true + }, + { + "name": "UNFILLED", + "display": "Unfilled", + "desc": "Unfilled response is received", + "side": "REQUESTER", + "terminal": true + }, + { + "name": "NEW", + "display": "New", + "desc": "Item is created on the supplier side for local fulfillment", + "side": "SUPPLIER", + "actions": [ + { + "name": "validate", + "desc": "Validate the request, e.g institutional patron via NCIP LookupUser, if enabled", + "transitions": [ + "VALIDATED" + ] + } + ] + }, + { + "name": "VALIDATED", + "display": "Validated", + "desc": "Request is valid", + "side": "SUPPLIER", + "actions": [ + { + "name": "will-supply", + "desc": "Indicate supplier will supply and send ISO18626 WillSupply", + "transitions": [ + "WILL_SUPPLY" + ] + }, + { + "name": "cannot-supply", + "desc": "Indicate cannot supply and send ISO18626 Unfilled", + "transitions": [ + "UNFILLED" + ] + }, + { + "name": "add-condition", + "desc": "Indicate will supply with conditions and send ISO18626 WillSupply", + "transitions": [ + "CONDITION_PENDING" + ] + } + ], + "events": [ + { + "name": "cancel-request", + "desc": "Requester sent ISO18626 Cancel", + "transitions": [ + "CANCEL_REQUESTED" + ] + } + ] + }, + { + "name": "WILL_SUPPLY", + "display": "Will Supply", + "desc": "After manual will-supply or successful auto-responder", + "side": "SUPPLIER", + "actions": [ + { + "name": "add-condition", + "desc": "Add conditions and notify requester", + "transitions": [ + "CONDITION_PENDING" + ] + }, + { + "name": "ship", + "desc": "Mark shipped; send ISO Loaned; NCIP CheckOutItem", + "transitions": [ + "SHIPPED" + ] + }, + { + "name": "cannot-supply", + "desc": "Indicate cannot supply", + "transitions": [ + "UNFILLED" + ] + } + ], + "events": [ + { + "name": "cancel-request", + "desc": "Requester sends ISO Cancel", + "transitions": [ + "CANCEL_REQUESTED" + ] + } + ] + }, + { + "name": "CONDITION_PENDING", + "display": "Condition Pending", + "desc": "Conditions are sent with a special ISO WillSupply", + "side": "SUPPLIER", + "actions": [ + { + "name": "cannot-supply", + "desc": "Indicate cannot supply", + "transitions": [ + "UNFILLED" + ] + } + ], + "events": [ + { + "name": "conditions-accepted", + "desc": "Requester accepts conditions", + "transitions": [ + "CONDITION_ACCEPTED" + ] + }, + { + "name": "condition-rejected", + "desc": "Requester rejects conditions (auto-responder may mark unfilled)", + "transitions": [ + "UNFILLED" + ] + }, + { + "name": "cancel-request", + "desc": "Requester sends ISO Cancel", + "transitions": [ + "CANCEL_REQUESTED" + ] + } + ] + }, + { + "name": "CONDITION_ACCEPTED", + "display": "Condition Accepted", + "desc": "After receiving conditions accepted notification", + "side": "SUPPLIER", + "actions": [ + { + "name": "ship", + "desc": "Mark shipped; send ISO Loaned; NCIP CheckOutItem", + "transitions": [ + "SHIPPED" + ] + }, + { + "name": "cannot-supply", + "desc": "Indicate cannot supply", + "transitions": [ + "UNFILLED" + ] + } + ], + "events": [ + { + "name": "cancel-request", + "desc": "Requester sends ISO Cancel", + "transitions": [ + "CANCEL_REQUESTED" + ] + } + ] + }, + { + "name": "SHIPPED", + "display": "Shipped", + "desc": "After marking shipped and sending ISO Loaned; NCIP CheckOutItem", + "side": "SUPPLIER", + "events": [ + { + "name": "shipped-return", + "desc": "Requester sends ISO ShippedReturn", + "transitions": [ + "SHIPPED_RETURN" + ] + } + ] + }, + { + "name": "SHIPPED_RETURN", + "display": "Shipped Return", + "desc": "After receiving ISO ShippedReturn message", + "side": "SUPPLIER", + "actions": [ + { + "name": "mark-received", + "desc": "Mark returned item received and complete (CheckInItem if configured)", + "transitions": [ + "COMPLETED" + ] + } + ] + }, + { + "name": "CANCEL_REQUESTED", + "display": "Cancel Requested", + "desc": "After receiving ISO18626 Cancel from requester", + "side": "SUPPLIER", + "actions": [ + { + "name": "mark-cancelled", + "desc": "Confirm cancellation", + "transitions": [ + "CANCELLED" + ] + }, + { + "name": "will-supply", + "desc": "Resume supplying despite cancel request", + "transitions": [ + "WILL_SUPPLY" + ] + } + ] + }, + { + "name": "COMPLETED", + "display": "Completed", + "desc": "After marking received and successful NCIP CheckInItem (if configured)", + "side": "SUPPLIER", + "terminal": true + }, + { + "name": "CANCELLED", + "display": "Cancelled", + "desc": "After marking cancelled or automatically if auto-cancellation is on", + "side": "SUPPLIER", + "terminal": true + }, + { + "name": "UNFILLED", + "display": "Unfilled", + "desc": "After manual cannot-supply or automatically if auto-responder is on", + "side": "SUPPLIER", + "terminal": true + } + ] +} \ No newline at end of file From f722a41d4ecf2379ea18a6782e286b68110eaa83 Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Mon, 2 Feb 2026 21:04:54 -0500 Subject: [PATCH 04/11] Add service to hold state model in memory --- broker/patron_request/api/api-handler.go | 26 +-------- broker/patron_request/service/action.go | 26 --------- .../patron_request/service/action_mapping.go | 16 +++--- .../service/action_mapping_test.go | 23 ++++++-- broker/patron_request/service/statemodel.go | 56 +++++++++++++++++++ .../patron_request/api/api-handler_test.go | 9 ++- 6 files changed, 92 insertions(+), 64 deletions(-) create mode 100644 broker/patron_request/service/statemodel.go diff --git a/broker/patron_request/api/api-handler.go b/broker/patron_request/api/api-handler.go index bd4dc8a8..3eeb0fc7 100644 --- a/broker/patron_request/api/api-handler.go +++ b/broker/patron_request/api/api-handler.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "maps" "net/http" "sync" @@ -41,33 +40,10 @@ func (a *PatronRequestApiHandler) GetStateModelModelsReturnables(w http.Response //ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{ // Other: logParams, //}) - stateModel, _ := prservice.LoadReturnablesStateModel() - + stateModel := a.actionMappingService.SMService.GetStateModel("returnables") //Need to come up with a controlled vocab writeJsonResponse(w, *stateModel) } -func makeStateList(stateMap map[pr_db.PatronRequestState][]pr_db.PatronRequestAction, stateSide proapi.StateSide) []proapi.State { - stateList := make([]proapi.State, 0, len(stateMap)) - var state proapi.State - for k := range maps.Keys(stateMap) { - state.Name = string(k) - state.Side = stateSide - actionList := make([]proapi.Action, 0, len(stateMap[k])) - for _, prAction := range stateMap[k] { - var action proapi.Action - action.Name = string(prAction) - //Where do we get the transitions? - actionList = append(actionList, action) - } - state.Actions = &actionList - } - return stateList -} - -func strPtr(s string) *string { - return &s -} - func (a *PatronRequestApiHandler) GetPatronRequests(w http.ResponseWriter, r *http.Request) { logParams := map[string]string{"method": "GetPatronRequests"} ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{ diff --git a/broker/patron_request/service/action.go b/broker/patron_request/service/action.go index d412d69a..f4f148d0 100644 --- a/broker/patron_request/service/action.go +++ b/broker/patron_request/service/action.go @@ -1,7 +1,6 @@ package prservice import ( - "embed" "encoding/json" "encoding/xml" "errors" @@ -13,7 +12,6 @@ import ( "github.com/indexdata/crosslink/broker/handler" "github.com/indexdata/crosslink/broker/lms" pr_db "github.com/indexdata/crosslink/broker/patron_request/db" - proapi "github.com/indexdata/crosslink/broker/patron_request/oapi" "github.com/indexdata/crosslink/iso18626" "github.com/jackc/pgx/v5/pgtype" ) @@ -79,30 +77,6 @@ type PatronRequestActionService struct { actionMappingService ActionMappingService } -//go:embed statemodels -var modelFS embed.FS - -func LoadReturnablesStateModel() (*proapi.StateModel, error) { - path := "statemodels/returnables.json" - stateModel, err := loadStateModel(path) - if err != nil { - return nil, err - } - return stateModel, nil -} - -func loadStateModel(path string) (*proapi.StateModel, error) { - data, err := modelFS.ReadFile(path) - if err != nil { - return nil, err - } - - var stateModel proapi.StateModel - err = json.Unmarshal(data, &stateModel) - - return &stateModel, err -} - func CreatePatronRequestActionService(prRepo pr_db.PrRepo, eventBus events.EventBus, iso18626Handler handler.Iso18626HandlerInterface, lmsCreator lms.LmsCreator) *PatronRequestActionService { return &PatronRequestActionService{ prRepo: prRepo, diff --git a/broker/patron_request/service/action_mapping.go b/broker/patron_request/service/action_mapping.go index 41869e23..ae4a7df5 100644 --- a/broker/patron_request/service/action_mapping.go +++ b/broker/patron_request/service/action_mapping.go @@ -15,11 +15,18 @@ type ActionMapping interface { } type ActionMappingService struct { + SMService StateModelService +} + +func (r *ActionMappingService) NewActionMappingService() *ActionMappingService { + return &ActionMappingService{ + SMService: StateModelService{}, + } } func (r *ActionMappingService) GetActionMapping(pr pr_db.PatronRequest) ActionMapping { //At a future point, we will check the PatronRequest loan type to decide what kind of mapping service to return - return NewReturnableActionMapping() + return NewReturnableActionMapping(r.SMService.GetStateModel("returnable")) } type ReturnableActionMapping struct { @@ -28,13 +35,8 @@ type ReturnableActionMapping struct { } /* Constructor function to initialize the mappings for the returnables */ -func NewReturnableActionMapping() *ReturnableActionMapping { +func NewReturnableActionMapping(stateModel *proapi.StateModel) *ReturnableActionMapping { r := new(ReturnableActionMapping) - stateModel, err := LoadReturnablesStateModel() - - if err != nil { - return nil - } borrowerMap := make(map[pr_db.PatronRequestState][]pr_db.PatronRequestAction) lenderMap := make(map[pr_db.PatronRequestState][]pr_db.PatronRequestAction) diff --git a/broker/patron_request/service/action_mapping_test.go b/broker/patron_request/service/action_mapping_test.go index eca74c33..794eaea0 100644 --- a/broker/patron_request/service/action_mapping_test.go +++ b/broker/patron_request/service/action_mapping_test.go @@ -1,6 +1,7 @@ package prservice import ( + "slices" "testing" pr_db "github.com/indexdata/crosslink/broker/patron_request/db" @@ -30,7 +31,9 @@ func TestNewReturnableActionMapping(t *testing.T) { LenderStateCancelRequested: {LenderActionMarkCancelled, LenderActionWillSupply}, } - returnableActionMapping := NewReturnableActionMapping() + stateModel, err := LoadReturnablesStateModel() + assert.Nil(t, err) + returnableActionMapping := NewReturnableActionMapping(stateModel) assert.NotNil(t, returnableActionMapping) @@ -54,12 +57,19 @@ func TestIsActionAvailable(t *testing.T) { func TestGetActionsForPatronRequest(t *testing.T) { // Borrower - assert.Equal(t, []pr_db.PatronRequestAction{BorrowerActionValidate}, actionMappingService.GetActionMapping(pr_db.PatronRequest{}).GetActionsForPatronRequest(pr_db.PatronRequest{Side: SideBorrowing, State: BorrowerStateNew})) - assert.Equal(t, []pr_db.PatronRequestAction{}, actionMappingService.GetActionMapping(pr_db.PatronRequest{}).GetActionsForPatronRequest(pr_db.PatronRequest{Side: SideBorrowing, State: BorrowerStateCompleted})) + listCompare(t, []pr_db.PatronRequestAction{BorrowerActionValidate}, actionMappingService.GetActionMapping(pr_db.PatronRequest{}).GetActionsForPatronRequest(pr_db.PatronRequest{Side: SideBorrowing, State: BorrowerStateNew})) + listCompare(t, []pr_db.PatronRequestAction{}, actionMappingService.GetActionMapping(pr_db.PatronRequest{}).GetActionsForPatronRequest(pr_db.PatronRequest{Side: SideBorrowing, State: BorrowerStateCompleted})) // Lender - assert.Equal(t, []pr_db.PatronRequestAction{LenderActionAddCondition, LenderActionCannotSupply, LenderActionShip}, actionMappingService.GetActionMapping(pr_db.PatronRequest{}).GetActionsForPatronRequest(pr_db.PatronRequest{Side: SideLending, State: LenderStateWillSupply})) - assert.Equal(t, []pr_db.PatronRequestAction{}, actionMappingService.GetActionMapping(pr_db.PatronRequest{}).GetActionsForPatronRequest(pr_db.PatronRequest{Side: SideLending, State: LenderStateShipped})) + listCompare(t, []pr_db.PatronRequestAction{LenderActionAddCondition, LenderActionCannotSupply, LenderActionShip}, actionMappingService.GetActionMapping(pr_db.PatronRequest{}).GetActionsForPatronRequest(pr_db.PatronRequest{Side: SideLending, State: LenderStateWillSupply})) + listCompare(t, []pr_db.PatronRequestAction{}, actionMappingService.GetActionMapping(pr_db.PatronRequest{}).GetActionsForPatronRequest(pr_db.PatronRequest{Side: SideLending, State: LenderStateShipped})) +} + +func listCompare(t *testing.T, list1 []pr_db.PatronRequestAction, list2 []pr_db.PatronRequestAction) { + assert.Equal(t, len(list1), len(list2)) + for i := range list1 { + assert.True(t, slices.Contains(list2, list1[i])) + } } func mapCompare(t *testing.T, map1 map[pr_db.PatronRequestState][]pr_db.PatronRequestAction, map2 map[pr_db.PatronRequestState][]pr_db.PatronRequestAction) { @@ -67,5 +77,8 @@ func mapCompare(t *testing.T, map1 map[pr_db.PatronRequestState][]pr_db.PatronRe listOne := map1[stateName] listTwo := map2[stateName] assert.Equal(t, len(listOne), len(listTwo)) + for i := range listOne { + assert.True(t, slices.Contains(listTwo, listOne[i])) + } } } diff --git a/broker/patron_request/service/statemodel.go b/broker/patron_request/service/statemodel.go new file mode 100644 index 00000000..f2d7c331 --- /dev/null +++ b/broker/patron_request/service/statemodel.go @@ -0,0 +1,56 @@ +package prservice + +import ( + "embed" + "encoding/json" + + proapi "github.com/indexdata/crosslink/broker/patron_request/oapi" +) + +type StateModelService struct { + stateMap map[string]*proapi.StateModel +} + +func (s *StateModelService) GetStateModel(modelName string) *proapi.StateModel { + if s.stateMap == nil { + s.stateMap = make(map[string]*proapi.StateModel) + } + + stateModel, ok := s.stateMap[modelName] + + if ok { + return stateModel + } + + //In the future, this will vary depending on the modelName + stateModel, err := LoadReturnablesStateModel() + if err == nil { + s.stateMap[modelName] = stateModel + return stateModel + } + return nil +} + +//go:embed statemodels +var modelFS embed.FS + +func LoadReturnablesStateModel() (*proapi.StateModel, error) { + path := "statemodels/returnables.json" + stateModel, err := loadStateModel(path) + if err != nil { + return nil, err + } + return stateModel, nil +} + +func loadStateModel(path string) (*proapi.StateModel, error) { + data, err := modelFS.ReadFile(path) + if err != nil { + return nil, err + } + + var stateModel proapi.StateModel + err = json.Unmarshal(data, &stateModel) + + return &stateModel, err +} diff --git a/broker/test/patron_request/api/api-handler_test.go b/broker/test/patron_request/api/api-handler_test.go index 3c7784c3..7ac9a334 100644 --- a/broker/test/patron_request/api/api-handler_test.go +++ b/broker/test/patron_request/api/api-handler_test.go @@ -125,7 +125,14 @@ func TestCrud(t *testing.T) { assert.NoError(t, err, "failed to unmarshal patron request") assert.Len(t, foundPrs, 2) - assert.Equal(t, newPr.ID, foundPrs[1].ID) + foundInList := false + + for _, pr := range foundPrs { + if pr.ID == newPr.ID { + foundInList = true + } + } + assert.True(t, foundInList) // GET by id thisPrPath := basePath + "/" + newPr.ID From b3727b2c80bb4166d01fdbb3cd768135302450fd Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Mon, 2 Feb 2026 21:07:09 -0500 Subject: [PATCH 05/11] Linting --- broker/patron_request/service/action_mapping.go | 1 - broker/patron_request/service/action_mapping_test.go | 1 - broker/patron_request/service/action_test.go | 1 - 3 files changed, 3 deletions(-) diff --git a/broker/patron_request/service/action_mapping.go b/broker/patron_request/service/action_mapping.go index ae4a7df5..45acfdf0 100644 --- a/broker/patron_request/service/action_mapping.go +++ b/broker/patron_request/service/action_mapping.go @@ -53,7 +53,6 @@ func NewReturnableActionMapping(stateModel *proapi.StateModel) *ReturnableAction lenderMap[pr_db.PatronRequestState(state.Name)] = nameList } } - } r.borrowerStateActionMapping = borrowerMap diff --git a/broker/patron_request/service/action_mapping_test.go b/broker/patron_request/service/action_mapping_test.go index 794eaea0..a4a40e55 100644 --- a/broker/patron_request/service/action_mapping_test.go +++ b/broker/patron_request/service/action_mapping_test.go @@ -40,7 +40,6 @@ func TestNewReturnableActionMapping(t *testing.T) { mapCompare(t, returnableActionMapping.borrowerStateActionMapping, borrowerStateActionMapping) mapCompare(t, returnableActionMapping.lenderStateActionMapping, lenderStateActionMapping) - } var actionMappingService = ActionMappingService{} diff --git a/broker/patron_request/service/action_test.go b/broker/patron_request/service/action_test.go index 956aa7dd..5856462e 100644 --- a/broker/patron_request/service/action_test.go +++ b/broker/patron_request/service/action_test.go @@ -884,5 +884,4 @@ func TestLoadReturnableStateModel(t *testing.T) { stateModel, err := LoadReturnablesStateModel() assert.Nil(t, err) assert.NotNil(t, stateModel) - } From 8d8391f747c70dd7058e451ab5a8ee7bc70c1761 Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Tue, 3 Feb 2026 09:40:28 -0500 Subject: [PATCH 06/11] Update statemodel endpoint to take model as a parameter. Add test for retrieval endpoint --- broker/patron_request/api/api-handler.go | 19 +++++++++++++------ broker/patron_request/oapi/open-api.yaml | 12 ++++++++++-- .../patron_request/api/api-handler_test.go | 12 ++++++++++++ 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/broker/patron_request/api/api-handler.go b/broker/patron_request/api/api-handler.go index 3eeb0fc7..3b95cdb1 100644 --- a/broker/patron_request/api/api-handler.go +++ b/broker/patron_request/api/api-handler.go @@ -35,12 +35,19 @@ func NewApiHandler(prRepo pr_db.PrRepo, eventBus events.EventBus, tenant common. } } -func (a *PatronRequestApiHandler) GetStateModelModelsReturnables(w http.ResponseWriter, r *http.Request) { - //logParams := map[string]string{"method": "GetStateModelModelsReturnables"} - //ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{ - // Other: logParams, - //}) - stateModel := a.actionMappingService.SMService.GetStateModel("returnables") //Need to come up with a controlled vocab +func (a *PatronRequestApiHandler) GetStateModelModelsModel(w http.ResponseWriter, r *http.Request, model string, params proapi.GetStateModelModelsModelParams) { + /* + logParams := map[string]string{"method": "GetStateModelModelsReturnables"} + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{ + Other: logParams, + }) + */ + stateModel := a.actionMappingService.SMService.GetStateModel(model) //Need to come up with a controlled vocab + + if stateModel == nil { + addNotFoundError(w) + return + } writeJsonResponse(w, *stateModel) } diff --git a/broker/patron_request/oapi/open-api.yaml b/broker/patron_request/oapi/open-api.yaml index cc7df50d..bf323418 100644 --- a/broker/patron_request/oapi/open-api.yaml +++ b/broker/patron_request/oapi/open-api.yaml @@ -210,9 +210,17 @@ components: - actionResult paths: - /state_model/models/returnables: + /state_model/models/{model}: get: - summary: Retrieve the returnables state model + summary: Retrieve a state model by name + parameters: + - in: path + name: model + schema: + type: string + required: true + description: The name of the statemodel to retrieve + - $ref: '#/components/parameters/Tenant' responses: '200': description: Successful retrieval of state model diff --git a/broker/test/patron_request/api/api-handler_test.go b/broker/test/patron_request/api/api-handler_test.go index 7ac9a334..5a1b5143 100644 --- a/broker/test/patron_request/api/api-handler_test.go +++ b/broker/test/patron_request/api/api-handler_test.go @@ -365,6 +365,18 @@ func TestActionsToCompleteState(t *testing.T) { assert.Equal(t, string(prservice.LenderStateCompleted), foundPr.State) } +func TestGetReturnableStateModel(t *testing.T) { + + respBytes := httpRequest(t, "GET", "/state_model/models/returnables", []byte{}, 200) + var retrievedStateModel proapi.StateModel + err := json.Unmarshal(respBytes, &retrievedStateModel) + assert.NoError(t, err, "failed to unmarshal state model") + returnablesStateModel, _ := prservice.LoadReturnablesStateModel() + assert.Equal(t, returnablesStateModel.Name, retrievedStateModel.Name) + assert.Equal(t, returnablesStateModel.Desc, retrievedStateModel.Desc) + assert.Equal(t, len(*returnablesStateModel.States), len(*retrievedStateModel.States)) +} + func httpRequest(t *testing.T, method string, uriPath string, reqbytes []byte, expectStatus int) []byte { client := http.DefaultClient hreq, err := http.NewRequest(method, getLocalhostWithPort()+uriPath, bytes.NewBuffer(reqbytes)) From b0340eec86c72626a56bfc6405664cfa7f731efc Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Tue, 3 Feb 2026 11:42:33 -0500 Subject: [PATCH 07/11] Refactor action mapping into single service --- broker/patron_request/api/api-handler.go | 8 +------- .../patron_request/service/action_mapping.go | 20 ++++++++++--------- .../service/action_mapping_test.go | 2 +- .../patron_request/api/api-handler_test.go | 1 - 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/broker/patron_request/api/api-handler.go b/broker/patron_request/api/api-handler.go index 3b95cdb1..bbc89c0d 100644 --- a/broker/patron_request/api/api-handler.go +++ b/broker/patron_request/api/api-handler.go @@ -36,13 +36,7 @@ func NewApiHandler(prRepo pr_db.PrRepo, eventBus events.EventBus, tenant common. } func (a *PatronRequestApiHandler) GetStateModelModelsModel(w http.ResponseWriter, r *http.Request, model string, params proapi.GetStateModelModelsModelParams) { - /* - logParams := map[string]string{"method": "GetStateModelModelsReturnables"} - ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{ - Other: logParams, - }) - */ - stateModel := a.actionMappingService.SMService.GetStateModel(model) //Need to come up with a controlled vocab + stateModel := a.actionMappingService.SMService.GetStateModel(model) if stateModel == nil { addNotFoundError(w) diff --git a/broker/patron_request/service/action_mapping.go b/broker/patron_request/service/action_mapping.go index 45acfdf0..f416737e 100644 --- a/broker/patron_request/service/action_mapping.go +++ b/broker/patron_request/service/action_mapping.go @@ -7,12 +7,14 @@ import ( proapi "github.com/indexdata/crosslink/broker/patron_request/oapi" ) +/* type ActionMapping interface { IsActionAvailable(pr pr_db.PatronRequest, action pr_db.PatronRequestAction) bool GetActionsForPatronRequest(pr pr_db.PatronRequest) []pr_db.PatronRequestAction GetBorrowerActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction GetLenderActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction } +*/ type ActionMappingService struct { SMService StateModelService @@ -24,19 +26,19 @@ func (r *ActionMappingService) NewActionMappingService() *ActionMappingService { } } -func (r *ActionMappingService) GetActionMapping(pr pr_db.PatronRequest) ActionMapping { +func (r *ActionMappingService) GetActionMapping(pr pr_db.PatronRequest) *ActionMapping { //At a future point, we will check the PatronRequest loan type to decide what kind of mapping service to return - return NewReturnableActionMapping(r.SMService.GetStateModel("returnable")) + return NewActionMapping(r.SMService.GetStateModel("returnable")) } -type ReturnableActionMapping struct { +type ActionMapping struct { borrowerStateActionMapping map[pr_db.PatronRequestState][]pr_db.PatronRequestAction lenderStateActionMapping map[pr_db.PatronRequestState][]pr_db.PatronRequestAction } /* Constructor function to initialize the mappings for the returnables */ -func NewReturnableActionMapping(stateModel *proapi.StateModel) *ReturnableActionMapping { - r := new(ReturnableActionMapping) +func NewActionMapping(stateModel *proapi.StateModel) *ActionMapping { + r := new(ActionMapping) borrowerMap := make(map[pr_db.PatronRequestState][]pr_db.PatronRequestAction) lenderMap := make(map[pr_db.PatronRequestState][]pr_db.PatronRequestAction) @@ -61,15 +63,15 @@ func NewReturnableActionMapping(stateModel *proapi.StateModel) *ReturnableAction return r } -func (r *ReturnableActionMapping) GetBorrowerActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction { +func (r *ActionMapping) GetBorrowerActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction { return r.borrowerStateActionMapping } -func (r *ReturnableActionMapping) GetLenderActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction { +func (r *ActionMapping) GetLenderActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction { return r.lenderStateActionMapping } -func (r *ReturnableActionMapping) IsActionAvailable(pr pr_db.PatronRequest, action pr_db.PatronRequestAction) bool { +func (r *ActionMapping) IsActionAvailable(pr pr_db.PatronRequest, action pr_db.PatronRequestAction) bool { if pr.Side == SideBorrowing { return isActionAvailable(pr.State, action, r.borrowerStateActionMapping) } else { @@ -77,7 +79,7 @@ func (r *ReturnableActionMapping) IsActionAvailable(pr pr_db.PatronRequest, acti } } -func (r *ReturnableActionMapping) GetActionsForPatronRequest(pr pr_db.PatronRequest) []pr_db.PatronRequestAction { +func (r *ActionMapping) GetActionsForPatronRequest(pr pr_db.PatronRequest) []pr_db.PatronRequestAction { if pr.Side == SideBorrowing { return getActionsByStateFromMapping(pr.State, r.borrowerStateActionMapping) } else { diff --git a/broker/patron_request/service/action_mapping_test.go b/broker/patron_request/service/action_mapping_test.go index a4a40e55..a4770e59 100644 --- a/broker/patron_request/service/action_mapping_test.go +++ b/broker/patron_request/service/action_mapping_test.go @@ -33,7 +33,7 @@ func TestNewReturnableActionMapping(t *testing.T) { stateModel, err := LoadReturnablesStateModel() assert.Nil(t, err) - returnableActionMapping := NewReturnableActionMapping(stateModel) + returnableActionMapping := NewActionMapping(stateModel) assert.NotNil(t, returnableActionMapping) diff --git a/broker/test/patron_request/api/api-handler_test.go b/broker/test/patron_request/api/api-handler_test.go index 5a1b5143..d550fc0f 100644 --- a/broker/test/patron_request/api/api-handler_test.go +++ b/broker/test/patron_request/api/api-handler_test.go @@ -366,7 +366,6 @@ func TestActionsToCompleteState(t *testing.T) { } func TestGetReturnableStateModel(t *testing.T) { - respBytes := httpRequest(t, "GET", "/state_model/models/returnables", []byte{}, 200) var retrievedStateModel proapi.StateModel err := json.Unmarshal(respBytes, &retrievedStateModel) From 7d96b46763609403e423081b4006607fb004bbd7 Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Tue, 3 Feb 2026 12:17:20 -0500 Subject: [PATCH 08/11] Make loading state model function generic with model name parameter --- broker/patron_request/service/action_mapping.go | 2 +- broker/patron_request/service/action_mapping_test.go | 2 +- broker/patron_request/service/action_test.go | 2 +- broker/patron_request/service/statemodel.go | 6 +++--- broker/test/patron_request/api/api-handler_test.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/broker/patron_request/service/action_mapping.go b/broker/patron_request/service/action_mapping.go index f416737e..4f4d96a8 100644 --- a/broker/patron_request/service/action_mapping.go +++ b/broker/patron_request/service/action_mapping.go @@ -28,7 +28,7 @@ func (r *ActionMappingService) NewActionMappingService() *ActionMappingService { func (r *ActionMappingService) GetActionMapping(pr pr_db.PatronRequest) *ActionMapping { //At a future point, we will check the PatronRequest loan type to decide what kind of mapping service to return - return NewActionMapping(r.SMService.GetStateModel("returnable")) + return NewActionMapping(r.SMService.GetStateModel("returnables")) } type ActionMapping struct { diff --git a/broker/patron_request/service/action_mapping_test.go b/broker/patron_request/service/action_mapping_test.go index a4770e59..2e42dd82 100644 --- a/broker/patron_request/service/action_mapping_test.go +++ b/broker/patron_request/service/action_mapping_test.go @@ -31,7 +31,7 @@ func TestNewReturnableActionMapping(t *testing.T) { LenderStateCancelRequested: {LenderActionMarkCancelled, LenderActionWillSupply}, } - stateModel, err := LoadReturnablesStateModel() + stateModel, err := LoadStateModelByName("returnables") assert.Nil(t, err) returnableActionMapping := NewActionMapping(stateModel) diff --git a/broker/patron_request/service/action_test.go b/broker/patron_request/service/action_test.go index 5856462e..13b0af28 100644 --- a/broker/patron_request/service/action_test.go +++ b/broker/patron_request/service/action_test.go @@ -881,7 +881,7 @@ func TestCallNumberFromIllRequest(t *testing.T) { } func TestLoadReturnableStateModel(t *testing.T) { - stateModel, err := LoadReturnablesStateModel() + stateModel, err := LoadStateModelByName("returnables") assert.Nil(t, err) assert.NotNil(t, stateModel) } diff --git a/broker/patron_request/service/statemodel.go b/broker/patron_request/service/statemodel.go index f2d7c331..b976e0ad 100644 --- a/broker/patron_request/service/statemodel.go +++ b/broker/patron_request/service/statemodel.go @@ -23,7 +23,7 @@ func (s *StateModelService) GetStateModel(modelName string) *proapi.StateModel { } //In the future, this will vary depending on the modelName - stateModel, err := LoadReturnablesStateModel() + stateModel, err := LoadStateModelByName(modelName) if err == nil { s.stateMap[modelName] = stateModel return stateModel @@ -34,8 +34,8 @@ func (s *StateModelService) GetStateModel(modelName string) *proapi.StateModel { //go:embed statemodels var modelFS embed.FS -func LoadReturnablesStateModel() (*proapi.StateModel, error) { - path := "statemodels/returnables.json" +func LoadStateModelByName(modelName string) (*proapi.StateModel, error) { + path := "statemodels/" + modelName + ".json" stateModel, err := loadStateModel(path) if err != nil { return nil, err diff --git a/broker/test/patron_request/api/api-handler_test.go b/broker/test/patron_request/api/api-handler_test.go index d550fc0f..4c0c69f0 100644 --- a/broker/test/patron_request/api/api-handler_test.go +++ b/broker/test/patron_request/api/api-handler_test.go @@ -370,7 +370,7 @@ func TestGetReturnableStateModel(t *testing.T) { var retrievedStateModel proapi.StateModel err := json.Unmarshal(respBytes, &retrievedStateModel) assert.NoError(t, err, "failed to unmarshal state model") - returnablesStateModel, _ := prservice.LoadReturnablesStateModel() + returnablesStateModel, _ := prservice.LoadStateModelByName("returnables") assert.Equal(t, returnablesStateModel.Name, retrievedStateModel.Name) assert.Equal(t, returnablesStateModel.Desc, retrievedStateModel.Desc) assert.Equal(t, len(*returnablesStateModel.States), len(*retrievedStateModel.States)) From 1a1c3c90c30cd4cf1d194630313b0b6e9a3d5e7e Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Wed, 4 Feb 2026 09:17:45 -0500 Subject: [PATCH 09/11] Clean up comments. Revert go.mod/go.sum changes --- broker/go.mod | 6 ++---- broker/go.sum | 10 ---------- broker/patron_request/service/action_mapping.go | 11 +---------- broker/patron_request/service/statemodel.go | 1 - go.work.sum | 2 +- httpclient/go.mod | 4 ++-- httpclient/go.sum | 6 ++++-- illmock/go.mod | 13 +++++++++---- illmock/go.sum | 17 +++++++++++++++-- ncip/go.mod | 7 ++----- ncip/go.sum | 14 +++++++------- 11 files changed, 43 insertions(+), 48 deletions(-) diff --git a/broker/go.mod b/broker/go.mod index 11099ddd..7aac6ce6 100644 --- a/broker/go.mod +++ b/broker/go.mod @@ -6,6 +6,8 @@ require ( github.com/indexdata/crosslink/httpclient v0.0.0 github.com/indexdata/crosslink/illmock v0.0.0 github.com/indexdata/crosslink/iso18626 v0.0.0 + github.com/indexdata/crosslink/marcxml v0.0.0 + github.com/indexdata/crosslink/sru v0.0.0 ) replace ( @@ -27,7 +29,6 @@ require ( github.com/jackc/pgx/v5 v5.8.0 github.com/lib/pq v1.10.9 github.com/oapi-codegen/oapi-codegen/v2 v2.5.1 - github.com/oapi-codegen/runtime v1.1.2 github.com/stretchr/testify v1.11.1 github.com/testcontainers/testcontainers-go v0.40.0 github.com/testcontainers/testcontainers-go/modules/postgres v0.40.0 @@ -37,7 +38,6 @@ require ( dario.cat/mergo v1.0.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect @@ -59,8 +59,6 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect github.com/go-openapi/swag v0.23.1 // indirect - github.com/indexdata/crosslink v0.0.0-20260121111122-3096ccb3a8ea // indirect - github.com/indexdata/crosslink/marcxml v0.0.0-20260122210555-37edcda908a8 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect diff --git a/broker/go.sum b/broker/go.sum index d0ce281f..95631f17 100644 --- a/broker/go.sum +++ b/broker/go.sum @@ -6,10 +6,6 @@ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEK github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= -github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= -github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -78,8 +74,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DW github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030 h1:RP9TZZTGoBo2Jx04FEWaRmNhCX/Fk8VR04tX0FMRzik= github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030/go.mod h1:zmSHcE8JyK94EWZrV7VyjLr2QfRoj+EeEOttl9wm64U= -github.com/indexdata/crosslink v0.0.0-20260121111122-3096ccb3a8ea h1:xYeIBOOcz3gdVSCqGt2llJUQ9KXvXSSXtc8vhFVZxd4= -github.com/indexdata/crosslink v0.0.0-20260121111122-3096ccb3a8ea/go.mod h1:r0ibawAkcS/t9mThAE9T2riuuU+jd9B7qUKOkBDIoGk= github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72 h1:/ssOlpKK1EOeWMCOUrxCOCZP+0Z1LD58Xg1nrU01TAQ= github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72/go.mod h1:0sW6Szxv8GNU3LBtK6mgBKDEUnlovPfghiG9xi+i0R8= github.com/indexdata/xsd2goxsl v1.3.0 h1:LZGBORHnO6olHBtvc6hQEefymdRuiM77FgtW4pCek7g= @@ -96,7 +90,6 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -140,8 +133,6 @@ github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/oapi-codegen/oapi-codegen/v2 v2.5.1 h1:5vHNY1uuPBRBWqB2Dp0G7YB03phxLQZupZTIZaeorjc= github.com/oapi-codegen/oapi-codegen/v2 v2.5.1/go.mod h1:ro0npU1BWkcGpCgGD9QwPp44l5OIZ94tB3eabnT7DjQ= -github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= -github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= @@ -178,7 +169,6 @@ github.com/speakeasy-api/jsonpath v0.6.2 h1:Mys71yd6u8kuowNCR0gCVPlVAHCmKtoGXYoA github.com/speakeasy-api/jsonpath v0.6.2/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw= github.com/speakeasy-api/openapi-overlay v0.10.2 h1:VOdQ03eGKeiHnpb1boZCGm7x8Haj6gST0P3SGTX95GU= github.com/speakeasy-api/openapi-overlay v0.10.2/go.mod h1:n0iOU7AqKpNFfEt6tq7qYITC4f0yzVVdFw0S7hukemg= -github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= diff --git a/broker/patron_request/service/action_mapping.go b/broker/patron_request/service/action_mapping.go index 4f4d96a8..a2f031e5 100644 --- a/broker/patron_request/service/action_mapping.go +++ b/broker/patron_request/service/action_mapping.go @@ -7,15 +7,6 @@ import ( proapi "github.com/indexdata/crosslink/broker/patron_request/oapi" ) -/* -type ActionMapping interface { - IsActionAvailable(pr pr_db.PatronRequest, action pr_db.PatronRequestAction) bool - GetActionsForPatronRequest(pr pr_db.PatronRequest) []pr_db.PatronRequestAction - GetBorrowerActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction - GetLenderActionsMap() map[pr_db.PatronRequestState][]pr_db.PatronRequestAction -} -*/ - type ActionMappingService struct { SMService StateModelService } @@ -27,7 +18,7 @@ func (r *ActionMappingService) NewActionMappingService() *ActionMappingService { } func (r *ActionMappingService) GetActionMapping(pr pr_db.PatronRequest) *ActionMapping { - //At a future point, we will check the PatronRequest loan type to decide what kind of mapping service to return + //TODO: check the PatronRequest loan type to decide what kind of state model/mapping to return return NewActionMapping(r.SMService.GetStateModel("returnables")) } diff --git a/broker/patron_request/service/statemodel.go b/broker/patron_request/service/statemodel.go index b976e0ad..6227af3b 100644 --- a/broker/patron_request/service/statemodel.go +++ b/broker/patron_request/service/statemodel.go @@ -22,7 +22,6 @@ func (s *StateModelService) GetStateModel(modelName string) *proapi.StateModel { return stateModel } - //In the future, this will vary depending on the modelName stateModel, err := LoadStateModelByName(modelName) if err == nil { s.stateMap[modelName] = stateModel diff --git a/go.work.sum b/go.work.sum index 7ea1bb9f..67e12fb8 100644 --- a/go.work.sum +++ b/go.work.sum @@ -624,7 +624,6 @@ github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHa github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= @@ -672,6 +671,7 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weK github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/indexdata/crosslink v0.0.0-20260121111122-3096ccb3a8ea/go.mod h1:r0ibawAkcS/t9mThAE9T2riuuU+jd9B7qUKOkBDIoGk= github.com/indexdata/go-utils v0.0.0-20240626111027-28caddc0fd2b/go.mod h1:0sW6Szxv8GNU3LBtK6mgBKDEUnlovPfghiG9xi+i0R8= github.com/intel/goresctrl v0.3.0 h1:K2D3GOzihV7xSBedGxONSlaw/un1LZgWsc9IfqipN4c= github.com/intel/goresctrl v0.3.0/go.mod h1:fdz3mD85cmP9sHD8JUlrNWAxvwM86CrbmVXltEKd7zk= diff --git a/httpclient/go.mod b/httpclient/go.mod index e4aeacf7..67f3f70c 100644 --- a/httpclient/go.mod +++ b/httpclient/go.mod @@ -5,9 +5,9 @@ go 1.25 require github.com/stretchr/testify v1.11.1 require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/httpclient/go.sum b/httpclient/go.sum index d8e1aa73..3aa9f6ad 100644 --- a/httpclient/go.sum +++ b/httpclient/go.sum @@ -1,5 +1,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -8,7 +9,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= diff --git a/illmock/go.mod b/illmock/go.mod index 99a80289..6ee25f54 100644 --- a/illmock/go.mod +++ b/illmock/go.mod @@ -5,28 +5,33 @@ go 1.25 require ( github.com/indexdata/crosslink/httpclient v0.0.0 github.com/indexdata/crosslink/iso18626 v0.0.0 + github.com/indexdata/crosslink/marcxml v0.0.0 + github.com/indexdata/crosslink/sru v0.0.0 github.com/indexdata/crosslink/ncip v0.0.0 + github.com/indexdata/crosslink/directory v0.0.0 ) replace ( - github.com/indexdata/crosslink/directory => ../directory github.com/indexdata/crosslink/httpclient => ../httpclient github.com/indexdata/crosslink/iso18626 => ../iso18626 github.com/indexdata/crosslink/marcxml => ../marcxml - github.com/indexdata/crosslink/ncip => ../ncip github.com/indexdata/crosslink/sru => ../sru + github.com/indexdata/crosslink/ncip => ../ncip + github.com/indexdata/crosslink/directory => ../directory ) require ( github.com/google/uuid v1.6.0 github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030 github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72 + github.com/oapi-codegen/runtime v1.1.2 github.com/stretchr/testify v1.11.1 ) require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/illmock/go.sum b/illmock/go.sum index 72ae1394..ff7efe0b 100644 --- a/illmock/go.sum +++ b/illmock/go.sum @@ -1,18 +1,31 @@ +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030 h1:RP9TZZTGoBo2Jx04FEWaRmNhCX/Fk8VR04tX0FMRzik= github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030/go.mod h1:zmSHcE8JyK94EWZrV7VyjLr2QfRoj+EeEOttl9wm64U= github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72 h1:/ssOlpKK1EOeWMCOUrxCOCZP+0Z1LD58Xg1nrU01TAQ= github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72/go.mod h1:0sW6Szxv8GNU3LBtK6mgBKDEUnlovPfghiG9xi+i0R8= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= +github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/ncip/go.mod b/ncip/go.mod index 20202d25..81ceb225 100644 --- a/ncip/go.mod +++ b/ncip/go.mod @@ -8,10 +8,7 @@ require ( ) require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rogpeppe/go-internal v1.13.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ncip/go.sum b/ncip/go.sum index bbdce919..c14175c1 100644 --- a/ncip/go.sum +++ b/ncip/go.sum @@ -1,12 +1,12 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72 h1:/ssOlpKK1EOeWMCOUrxCOCZP+0Z1LD58Xg1nrU01TAQ= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/indexdata/go-utils v0.0.0-20250311160228-a87f76a1f868 h1:GKZL+YkrCuzL7N9lewA6uuJhjzgOkb4Az0F9+1xmbeI= +github.com/indexdata/go-utils v0.0.0-20250311160228-a87f76a1f868/go.mod h1:0sW6Szxv8GNU3LBtK6mgBKDEUnlovPfghiG9xi+i0R8= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From cedc508c6e55e1fff9f22cf35fc5eaceca276762 Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Wed, 4 Feb 2026 10:37:10 -0500 Subject: [PATCH 10/11] Fix collision in yaml, fix imports --- broker/oapi/open-api.yaml | 6 ++++-- broker/patron_request/service/action_mapping.go | 2 +- broker/patron_request/service/statemodel.go | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/broker/oapi/open-api.yaml b/broker/oapi/open-api.yaml index 59ade5a5..9033a2e3 100644 --- a/broker/oapi/open-api.yaml +++ b/broker/oapi/open-api.yaml @@ -590,7 +590,7 @@ components: type: array description: List of all events that may be triggered to the request when in this state items: - $ref: '#/components/schemas/Event' + $ref: '#/components/schemas/SMEvent' terminal: type: boolean description: Indicates if the state is terminal (meaning no actions or events are allowed in this state) @@ -622,7 +622,7 @@ components: required: - name - Event: + SMEvent: type: object title: Event additionalProperties: false @@ -1188,6 +1188,8 @@ paths: /state_model/models/{model}: get: summary: Retrieve a state model by name + tags: + - patron-requests-api parameters: - in: path name: model diff --git a/broker/patron_request/service/action_mapping.go b/broker/patron_request/service/action_mapping.go index a2f031e5..7bca086f 100644 --- a/broker/patron_request/service/action_mapping.go +++ b/broker/patron_request/service/action_mapping.go @@ -4,7 +4,7 @@ import ( "slices" pr_db "github.com/indexdata/crosslink/broker/patron_request/db" - proapi "github.com/indexdata/crosslink/broker/patron_request/oapi" + "github.com/indexdata/crosslink/broker/patron_request/proapi" ) type ActionMappingService struct { diff --git a/broker/patron_request/service/statemodel.go b/broker/patron_request/service/statemodel.go index 6227af3b..fdd7e7b0 100644 --- a/broker/patron_request/service/statemodel.go +++ b/broker/patron_request/service/statemodel.go @@ -4,7 +4,7 @@ import ( "embed" "encoding/json" - proapi "github.com/indexdata/crosslink/broker/patron_request/oapi" + "github.com/indexdata/crosslink/broker/patron_request/proapi" ) type StateModelService struct { From 9e32d389a0e331b2de9a1f7dcfb85b40ba83b43c Mon Sep 17 00:00:00 2001 From: Kurt Nordstrom Date: Wed, 4 Feb 2026 10:53:27 -0500 Subject: [PATCH 11/11] Fix broken test file --- broker/test/patron_request/api/api-handler_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/broker/test/patron_request/api/api-handler_test.go b/broker/test/patron_request/api/api-handler_test.go index bf157c79..c3d32230 100644 --- a/broker/test/patron_request/api/api-handler_test.go +++ b/broker/test/patron_request/api/api-handler_test.go @@ -120,20 +120,8 @@ func TestCrud(t *testing.T) { err = json.Unmarshal(respBytes, &foundPrs) assert.NoError(t, err, "failed to unmarshal patron request") -<<<<<<< HEAD - assert.Len(t, foundPrs, 2) - foundInList := false - - for _, pr := range foundPrs { - if pr.ID == newPr.ID { - foundInList = true - } - } - assert.True(t, foundInList) -======= assert.Equal(t, int64(1), foundPrs.About.Count) assert.Equal(t, *newPr.Id, foundPrs.Items[0].Id) ->>>>>>> main // GET by id thisPrPath := basePath + "/" + *newPr.Id