From 79a1ec5ebf3a7a66de397550a1a88c2b188e68bc Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 27 Apr 2026 15:40:44 +0200 Subject: [PATCH 1/8] Add docker-compose-reservoir A configuation where broker is configure do to holdings via SRU . --- bruno/docker-compose-reservoir.yml | 85 ++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 bruno/docker-compose-reservoir.yml diff --git a/bruno/docker-compose-reservoir.yml b/bruno/docker-compose-reservoir.yml new file mode 100644 index 00000000..e3fefbfa --- /dev/null +++ b/bruno/docker-compose-reservoir.yml @@ -0,0 +1,85 @@ +services: + postgres: + image: postgres:16 + container_name: crosslink_postgres + environment: + POSTGRES_USER: crosslink + POSTGRES_PASSWORD: crosslink + POSTGRES_DB: crosslink + ports: + - "25432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U crosslink -d crosslink"] + interval: 5s + timeout: 3s + retries: 20 + start_period: 5s + + broker: + build: + context: .. + dockerfile: broker/Dockerfile + container_name: crosslink_broker + depends_on: + postgres: + condition: service_healthy + environment: + HTTP_PORT: 8080 + DB_TYPE: postgres + DB_USER: crosslink + DB_PASSWORD: crosslink + DB_HOST: postgres + DB_DATABASE: crosslink + DB_PORT: 5432 + DB_PROVISION: "true" + DB_MIGRATE: "true" + HOLDINGS_ADAPTER: sru + SRU_URL: http://reservoir:8082/reservoir/sru + DIRECTORY_ADAPTER: api + DIRECTORY_API_URL: http://illmock:8081/directory/entries + TENANT_TO_SYMBOL: "ISIL:US-{tenant}" + ports: + - "8080:8080" + + illmock: + build: + context: .. + dockerfile: illmock/Dockerfile + container_name: crosslink_illmock + command: ["/illmock"] + environment: + HTTP_PORT: 8081 + MOCK_DIRECTORY_ENTRIES_PATH: /data/directory.json + volumes: + - ./:/data:ro + ports: + - "8081:8081" + + reservoir-init: + image: postgres:latest + container_name: reservoir_init + depends_on: + postgres: + condition: service_healthy + entrypoint: [ "bash", "-c", "psql -h postgres -U crosslink -d crosslink -c 'CREATE ROLE reservoir NOLOGIN;' && psql -h postgres -U crosslink -d crosslink -c 'GRANT reservoir TO crosslink;' && psql -h postgres -U crosslink -d crosslink -c 'CREATE SCHEMA default_mod_reservoir AUTHORIZATION reservoir;'" ] + environment: + PGPASSWORD: crosslink + + reservior: + build: + context: ../../reservoir + dockerfile: Dockerfile-jit + container_name: reservoir + depends_on: + reservoir-init: + condition: service_completed_successfully + environment: + HTTP_PORT: 8082 + DB_USERNAME: crosslink + DB_PASSWORD: crosslink + DB_HOST: postgres + DB_DATABASE: crosslink + DB_PORT: 5432 + TENANT_DEFAULT: default + ports: + - "8082:8082" From 7db70c7472a48f9ae8bebce309271e71be3920e3 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 27 Apr 2026 15:55:26 +0200 Subject: [PATCH 2/8] Only create role , namespace if not created already --- bruno/docker-compose-reservoir.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bruno/docker-compose-reservoir.yml b/bruno/docker-compose-reservoir.yml index e3fefbfa..f55ccf37 100644 --- a/bruno/docker-compose-reservoir.yml +++ b/bruno/docker-compose-reservoir.yml @@ -61,7 +61,12 @@ services: depends_on: postgres: condition: service_healthy - entrypoint: [ "bash", "-c", "psql -h postgres -U crosslink -d crosslink -c 'CREATE ROLE reservoir NOLOGIN;' && psql -h postgres -U crosslink -d crosslink -c 'GRANT reservoir TO crosslink;' && psql -h postgres -U crosslink -d crosslink -c 'CREATE SCHEMA default_mod_reservoir AUTHORIZATION reservoir;'" ] + entrypoint: [ + "bash", "-c", + "psql -h postgres -U crosslink -d crosslink -c \"DO \\$\\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'reservoir') THEN CREATE ROLE reservoir NOLOGIN; END IF; END \\$\\$;\" && \ + psql -h postgres -U crosslink -d crosslink -c 'GRANT reservoir TO crosslink;' && \ + psql -h postgres -U crosslink -d crosslink -c \"DO \\$\\$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'default_mod_reservoir') THEN CREATE SCHEMA default_mod_reservoir AUTHORIZATION reservoir; END IF; END \\$\\$;\"" + ] environment: PGPASSWORD: crosslink From f811ca519704564970805cd86934e0eb2bd75de5 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 27 Apr 2026 16:42:08 +0200 Subject: [PATCH 3/8] Enable Isbn/issn lookup for broker --- bruno/docker-compose-reservoir.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/bruno/docker-compose-reservoir.yml b/bruno/docker-compose-reservoir.yml index f55ccf37..daff8b13 100644 --- a/bruno/docker-compose-reservoir.yml +++ b/bruno/docker-compose-reservoir.yml @@ -34,6 +34,7 @@ services: DB_PROVISION: "true" DB_MIGRATE: "true" HOLDINGS_ADAPTER: sru + HOLDINGS_ISXN_LOOKUP: "true" SRU_URL: http://reservoir:8082/reservoir/sru DIRECTORY_ADAPTER: api DIRECTORY_API_URL: http://illmock:8081/directory/entries From 8b07d246cdcdf141fb6044bd281e9931a0cf8672 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 27 Apr 2026 16:42:40 +0200 Subject: [PATCH 4/8] Add bruno folder 'Reservior' with ILL request --- bruno/README.md | 16 ++++- .../Borrowing side send-request check.bru | 40 +++++++++++++ .../Reservoir/Borrowing side send-request.bru | 45 ++++++++++++++ .../Reservoir/Create borrowing side PR.bru | 60 +++++++++++++++++++ 4 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 bruno/crosslink/Reservoir/Borrowing side send-request check.bru create mode 100644 bruno/crosslink/Reservoir/Borrowing side send-request.bru create mode 100644 bruno/crosslink/Reservoir/Create borrowing side PR.bru diff --git a/bruno/README.md b/bruno/README.md index cb646fe8..978b6387 100644 --- a/bruno/README.md +++ b/bruno/README.md @@ -1,4 +1,6 @@ -# Patron Request API E2E test +# Bruno + +## Patron Request API E2E test The provided Bruno API test includes an end-to-end execution of a happy path exchange between the requester (borrower) and supplier (lender). @@ -14,5 +16,15 @@ docker compose up 3. In Bruno, load the `LocalDev` environment. -4. Run all steps in the Bruno runner. All HTTP response codes and validations should be green. +4. Run all steps in the Bruno runner in the `PR Happy Flow` folder. All HTTP response codes and validations should be green. + +## Reservoir (incomplete) + +``` +docker compose -f docker-compose-reservoir.yml up +``` + +Start Bruno and load the `LocalDev` environment. +Select the `Reservoir` flow in Bruno. Only the first parts of the Happy flow is currently in this holder. This +is merely to test that SRU lookup is operational. \ No newline at end of file diff --git a/bruno/crosslink/Reservoir/Borrowing side send-request check.bru b/bruno/crosslink/Reservoir/Borrowing side send-request check.bru new file mode 100644 index 00000000..f3840d2f --- /dev/null +++ b/bruno/crosslink/Reservoir/Borrowing side send-request check.bru @@ -0,0 +1,40 @@ +meta { + name: Borrowing side send-request check + type: http + seq: 3 +} + +get { + url: {{host}}/patron_requests/{{prId}}?side=borrowing + body: json + auth: basic +} + +params:query { + side: borrowing +} + +headers { + X-Okapi-User-Id: {{userName}} + X-Okapi-Tenant: {{OkapiTenantReq}} +} + +auth:basic { + username: {{userName}} + password: {{userPassword}} +} + +script:post-response { + test("for send-request", function() { + const pr = res.getBody(); + expect(pr.lastAction).to.equal("send-request") + expect(pr.lastActionResult).to.equal("SUCCESS") + }); +} + +settings { + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 +} diff --git a/bruno/crosslink/Reservoir/Borrowing side send-request.bru b/bruno/crosslink/Reservoir/Borrowing side send-request.bru new file mode 100644 index 00000000..d4a35373 --- /dev/null +++ b/bruno/crosslink/Reservoir/Borrowing side send-request.bru @@ -0,0 +1,45 @@ +meta { + name: Borrowing side send-request + type: http + seq: 2 +} + +post { + url: {{host}}/patron_requests/{{prId}}/action?side=borrowing + body: json + auth: basic +} + +params:query { + side: borrowing +} + +headers { + X-Okapi-User-Id: {{userName}} + X-Okapi-Tenant: {{OkapiTenantReq}} +} + +auth:basic { + username: {{userName}} + password: {{userPassword}} +} + +body:json { + { + "action": "send-request" + } +} + +script:post-response { + test("for success", function() { + const pr = res.getBody(); + expect(pr.result).to.equal("SUCCESS") + }); +} + +settings { + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 +} diff --git a/bruno/crosslink/Reservoir/Create borrowing side PR.bru b/bruno/crosslink/Reservoir/Create borrowing side PR.bru new file mode 100644 index 00000000..977035e9 --- /dev/null +++ b/bruno/crosslink/Reservoir/Create borrowing side PR.bru @@ -0,0 +1,60 @@ +meta { + name: Create borrowing side PR + type: http + seq: 1 +} + +post { + url: {{host}}/patron_requests + body: json + auth: basic +} + +headers { + X-Okapi-User-Id: {{userName}} + X-Okapi-Tenant: {{OkapiTenantReq}} +} + +auth:basic { + username: {{userName}} + password: {{userPassword}} +} + +body:json { + { + "illRequest": { + "bibliographicInfo": { + "supplierUniqueRecordId": "429e871b-9cd3-465a-8d94-a82bac2d9a44", + "title" : "my fake title", + "bibliographicItemId": [ + { + "bibliographicItemIdentifier": "123-456-789", + "bibliographicItemIdentifierCode": { + "#text": "ISBN" + } + } + ] + } + }, + "patron": "{{requesterPatron}}", + "side": "borrowing", + "state": "NEW", + "requesterSymbol": "{{requesterSymbol}}" + } +} + +script:post-response { + const responseData = res.getBody(); + + test("Capture patron request id", () => { + expect(responseData.id).to.be.a("string").and.not.empty; + bru.setVar("prId", responseData.id); + }); +} + +settings { + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 +} From 474adc4ce8e117aa74e0d5eadde8358cb9b3b741 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 27 Apr 2026 16:52:41 +0200 Subject: [PATCH 5/8] Misc readme updates --- bruno/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bruno/README.md b/bruno/README.md index 978b6387..604c3f5a 100644 --- a/bruno/README.md +++ b/bruno/README.md @@ -1,5 +1,7 @@ # Bruno +This directory files for running tests with Bruno. + ## Patron Request API E2E test The provided Bruno API test includes an end-to-end execution of a happy path exchange between the requester (borrower) and supplier (lender). @@ -20,11 +22,17 @@ docker compose up ## Reservoir (incomplete) +This is similar to the E2E test with the twist that it uses holdings SRU lookup. + +The docker compose file `docker-compose-reservoir.yml` assumes reservoir in `../../reservoir`, ie `crosslink` and `reservoir` side by side. + +Start with: + ``` docker compose -f docker-compose-reservoir.yml up ``` Start Bruno and load the `LocalDev` environment. -Select the `Reservoir` flow in Bruno. Only the first parts of the Happy flow is currently in this holder. This +Select the `Reservoir` folder in Bruno. Only the first parts of the Happy flow is currently in this holder. This is merely to test that SRU lookup is operational. \ No newline at end of file From 299051f7d8891386857e7f93f98dc6e66343f731 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 27 Apr 2026 18:37:09 +0200 Subject: [PATCH 6/8] Add script to load MARC record for reservoir --- bruno/README.md | 9 +++++++- .../Reservoir/Create borrowing side PR.bru | 2 +- bruno/reservoir/1.mrc | 1 + bruno/reservoir/config-matchkeys-isxn.json | 5 ++++ bruno/reservoir/config-pool-isxn.json | 9 ++++++++ bruno/reservoir/load-records.sh | 23 +++++++++++++++++++ 6 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 bruno/reservoir/1.mrc create mode 100644 bruno/reservoir/config-matchkeys-isxn.json create mode 100644 bruno/reservoir/config-pool-isxn.json create mode 100755 bruno/reservoir/load-records.sh diff --git a/bruno/README.md b/bruno/README.md index 604c3f5a..a9d6ecbc 100644 --- a/bruno/README.md +++ b/bruno/README.md @@ -32,7 +32,14 @@ Start with: docker compose -f docker-compose-reservoir.yml up ``` +Load at least one MARC for reservoir with the `isxn` matcher with: + +``` +cd reservoir +./load-records.sh +``` + Start Bruno and load the `LocalDev` environment. Select the `Reservoir` folder in Bruno. Only the first parts of the Happy flow is currently in this holder. This -is merely to test that SRU lookup is operational. \ No newline at end of file +is merely to test that SRU lookup is operational. diff --git a/bruno/crosslink/Reservoir/Create borrowing side PR.bru b/bruno/crosslink/Reservoir/Create borrowing side PR.bru index 977035e9..e16c8061 100644 --- a/bruno/crosslink/Reservoir/Create borrowing side PR.bru +++ b/bruno/crosslink/Reservoir/Create borrowing side PR.bru @@ -28,7 +28,7 @@ body:json { "title" : "my fake title", "bibliographicItemId": [ { - "bibliographicItemIdentifier": "123-456-789", + "bibliographicItemIdentifier": "9781172431779", "bibliographicItemIdentifierCode": { "#text": "ISBN" } diff --git a/bruno/reservoir/1.mrc b/bruno/reservoir/1.mrc new file mode 100644 index 00000000..43e22d2c --- /dev/null +++ b/bruno/reservoir/1.mrc @@ -0,0 +1 @@ +00990cam a2200229u 4500001001000000008004100010020001800051020001500069041000800084100003500092245003300127264003900160300002500199650003000224650002000254949008100274949008100355949008100436949008100517949008100598949008100679015630091250415t20102010 spa d a9781172431779 a1172431779 aspa1 aSaavedra Lamas, Carlos 1880- - aPor las provincias del norte 1aCharleston SC :bNabu Press,c2010 a134 pages ;c9.69 in 4aHistory / General History 4aHistory/General vIngram Book CompanyrWholesaleraIn stockp20.75cUSDyRRP excluding taxtUS vIngram Book CompanyrWholesaleraIn stockp14.99cGBPyRRP excluding taxtGB vIngram Book CompanyrWholesaleraIn stockp21.99cEURyRRP excluding taxtDE vIngram Book CompanyrWholesaleraIn stockp23.99cCADyRRP excluding taxtCA vIngram Book CompanyrWholesaleraIn stockp35.19cAUDyRRP including taxtAU vIngram Book CompanyrWholesaleraIn stockp31.99cAUDyRRP excluding taxtAU \ No newline at end of file diff --git a/bruno/reservoir/config-matchkeys-isxn.json b/bruno/reservoir/config-matchkeys-isxn.json new file mode 100644 index 00000000..909aae51 --- /dev/null +++ b/bruno/reservoir/config-matchkeys-isxn.json @@ -0,0 +1,5 @@ +{ + "id": "isxn-matcher", + "type": "javascript", + "url": "https://raw.githubusercontent.com/indexdata/reservoir/master/js/matchkeys/isxn/isxn.mjs" +} diff --git a/bruno/reservoir/config-pool-isxn.json b/bruno/reservoir/config-pool-isxn.json new file mode 100644 index 00000000..250c6d9c --- /dev/null +++ b/bruno/reservoir/config-pool-isxn.json @@ -0,0 +1,9 @@ +{ + "id": "isxn", + "matcher": "isxn-matcher::matchkey", + "cql": { + "isbn" : "isxn-matcher::normIsbn", + "issn" : "isxn-matcher::normIssn" + }, + "update": "ingest" +} diff --git a/bruno/reservoir/load-records.sh b/bruno/reservoir/load-records.sh new file mode 100755 index 00000000..7d1a0735 --- /dev/null +++ b/bruno/reservoir/load-records.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +OKAPI_URL=http://localhost:8082 + +curl -w'\n' -HContent-type:application/json \ + $OKAPI_URL/reservoir/config/modules -d @config-matchkeys-isxn.json + +# configure pool +curl -w'\n' -HContent-type:application/json \ + $OKAPI_URL/reservoir/config/matchkeys -d @config-pool-isxn.json + +# initialize pool +curl -w '\n' -XPUT $OKAPI_URL/reservoir/config/matchkeys/isxn/initialize + +filename=1.mrc + +set -x + +curl -s \ + -HContent-Encoding:application/octet-stream \ + -T $filename "$OKAPI_URL/reservoir/upload?sourceId=ISIL:US-RS3&sourceVersion=1&xmlFixing=true&fileName=$filename&localIdPath=%24.marc.fields%5B%2A%5D.001" + +curl -s "$OKAPI_URL/reservoir/sru?maximumRecords=1&query=isbn%3D9781172431779" From 75ab6ae874e64798139dfa5c815709e1820fc432 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 27 Apr 2026 20:05:09 +0200 Subject: [PATCH 7/8] Use marc-transformer for cluster marcxml --- bruno/reservoir/load-records.sh | 6 ++++++ bruno/reservoir/marc-transformer-conf.json | 1 + 2 files changed, 7 insertions(+) create mode 100644 bruno/reservoir/marc-transformer-conf.json diff --git a/bruno/reservoir/load-records.sh b/bruno/reservoir/load-records.sh index 7d1a0735..1e25f76d 100755 --- a/bruno/reservoir/load-records.sh +++ b/bruno/reservoir/load-records.sh @@ -2,6 +2,12 @@ OKAPI_URL=http://localhost:8082 +curl -w'\n' -HContent-type:application/json \ + $OKAPI_URL/reservoir/config/modules -d @marc-transformer-conf.json + +curl -HContent-Type:application/json \ + -XPUT $OKAPI_URL/reservoir/config/oai -d'{"transformer":"marc-transformer::cluster_transform"}' + curl -w'\n' -HContent-type:application/json \ $OKAPI_URL/reservoir/config/modules -d @config-matchkeys-isxn.json diff --git a/bruno/reservoir/marc-transformer-conf.json b/bruno/reservoir/marc-transformer-conf.json new file mode 100644 index 00000000..430ecc89 --- /dev/null +++ b/bruno/reservoir/marc-transformer-conf.json @@ -0,0 +1 @@ +{"id":"marc-transformer","type":"javascript","url":"https://raw.githubusercontent.com/openlibraryenvironment/reshare-library-configurations/master/crosslink/transform.mjs"} From c768d8e8aa0047ae3e026032c777bd8ac96bc2c4 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Tue, 28 Apr 2026 14:28:45 +0200 Subject: [PATCH 8/8] No need to supply ISIL: in load --- bruno/reservoir/load-records.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bruno/reservoir/load-records.sh b/bruno/reservoir/load-records.sh index 1e25f76d..770354a1 100755 --- a/bruno/reservoir/load-records.sh +++ b/bruno/reservoir/load-records.sh @@ -24,6 +24,6 @@ set -x curl -s \ -HContent-Encoding:application/octet-stream \ - -T $filename "$OKAPI_URL/reservoir/upload?sourceId=ISIL:US-RS3&sourceVersion=1&xmlFixing=true&fileName=$filename&localIdPath=%24.marc.fields%5B%2A%5D.001" + -T $filename "$OKAPI_URL/reservoir/upload?sourceId=US-RS3&sourceVersion=1&xmlFixing=true&fileName=$filename&localIdPath=%24.marc.fields%5B%2A%5D.001" curl -s "$OKAPI_URL/reservoir/sru?maximumRecords=1&query=isbn%3D9781172431779"