diff --git a/broker/README.md b/broker/README.md index 987c0038..c3fce557 100644 --- a/broker/README.md +++ b/broker/README.md @@ -82,7 +82,7 @@ Configuration is provided via environment variables: | `DB_DATABASE` | Database name | `crosslink` | | `DB_PORT` | Database port | `25432` | | `DB_SCHEMA` | Database schema to use | `crosslink_broker` | -| `DB_PROVISION` | Should app create DB role/schema (`true`/`false`) | `false` | +| `DB_PROVISION` | Should app create DB role/schema (`true`/`false`) | `false` | | `DB_MIGRATE` | Should app run DB migrations (`true`/`false`) | `true` | | `LOG_LEVEL` | Log level: `ERROR`, `WARN`, `INFO`, `DEBUG` | `INFO` | | `ENABLE_JSON_LOG` | Should JSON log format be enabled | `false` | @@ -108,6 +108,7 @@ Configuration is provided via environment variables: | `TENANT_TO_SYMBOL` | Pattern to map tenant to `requesterSymbol` when accessing the API via Okapi, | (empty value) | | | the `{tenant}` token is replaced by the `X-Okapi-Tenant` header value | | | `SUPPLIER_PATRON_PATTERN` | Pattern used to create patron ID when receiving Request on supplier side | `%v_user` | +| `LANGUAGE` | Language parameter used for ts_vector search in DB | `english` | # Build diff --git a/broker/migrations/022_add_items_to_pr.down.sql b/broker/migrations/022_add_items_to_pr.down.sql new file mode 100644 index 00000000..c12328d8 --- /dev/null +++ b/broker/migrations/022_add_items_to_pr.down.sql @@ -0,0 +1,40 @@ +DROP TRIGGER IF EXISTS trigger_update_patron_request_search_tsvector ON patron_request; + +DROP FUNCTION IF EXISTS update_patron_request_search_tsvector; + +DROP INDEX IF EXISTS idx_patron_request_search; + +DROP VIEW IF EXISTS patron_request_search_view; + +ALTER TABLE patron_request DROP COLUMN IF EXISTS search; +ALTER TABLE patron_request DROP COLUMN IF EXISTS language; + +DROP TRIGGER IF EXISTS trigger_update_patron_request_items ON item; + +DROP FUNCTION IF EXISTS update_patron_request_items CASCADE; + +ALTER TABLE patron_request DROP COLUMN IF EXISTS items; + +CREATE OR REPLACE VIEW patron_request_search_view AS +SELECT + pr.*, + EXISTS ( + SELECT 1 + FROM notification n + WHERE n.pr_id = pr.id + ) AS has_notification, + EXISTS ( + SELECT 1 + FROM notification n + WHERE n.pr_id = pr.id and cost is not null + ) AS has_cost, + EXISTS ( + SELECT 1 + FROM notification n + WHERE n.pr_id = pr.id and acknowledged_at is null + ) AS has_unread_notification, + pr.ill_request -> 'serviceInfo' ->> 'serviceType' AS service_type, + pr.ill_request -> 'serviceInfo' -> 'serviceLevel' ->> '#text' AS service_level, + immutable_to_timestamp(pr.ill_request -> 'serviceInfo' ->> 'needBeforeDate') AS needed_at +FROM patron_request pr; + diff --git a/broker/migrations/022_add_items_to_pr.up.sql b/broker/migrations/022_add_items_to_pr.up.sql new file mode 100644 index 00000000..fc1b4a0b --- /dev/null +++ b/broker/migrations/022_add_items_to_pr.up.sql @@ -0,0 +1,134 @@ +ALTER TABLE patron_request ADD COLUMN items JSONB NOT NULL DEFAULT '[]'::jsonb; + +CREATE OR REPLACE FUNCTION update_patron_request_items() +RETURNS TRIGGER AS $$ +BEGIN +UPDATE patron_request +SET items = ( + SELECT jsonb_agg( + (to_jsonb(i) - 'pr_id') || + jsonb_build_object( + 'created_at', + to_char(i.created_at, 'YYYY-MM-DD"T"HH24:MI:SS.US') || to_char(i.created_at, 'TZH:TZM') + ) + ) + FROM item i + WHERE i.pr_id = NEW.pr_id +) +WHERE id = NEW.pr_id; + +RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Create the trigger +CREATE TRIGGER trigger_update_patron_request_items + AFTER INSERT OR UPDATE ON item + FOR EACH ROW + EXECUTE FUNCTION update_patron_request_items(); + + +-- Add the search field as a tsvector column +ALTER TABLE patron_request + ADD COLUMN search tsvector, + ADD COLUMN language regconfig NOT NULL DEFAULT 'english'; + +-- Create a trigger function to update the search tsvector +CREATE OR REPLACE FUNCTION update_patron_request_search_tsvector() +RETURNS TRIGGER AS $$ +BEGIN + -- Update the search tsvector column + NEW.search := to_tsvector(NEW.language, + COALESCE(NEW.requester_req_id, '') || ' ' || + COALESCE(NEW.patron, '') || ' ' || + COALESCE(NEW.ill_request->'patronInfo'->>'givenName', '') || ' ' || + COALESCE(NEW.ill_request->'patronInfo'->>'surname', '') || ' ' || + COALESCE(NEW.ill_request->'patronInfo'->>'patronId', '') || ' ' || + COALESCE(NEW.ill_request->'bibliographicInfo'->>'title', '') || ' ' || + COALESCE(NEW.ill_request->'bibliographicInfo'->>'author', '') || ' ' || + COALESCE( + (SELECT string_agg( + COALESCE(item->>'item_id', '') || ' ' || + COALESCE(item->>'barcode', '') || ' ' || + COALESCE(item->>'call_number', ''), ' ' + ) + FROM jsonb_array_elements(NEW.items) AS item), '' + ) + ); + +RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Create a trigger to update the search tsvector on insert or update +CREATE TRIGGER trigger_update_patron_request_search_tsvector + BEFORE INSERT OR UPDATE ON patron_request + FOR EACH ROW + EXECUTE FUNCTION update_patron_request_search_tsvector(); + +CREATE INDEX idx_patron_request_search ON patron_request USING gin(search); + +DROP VIEW IF EXISTS patron_request_search_view; +CREATE OR REPLACE VIEW patron_request_search_view AS +SELECT + pr.*, + EXISTS ( + SELECT 1 + FROM notification n + WHERE n.pr_id = pr.id + ) AS has_notification, + EXISTS ( + SELECT 1 + FROM notification n + WHERE n.pr_id = pr.id and cost is not null + ) AS has_cost, + EXISTS ( + SELECT 1 + FROM notification n + WHERE n.pr_id = pr.id and acknowledged_at is null + ) AS has_unread_notification, + pr.ill_request -> 'serviceInfo' ->> 'serviceType' AS service_type, + pr.ill_request -> 'serviceInfo' -> 'serviceLevel' ->> '#text' AS service_level, + immutable_to_timestamp(pr.ill_request -> 'serviceInfo' ->> 'needBeforeDate') AS needed_at +FROM patron_request pr; + +-- One-time backfill of items for existing patron_request rows +UPDATE patron_request pr +SET items = COALESCE( + ( + SELECT jsonb_agg( + (to_jsonb(i) - 'pr_id') || + jsonb_build_object( + 'created_at', + to_char(i.created_at, 'YYYY-MM-DD"T"HH24:MI:SS.US') || to_char(i.created_at, 'TZH:TZM') + ) + ) + FROM item i + WHERE i.pr_id = pr.id + ), + '[]'::jsonb + ); +-- One-time backfill of search tsvector for existing patron_request rows +UPDATE patron_request pr +SET search = to_tsvector( + pr.language, + COALESCE(pr.requester_req_id, '') || ' ' || + COALESCE(pr.patron, '') || ' ' || + COALESCE(pr.ill_request->'patronInfo'->>'givenName', '') || ' ' || + COALESCE(pr.ill_request->'patronInfo'->>'surname', '') || ' ' || + COALESCE(pr.ill_request->'patronInfo'->>'patronId', '') || ' ' || + COALESCE(pr.ill_request->'bibliographicInfo'->>'title', '') || ' ' || + COALESCE(pr.ill_request->'bibliographicInfo'->>'author', '') || ' ' || + COALESCE( + ( + SELECT string_agg( + COALESCE(item->>'item_id', '') || ' ' || + COALESCE(item->>'barcode', '') || ' ' || + COALESCE(item->>'call_number', ''), + ' ' + ) + FROM jsonb_array_elements(pr.items) AS item + ), + '' + ) + ); \ No newline at end of file diff --git a/broker/oapi/open-api.yaml b/broker/oapi/open-api.yaml index 6a585c37..f1622594 100644 --- a/broker/oapi/open-api.yaml +++ b/broker/oapi/open-api.yaml @@ -477,6 +477,11 @@ components: lastActionResult: type: string description: Latest action status ("NEW", "PROCESSING", "SUCCESS", "PROBLEM", "ERROR") + items: + type: array + description: List of patron request items + items: + $ref: '#/components/schemas/PrItem' required: - id - timestamp @@ -1140,7 +1145,8 @@ paths: Use this endpoint to retrieve patron requests. Query parameter cql can be used to filter the results. With cql you can use these fields state, side, requester_symbol, supplier_symbol, needs_attention, - has_notification, has_cost, has_unread_notification, service_type, service_level, created_at, needed_at, requester_req_id. + has_notification, has_cost, has_unread_notification, service_type, service_level, created_at, needed_at, + requester_req_id, title, patron, cql.serverChoice. tags: - patron-requests-api parameters: diff --git a/broker/patron_request/api/api-handler.go b/broker/patron_request/api/api-handler.go index 116f3c2c..10c98ebd 100644 --- a/broker/patron_request/api/api-handler.go +++ b/broker/patron_request/api/api-handler.go @@ -215,7 +215,7 @@ func (a *PatronRequestApiHandler) PostPatronRequests(w http.ResponseWriter, r *h return } dbreq := buildDbPatronRequest(&newPr, params.XOkapiTenant, creationTime, requesterReqId, illRequest) - pr, err := a.prRepo.CreatePatronRequest(ctx, (pr_db.CreatePatronRequestParams)(dbreq)) + pr, err := a.prRepo.CreatePatronRequest(ctx, pr_db.CreatePatronRequestParams(dbreq)) if err != nil { var pgErr *pgconn.PgError if errors.As(err, &pgErr) && pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) { @@ -548,6 +548,10 @@ func addNotFoundError(w http.ResponseWriter) { } func toApiPatronRequest(request pr_db.PatronRequest, illRequest iso18626.Request) proapi.PatronRequest { + items := []proapi.PrItem{} + for _, item := range request.Items { + items = append(items, toApiPrItem(item)) + } return proapi.PatronRequest{ Id: request.ID, Timestamp: request.Timestamp.Time, @@ -562,6 +566,7 @@ func toApiPatronRequest(request pr_db.PatronRequest, illRequest iso18626.Request LastAction: toString(request.LastAction), LastActionOutcome: toString(request.LastActionOutcome), LastActionResult: toString(request.LastActionResult), + Items: &items, } } @@ -691,6 +696,8 @@ func buildDbPatronRequest( IllRequest: illRequest, Tenant: getDbText(tenant), RequesterReqID: getDbText(&requesterReqId), + Language: pr_db.LANGUAGE, + Items: []pr_db.PrItem{}, // LastAction, LastActionOutcome and LastActionResult are not set on creation // they will be updated when the first action is executed. } @@ -716,6 +723,17 @@ func toApiItem(item pr_db.Item) proapi.PrItem { } } +func toApiPrItem(item pr_db.PrItem) proapi.PrItem { + return proapi.PrItem{ + Id: item.ID, + Barcode: item.Barcode, + CallNumber: item.CallNumber, + ItemId: item.ItemID, + Title: item.Title, + CreatedAt: time.Time(item.CreatedAt), + } +} + func toApiNotification(notification pr_db.Notification) (proapi.PrNotification, error) { var ackAt *time.Time if notification.AcknowledgedAt.Valid { diff --git a/broker/patron_request/db/prcql.go b/broker/patron_request/db/prcql.go index a7890407..97504953 100644 --- a/broker/patron_request/db/prcql.go +++ b/broker/patron_request/db/prcql.go @@ -7,8 +7,11 @@ import ( "github.com/indexdata/cql-go/cql" "github.com/indexdata/cql-go/pgcql" + "github.com/indexdata/go-utils/utils" ) +var LANGUAGE = utils.GetEnv("LANGUAGE", "english") + type FieldAllRecords struct{} func (f *FieldAllRecords) GetColumn() string { return "" } @@ -64,6 +67,15 @@ func handlePatronRequestsQuery(cqlString string, noBaseArgs int) (pgcql.Query, e nf = pgcql.NewFieldDate() def.AddField("needed_at", nf) + f = pgcql.NewFieldString().WithExact().WithColumn("ill_request->'bibliographicInfo'->>'title'") + def.AddField("title", f) + + f = pgcql.NewFieldString().WithExact() + def.AddField("patron", f) + + ftv := pgcql.NewFieldTsVector().WithLanguage(LANGUAGE).WithServerChoiceRel(cql.ALL).WithColumn("search") + def.AddField("cql.serverChoice", ftv) + var parser cql.Parser query, err := parser.Parse(cqlString) if err != nil { @@ -118,6 +130,8 @@ func (q *Queries) ListPatronRequestsCql(ctx context.Context, db DBTX, arg ListPa &i.LastAction, &i.LastActionOutcome, &i.LastActionResult, + &i.Language, + &i.Items, &i.FullCount, ); err != nil { return nil, err diff --git a/broker/patron_request/db/prmodels.go b/broker/patron_request/db/prmodels.go index f11e02fe..779bf977 100644 --- a/broker/patron_request/db/prmodels.go +++ b/broker/patron_request/db/prmodels.go @@ -1,5 +1,9 @@ package pr_db +import ( + "time" +) + type PatronRequestState string type PatronRequestSide string type PatronRequestAction string @@ -10,3 +14,12 @@ const ( NotificationRejected NotificationReceipt = "REJECTED" NotificationSeen NotificationReceipt = "SEEN" ) + +type PrItem struct { + ID string `json:"id"` + Barcode string `json:"barcode"` + CallNumber *string `json:"call_number"` + Title *string `json:"title"` + ItemID *string `json:"item_id"` + CreatedAt time.Time `json:"created_at"` +} diff --git a/broker/patron_request/db/prrepo.go b/broker/patron_request/db/prrepo.go index 563783ae..ae5e6bfc 100644 --- a/broker/patron_request/db/prrepo.go +++ b/broker/patron_request/db/prrepo.go @@ -90,6 +90,8 @@ func (r *PgPrRepo) ListPatronRequests(ctx common.ExtendedContext, params ListPat LastAction: r.LastAction, LastActionOutcome: r.LastActionOutcome, LastActionResult: r.LastActionResult, + Language: r.Language, + Items: r.Items, }) } } else { diff --git a/broker/patron_request/service/action_test.go b/broker/patron_request/service/action_test.go index db32d832..c8f8bf4d 100644 --- a/broker/patron_request/service/action_test.go +++ b/broker/patron_request/service/action_test.go @@ -1342,16 +1342,48 @@ func (r *MockPrRepo) UpdatePatronRequest(ctx common.ExtendedContext, params pr_d if strings.Contains(params.ID, "error") || strings.Contains(params.RequesterReqID.String, "error") { return pr_db.PatronRequest{}, errors.New("db error") } - r.savedPr = pr_db.PatronRequest(params) - return pr_db.PatronRequest(params), nil + r.savedPr = pr_db.PatronRequest{ + ID: params.ID, + Timestamp: params.Timestamp, + IllRequest: params.IllRequest, + State: params.State, + Side: params.Side, + Patron: params.Patron, + RequesterSymbol: params.RequesterSymbol, + SupplierSymbol: params.SupplierSymbol, + Tenant: params.Tenant, + RequesterReqID: params.RequesterReqID, + NeedsAttention: params.NeedsAttention, + LastAction: params.LastAction, + LastActionOutcome: params.LastActionOutcome, + LastActionResult: params.LastActionResult, + Language: params.Language, + } + return r.savedPr, nil } func (r *MockPrRepo) CreatePatronRequest(ctx common.ExtendedContext, params pr_db.CreatePatronRequestParams) (pr_db.PatronRequest, error) { if strings.Contains(params.ID, "error") || strings.Contains(params.RequesterReqID.String, "error") { return pr_db.PatronRequest{}, errors.New("db error") } - r.savedPr = pr_db.PatronRequest(params) - return pr_db.PatronRequest(params), nil + r.savedPr = pr_db.PatronRequest{ + ID: params.ID, + Timestamp: params.Timestamp, + IllRequest: params.IllRequest, + State: params.State, + Side: params.Side, + Patron: params.Patron, + RequesterSymbol: params.RequesterSymbol, + SupplierSymbol: params.SupplierSymbol, + Tenant: params.Tenant, + RequesterReqID: params.RequesterReqID, + NeedsAttention: params.NeedsAttention, + LastAction: params.LastAction, + LastActionOutcome: params.LastActionOutcome, + LastActionResult: params.LastActionResult, + Language: params.Language, + } + return r.savedPr, nil } func (r *MockPrRepo) GetLendingRequestBySupplierSymbolAndRequesterReqId(ctx common.ExtendedContext, symbol string, requesterReqId string) (pr_db.PatronRequest, error) { diff --git a/broker/patron_request/service/message-handler.go b/broker/patron_request/service/message-handler.go index c37ef1df..c2ae08b7 100644 --- a/broker/patron_request/service/message-handler.go +++ b/broker/patron_request/service/message-handler.go @@ -383,6 +383,8 @@ func (m *PatronRequestMessageHandler) handleRequestMessage(ctx common.ExtendedCo IllRequest: request, SupplierSymbol: getDbText(supplierSymbol), RequesterReqID: getDbText(raRequestId), + Language: pr_db.LANGUAGE, + Items: []pr_db.PrItem{}, }) if err != nil { status, response, handleErr := createRequestResponse(request, iso18626.TypeMessageStatusERROR, &iso18626.ErrorData{ diff --git a/broker/sqlc/pr_query.sql b/broker/sqlc/pr_query.sql index e0918204..38d140f9 100644 --- a/broker/sqlc/pr_query.sql +++ b/broker/sqlc/pr_query.sql @@ -12,7 +12,7 @@ WHERE id = $1 LIMIT 1; -- name: ListPatronRequests :many -SELECT id, timestamp, ill_request, state, side, patron, requester_symbol, supplier_symbol, tenant, requester_req_id, needs_attention, last_action, last_action_outcome, last_action_result, COUNT(*) OVER () as full_count +SELECT id, timestamp, ill_request, state, side, patron, requester_symbol, supplier_symbol, tenant, requester_req_id, needs_attention, last_action, last_action_outcome, last_action_result, language, items, COUNT(*) OVER () as full_count FROM patron_request_search_view ORDER BY timestamp LIMIT $1 OFFSET $2; @@ -31,13 +31,15 @@ SET timestamp = $2, needs_attention = $11, last_action = $12, last_action_outcome = $13, - last_action_result = $14 + last_action_result = $14, + items = $15, + language = $16 WHERE id = $1 RETURNING sqlc.embed(patron_request); -- name: CreatePatronRequest :one -INSERT INTO patron_request (id, timestamp, ill_request, state, side, patron, requester_symbol, supplier_symbol, tenant, requester_req_id, needs_attention, last_action, last_action_outcome, last_action_result) -VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) +INSERT INTO patron_request (id, timestamp, ill_request, state, side, patron, requester_symbol, supplier_symbol, tenant, requester_req_id, needs_attention, last_action, last_action_outcome, last_action_result, items, language) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) RETURNING sqlc.embed(patron_request); -- name: DeletePatronRequest :exec diff --git a/broker/sqlc/pr_schema.sql b/broker/sqlc/pr_schema.sql index 6a89b85f..94c3ee19 100644 --- a/broker/sqlc/pr_schema.sql +++ b/broker/sqlc/pr_schema.sql @@ -14,7 +14,9 @@ CREATE TABLE patron_request needs_attention BOOLEAN NOT NULL DEFAULT false, last_action VARCHAR, last_action_outcome VARCHAR, - last_action_result VARCHAR + last_action_result VARCHAR, + items JSONB NOT NULL DEFAULT '[]'::jsonb, + language regconfig NOT NULL DEFAULT 'english' ); CREATE OR REPLACE FUNCTION get_next_hrid(prefix VARCHAR) RETURNS VARCHAR AS $$ diff --git a/broker/sqlc/sqlc.yaml b/broker/sqlc/sqlc.yaml index 5a774b7a..e1d83221 100644 --- a/broker/sqlc/sqlc.yaml +++ b/broker/sqlc/sqlc.yaml @@ -84,6 +84,12 @@ sql: - column: "patron_request.side" go_type: type: "PatronRequestSide" + - column: "patron_request.items" + go_type: + type: "[]PrItem" + - column: "patron_request_search_view.items" + go_type: + type: "[]PrItem" - column: "notification.side" go_type: type: "PatronRequestSide" diff --git a/broker/test/patron_request/api/api-handler_test.go b/broker/test/patron_request/api/api-handler_test.go index 27eea3f6..c7022b7e 100644 --- a/broker/test/patron_request/api/api-handler_test.go +++ b/broker/test/patron_request/api/api-handler_test.go @@ -19,6 +19,7 @@ import ( "github.com/indexdata/crosslink/broker/patron_request/proapi" "github.com/indexdata/crosslink/directory" "github.com/indexdata/crosslink/iso18626" + "github.com/jackc/pgx/v5/pgtype" "github.com/google/uuid" "github.com/indexdata/crosslink/broker/adapter" @@ -183,7 +184,8 @@ func TestCrud(t *testing.T) { "side%3Dborrowing%20and%20requester_symbol%3D"+*foundPr.RequesterSymbol+ "%20and%20requester_req_id%3D"+*foundPr.RequesterRequestId+"%20and%20needs_attention%3Dfalse%20and%20"+ "has_notification%3Dfalse%20and%20has_cost%3Dfalse%20and%20has_unread_notification%3Dfalse%20and%20"+ - "service_type%3DCopy%20and%20service_level%3DCopy%20and%20created_at%3E2026-03-16%20and%20needed_at%3E2026-03-16", []byte{}, 200) + "service_type%3DCopy%20and%20service_level%3DCopy%20and%20created_at%3E2026-03-16%20and%20needed_at%3E2026-03-16"+ + "%20and%20title%3D%22Typed%20request%20round%20trip%22%20and%20patron%3Dp1%20and%20cql.serverChoice%20all%20round", []byte{}, 200) err = json.Unmarshal(respBytes, &foundPrs) assert.NoError(t, err, "failed to unmarshal patron request") @@ -572,6 +574,72 @@ func TestGetStateModelCapabilities(t *testing.T) { assert.True(t, slices.Contains(capabilities.SupplierMessageEvents, string(prservice.SupplierCancelRejected))) } +func TestServerChoice(t *testing.T) { + appCtx := common.CreateExtCtxWithArgs(context.Background(), nil) + prId := uuid.NewString() + _, err := prRepo.CreatePatronRequest(appCtx, pr_db.CreatePatronRequestParams{ + ID: prId, + Timestamp: pgtype.Timestamp{ + Time: time.Now(), + Valid: true, + }, + Side: prservice.SideBorrowing, + RequesterSymbol: pgtype.Text{ + String: "ISIL:REQ", + Valid: true, + }, + State: prservice.BorrowerStateValidated, + Language: "english", + RequesterReqID: pgtype.Text{ + String: "REQ-123", + Valid: true, + }, + Patron: pgtype.Text{ + String: "P456", + Valid: true, + }, + IllRequest: iso18626.Request{ + BibliographicInfo: iso18626.BibliographicInfo{ + Title: "Do Androids Dream of Electric Sheep?", + Author: "Ray Bradbury", + }, + PatronInfo: &iso18626.PatronInfo{ + GivenName: "John", + Surname: "Doe", + PatronId: "PP-789", + }, + }, + Items: []pr_db.PrItem{}, + }) + assert.NoError(t, err) + itemId := uuid.NewString() + _, err = prRepo.SaveItem(appCtx, pr_db.SaveItemParams{ + ID: itemId, + PrID: prId, + Barcode: "BAR-321", + CallNumber: pgtype.Text{ + String: "CAL-321", + Valid: true, + }, + ItemID: pgtype.Text{ + String: "ITEM-321", + Valid: true, + }, + CreatedAt: pgtype.Timestamp{ + Time: time.Now(), + Valid: true, + }, + }) + assert.NoError(t, err) + + respBytes := httpRequest(t, "GET", basePath+"?symbol=ISIL:REQ&side=borrowing&cal=cql.serverChoice%20all%20%22REQ-123%20P456%20Dream%20Ray%20Bradbury%20John%20Doe%20PP-789%20BAR-321%20CAL-321%20ITEM-321%22", []byte{}, 200) + var foundPrs proapi.PatronRequests + err = json.Unmarshal(respBytes, &foundPrs) + assert.NoError(t, err) + assert.Equal(t, int64(1), foundPrs.About.Count) + assert.Equal(t, prId, foundPrs.Items[0].Id) +} + func httpRequest2(t *testing.T, method string, uriPath string, reqbytes []byte, expectStatus int) (*http.Response, []byte) { client := http.DefaultClient hreq, err := http.NewRequest(method, getLocalhostWithPort()+uriPath, bytes.NewBuffer(reqbytes)) diff --git a/broker/test/patron_request/db/prrepo_test.go b/broker/test/patron_request/db/prrepo_test.go index 7d550182..d7063271 100644 --- a/broker/test/patron_request/db/prrepo_test.go +++ b/broker/test/patron_request/db/prrepo_test.go @@ -75,6 +75,8 @@ func TestItem(t *testing.T) { Time: time.Now(), Valid: true, }, + Language: "english", + Items: []pr_db.PrItem{}, }) assert.NoError(t, err) @@ -163,6 +165,8 @@ func TestNotification(t *testing.T) { Time: time.Now(), Valid: true, }, + Language: "english", + Items: []pr_db.PrItem{}, }) assert.NoError(t, err)