diff --git a/broker/Makefile b/broker/Makefile index 87041a0d..cf17c6bd 100644 --- a/broker/Makefile +++ b/broker/Makefile @@ -95,7 +95,6 @@ deps-update: $(GO) mod tidy deps-update-tools: - $(GO) get github.com/indexdata/xsd2goxsl@latest $(GO) get github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest $(GO) mod tidy diff --git a/broker/go.mod b/broker/go.mod index 8cf10dd2..511900b8 100644 --- a/broker/go.mod +++ b/broker/go.mod @@ -24,11 +24,11 @@ replace ( require ( github.com/dustin/go-humanize v1.0.1 + github.com/go-playground/validator/v10 v10.30.1 github.com/golang-migrate/migrate/v4 v4.19.1 github.com/google/uuid v1.6.0 github.com/indexdata/cql-go v1.0.1-0.20260320114910-316aba36a2ce github.com/indexdata/go-utils v0.0.0-20260218142542-28abe67711aa - github.com/indexdata/xsd2goxsl v1.3.0 github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6 github.com/jackc/pgx/v5 v5.9.1 github.com/lib/pq v1.12.0 @@ -60,18 +60,22 @@ require ( github.com/dprotaso/go-yit v0.0.0-20250512143907-c109d19d21e6 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/getkin/kin-openapi v0.133.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect 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/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // 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 github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/compress v1.18.5 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20260324052639-156f7da3f749 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mailru/easyjson v0.9.0 // indirect diff --git a/broker/go.sum b/broker/go.sum index ea372789..8685675b 100644 --- a/broker/go.sum +++ b/broker/go.sum @@ -54,6 +54,8 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= +github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -68,6 +70,14 @@ github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMK github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= +github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA= @@ -84,8 +94,6 @@ github.com/indexdata/cql-go v1.0.1-0.20260320114910-316aba36a2ce h1:3jQAjgancsKw github.com/indexdata/cql-go v1.0.1-0.20260320114910-316aba36a2ce/go.mod h1:zmSHcE8JyK94EWZrV7VyjLr2QfRoj+EeEOttl9wm64U= github.com/indexdata/go-utils v0.0.0-20260218142542-28abe67711aa h1:cXVO434D+Guc1a1jcIB/ga5xO5VyWx1He1R0gJov7MY= github.com/indexdata/go-utils v0.0.0-20260218142542-28abe67711aa/go.mod h1:0sW6Szxv8GNU3LBtK6mgBKDEUnlovPfghiG9xi+i0R8= -github.com/indexdata/xsd2goxsl v1.3.0 h1:LZGBORHnO6olHBtvc6hQEefymdRuiM77FgtW4pCek7g= -github.com/indexdata/xsd2goxsl v1.3.0/go.mod h1:8sXNGyaUfpRCgSCamB5p9GQNLyPHF5N2MYBrhWSss8Y= github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6 h1:D/V0gu4zQ3cL2WKeVNVM4r2gLxGGf6McLwgXzRTo2RQ= github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -108,6 +116,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.12.0 h1:mC1zeiNamwKBecjHarAr26c/+d8V5w/u4J0I/yASbJo= github.com/lib/pq v1.12.0/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/lufia/plan9stats v0.0.0-20260324052639-156f7da3f749 h1:Qj3hTcdWH8uMZDI41HNuTuJN525C7NBrbtH5kSO6fPk= diff --git a/broker/patron_request/api/api-handler.go b/broker/patron_request/api/api-handler.go index efe0166d..5e4dd42d 100644 --- a/broker/patron_request/api/api-handler.go +++ b/broker/patron_request/api/api-handler.go @@ -4,10 +4,12 @@ import ( "context" "encoding/json" "errors" + "fmt" "net/http" "strings" "time" + "github.com/go-playground/validator/v10" "github.com/google/uuid" "github.com/indexdata/cql-go/cqlbuilder" "github.com/indexdata/crosslink/broker/api" @@ -29,6 +31,10 @@ type ActionTaskProcessor interface { ProcessInvokeActionTask(ctx common.ExtendedContext, event events.Event) (events.Event, error) } +var illRequestValidator = validator.New(validator.WithRequiredStructEnabled()) +var brokerSymbol = utils.GetEnv("BROKER_SYMBOL", "ISIL:BROKER") +var errInvalidPatronRequest = errors.New("invalid patron request") + type PatronRequestApiHandler struct { limitDefault int32 prRepo pr_db.PrRepo @@ -198,11 +204,17 @@ func (a *PatronRequestApiHandler) PostPatronRequests(w http.ResponseWriter, r *h return } newPr.RequesterSymbol = &symbol - dbreq, err := a.toDbPatronRequest(ctx, newPr, params.XOkapiTenant) + creationTime := pgtype.Timestamp{Valid: true, Time: time.Now()} + illRequest, requesterReqId, err := a.parseAndValidateIllRequest(ctx, &newPr, creationTime.Time) if err != nil { + if errors.Is(err, errInvalidPatronRequest) { + addBadRequestError(ctx, w, err) + return + } addInternalError(ctx, w, err) return } + dbreq := buildDbPatronRequest(&newPr, params.XOkapiTenant, creationTime, requesterReqId, illRequest) pr, err := a.prRepo.CreatePatronRequest(ctx, (pr_db.CreatePatronRequestParams)(dbreq)) if err != nil { var pgErr *pgconn.PgError @@ -551,44 +563,40 @@ func toString(text pgtype.Text) *string { return value } -func (a *PatronRequestApiHandler) toDbPatronRequest(ctx common.ExtendedContext, request proapi.CreatePatronRequest, tenant *string) (pr_db.PatronRequest, error) { - creationTime := pgtype.Timestamp{Valid: true, Time: time.Now()} - var id string +func (a *PatronRequestApiHandler) parseAndValidateIllRequest( + ctx common.ExtendedContext, + request *proapi.CreatePatronRequest, + creationTime time.Time, +) (iso18626.Request, string, error) { + if request.RequesterSymbol == nil || *request.RequesterSymbol == "" { + return iso18626.Request{}, "", fmt.Errorf("%w: requesterSymbol must be specified", errInvalidPatronRequest) + } + reqSymbolType, reqSymbolValue, err := parseAgencySymbol(*request.RequesterSymbol) + if err != nil { + return iso18626.Request{}, "", fmt.Errorf("%w: requesterSymbol: %w", errInvalidPatronRequest, err) + } + var requesterReqId string if request.Id != nil { - id = *request.Id + requesterReqId = *request.Id } else { - prefix := strings.SplitN(*request.RequesterSymbol, ":", 2)[1] - hrid, err := a.prRepo.GetNextHrid(ctx, prefix) + hrid, err := a.prRepo.GetNextHrid(ctx, reqSymbolValue) if err != nil { - return pr_db.PatronRequest{}, err + return iso18626.Request{}, "", err } - id = hrid - } - var illRequest iso18626.Request - if request.IllRequest != nil { - illRequestBytes := utils.Must(json.Marshal(request.IllRequest)) - err := json.Unmarshal(illRequestBytes, &illRequest) - if err != nil { - return pr_db.PatronRequest{}, err - } - illRequest.Header.Timestamp = utils.XSDDateTime{Time: creationTime.Time} - illRequest.Header.RequestingAgencyRequestId = id + requesterReqId = hrid + } + illRequest, err := parseAndValidateIllRequestPayload( + request.IllRequest, + reqSymbolType, + reqSymbolValue, + requesterReqId, + creationTime, + ) + if err != nil { + return iso18626.Request{}, "", err } - return pr_db.PatronRequest{ - ID: id, - Timestamp: creationTime, - State: prservice.BorrowerStateNew, - Side: prservice.SideBorrowing, - Patron: getDbText(request.Patron), - RequesterSymbol: getDbText(request.RequesterSymbol), - SupplierSymbol: getDbText(nil), - IllRequest: illRequest, - Tenant: getDbText(tenant), - RequesterReqID: getDbText(&id), - // LastAction, LastActionOutcome and LastActionResult are not set on creation - // they will be updated when the first action is executed. - }, nil + return illRequest, requesterReqId, nil } func getId(id string) string { @@ -608,6 +616,85 @@ func getDbText(value *string) pgtype.Text { } } +func parseAgencySymbol(symbol string) (string, string, error) { + scheme, value, ok := strings.Cut(symbol, ":") + if !ok || scheme == "" || value == "" { + return "", "", fmt.Errorf("expected format SCHEME:VALUE, got %q", symbol) + } + return scheme, value, nil +} + +func parseAndValidateIllRequestPayload( + rawIllRequest map[string]interface{}, + reqSymbolType string, + reqSymbolValue string, + requesterReqId string, + creationTime time.Time, +) (iso18626.Request, error) { + var illRequest iso18626.Request + if rawIllRequest == nil { + return iso18626.Request{}, fmt.Errorf("%w: missing required illRequest payload", errInvalidPatronRequest) + } + illRequestBytes, err := json.Marshal(rawIllRequest) + if err != nil { + return iso18626.Request{}, fmt.Errorf("%w: illRequest: %w", errInvalidPatronRequest, err) + } + err = json.Unmarshal(illRequestBytes, &illRequest) + if err != nil { + return iso18626.Request{}, fmt.Errorf("%w: illRequest: %w", errInvalidPatronRequest, err) + } + suppSymbolType, suppSymbolValue, err := parseAgencySymbol(brokerSymbol) + if err != nil { + return iso18626.Request{}, fmt.Errorf("invalid BROKER_SYMBOL %q: %w", brokerSymbol, err) + } + illRequest.Header.RequestingAgencyId = iso18626.TypeAgencyId{ + AgencyIdType: iso18626.TypeSchemeValuePair{Text: reqSymbolType}, + AgencyIdValue: reqSymbolValue, + } + illRequest.Header.SupplyingAgencyId = iso18626.TypeAgencyId{ + AgencyIdType: iso18626.TypeSchemeValuePair{Text: suppSymbolType}, + AgencyIdValue: suppSymbolValue, + } + illRequest.Header.Timestamp = utils.XSDDateTime{Time: creationTime} + illRequest.Header.RequestingAgencyRequestId = requesterReqId + if err = validateIllRequest(illRequest); err != nil { + return iso18626.Request{}, fmt.Errorf("%w: invalid illRequest: %w", errInvalidPatronRequest, err) + } + return illRequest, nil +} + +func buildDbPatronRequest( + request *proapi.CreatePatronRequest, + tenant *string, + creationTime pgtype.Timestamp, + requesterReqId string, + illRequest iso18626.Request, +) pr_db.PatronRequest { + return pr_db.PatronRequest{ + ID: requesterReqId, + Timestamp: creationTime, + State: prservice.BorrowerStateNew, + Side: prservice.SideBorrowing, + Patron: getDbText(request.Patron), + RequesterSymbol: getDbText(request.RequesterSymbol), + SupplierSymbol: getDbText(nil), + IllRequest: illRequest, + Tenant: getDbText(tenant), + RequesterReqID: getDbText(&requesterReqId), + // LastAction, LastActionOutcome and LastActionResult are not set on creation + // they will be updated when the first action is executed. + } +} + +func validateIllRequest(request iso18626.Request) error { + requestForValidation := request + if requestForValidation.Header.MultipleItemRequestId == "" { + //schema workaround + requestForValidation.Header.MultipleItemRequestId = "#empty" + } + return illRequestValidator.Struct(requestForValidation) +} + func toApiItem(item pr_db.Item) proapi.PrItem { return proapi.PrItem{ Id: item.ID, diff --git a/broker/patron_request/api/api-handler_test.go b/broker/patron_request/api/api-handler_test.go index cf08baad..3c1c05ae 100644 --- a/broker/patron_request/api/api-handler_test.go +++ b/broker/patron_request/api/api-handler_test.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" "testing" + "time" "github.com/google/uuid" "github.com/indexdata/crosslink/broker/common" @@ -112,7 +113,19 @@ func TestGetPatronRequestsWithRequesterReqId(t *testing.T) { func TestPostPatronRequests(t *testing.T) { handler := NewPrApiHandler(new(PrRepoError), mockEventBus, mockEventRepo, common.NewTenant(""), 10) - toCreate := proapi.PatronRequest{Id: "1", RequesterSymbol: &symbol} + id := "1" + toCreate := proapi.CreatePatronRequest{ + Id: &id, + RequesterSymbol: &symbol, + IllRequest: map[string]interface{}{ + "bibliographicInfo": map[string]interface{}{ + "title": "test", + }, + "serviceInfo": map[string]interface{}{ + "serviceType": "Copy", + }, + }, + } jsonBytes, err := json.Marshal(toCreate) assert.NoError(t, err, "failed to marshal patron request") req, err := http.NewRequest("POST", "/", bytes.NewBuffer(jsonBytes)) @@ -148,6 +161,26 @@ func TestPostPatronRequestsInvalidJson(t *testing.T) { assert.Contains(t, rr.Body.String(), "invalid character") } +func TestPostPatronRequestsInvalidIllRequestShape(t *testing.T) { + handler := NewPrApiHandler(new(PrRepoError), mockEventBus, mockEventRepo, common.NewTenant(""), 10) + toCreate := proapi.CreatePatronRequest{ + Id: ptr("1"), + RequesterSymbol: &symbol, + IllRequest: map[string]interface{}{ + "header": "invalid", + }, + } + jsonBytes, err := json.Marshal(toCreate) + assert.NoError(t, err, "failed to marshal patron request") + req, err := http.NewRequest("POST", "/", bytes.NewBuffer(jsonBytes)) + assert.NoError(t, err, "failed to create request") + rr := httptest.NewRecorder() + tenant := proapi.Tenant("test-lib") + handler.PostPatronRequests(rr, req, proapi.PostPatronRequestsParams{XOkapiTenant: &tenant}) + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "illRequest") +} + func TestDeletePatronRequestsIdNotFound(t *testing.T) { handler := NewPrApiHandler(new(PrRepoError), mockEventBus, mockEventRepo, common.NewTenant(""), 10) req, _ := http.NewRequest("POST", "/", nil) @@ -361,23 +394,72 @@ func TestGetPatronRequestsIdNotificationsErrorGettingEvents(t *testing.T) { assert.Contains(t, rr.Body.String(), "DB error") } -func TestToDbPatronRequest(t *testing.T) { +func TestParseAndValidateIllRequestAndBuildDbPatronRequest(t *testing.T) { handler := NewPrApiHandler(new(PrRepoError), mockEventBus, mockEventRepo, common.NewTenant(""), 10) ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + creationTime := time.Now() id := uuid.NewString() + reqWithID := &proapi.CreatePatronRequest{ + Id: &id, + RequesterSymbol: &symbol, + IllRequest: map[string]interface{}{ + "serviceInfo": map[string]interface{}{ + "serviceType": "Copy", + }, + "bibliographicInfo": map[string]interface{}{ + "title": "Test title", + }, + }, + } - pr, err := handler.toDbPatronRequest(ctx, proapi.CreatePatronRequest{Id: &id, RequesterSymbol: &symbol}, nil) + illRequest, requesterReqID, err := handler.parseAndValidateIllRequest(ctx, reqWithID, creationTime) assert.NoError(t, err) + assert.Equal(t, id, requesterReqID) + pr := buildDbPatronRequest(reqWithID, nil, pgtype.Timestamp{Valid: true, Time: creationTime}, requesterReqID, illRequest) assert.Equal(t, id, pr.ID) assert.True(t, pr.Timestamp.Valid) assert.True(t, pr.RequesterReqID.Valid) assert.Equal(t, id, pr.RequesterReqID.String) assert.False(t, pr.SupplierSymbol.Valid) - pr, err = handler.toDbPatronRequest(ctx, proapi.CreatePatronRequest{RequesterSymbol: &symbol}, nil) - assert.NoError(t, err) - assert.Equal(t, "REQ-1", pr.ID) - assert.True(t, pr.Timestamp.Valid) + reqWithoutID := &proapi.CreatePatronRequest{RequesterSymbol: &symbol} + _, _, err = handler.parseAndValidateIllRequest(ctx, reqWithoutID, creationTime) + assert.Error(t, err) + assert.True(t, errors.Is(err, errInvalidPatronRequest)) +} + +func TestParseAndValidateIllRequestInvalidRequesterSymbol(t *testing.T) { + handler := NewPrApiHandler(new(PrRepoError), mockEventBus, mockEventRepo, common.NewTenant(""), 10) + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + invalidSymbol := "REQ" + + _, _, err := handler.parseAndValidateIllRequest(ctx, &proapi.CreatePatronRequest{RequesterSymbol: &invalidSymbol}, time.Now()) + assert.Error(t, err) + assert.True(t, errors.Is(err, errInvalidPatronRequest)) +} + +func TestParseAndValidateIllRequestInvalidBrokerSymbol(t *testing.T) { + handler := NewPrApiHandler(new(PrRepoError), mockEventBus, mockEventRepo, common.NewTenant(""), 10) + ctx := common.CreateExtCtxWithArgs(context.Background(), &common.LoggerArgs{}) + previousBrokerSymbol := brokerSymbol + brokerSymbol = "BROKER" + defer func() { + brokerSymbol = previousBrokerSymbol + }() + + _, _, err := handler.parseAndValidateIllRequest(ctx, &proapi.CreatePatronRequest{ + RequesterSymbol: &symbol, + IllRequest: map[string]interface{}{ + "serviceInfo": map[string]interface{}{ + "serviceType": "Copy", + }, + "bibliographicInfo": map[string]interface{}{ + "title": "Test title", + }, + }, + }, time.Now()) + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid BROKER_SYMBOL") } type PrRepoError struct { @@ -443,3 +525,7 @@ type MockEventBus struct { mock.Mock events.EventBus } + +func ptr(value string) *string { + return &value +} diff --git a/broker/test/patron_request/api/api-handler_test.go b/broker/test/patron_request/api/api-handler_test.go index 2ad6bb73..f4e0e8aa 100644 --- a/broker/test/patron_request/api/api-handler_test.go +++ b/broker/test/patron_request/api/api-handler_test.go @@ -484,6 +484,37 @@ func TestActionsToCompleteState(t *testing.T) { assert.True(t, len(events) > 5) } +func TestPostPatronRequestRejectsInvalidIllRequest(t *testing.T) { + requesterSymbol := "localISIL:REQ" + uuid.NewString() + + reqPeer := apptest.CreatePeerWithModeAndVendor(t, illRepo, requesterSymbol, adapter.MOCK_CLIENT_URL, app.BROKER_MODE, directory.CrossLink, + directory.Entry{ + LmsConfig: &directory.LmsConfig{ + FromAgency: "from-agency", + Address: ncipMockUrl, + }, + }) + assert.NotNil(t, reqPeer) + + newPr := proapi.CreatePatronRequest{ + RequesterSymbol: &requesterSymbol, + IllRequest: map[string]interface{}{ + "bibliographicInfo": map[string]interface{}{ + "title": "Invalid request", + }, + "serviceInfo": map[string]interface{}{ + "serviceType": "Broken", + }, + }, + } + newPrBytes, err := json.Marshal(newPr) + assert.NoError(t, err, "failed to marshal patron request") + + respBytes := httpRequest(t, "POST", basePath, newPrBytes, 400) + assert.Contains(t, string(respBytes), "invalid illRequest") + assert.Contains(t, string(respBytes), "ServiceType") +} + func TestGetReturnableStateModel(t *testing.T) { respBytes := httpRequest(t, "GET", "/state_model/models/returnables", []byte{}, 200) var retrievedStateModel proapi.StateModel diff --git a/broker/tools.go b/broker/tools.go index 292e0e53..9eb24621 100644 --- a/broker/tools.go +++ b/broker/tools.go @@ -4,6 +4,5 @@ package tools //build-time toolchain dependencies import ( - _ "github.com/indexdata/xsd2goxsl" _ "github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen" ) diff --git a/iso18626/Makefile b/iso18626/Makefile index ab1e838d..6101fb28 100644 --- a/iso18626/Makefile +++ b/iso18626/Makefile @@ -10,17 +10,18 @@ GEN_SCHEMA_OUT=ill_gen.go all: generate -generate: generate-schema +generate: generate-schema generate-schema: $(GEN_SCHEMA_OUT) $(GEN_SCHEMA_OUT): $(GEN_SCHEMA_IN) - $(GO) run github.com/indexdata/xsd2goxsl@v1.1.0 $(GEN_SCHEMA_IN) $(GEN_SCHEMA_OUT) \ + $(GO) run github.com/indexdata/xsd2goxsl@v1.4.0 $(GEN_SCHEMA_IN) $(GEN_SCHEMA_OUT) \ 'qAttrImport=utils "github.com/indexdata/go-utils/utils"' \ qAttrType=utils.PrefixAttr \ dateTimeType=utils.XSDDateTime \ decimalType=utils.XSDDecimal \ - json=yes + json=yes \ + validate=yes $(GO) fmt $(GEN_SCHEMA_OUT) clean: diff --git a/marcxml/Makefile b/marcxml/Makefile index 06ead7e7..a1c69639 100644 --- a/marcxml/Makefile +++ b/marcxml/Makefile @@ -5,11 +5,11 @@ OS ?= $(shell uname) XSLT ?= xsltproc GEN_FILES = MARC21slim_gen.go .PHONY: all generate generate-schema lint check clean deps-update -XSD2GO = $(GO) run github.com/indexdata/xsd2goxsl@v1.2.0 +XSD2GO = $(GO) run github.com/indexdata/xsd2goxsl@v1.4.0 all: generate -generate: generate-schema +generate: generate-schema generate-schema: $(GEN_FILES) diff --git a/ncip/Makefile b/ncip/Makefile index d17cbda5..752fefdf 100644 --- a/ncip/Makefile +++ b/ncip/Makefile @@ -8,7 +8,7 @@ GEN_SCHEMA_OUT=ncip_gen.go COVERAGE=coverage.out .PHONY: all generate generate-schema lint check clean deps-update -XSD2GO = $(GO) run github.com/indexdata/xsd2goxsl@9a581ac +XSD2GO = $(GO) run github.com/indexdata/xsd2goxsl@v1.4.0 all: generate diff --git a/sru/Makefile b/sru/Makefile index 8382380f..062ef9cf 100644 --- a/sru/Makefile +++ b/sru/Makefile @@ -11,7 +11,7 @@ GEN_FILES = sru_gen.go \ scan/scan_gen.go .PHONY: all generate generate-schema lint check clean deps-update -XSD2GO = $(GO) run github.com/indexdata/xsd2goxsl@9a581ac +XSD2GO = $(GO) run github.com/indexdata/xsd2goxsl@v1.4.0 define nsImports namespaced=yes \ @@ -24,7 +24,7 @@ endef all: generate -generate: generate-schema +generate: generate-schema generate-schema: $(GEN_FILES)