@defra/forms-engine-plugin 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/.server/server/plugins/engine/models/FormModel.d.ts +2 -0
  2. package/.server/server/plugins/engine/models/FormModel.js +4 -1
  3. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  4. package/.server/server/plugins/engine/outputFormatters/adapter/v1.d.ts +4 -0
  5. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +25 -0
  6. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
  7. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +7 -6
  8. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  9. package/.server/server/plugins/engine/routes/index.js +3 -1
  10. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  11. package/.server/server/plugins/engine/types/schema.js +3 -2
  12. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  13. package/.server/server/plugins/engine/types.d.ts +3 -1
  14. package/.server/server/plugins/engine/types.js.map +1 -1
  15. package/package.json +2 -2
  16. package/src/server/plugins/engine/models/FormModel.test.ts +64 -0
  17. package/src/server/plugins/engine/models/FormModel.ts +5 -2
  18. package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +446 -13
  19. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +37 -0
  20. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +8 -6
  21. package/src/server/plugins/engine/routes/index.ts +3 -1
  22. package/src/server/plugins/engine/types/schema.test.ts +40 -0
  23. package/src/server/plugins/engine/types/schema.ts +3 -1
  24. package/src/server/plugins/engine/types.ts +3 -0
@@ -40,6 +40,11 @@ export function format(
40
40
 
41
41
  const transformedData = v2DataParsed.data
42
42
 
43
+ const versionMetadata = getVersionMetadata(
44
+ context.submittedVersionNumber,
45
+ formMetadata
46
+ )
47
+
43
48
  const meta: FormAdapterSubmissionMessageMeta = {
44
49
  schemaVersion: FormAdapterSubmissionSchemaVersion.V1,
45
50
  timestamp: new Date(),
@@ -51,6 +56,10 @@ export function format(
51
56
  isPreview: formStatus.isPreview,
52
57
  notificationEmail: formMetadata?.notificationEmail ?? ''
53
58
  }
59
+
60
+ if (versionMetadata) {
61
+ meta.versionMetadata = versionMetadata
62
+ }
54
63
  const data: FormAdapterSubmissionMessageData = transformedData
55
64
 
56
65
  const result: FormAdapterSubmissionMessageResult = {
@@ -66,6 +75,34 @@ export function format(
66
75
  return JSON.stringify(payload)
67
76
  }
68
77
 
78
+ export function getVersionMetadata(
79
+ submittedVersionNumber: number | undefined,
80
+ formMetadata?: FormMetadata
81
+ ): { versionNumber: number; createdAt: Date } | undefined {
82
+ if (!formMetadata?.versions?.length) {
83
+ return undefined
84
+ }
85
+
86
+ if (submittedVersionNumber !== undefined) {
87
+ const submittedVersion = formMetadata.versions.find(
88
+ (v) => v.versionNumber === submittedVersionNumber
89
+ )
90
+ if (submittedVersion) {
91
+ return {
92
+ versionNumber: submittedVersion.versionNumber,
93
+ createdAt: submittedVersion.createdAt
94
+ }
95
+ }
96
+ }
97
+
98
+ // fallback to first available version
99
+ const firstVersion = formMetadata.versions[0]
100
+ return {
101
+ versionNumber: firstVersion.versionNumber,
102
+ createdAt: firstVersion.createdAt
103
+ }
104
+ }
105
+
69
106
  function extractCsvFiles(
70
107
  submitResponse: SubmitResponsePayload
71
108
  ): FormAdapterSubmissionMessageResult['files'] {
@@ -29,13 +29,15 @@ export function format(
29
29
 
30
30
  const categorisedData = categoriseData(items)
31
31
 
32
+ const meta: Record<string, unknown> = {
33
+ schemaVersion: '2',
34
+ timestamp: now.toISOString(),
35
+ definition: model.def,
36
+ referenceNumber: context.referenceNumber
37
+ }
38
+
32
39
  const data = {
33
- meta: {
34
- schemaVersion: '2',
35
- timestamp: now.toISOString(),
36
- definition: model.def,
37
- referenceNumber: context.referenceNumber
38
- },
40
+ meta,
39
41
  data: categorisedData
40
42
  }
41
43
 
@@ -151,10 +151,12 @@ export function makeLoadFormPreHandler(server: Server, options: PluginOptions) {
151
151
  : `${prefix}/${slug}`
152
152
  ).substring(1)
153
153
 
154
+ const versionNumber = metadata.versions?.[0]?.versionNumber
155
+
154
156
  // Construct the form model
155
157
  const model = new FormModel(
156
158
  definition,
157
- { basePath },
159
+ { basePath, versionNumber },
158
160
  services,
159
161
  controllers
160
162
  )
@@ -156,5 +156,45 @@ describe('Schema validation', () => {
156
156
  formAdapterSubmissionMessagePayloadSchema.validate(payloadWithoutData)
157
157
  expect(error).toBeDefined()
158
158
  })
159
+
160
+ it('should validate payload with versionMetadata', () => {
161
+ const payloadWithVersion = {
162
+ ...validPayload,
163
+ meta: {
164
+ ...validPayload.meta,
165
+ versionMetadata: {
166
+ versionNumber: 19,
167
+ createdAt: new Date('2025-09-08T09:28:15.576Z')
168
+ }
169
+ }
170
+ }
171
+ const { error } =
172
+ formAdapterSubmissionMessagePayloadSchema.validate(payloadWithVersion)
173
+ expect(error).toBeUndefined()
174
+ })
175
+
176
+ it('should validate payload without versionMetadata', () => {
177
+ const { error } =
178
+ formAdapterSubmissionMessagePayloadSchema.validate(validPayload)
179
+ expect(error).toBeUndefined()
180
+ })
181
+
182
+ it('should reject invalid versionMetadata', () => {
183
+ const payloadWithInvalidVersion = {
184
+ ...validPayload,
185
+ meta: {
186
+ ...validPayload.meta,
187
+ versionMetadata: {
188
+ versionNumber: 'not-a-number', // Invalid - should be number
189
+ createdAt: new Date('2025-09-08T09:28:15.576Z')
190
+ }
191
+ }
192
+ }
193
+ const { error } = formAdapterSubmissionMessagePayloadSchema.validate(
194
+ payloadWithInvalidVersion
195
+ )
196
+ expect(error).toBeDefined()
197
+ expect(error?.message).toContain('must be a number')
198
+ })
159
199
  })
160
200
  })
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  FormStatus,
3
+ formVersionMetadataSchema,
3
4
  idSchema,
4
5
  notificationEmailAddressSchema,
5
6
  slugSchema,
@@ -29,7 +30,8 @@ export const formAdapterSubmissionMessageMetaSchema =
29
30
  .valid(...Object.values(FormStatus))
30
31
  .required(),
31
32
  isPreview: Joi.boolean().required(),
32
- notificationEmail: notificationEmailAddressSchema.required()
33
+ notificationEmail: notificationEmailAddressSchema.required(),
34
+ versionMetadata: formVersionMetadataSchema.optional()
33
35
  })
34
36
 
35
37
  export const formAdapterSubmissionMessageDataSchema =
@@ -3,6 +3,7 @@ import {
3
3
  type Event,
4
4
  type FormDefinition,
5
5
  type FormMetadata,
6
+ type FormVersionMetadata,
6
7
  type Item,
7
8
  type List,
8
9
  type Page
@@ -179,6 +180,7 @@ export interface FormContext {
179
180
  pageMap: Map<string, PageControllerClass>
180
181
  componentMap: Map<string, Component>
181
182
  referenceNumber: string
183
+ submittedVersionNumber?: number
182
184
  }
183
185
 
184
186
  export type FormContextRequest = (
@@ -405,6 +407,7 @@ export interface FormAdapterSubmissionMessageMeta {
405
407
  status: FormStatus
406
408
  isPreview: boolean
407
409
  notificationEmail: string
410
+ versionMetadata?: FormVersionMetadata
408
411
  }
409
412
 
410
413
  export type FormAdapterSubmissionMessageMetaSerialised = Omit<