diff --git a/package-lock.json b/package-lock.json index 0c19333..82b64e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11129,7 +11129,7 @@ }, "packages/events": { "name": "@nhsdigital/nhs-notify-event-schemas-letter-rendering", - "version": "1.1.3", + "version": "1.1.4", "dependencies": { "@asyncapi/bundler": "^0.6.4", "zod": "^4.1.11" diff --git a/packages/events/package.json b/packages/events/package.json index dc63dd2..8ec6d62 100644 --- a/packages/events/package.json +++ b/packages/events/package.json @@ -40,5 +40,5 @@ "test:unit": "jest", "prepare": "npm run build" }, - "version": "1.1.3" + "version": "1.1.4" } diff --git a/packages/events/src/events/__tests__/event-envelope.test.ts b/packages/events/src/events/__tests__/event-envelope.test.ts index 620df91..dda4cf4 100644 --- a/packages/events/src/events/__tests__/event-envelope.test.ts +++ b/packages/events/src/events/__tests__/event-envelope.test.ts @@ -28,6 +28,7 @@ describe("EventEnvelope schema validation", () => { recordedtime: "2025-10-01T10:15:30.250Z", severitynumber: 2, severitytext: "INFO", + plane: "data", }; describe("basic validation", () => { @@ -44,6 +45,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "TRACE", severitynumber: 0, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -55,6 +57,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "DEBUG", severitynumber: 1, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -66,6 +69,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "INFO", severitynumber: 2, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -77,6 +81,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "WARN", severitynumber: 3, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -88,6 +93,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "ERROR", severitynumber: 4, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -99,6 +105,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "FATAL", severitynumber: 5, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -110,6 +117,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "TRACE", severitynumber: 1, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -121,6 +129,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "DEBUG", severitynumber: 2, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -132,6 +141,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "INFO", severitynumber: 1, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -143,6 +153,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "WARN", severitynumber: 2, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -154,6 +165,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "ERROR", severitynumber: 3, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -165,6 +177,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "FATAL", severitynumber: 4, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -176,6 +189,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: undefined, severitynumber: 2, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -187,6 +201,7 @@ describe("EventEnvelope schema validation", () => { ...baseValidEnvelope, severitytext: "INFO", severitynumber: 2, + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -208,6 +223,7 @@ describe("EventEnvelope schema validation", () => { dataclassification: "restricted", dataregulation: "GDPR", datacategory: "sensitive", + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -216,65 +232,12 @@ describe("EventEnvelope schema validation", () => { }); }); - describe("source validation", () => { - it("should accept source with letter-rendering plane", () => { - const envelope = { - ...baseValidEnvelope, - source: "/data-plane/letter-rendering/ordering", - }; - - const result = $Envelope.safeParse(envelope); - expect(result.error).toBeUndefined(); - expect(result.success).toBe(true); - }); - - it("should accept source with digital-letters domain", () => { - const envelope = { - ...baseValidEnvelope, - source: "/data-plane/digital-letters/ordering", - }; - - const result = $Envelope.safeParse(envelope); - expect(result.error).toBeUndefined(); - expect(result.success).toBe(true); - }); - - it("should accept source with digital-letters domain and additional path", () => { - const envelope = { - ...baseValidEnvelope, - source: "/data-plane/digital-letters/ordering/sub-path", - }; - - const result = $Envelope.safeParse(envelope); - expect(result.error).toBeUndefined(); - expect(result.success).toBe(true); - }); - - it("should accept source with letter-rendering plane and additional path", () => { - const envelope = { - ...baseValidEnvelope, - source: "/data-plane/letter-rendering/ordering/sub-path/more", - }; - - const result = $Envelope.safeParse(envelope); - expect(result.error).toBeUndefined(); - expect(result.success).toBe(true); - }); - + describe("edge cases", () => { it("should reject invalid source pattern", () => { const envelope = { ...baseValidEnvelope, source: "/invalid-plane/test", - }; - - const result = $Envelope.safeParse(envelope); - expect(result.success).toBe(false); - }); - - it("should reject source without data-plane prefix", () => { - const envelope = { - ...baseValidEnvelope, - source: "/digital-letters/ordering", + plane: "data", }; const result = $Envelope.safeParse(envelope); @@ -304,6 +267,7 @@ describe("EventEnvelope schema validation", () => { recordedtime: "2025-10-01T10:15:30.250Z", severitynumber: 2, severitytext: "INFO" as const, + plane: "data", }; it("should accept subject with valid prefix when prefix is required", () => { @@ -311,6 +275,7 @@ describe("EventEnvelope schema validation", () => { ...baseLetterEnvelope, subject: "letter-origin/letter-rendering/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + plane: "data", }; const result = $EnvelopeWithPrefix.safeParse(envelope); @@ -322,6 +287,7 @@ describe("EventEnvelope schema validation", () => { const envelope = { ...baseLetterEnvelope, subject: "letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + plane: "data", }; const result = $EnvelopeWithPrefix.safeParse(envelope); @@ -334,6 +300,7 @@ describe("EventEnvelope schema validation", () => { const envelope = { ...baseLetterEnvelope, subject: "letter-origin/letter/f47ac10b-58cc-4372-a567-0e02b2c3d479", + plane: "data", }; const result = $EnvelopeWithPrefix.safeParse(envelope); @@ -359,6 +326,7 @@ describe("EventEnvelope schema validation", () => { recordedtime: "2025-10-01T10:15:30.250Z", severitynumber: 2, severitytext: "INFO" as const, + plane: "data", }; const result = $EnvelopeNoPrefix.safeParse(envelope); @@ -378,6 +346,7 @@ describe("EventEnvelope schema validation", () => { const envelope = { ...baseLetterEnvelope, subject: "a/b/c/letter/test-id-123", + plane: "data", }; const result = $EnvelopeMultiSegmentPrefix.safeParse(envelope); @@ -404,10 +373,57 @@ describe("EventEnvelope schema validation", () => { recordedtime: "2025-10-01T10:15:30.250Z", severitynumber: 2, severitytext: "INFO" as const, + plane: "data", }; const result = $EnvelopeNoPrefix.safeParse(envelope); expect(result.success).toBe(false); }); }); + + // Additional tests for digital-letters source and optional request ID fields + describe("source validation", () => { + it("should accept source with digital-letters domain", () => { + const envelope = { + ...baseValidEnvelope, + source: "/data-plane/digital-letters/ordering", + plane: "data", + }; + const result = $Envelope.safeParse(envelope); + expect(result.error).toBeUndefined(); + expect(result.success).toBe(true); + }); + it("should accept source with digital-letters domain and additional path", () => { + const envelope = { + ...baseValidEnvelope, + source: "/data-plane/digital-letters/ordering/sub-path", + plane: "data", + }; + const result = $Envelope.safeParse(envelope); + expect(result.error).toBeUndefined(); + expect(result.success).toBe(true); + }); + it("should accept source with letter-rendering plane and additional path", () => { + const envelope = { + ...baseValidEnvelope, + source: "/data-plane/letter-rendering/ordering/sub-path/more", + plane: "data", + }; + const result = $Envelope.safeParse(envelope); + expect(result.error).toBeUndefined(); + expect(result.success).toBe(true); + }); + }); + describe("optional request ID fields", () => { + it("should accept envelope with optional requestId fields", () => { + const envelope = { + ...baseValidEnvelope, + requestId: "req-12345", + correlationId: "corr-67890", + plane: "data", + }; + const result = $Envelope.safeParse(envelope); + expect(result.success).toBe(true); + }); + }); }); diff --git a/packages/events/src/events/__tests__/testData/letter-request-prepared-valid.json b/packages/events/src/events/__tests__/testData/letter-request-prepared-valid.json index 1d44616..30a6d6d 100644 --- a/packages/events/src/events/__tests__/testData/letter-request-prepared-valid.json +++ b/packages/events/src/events/__tests__/testData/letter-request-prepared-valid.json @@ -17,6 +17,7 @@ "datacontenttype": "application/json", "dataschema": "https://notify.nhs.uk/cloudevents/schemas/letter-rendering/letter-request.prepared.1.0.0.schema.json", "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", "recordedtime": "2025-08-28T08:45:00.000Z", "severitynumber": 2, "severitytext": "INFO", diff --git a/packages/events/src/events/__tests__/testData/letter-request-prepared-with-invalid-major-version.json b/packages/events/src/events/__tests__/testData/letter-request-prepared-with-invalid-major-version.json index d493066..f0dce4f 100644 --- a/packages/events/src/events/__tests__/testData/letter-request-prepared-with-invalid-major-version.json +++ b/packages/events/src/events/__tests__/testData/letter-request-prepared-with-invalid-major-version.json @@ -17,6 +17,7 @@ "datacontenttype": "application/json", "dataschema": "https://notify.nhs.uk/cloudevents/schemas/letter-rendering/letter-request.prepared.0.1.0.schema.json", "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", "recordedtime": "2025-08-28T08:45:00.000Z", "severitynumber": 2, "severitytext": "INFO", diff --git a/packages/events/src/events/__tests__/testData/letter-request-prepared-with-missing-fields.json b/packages/events/src/events/__tests__/testData/letter-request-prepared-with-missing-fields.json index 2d6a91e..5b03b0a 100644 --- a/packages/events/src/events/__tests__/testData/letter-request-prepared-with-missing-fields.json +++ b/packages/events/src/events/__tests__/testData/letter-request-prepared-with-missing-fields.json @@ -16,6 +16,7 @@ "datacontenttype": "application/json", "dataschema": "https://notify.nhs.uk/cloudevents/schemas/letter-rendering/letter-request.prepared.1.0.0.schema.json", "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", "recordedtime": "2025-08-28T08:45:00.000Z", "severitynumber": 2, "severitytext": "INFO", diff --git a/packages/events/src/events/__tests__/testData/letter-request-prepared-with-partial-optional-fields.json b/packages/events/src/events/__tests__/testData/letter-request-prepared-with-partial-optional-fields.json index c0160af..15cb6e2 100644 --- a/packages/events/src/events/__tests__/testData/letter-request-prepared-with-partial-optional-fields.json +++ b/packages/events/src/events/__tests__/testData/letter-request-prepared-with-partial-optional-fields.json @@ -14,6 +14,7 @@ "datacontenttype": "application/json", "dataschema": "https://notify.nhs.uk/cloudevents/schemas/letter-rendering/letter-request.prepared.1.0.0.schema.json", "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", "recordedtime": "2025-08-28T08:45:00.000Z", "severitynumber": 2, "severitytext": "INFO", diff --git a/packages/events/src/events/__tests__/testData/letter-request-prepared-without-optional-fields.json b/packages/events/src/events/__tests__/testData/letter-request-prepared-without-optional-fields.json index cae682f..6f6b83a 100644 --- a/packages/events/src/events/__tests__/testData/letter-request-prepared-without-optional-fields.json +++ b/packages/events/src/events/__tests__/testData/letter-request-prepared-without-optional-fields.json @@ -12,6 +12,7 @@ "datacontenttype": "application/json", "dataschema": "https://notify.nhs.uk/cloudevents/schemas/letter-rendering/letter-request.prepared.1.0.0.schema.json", "id": "23f1f09c-a555-4d9b-8405-0b33490bc920", + "plane": "data", "recordedtime": "2025-08-28T08:45:00.000Z", "severitynumber": 2, "severitytext": "INFO", diff --git a/packages/events/src/events/event-envelope.ts b/packages/events/src/events/event-envelope.ts index 233193f..a2bad26 100644 --- a/packages/events/src/events/event-envelope.ts +++ b/packages/events/src/events/event-envelope.ts @@ -135,6 +135,12 @@ export function EventEnvelope( }), ), + plane: z.literal("data").meta({ + title: "Event Plane", + description: "The event bus in which the event was generated.", + examples: ["data"], + }), + recordedtime: z.iso.datetime().meta({ title: "Recorded Time", description: