From 15f7b9a9c64d2455b1199633ba44e0b66451d21c Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Mon, 26 Jan 2026 12:21:38 +0000 Subject: [PATCH 01/15] init --- internal/events/package.json | 2 +- .../schemas/examples/letter.ACCEPTED.json | 13 +++++--- .../schemas/examples/letter.CANCELLED.json | 32 +++++++++++++++++++ .../schemas/examples/letter.DELIVERED.json | 30 +++++++++++++++++ .../schemas/examples/letter.DISPATCHED.json | 30 +++++++++++++++++ .../schemas/examples/letter.ENCLOSED.json | 30 +++++++++++++++++ .../schemas/examples/letter.FAILED.json | 32 +++++++++++++++++++ .../schemas/examples/letter.FORWARDED.json | 13 +++++--- .../schemas/examples/letter.PENDING.json | 28 ++++++++++++++++ .../schemas/examples/letter.REJECTED.json | 32 +++++++++++++++++++ .../schemas/examples/letter.RETURNED.json | 13 +++++--- internal/events/src/index.ts | 1 + internal/events/src/version.ts | 5 +++ 13 files changed, 245 insertions(+), 16 deletions(-) create mode 100644 internal/events/schemas/examples/letter.CANCELLED.json create mode 100644 internal/events/schemas/examples/letter.DELIVERED.json create mode 100644 internal/events/schemas/examples/letter.DISPATCHED.json create mode 100644 internal/events/schemas/examples/letter.ENCLOSED.json create mode 100644 internal/events/schemas/examples/letter.FAILED.json create mode 100644 internal/events/schemas/examples/letter.PENDING.json create mode 100644 internal/events/schemas/examples/letter.REJECTED.json create mode 100644 internal/events/src/version.ts diff --git a/internal/events/package.json b/internal/events/package.json index 08abc7453..135961c97 100644 --- a/internal/events/package.json +++ b/internal/events/package.json @@ -50,5 +50,5 @@ "typecheck": "tsc --noEmit" }, "types": "dist/index.d.ts", - "version": "1.0.9" + "version": "1.0.10" } diff --git a/internal/events/schemas/examples/letter.ACCEPTED.json b/internal/events/schemas/examples/letter.ACCEPTED.json index 77a66c5b2..62ea85bb9 100644 --- a/internal/events/schemas/examples/letter.ACCEPTED.json +++ b/internal/events/schemas/examples/letter.ACCEPTED.json @@ -1,26 +1,29 @@ { "data": { "billingRef": "1y3q9v1zzzz", - "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", "groupId": "client_template", "origin": { "domain": "letter-rendering", "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", - "source": "/data-plane/letter-rendering/prod/render-pdf", - "subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5" + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" }, "specificationId": "1y3q9v1zzzz", - "status": "ACCEPTED" + "status": "ACCEPTED", + "supplierId": "supplier-12345" }, "datacontenttype": "application/json", "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.ACCEPTED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", "recordedtime": "2025-08-28T08:45:00.000Z", "severitynumber": 2, "severitytext": "INFO", "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", "specversion": "1.0", - "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", "time": "2025-08-28T08:45:00.000Z", "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", "type": "uk.nhs.notify.supplier-api.letter.ACCEPTED.v1" diff --git a/internal/events/schemas/examples/letter.CANCELLED.json b/internal/events/schemas/examples/letter.CANCELLED.json new file mode 100644 index 000000000..95c7638d7 --- /dev/null +++ b/internal/events/schemas/examples/letter.CANCELLED.json @@ -0,0 +1,32 @@ +{ + "data": { + "billingRef": "1y3q9v1zzzz", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" + }, + "reasonCode": "CANCELLATION_CODE", + "reasonText": "Cancellation reason", + "specificationId": "1y3q9v1zzzz", + "status": "CANCELLED", + "supplierId": "supplier-12345" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.CANCELLED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.CANCELLED.v1" +} diff --git a/internal/events/schemas/examples/letter.DELIVERED.json b/internal/events/schemas/examples/letter.DELIVERED.json new file mode 100644 index 000000000..a15fd21e9 --- /dev/null +++ b/internal/events/schemas/examples/letter.DELIVERED.json @@ -0,0 +1,30 @@ +{ + "data": { + "billingRef": "1y3q9v1zzzz", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" + }, + "specificationId": "1y3q9v1zzzz", + "status": "DELIVERED", + "supplierId": "supplier-12345" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.DELIVERED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.DELIVERED.v1" +} diff --git a/internal/events/schemas/examples/letter.DISPATCHED.json b/internal/events/schemas/examples/letter.DISPATCHED.json new file mode 100644 index 000000000..782ee5c15 --- /dev/null +++ b/internal/events/schemas/examples/letter.DISPATCHED.json @@ -0,0 +1,30 @@ +{ + "data": { + "billingRef": "1y3q9v1zzzz", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" + }, + "specificationId": "1y3q9v1zzzz", + "status": "DISPATCHED", + "supplierId": "supplier-12345" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.DISPATCHED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.DISPATCHED.v1" +} diff --git a/internal/events/schemas/examples/letter.ENCLOSED.json b/internal/events/schemas/examples/letter.ENCLOSED.json new file mode 100644 index 000000000..610d157da --- /dev/null +++ b/internal/events/schemas/examples/letter.ENCLOSED.json @@ -0,0 +1,30 @@ +{ + "data": { + "billingRef": "1y3q9v1zzzz", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" + }, + "specificationId": "1y3q9v1zzzz", + "status": "ENCLOSED", + "supplierId": "supplier-12345" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.ENCLOSED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.ENCLOSED.v1" +} diff --git a/internal/events/schemas/examples/letter.FAILED.json b/internal/events/schemas/examples/letter.FAILED.json new file mode 100644 index 000000000..9ef34aa14 --- /dev/null +++ b/internal/events/schemas/examples/letter.FAILED.json @@ -0,0 +1,32 @@ +{ + "data": { + "billingRef": "1y3q9v1zzzz", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" + }, + "reasonCode": "FAILURE_CODE", + "reasonText": "Failure reason", + "specificationId": "1y3q9v1zzzz", + "status": "FAILED", + "supplierId": "supplier-12345" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.FAILED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.FAILED.v1" +} diff --git a/internal/events/schemas/examples/letter.FORWARDED.json b/internal/events/schemas/examples/letter.FORWARDED.json index cd5bd1731..bc1f48cf3 100644 --- a/internal/events/schemas/examples/letter.FORWARDED.json +++ b/internal/events/schemas/examples/letter.FORWARDED.json @@ -1,28 +1,31 @@ { "data": { "billingRef": "1y3q9v1zzzz", - "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", "groupId": "client_template", "origin": { "domain": "letter-rendering", "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", - "source": "/data-plane/letter-rendering/prod/render-pdf", - "subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5" + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" }, "reasonCode": "RNIB", "reasonText": "RNIB", "specificationId": "1y3q9v1zzzz", - "status": "FORWARDED" + "status": "FORWARDED", + "supplierId": "supplier-12345" }, "datacontenttype": "application/json", "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.FORWARDED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", "recordedtime": "2025-08-28T08:45:00.000Z", "severitynumber": 2, "severitytext": "INFO", "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", "specversion": "1.0", - "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", "time": "2025-08-28T08:45:00.000Z", "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", "type": "uk.nhs.notify.supplier-api.letter.FORWARDED.v1" diff --git a/internal/events/schemas/examples/letter.PENDING.json b/internal/events/schemas/examples/letter.PENDING.json new file mode 100644 index 000000000..ea86081f0 --- /dev/null +++ b/internal/events/schemas/examples/letter.PENDING.json @@ -0,0 +1,28 @@ +{ + "data": { + "billingRef": "1y3q9v1zzzz", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" + }, + "status": "PENDING" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.PENDING.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.PENDING.v1" +} diff --git a/internal/events/schemas/examples/letter.REJECTED.json b/internal/events/schemas/examples/letter.REJECTED.json new file mode 100644 index 000000000..d8fee1b71 --- /dev/null +++ b/internal/events/schemas/examples/letter.REJECTED.json @@ -0,0 +1,32 @@ +{ + "data": { + "billingRef": "1y3q9v1zzzz", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" + }, + "reasonCode": "REJECTION_CODE", + "reasonText": "Rejection reason", + "specificationId": "1y3q9v1zzzz", + "status": "REJECTED", + "supplierId": "supplier-12345" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.REJECTED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.REJECTED.v1" +} diff --git a/internal/events/schemas/examples/letter.RETURNED.json b/internal/events/schemas/examples/letter.RETURNED.json index 1f54aea5a..7b7121d8f 100644 --- a/internal/events/schemas/examples/letter.RETURNED.json +++ b/internal/events/schemas/examples/letter.RETURNED.json @@ -1,28 +1,31 @@ { "data": { "billingRef": "1y3q9v1zzzz", - "domainId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", "groupId": "client_template", "origin": { "domain": "letter-rendering", "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", - "source": "/data-plane/letter-rendering/prod/render-pdf", - "subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5" + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" }, "reasonCode": "R07", "reasonText": "No such address", "specificationId": "1y3q9v1zzzz", - "status": "RETURNED" + "status": "RETURNED", + "supplierId": "supplier-12345" }, "datacontenttype": "application/json", "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.RETURNED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", "recordedtime": "2025-08-28T08:45:00.000Z", "severitynumber": 2, "severitytext": "INFO", "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", "specversion": "1.0", - "subject": "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", "time": "2025-08-28T08:45:00.000Z", "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", "type": "uk.nhs.notify.supplier-api.letter.RETURNED.v1" diff --git a/internal/events/src/index.ts b/internal/events/src/index.ts index c4255d26c..f19f9d258 100644 --- a/internal/events/src/index.ts +++ b/internal/events/src/index.ts @@ -4,3 +4,4 @@ export { default as DomainBase } from "./domain/domain-base"; export * from "./events/event-envelope"; export * from "./events/letter-events"; export * from "./events/mi-events"; +export * from "./version" \ No newline at end of file diff --git a/internal/events/src/version.ts b/internal/events/src/version.ts new file mode 100644 index 000000000..94e4e78d8 --- /dev/null +++ b/internal/events/src/version.ts @@ -0,0 +1,5 @@ +import { version } from "../package.json"; + +export { version as VERSION } from "../package.json"; + +export const [MAJOR_VERSION] = version.split("."); From 06c3f4cb7c18dcfcf382b580bf940825143bad1c Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Tue, 27 Jan 2026 08:41:37 +0000 Subject: [PATCH 02/15] update examples --- .../schemas/examples/letter.PENDING.json | 3 +- .../schemas/examples/letter.PRINTED.json | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 internal/events/schemas/examples/letter.PRINTED.json diff --git a/internal/events/schemas/examples/letter.PENDING.json b/internal/events/schemas/examples/letter.PENDING.json index ea86081f0..2f57d388f 100644 --- a/internal/events/schemas/examples/letter.PENDING.json +++ b/internal/events/schemas/examples/letter.PENDING.json @@ -9,7 +9,8 @@ "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" }, - "status": "PENDING" + "status": "PENDING", + "supplierId": "supplier-12345" }, "datacontenttype": "application/json", "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.PENDING.1.0.0.schema.json", diff --git a/internal/events/schemas/examples/letter.PRINTED.json b/internal/events/schemas/examples/letter.PRINTED.json new file mode 100644 index 000000000..9c8cf131d --- /dev/null +++ b/internal/events/schemas/examples/letter.PRINTED.json @@ -0,0 +1,30 @@ +{ + "data": { + "billingRef": "1y3q9v1zzzz", + "domainId": "e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "groupId": "client_template", + "origin": { + "domain": "letter-rendering", + "event": "f47ac10b-58cc-4372-a567-0e02b2c3d479", + "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", + "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" + }, + "specificationId": "1y3q9v1zzzz", + "status": "PRINTED", + "supplierId": "supplier-12345" + }, + "datacontenttype": "application/json", + "dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.PRINTED.1.0.0.schema.json", + "dataschemaversion": "1.0.0", + "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", + "recordedtime": "2025-08-28T08:45:00.000Z", + "severitynumber": 2, + "severitytext": "INFO", + "source": "/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status", + "specversion": "1.0", + "subject": "letter-origin/letter-rendering/letter/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96_38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq", + "time": "2025-08-28T08:45:00.000Z", + "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", + "type": "uk.nhs.notify.supplier-api.letter.PRINTED.v1" +} From 24f4385a9b1fcf21065c0872567fa19723b3876c Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Tue, 27 Jan 2026 14:59:57 +0000 Subject: [PATCH 03/15] init --- Makefile | 3 + package-lock.json | 106 ++++++++++++++++-- package.json | 3 +- tests/contracts/provider/.gitignore | 7 ++ .../letter-status.provider.pact.test.ts | 25 +++++ .../provider/__tests__/utils/utils.ts | 56 +++++++++ tests/contracts/provider/jest.config.ts | 15 +++ tests/contracts/provider/package.json | 21 ++++ tests/contracts/provider/scripts/test.sh | 34 ++++++ tests/contracts/provider/tsconfig.json | 12 ++ 10 files changed, 272 insertions(+), 10 deletions(-) create mode 100644 tests/contracts/provider/.gitignore create mode 100644 tests/contracts/provider/__tests__/letter-status.provider.pact.test.ts create mode 100644 tests/contracts/provider/__tests__/utils/utils.ts create mode 100644 tests/contracts/provider/jest.config.ts create mode 100644 tests/contracts/provider/package.json create mode 100755 tests/contracts/provider/scripts/test.sh create mode 100644 tests/contracts/provider/tsconfig.json diff --git a/Makefile b/Makefile index 9e4b8db20..14f31336f 100644 --- a/Makefile +++ b/Makefile @@ -106,6 +106,9 @@ test-component: test-performance: (cd tests && npm install && npm run test:performance) +test-contract: # Run provider contract tests @Testing + npm run test:contracts --workspace tests/contracts/provider + version: rm -f .version make version-create-effective-file dir=. diff --git a/package-lock.json b/package-lock.json index 64a58b073..66d4d4259 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "lambdas/*", "pact-contracts", "scripts/utilities/*", - "tests" + "tests", + "tests/contracts/*" ], "dependencies": { "@aws-sdk/client-api-gateway": "^3.906.0", @@ -125,7 +126,7 @@ }, "internal/events": { "name": "@nhsdigital/nhs-notify-event-schemas-supplier-api", - "version": "1.0.9", + "version": "1.0.10", "license": "MIT", "dependencies": { "@asyncapi/bundler": "^0.6.4", @@ -6079,6 +6080,10 @@ "dev": true, "license": "MIT" }, + "node_modules/@sap/contracts-provider": { + "resolved": "tests/contracts/provider", + "link": true + }, "node_modules/@sinclair/typebox": { "version": "0.34.47", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", @@ -21309,13 +21314,6 @@ "node": ">=18.17" } }, - "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", - "extraneous": true, - "license": "MIT" - }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -22333,6 +22331,40 @@ "typescript": "^5.9.3" } }, + "tests/contracts/provider": { + "name": "@sap/contracts-provider", + "version": "1.0.0", + "dependencies": { + "@nhsdigital/nhs-notify-event-schemas-supplier-api": "*", + "@pact-foundation/pact": "^16.0.4" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/jest": "^30.0.0", + "@types/node": "^22.0.0", + "glob": "^11.0.0", + "jest": "^30.0.0", + "ts-jest": "^29.4.0", + "typescript": "^5.9.3" + } + }, + "tests/contracts/provider/node_modules/@types/node": { + "version": "22.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", + "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "tests/contracts/provider/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "tests/node_modules/@types/node": { "version": "24.10.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", @@ -22343,6 +22375,62 @@ "undici-types": "~7.16.0" } }, + "tests/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "tests/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "tests/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "tests/node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", diff --git a/package.json b/package.json index 077c7a797..ea770ef3e 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "lambdas/*", "pact-contracts", "scripts/utilities/*", - "tests" + "tests", + "tests/contracts/*" ] } diff --git a/tests/contracts/provider/.gitignore b/tests/contracts/provider/.gitignore new file mode 100644 index 000000000..2359445b1 --- /dev/null +++ b/tests/contracts/provider/.gitignore @@ -0,0 +1,7 @@ +# Downloaded contracts and packages +.contracts/ +.packages/ + +# Build artifacts +dist/ +node_modules/ diff --git a/tests/contracts/provider/__tests__/letter-status.provider.pact.test.ts b/tests/contracts/provider/__tests__/letter-status.provider.pact.test.ts new file mode 100644 index 000000000..7954b8aa0 --- /dev/null +++ b/tests/contracts/provider/__tests__/letter-status.provider.pact.test.ts @@ -0,0 +1,25 @@ +import { MessageProviderPact } from "@pact-foundation/pact"; +import { + getAllLetterStatuses, + getMessageProviderForStatus, + getPactUrlForStatus, +} from "./utils/utils"; + +const CONSUMER_PACKAGE = "@nhsdigital/notify-core-consumer-contracts"; + +describe("Supplier API letter status provider tests", () => { + const statuses = getAllLetterStatuses(); + + describe.each(statuses)("letter.%s event", (status) => { + test(`verifies letter-${status.toLowerCase()} pact`, async () => { + const p = new MessageProviderPact({ + provider: `letter-${status.toLowerCase()}`, + messageProviders: getMessageProviderForStatus(status), + pactUrls: [getPactUrlForStatus(CONSUMER_PACKAGE, status)], + logLevel: "error", + }); + + await expect(p.verify()).resolves.not.toThrow(); + }, 60_000); + }); +}); diff --git a/tests/contracts/provider/__tests__/utils/utils.ts b/tests/contracts/provider/__tests__/utils/utils.ts new file mode 100644 index 000000000..8a40ab2da --- /dev/null +++ b/tests/contracts/provider/__tests__/utils/utils.ts @@ -0,0 +1,56 @@ +import path from "node:path"; +import fs from "node:fs"; +import { globSync } from "glob"; + +const LETTER_STATUSES = [ + "ACCEPTED", + "CANCELLED", + "DELIVERED", + "DISPATCHED", + "ENCLOSED", + "FAILED", + "FORWARDED", + "PENDING", + "PRINTED", + "REJECTED", + "RETURNED", +] as const; + +type LetterStatus = (typeof LETTER_STATUSES)[number]; + +export function getExampleEvent(status: LetterStatus): unknown { + const examplePath = path.join( + __dirname, + "../../../../../internal/events/schemas/examples", + `letter.${status}.json` + ); + + const content = fs.readFileSync(examplePath, "utf-8"); + return JSON.parse(content); +} + +export function getMessageProviderForStatus( + status: LetterStatus +): Record Promise> { + return { + [`letter-${status.toLowerCase()}`]: async () => getExampleEvent(status), + }; +} + +export function getPactUrlForStatus( + consumerPackage: string, + status: LetterStatus +): string { + const contractsDir = path.join( + __dirname, + "../../.contracts", + consumerPackage, + "pacts/supplier-api" + ); + + return path.join(contractsDir, `core-letter-${status.toLowerCase()}.json`); +} + +export function getAllLetterStatuses(): readonly LetterStatus[] { + return LETTER_STATUSES; +} diff --git a/tests/contracts/provider/jest.config.ts b/tests/contracts/provider/jest.config.ts new file mode 100644 index 000000000..dd91529ca --- /dev/null +++ b/tests/contracts/provider/jest.config.ts @@ -0,0 +1,15 @@ +import type { Config } from "jest"; + +const config: Config = { + testEnvironment: "node", + transform: { + "^.+\\.tsx?$": "ts-jest", + }, + testMatch: ["**/*.test.ts"], + moduleNameMapper: { + "@nhsdigital/nhs-notify-event-schemas-supplier-api$": + "/../../../internal/events/src", + }, +}; + +export default config; diff --git a/tests/contracts/provider/package.json b/tests/contracts/provider/package.json new file mode 100644 index 000000000..e1783fc11 --- /dev/null +++ b/tests/contracts/provider/package.json @@ -0,0 +1,21 @@ +{ + "name": "@sap/contracts-provider", + "version": "1.0.0", + "private": true, + "scripts": { + "test:contracts": "./scripts/test.sh" + }, + "dependencies": { + "@nhsdigital/nhs-notify-event-schemas-supplier-api": "*", + "@pact-foundation/pact": "^16.0.4" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/jest": "^30.0.0", + "@types/node": "^22.0.0", + "glob": "^11.0.0", + "jest": "^30.0.0", + "ts-jest": "^29.4.0", + "typescript": "^5.9.3" + } +} diff --git a/tests/contracts/provider/scripts/test.sh b/tests/contracts/provider/scripts/test.sh new file mode 100755 index 000000000..89ab6c1e9 --- /dev/null +++ b/tests/contracts/provider/scripts/test.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR="$(git rev-parse --show-toplevel)" +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +TESTS_ROOT="$(dirname "$SCRIPT_DIR")" + +cd "$TESTS_ROOT" + +rm -rf .packages .contracts +mkdir -p .packages + +# Use local tarball if available (for development), otherwise fetch from npm +CORE_CONTRACTS_LOCAL="${CORE_CONTRACTS_LOCAL:-}" + +if [[ -n "$CORE_CONTRACTS_LOCAL" && -f "$CORE_CONTRACTS_LOCAL" ]]; then + echo "Using local core consumer contracts: $CORE_CONTRACTS_LOCAL" + PKG_NAME="@nhsdigital/notify-core-consumer-contracts" + mkdir -p ".contracts/$PKG_NAME" + tar -xvzf "$CORE_CONTRACTS_LOCAL" -C ".contracts/$PKG_NAME" --strip-components=1 +else + CONSUMER_PACKAGES=( + "@nhsdigital/notify-core-consumer-contracts" + ) + + for PKG in "${CONSUMER_PACKAGES[@]}"; do + mkdir -p ".contracts/$PKG" + TGZ_NAME=$(npm pack "$PKG" --pack-destination .packages) + tar -xvzf ".packages/$TGZ_NAME" -C ".contracts/$PKG" --strip-components=1 + done +fi + +npx jest --runInBand diff --git a/tests/contracts/provider/tsconfig.json b/tests/contracts/provider/tsconfig.json new file mode 100644 index 000000000..3d815db46 --- /dev/null +++ b/tests/contracts/provider/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@tsconfig/node22/tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./", + "resolveJsonModule": true, + "esModuleInterop": true, + "isolatedModules": true + }, + "include": ["**/*.ts"], + "exclude": ["node_modules", "dist"] +} From f235408ffc38ca7f25c7cd14dfaa6b5c8aee2f91 Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Tue, 27 Jan 2026 15:15:17 +0000 Subject: [PATCH 04/15] whitespace fix --- internal/events/src/index.ts | 2 +- tests/contracts/provider/.gitignore | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 tests/contracts/provider/.gitignore diff --git a/internal/events/src/index.ts b/internal/events/src/index.ts index f19f9d258..8b89d770f 100644 --- a/internal/events/src/index.ts +++ b/internal/events/src/index.ts @@ -4,4 +4,4 @@ export { default as DomainBase } from "./domain/domain-base"; export * from "./events/event-envelope"; export * from "./events/letter-events"; export * from "./events/mi-events"; -export * from "./version" \ No newline at end of file +export * from "./version" diff --git a/tests/contracts/provider/.gitignore b/tests/contracts/provider/.gitignore new file mode 100644 index 000000000..65c82a45d --- /dev/null +++ b/tests/contracts/provider/.gitignore @@ -0,0 +1,2 @@ +.contracts/ +.packages/ From ab5c939289adbd45b20250eebeb1a7cd12c199c0 Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Tue, 27 Jan 2026 15:21:12 +0000 Subject: [PATCH 05/15] fmt fix --- internal/events/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/events/src/index.ts b/internal/events/src/index.ts index 8b89d770f..339ddcd64 100644 --- a/internal/events/src/index.ts +++ b/internal/events/src/index.ts @@ -4,4 +4,4 @@ export { default as DomainBase } from "./domain/domain-base"; export * from "./events/event-envelope"; export * from "./events/letter-events"; export * from "./events/mi-events"; -export * from "./version" +export * from "./version"; From 8a37ec0e34f7f02244f3aa55ea4bff84d8ef3276 Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Tue, 27 Jan 2026 15:26:28 +0000 Subject: [PATCH 06/15] add spec id to PENDING --- internal/events/schemas/examples/letter.PENDING.json | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/events/schemas/examples/letter.PENDING.json b/internal/events/schemas/examples/letter.PENDING.json index 2f57d388f..c5c0fe697 100644 --- a/internal/events/schemas/examples/letter.PENDING.json +++ b/internal/events/schemas/examples/letter.PENDING.json @@ -9,6 +9,7 @@ "source": "/data-plane/letter-rendering/comms-mgr-prod/prod", "subject": "client/e9dc9ee9-b2f8-4ef2-8351-73a9a29f8e96/letter-request/38U220Ghb1WXF8hCK6hL0LGya3Y_38U23YjtnKSZypvaCSb07adMOlq" }, + "specificationId": "1y3q9v1zzzz", "status": "PENDING", "supplierId": "supplier-12345" }, From 5fbc6006fb9c76408ac106d81cd792d9a1f04bdd Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Tue, 27 Jan 2026 16:33:13 +0000 Subject: [PATCH 07/15] rm override --- tests/contracts/provider/scripts/test.sh | 28 ++++++++---------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/tests/contracts/provider/scripts/test.sh b/tests/contracts/provider/scripts/test.sh index 89ab6c1e9..80a13e778 100755 --- a/tests/contracts/provider/scripts/test.sh +++ b/tests/contracts/provider/scripts/test.sh @@ -11,24 +11,14 @@ cd "$TESTS_ROOT" rm -rf .packages .contracts mkdir -p .packages -# Use local tarball if available (for development), otherwise fetch from npm -CORE_CONTRACTS_LOCAL="${CORE_CONTRACTS_LOCAL:-}" - -if [[ -n "$CORE_CONTRACTS_LOCAL" && -f "$CORE_CONTRACTS_LOCAL" ]]; then - echo "Using local core consumer contracts: $CORE_CONTRACTS_LOCAL" - PKG_NAME="@nhsdigital/notify-core-consumer-contracts" - mkdir -p ".contracts/$PKG_NAME" - tar -xvzf "$CORE_CONTRACTS_LOCAL" -C ".contracts/$PKG_NAME" --strip-components=1 -else - CONSUMER_PACKAGES=( - "@nhsdigital/notify-core-consumer-contracts" - ) - - for PKG in "${CONSUMER_PACKAGES[@]}"; do - mkdir -p ".contracts/$PKG" - TGZ_NAME=$(npm pack "$PKG" --pack-destination .packages) - tar -xvzf ".packages/$TGZ_NAME" -C ".contracts/$PKG" --strip-components=1 - done -fi +CONSUMER_PACKAGES=( + "@nhsdigital/notify-core-consumer-contracts" +) + +for PKG in "${CONSUMER_PACKAGES[@]}"; do + mkdir -p ".contracts/$PKG" + TGZ_NAME=$(npm pack "$PKG" --pack-destination .packages) + tar -xvzf ".packages/$TGZ_NAME" -C ".contracts/$PKG" --strip-components=1 +done npx jest --runInBand From 11f61f1c92293e34f1492e158b8b558d493e8290 Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Tue, 27 Jan 2026 16:48:47 +0000 Subject: [PATCH 08/15] add typecheck --- tests/contracts/provider/__tests__/utils/utils.ts | 11 +++++------ tests/contracts/provider/package.json | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/contracts/provider/__tests__/utils/utils.ts b/tests/contracts/provider/__tests__/utils/utils.ts index 8a40ab2da..df6f4546c 100644 --- a/tests/contracts/provider/__tests__/utils/utils.ts +++ b/tests/contracts/provider/__tests__/utils/utils.ts @@ -1,6 +1,5 @@ import path from "node:path"; import fs from "node:fs"; -import { globSync } from "glob"; const LETTER_STATUSES = [ "ACCEPTED", @@ -22,15 +21,15 @@ export function getExampleEvent(status: LetterStatus): unknown { const examplePath = path.join( __dirname, "../../../../../internal/events/schemas/examples", - `letter.${status}.json` + `letter.${status}.json`, ); - const content = fs.readFileSync(examplePath, "utf-8"); + const content = fs.readFileSync(examplePath, "utf8"); return JSON.parse(content); } export function getMessageProviderForStatus( - status: LetterStatus + status: LetterStatus, ): Record Promise> { return { [`letter-${status.toLowerCase()}`]: async () => getExampleEvent(status), @@ -39,13 +38,13 @@ export function getMessageProviderForStatus( export function getPactUrlForStatus( consumerPackage: string, - status: LetterStatus + status: LetterStatus, ): string { const contractsDir = path.join( __dirname, "../../.contracts", consumerPackage, - "pacts/supplier-api" + "pacts/supplier-api", ); return path.join(contractsDir, `core-letter-${status.toLowerCase()}.json`); diff --git a/tests/contracts/provider/package.json b/tests/contracts/provider/package.json index e1783fc11..00091b95a 100644 --- a/tests/contracts/provider/package.json +++ b/tests/contracts/provider/package.json @@ -3,7 +3,8 @@ "version": "1.0.0", "private": true, "scripts": { - "test:contracts": "./scripts/test.sh" + "test:contracts": "./scripts/test.sh", + "typecheck": "tsc --noEmit" }, "dependencies": { "@nhsdigital/nhs-notify-event-schemas-supplier-api": "*", From b1664697381bf9ca13a2a2759a2f464c5e225b76 Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Tue, 27 Jan 2026 16:57:16 +0000 Subject: [PATCH 09/15] add cleanup utils --- .../__tests__/letter-status.provider.pact.test.ts | 6 ++---- tests/contracts/provider/__tests__/utils/utils.ts | 13 +++++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/contracts/provider/__tests__/letter-status.provider.pact.test.ts b/tests/contracts/provider/__tests__/letter-status.provider.pact.test.ts index 7954b8aa0..7d6b3b2b2 100644 --- a/tests/contracts/provider/__tests__/letter-status.provider.pact.test.ts +++ b/tests/contracts/provider/__tests__/letter-status.provider.pact.test.ts @@ -1,6 +1,6 @@ import { MessageProviderPact } from "@pact-foundation/pact"; import { - getAllLetterStatuses, + LETTER_STATUSES, getMessageProviderForStatus, getPactUrlForStatus, } from "./utils/utils"; @@ -8,9 +8,7 @@ import { const CONSUMER_PACKAGE = "@nhsdigital/notify-core-consumer-contracts"; describe("Supplier API letter status provider tests", () => { - const statuses = getAllLetterStatuses(); - - describe.each(statuses)("letter.%s event", (status) => { + describe.each(LETTER_STATUSES)("letter.%s event", (status) => { test(`verifies letter-${status.toLowerCase()} pact`, async () => { const p = new MessageProviderPact({ provider: `letter-${status.toLowerCase()}`, diff --git a/tests/contracts/provider/__tests__/utils/utils.ts b/tests/contracts/provider/__tests__/utils/utils.ts index df6f4546c..15fe204b9 100644 --- a/tests/contracts/provider/__tests__/utils/utils.ts +++ b/tests/contracts/provider/__tests__/utils/utils.ts @@ -1,7 +1,7 @@ import path from "node:path"; import fs from "node:fs"; -const LETTER_STATUSES = [ +export const LETTER_STATUSES = [ "ACCEPTED", "CANCELLED", "DELIVERED", @@ -40,14 +40,15 @@ export function getPactUrlForStatus( consumerPackage: string, status: LetterStatus, ): string { - const contractsDir = path.join( + return path.join( __dirname, - "../../.contracts", + "../../", + ".contracts", consumerPackage, - "pacts/supplier-api", + "pacts", + "supplier-api", + `core-letter-${status.toLowerCase()}.json`, ); - - return path.join(contractsDir, `core-letter-${status.toLowerCase()}.json`); } export function getAllLetterStatuses(): readonly LetterStatus[] { From 8e226325ea046c702f31046408d07d37b175048b Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Tue, 27 Jan 2026 17:00:13 +0000 Subject: [PATCH 10/15] add lint --- package-lock.json | 1 + tests/contracts/provider/package.json | 3 +++ 2 files changed, 4 insertions(+) diff --git a/package-lock.json b/package-lock.json index 66d4d4259..9d7aec58d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22342,6 +22342,7 @@ "@tsconfig/node22": "^22.0.2", "@types/jest": "^30.0.0", "@types/node": "^22.0.0", + "eslint": "^9.27.0", "glob": "^11.0.0", "jest": "^30.0.0", "ts-jest": "^29.4.0", diff --git a/tests/contracts/provider/package.json b/tests/contracts/provider/package.json index 00091b95a..a49749cde 100644 --- a/tests/contracts/provider/package.json +++ b/tests/contracts/provider/package.json @@ -3,6 +3,8 @@ "version": "1.0.0", "private": true, "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix", "test:contracts": "./scripts/test.sh", "typecheck": "tsc --noEmit" }, @@ -14,6 +16,7 @@ "@tsconfig/node22": "^22.0.2", "@types/jest": "^30.0.0", "@types/node": "^22.0.0", + "eslint": "^9.27.0", "glob": "^11.0.0", "jest": "^30.0.0", "ts-jest": "^29.4.0", From e7254bfbc3027a3fbe3fec204080a28cc7744b99 Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Tue, 27 Jan 2026 17:06:09 +0000 Subject: [PATCH 11/15] skip unit tests --- tests/contracts/provider/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/contracts/provider/package.json b/tests/contracts/provider/package.json index a49749cde..8f603ba5a 100644 --- a/tests/contracts/provider/package.json +++ b/tests/contracts/provider/package.json @@ -6,6 +6,7 @@ "lint": "eslint .", "lint:fix": "eslint . --fix", "test:contracts": "./scripts/test.sh", + "test:unit": "echo Unit tests not required", "typecheck": "tsc --noEmit" }, "dependencies": { From ea06f5eb0616e9660319673a0e702c1fb56832be Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Thu, 29 Jan 2026 16:22:23 +0000 Subject: [PATCH 12/15] use install helper --- tests/pact-tests/run-pact-tests.sh | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/pact-tests/run-pact-tests.sh b/tests/pact-tests/run-pact-tests.sh index 163268e44..558b22fac 100755 --- a/tests/pact-tests/run-pact-tests.sh +++ b/tests/pact-tests/run-pact-tests.sh @@ -2,8 +2,23 @@ set -euo pipefail +install_package() { + local package=$1 + local tmplog + tmplog="$(mktemp)" + + pnpm install --no-lockfile "$package" 2>&1 | tee "$tmplog" + + if grep -q 'ERR_PNPM' "$tmplog"; then + echo "Error: pnpm install failed for $package" >&2 + exit 1 + fi + + pnpm list "$package" +} + # Ensure we have the latest package matching the major version -npm install --no-lockfile @nhsdigital/nhs-notify-event-schemas-letter-rendering@^2.0.0 +install_package @nhsdigital/nhs-notify-event-schemas-letter-rendering@^2 # Remove old PACTs rm -rf ./.pacts From cee4db9eeba479eaba908c3f0e93b155a2a5b3df Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Thu, 29 Jan 2026 16:39:43 +0000 Subject: [PATCH 13/15] revert run-pact-test --- tests/pact-tests/run-pact-tests.sh | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/tests/pact-tests/run-pact-tests.sh b/tests/pact-tests/run-pact-tests.sh index 558b22fac..a61a9f643 100755 --- a/tests/pact-tests/run-pact-tests.sh +++ b/tests/pact-tests/run-pact-tests.sh @@ -2,23 +2,8 @@ set -euo pipefail -install_package() { - local package=$1 - local tmplog - tmplog="$(mktemp)" - - pnpm install --no-lockfile "$package" 2>&1 | tee "$tmplog" - - if grep -q 'ERR_PNPM' "$tmplog"; then - echo "Error: pnpm install failed for $package" >&2 - exit 1 - fi - - pnpm list "$package" -} - # Ensure we have the latest package matching the major version -install_package @nhsdigital/nhs-notify-event-schemas-letter-rendering@^2 +npm install --no-lockfile @nhsdigital/nhs-notify-event-schemas-letter-rendering@^2 # Remove old PACTs rm -rf ./.pacts From 443994726258d4bc4c7bd379ed68d86fc846c914 Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Thu, 29 Jan 2026 17:39:01 +0000 Subject: [PATCH 14/15] add NODE_AUTH_TOKEN --- .github/actions/acceptance-tests/action.yml | 1 - .github/actions/node-install/action.yaml | 5 +---- .github/workflows/pr_closed.yaml | 16 ++++++---------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/.github/actions/acceptance-tests/action.yml b/.github/actions/acceptance-tests/action.yml index bf6148cda..3a0998696 100644 --- a/.github/actions/acceptance-tests/action.yml +++ b/.github/actions/acceptance-tests/action.yml @@ -37,7 +37,6 @@ runs: - name: "Repo setup" uses: ./.github/actions/node-install with: - node-version: ${{ steps.nodejs_version.outputs.nodejs_version }} GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }} - name: "Set PR NUMBER" diff --git a/.github/actions/node-install/action.yaml b/.github/actions/node-install/action.yaml index 534ebc097..48527b570 100644 --- a/.github/actions/node-install/action.yaml +++ b/.github/actions/node-install/action.yaml @@ -2,9 +2,6 @@ name: 'npm install and setup' description: 'Setup node, authenticate github package repository and perform clean npm install' inputs: - node-version: - description: 'Node.js version' - required: true GITHUB_TOKEN: description: "Token for access to github package registry" required: true @@ -15,7 +12,7 @@ runs: - name: 'Use Node.js' uses: actions/setup-node@v6 with: - node-version: '${{ inputs.node-version }}' + node-version-file: '.tool-versions' registry-url: 'https://npm.pkg.github.com' scope: '@nhsdigital' diff --git a/.github/workflows/pr_closed.yaml b/.github/workflows/pr_closed.yaml index 003cf976e..4a47f14f7 100644 --- a/.github/workflows/pr_closed.yaml +++ b/.github/workflows/pr_closed.yaml @@ -80,7 +80,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v4 with: - node-version: ${{ inputs.nodejs_version }} + node-version-file: '.tool-versions' registry-url: 'https://npm.pkg.github.com' - name: check if local version differs from latest published version @@ -113,18 +113,14 @@ jobs: steps: - name: "Checkout code" uses: actions/checkout@v5.0.0 - - name: Setup NodeJS - uses: actions/setup-node@v4 + - name: "Repo setup" + uses: ./.github/actions/node-install with: - node-version: ${{ inputs.nodejs_version }} - registry-url: 'https://npm.pkg.github.com' - - name: "Install dependencies" - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: npm ci + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Run provider contract tests" run: make test-contract env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_PACKAGES_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish-event-schemas: @@ -145,7 +141,7 @@ jobs: - name: Setup NodeJS uses: actions/setup-node@v4 with: - node-version: ${{ inputs.nodejs_version }} + node-version-file: '.tool-versions' registry-url: 'https://npm.pkg.github.com' - name: Install dependencies From b5cff51073d15f64da1171498026cfc9c674a368 Mon Sep 17 00:00:00 2001 From: "alex.nuttall1" Date: Thu, 29 Jan 2026 17:48:06 +0000 Subject: [PATCH 15/15] rm PACKAGES_TOKEN --- .github/workflows/pr_closed.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pr_closed.yaml b/.github/workflows/pr_closed.yaml index 4a47f14f7..31f81c713 100644 --- a/.github/workflows/pr_closed.yaml +++ b/.github/workflows/pr_closed.yaml @@ -121,7 +121,6 @@ jobs: run: make test-contract env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_PACKAGES_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish-event-schemas: name: Publish event schemas package to GitHub package registry