Skip to content

Commit 4e58f88

Browse files
authored
CCM-12896: Automatically Generate Typescript Types and Validator Code from Event JSON Schemas (#138)
* CCM-12896: Generate JSON schemas during make config * CCM-12896: Generate validation code from JSON schemas using ajv * CCM-12896: Generate TypeScript types from JSON schemas using json-schema-to-typescript
1 parent a5609d7 commit 4e58f88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1268
-1878
lines changed

.github/workflows/stage-2-test.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
npm ci
5858
- name: "Generate dependencies"
5959
run: |
60-
npm run generate-dependencies --workspaces --if-present
60+
npm run generate-dependencies
6161
git diff --exit-code
6262
test-unit:
6363
name: "Unit tests"
@@ -71,7 +71,7 @@ jobs:
7171
npm ci
7272
- name: "Generate dependencies"
7373
run: |
74-
npm run generate-dependencies --workspaces --if-present
74+
npm run generate-dependencies
7575
- name: "Run unit test suite"
7676
run: |
7777
make test-unit
@@ -104,7 +104,7 @@ jobs:
104104
npm ci
105105
- name: "Generate dependencies"
106106
run: |
107-
npm run generate-dependencies --workspaces --if-present
107+
npm run generate-dependencies
108108
- name: "Run linting"
109109
run: |
110110
make test-lint
@@ -120,7 +120,7 @@ jobs:
120120
npm ci
121121
- name: "Generate dependencies"
122122
run: |
123-
npm run generate-dependencies --workspaces --if-present
123+
npm run generate-dependencies
124124
- name: "Run typecheck"
125125
run: |
126126
make test-typecheck

.github/workflows/stage-3-build.yaml

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -44,40 +44,3 @@ jobs:
4444
uses: ./.github/actions/build-docs
4545
with:
4646
version: "${{ inputs.version }}"
47-
48-
49-
50-
# artefact-1:
51-
# name: "Artefact 1"
52-
# runs-on: ubuntu-latest
53-
# timeout-minutes: 3
54-
# steps:
55-
# - name: "Checkout code"
56-
# uses: actions/checkout@v5
57-
# - name: "Build artefact 1"
58-
# run: |
59-
# echo "Building artefact 1 ..."
60-
# - name: "Check artefact 1"
61-
# run: |
62-
# echo "Checking artefact 1 ..."
63-
# - name: "Upload artefact 1"
64-
# run: |
65-
# echo "Uploading artefact 1 ..."
66-
# # Use either action/cache or action/upload-artifact
67-
# artefact-n:
68-
# name: "Artefact n"
69-
# runs-on: ubuntu-latest
70-
# timeout-minutes: 3
71-
# steps:
72-
# - name: "Checkout code"
73-
# uses: actions/checkout@v5
74-
# - name: "Build artefact n"
75-
# run: |
76-
# echo "Building artefact n ..."
77-
# - name: "Check artefact n"
78-
# run: |
79-
# echo "Checking artefact n ..."
80-
# - name: "Upload artefact n"
81-
# run: |
82-
# echo "Uploading artefact n ..."
83-
# # Use either action/cache or action/upload-artifact

Makefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ quick-start: config clean build serve-docs # Quick start target to setup, build
1212
dependencies: # Install dependencies needed to build and test the project @Pipeline
1313
# TODO: Implement installation of your project dependencies
1414

15+
generate: # Generate any autogenerated output @Pipeline
16+
npm run generate-dependencies
17+
1518
build: # Build the project artefact @Pipeline
1619
$(MAKE) -C docs build
1720

@@ -30,12 +33,14 @@ clean:: # Clean-up project resources (main) @Operations
3033
$(MAKE) -C src/eventcatalogasyncapiimporter clean
3134
$(MAKE) -C src/eventcatalogasyncapiimporter clean-output
3235
rm -f .version
33-
# TODO: Implement project resources clean-up step
36+
npm run clean
3437

3538
config:: _install-dependencies version # Configure development environment (main) @Configuration
3639
$(MAKE) -C docs install
3740
$(MAKE) -C src/cloudevents install
3841
$(MAKE) -C src/eventcatalogasyncapiimporter install
42+
npm install
43+
$(MAKE) generate
3944

4045
serve-docs:
4146
$(MAKE) -C docs s
@@ -50,5 +55,5 @@ ${VERBOSE}.SILENT: \
5055
build \
5156
clean \
5257
config \
53-
dependencies \
58+
generate \
5459
deploy \

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ Installation and configuration of the toolchain dependencies
7676
make config
7777
```
7878
79+
Note that the `make config` command will also build the flattened event schemas and auto-generate a Typescript
80+
type and JS validator code for each event. See the [typescript-schema-generator](src/typescript-schema-generator/)
81+
package for more details.
82+
7983
## Usage
8084

8185
### Testing

docs/Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ build: clean build-schemas copy-schema-docs copy-schema-yaml-docs copy-schemas b
4343

4444
build-ci: build-schemas-ci copy-schema-docs copy-schema-yaml-docs copy-schemas build-eventcatalog build-docs show-build-output
4545

46-
4746
build-eventcatalog:
4847
$(MAKE) build-eventcatalog-prereq
4948
make -C $(EVENT_CAT_ROOT_DIR) build BASE_URL=$(EVENT_CAT_URL)

infrastructure/terraform/components/dl/pre.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66

77
npm ci
88

9-
npm run generate-dependencies --workspaces --if-present
9+
npm run generate-dependencies
1010

1111
npm run lambda-build --workspaces --if-present

lambdas/mesh-poll/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
22
"dependencies": {
3-
"aws-lambda": "^1.0.7"
3+
"aws-lambda": "^1.0.7",
4+
"digital-letters-events": "^0.0.1"
45
},
56
"devDependencies": {
67
"@tsconfig/node22": "^22.0.2",
78
"@types/aws-lambda": "^8.10.155",
8-
"@types/jest": "^30.0.0",
9-
"jest": "^30.2.0",
9+
"@types/jest": "^29.5.14",
10+
"jest": "^29.7.0",
1011
"jest-mock-extended": "^4.0.0",
1112
"typescript": "^5.9.3"
1213
},
Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,55 @@
11
import type { Context } from 'aws-lambda';
22
import { mockDeep } from 'jest-mock-extended';
3+
import { PDMResourceSubmitted } from 'digital-letters-events';
34
import { handler } from '..';
45

6+
const context = mockDeep<Context>();
7+
const callback = jest.fn();
8+
59
describe('event-logging Lambda', () => {
610
it('logs the input event and returns 200', async () => {
7-
const event = { foo: 'bar' };
8-
const context = mockDeep<Context>();
9-
const callback = jest.fn();
11+
const event: PDMResourceSubmitted = {
12+
type: 'uk.nhs.notify.digital.letters.pdm.resource.submitted.v1',
13+
source:
14+
'/nhs/england/notify/staging/dev-647563337/data-plane/digitalletters/pdm',
15+
dataschema:
16+
'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10-draft/data/digital-letters-pdm-resource-submitted-data.schema.json',
17+
specversion: '1.0',
18+
id: '0249e529-f947-4012-819e-b634eb71be79',
19+
subject:
20+
'customer/7ff8ed41-cd5f-20e4-ef4e-34f96d8cc8ac/75027ace-9b8c-bcfe-866e-6c24242cffc3/q58dnxk5e/4cbek805wwx/yiaw7bl0d/her/1ccb7eb8-c6fe-0a42-279a-2a0e48ff1ca9/zk',
21+
time: '2025-11-21T16:01:52.268Z',
22+
datacontenttype: 'application/json',
23+
traceparent: '00-ee4790eb6821064c645406abe918b3da-3a4e6957ce2a15de-01',
24+
tracestate: 'nisi quis',
25+
partitionkey: 'customer-7ff8ed41',
26+
recordedtime: '2025-11-21T16:01:53.268Z',
27+
sampledrate: 1,
28+
sequence: '00000000000350773861',
29+
severitytext: 'INFO',
30+
severitynumber: 2,
31+
dataclassification: 'restricted',
32+
dataregulation: 'ISO-27001',
33+
datacategory: 'non-sensitive',
34+
data: {
35+
messageReference: 'incididunt Ut aute laborum',
36+
senderId: 'officia voluptate culpa Ut dolor',
37+
resourceId: 'a2bcbb42-ab7e-42b6-88d6-74f8d3ca4a09',
38+
retryCount: 97_903_257,
39+
},
40+
};
41+
1042
const result = await handler(event, context, callback);
1143

1244
expect(result).toEqual({
1345
statusCode: 200,
1446
body: 'Event logged',
1547
});
1648
});
49+
50+
it('throws an error if an invalid event is provided', async () => {
51+
const invalidEvent = { foo: 'bar' };
52+
53+
await expect(handler(invalidEvent, context, callback)).rejects.toThrow();
54+
});
1755
});

lambdas/mesh-poll/src/index.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,55 @@
1+
/* eslint-disable no-console -- Allowing console logging as this is an example file. */
12
// Replace me with the actual code for your Lambda function
23
import { Handler } from 'aws-lambda';
4+
import { PDMResourceSubmitted } from 'digital-letters-events';
5+
import eventValidator from 'digital-letters-events/PDMResourceSubmitted.js';
36

4-
export const handler: Handler = async (event) => {
5-
// eslint-disable-next-line no-console
7+
export const handler: Handler = async (event: PDMResourceSubmitted) => {
68
console.log('Received event:', event);
9+
10+
// We can build a new PDMResourceSubmitted event object like this:
11+
const pdmResourceSubmittedEvent: PDMResourceSubmitted = {
12+
type: 'uk.nhs.notify.digital.letters.pdm.resource.submitted.v1',
13+
source:
14+
'/nhs/england/notify/staging/dev-647563337/data-plane/digitalletters/pdm',
15+
dataschema:
16+
'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10-draft/data/digital-letters-pdm-resource-submitted-data.schema.json',
17+
specversion: '1.0',
18+
id: '0249e529-f947-4012-819e-b634eb71be79',
19+
subject:
20+
'customer/7ff8ed41-cd5f-20e4-ef4e-34f96d8cc8ac/75027ace-9b8c-bcfe-866e-6c24242cffc3/q58dnxk5e/4cbek805wwx/yiaw7bl0d/her/1ccb7eb8-c6fe-0a42-279a-2a0e48ff1ca9/zk',
21+
time: '2025-11-21T16:01:52.268Z',
22+
datacontenttype: 'application/json',
23+
traceparent: '00-ee4790eb6821064c645406abe918b3da-3a4e6957ce2a15de-01',
24+
tracestate: 'nisi quis',
25+
partitionkey: 'customer-7ff8ed41',
26+
recordedtime: '2025-11-21T16:01:53.268Z',
27+
sampledrate: 1,
28+
sequence: '00000000000350773861',
29+
severitytext: 'INFO',
30+
severitynumber: 2,
31+
dataclassification: 'restricted',
32+
dataregulation: 'ISO-27001',
33+
datacategory: 'non-sensitive',
34+
data: {
35+
messageReference: 'incididunt Ut aute laborum',
36+
senderId: 'officia voluptate culpa Ut dolor',
37+
resourceId: 'a2bcbb42-ab7e-42b6-88d6-74f8d3ca4a09',
38+
retryCount: 97_903_257,
39+
},
40+
};
41+
42+
console.log('PDM resource submitted event:', pdmResourceSubmittedEvent);
43+
44+
// We can validate an event like this:
45+
const isEventValid = eventValidator(event);
46+
if (isEventValid) {
47+
console.log('pdmResourceSubmittedEvent is valid!');
48+
} else {
49+
console.error('Validation failure!', eventValidator.errors);
50+
throw new Error('Event validation failed');
51+
}
52+
753
return {
854
statusCode: 200,
955
body: 'Event logged',

lambdas/mesh-poll/tsconfig.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
2+
"compilerOptions": {
3+
"allowJs": true,
4+
"isolatedModules": true
5+
},
26
"extends": "@tsconfig/node22/tsconfig.json",
37
"include": [
48
"src/**/*",

0 commit comments

Comments
 (0)