@defra/forms-engine-plugin 2.1.7 → 2.1.9
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.
- package/.server/server/plugins/engine/components/CheckboxesField.d.ts +2 -0
- package/.server/server/plugins/engine/components/CheckboxesField.js +13 -7
- package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
- package/.server/server/plugins/engine/components/DatePartsField.d.ts +2 -0
- package/.server/server/plugins/engine/components/DatePartsField.js +11 -5
- package/.server/server/plugins/engine/components/DatePartsField.js.map +1 -1
- package/.server/server/plugins/engine/components/FileUploadField.d.ts +2 -0
- package/.server/server/plugins/engine/components/FileUploadField.js +9 -3
- package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -1
- package/.server/server/plugins/engine/components/FormComponent.d.ts +2 -0
- package/.server/server/plugins/engine/components/FormComponent.js +11 -4
- package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
- package/.server/server/plugins/engine/components/ListFormComponent.d.ts +1 -0
- package/.server/server/plugins/engine/components/ListFormComponent.js +6 -4
- package/.server/server/plugins/engine/components/ListFormComponent.js.map +1 -1
- package/.server/server/plugins/engine/components/MonthYearField.d.ts +2 -0
- package/.server/server/plugins/engine/components/MonthYearField.js +9 -3
- package/.server/server/plugins/engine/components/MonthYearField.js.map +1 -1
- package/.server/server/plugins/engine/components/UkAddressField.d.ts +3 -1
- package/.server/server/plugins/engine/components/UkAddressField.js +12 -5
- package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +27 -12
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
- package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +1 -4
- package/.server/server/plugins/engine/outputFormatters/machine/v2.js +10 -9
- package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
- package/.server/server/plugins/engine/types/index.d.ts +0 -1
- package/.server/server/plugins/engine/types/index.js.map +1 -1
- package/.server/server/plugins/engine/types/schema.d.ts +2 -1
- package/.server/server/plugins/engine/types/schema.js +13 -2
- package/.server/server/plugins/engine/types/schema.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +24 -3
- package/.server/server/plugins/engine/types.js +4 -0
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/package.json +1 -1
- package/src/server/plugins/engine/components/CheckboxesField.ts +18 -7
- package/src/server/plugins/engine/components/DatePartsField.ts +17 -6
- package/src/server/plugins/engine/components/FileUploadField.ts +14 -3
- package/src/server/plugins/engine/components/FormComponent.ts +17 -5
- package/src/server/plugins/engine/components/ListFormComponent.ts +10 -3
- package/src/server/plugins/engine/components/MonthYearField.ts +14 -4
- package/src/server/plugins/engine/components/UkAddressField.ts +16 -6
- package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +192 -2
- package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +38 -12
- package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +2 -0
- package/src/server/plugins/engine/outputFormatters/machine/v2.ts +18 -28
- package/src/server/plugins/engine/types/index.ts +0 -2
- package/src/server/plugins/engine/types/schema.test.ts +68 -60
- package/src/server/plugins/engine/types/schema.ts +23 -2
- package/src/server/plugins/engine/types.ts +33 -3
|
@@ -65,12 +65,9 @@ export class CheckboxesField extends SelectionControlField {
|
|
|
65
65
|
return this.isValue(value) ? value : undefined
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
getDisplayStringFromFormValue(selected: (string | number | boolean)[]) {
|
|
69
69
|
const { items } = this
|
|
70
70
|
|
|
71
|
-
// Selected checkbox values
|
|
72
|
-
const selected = this.getFormValueFromState(state) ?? []
|
|
73
|
-
|
|
74
71
|
// Map selected values to text
|
|
75
72
|
return items
|
|
76
73
|
.filter((item) => selected.includes(item.value))
|
|
@@ -78,9 +75,9 @@ export class CheckboxesField extends SelectionControlField {
|
|
|
78
75
|
.join(', ')
|
|
79
76
|
}
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
getContextValueFromFormValue(
|
|
79
|
+
values: (string | number | boolean)[] | undefined
|
|
80
|
+
): (string | number | boolean)[] {
|
|
84
81
|
/**
|
|
85
82
|
* For evaluation context purposes, optional {@link CheckboxesField}
|
|
86
83
|
* with an undefined value (i.e. nothing selected) should default to [].
|
|
@@ -95,6 +92,20 @@ export class CheckboxesField extends SelectionControlField {
|
|
|
95
92
|
return values ?? []
|
|
96
93
|
}
|
|
97
94
|
|
|
95
|
+
getDisplayStringFromState(state: FormSubmissionState) {
|
|
96
|
+
// Selected checkbox values
|
|
97
|
+
const selected = this.getFormValueFromState(state) ?? []
|
|
98
|
+
|
|
99
|
+
// Map selected values to text
|
|
100
|
+
return this.getDisplayStringFromFormValue(selected)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getContextValueFromState(state: FormSubmissionState) {
|
|
104
|
+
const values = this.getFormValueFromState(state)
|
|
105
|
+
|
|
106
|
+
return this.getContextValueFromFormValue(values)
|
|
107
|
+
}
|
|
108
|
+
|
|
98
109
|
isValue(value?: FormStateValue | FormState): value is Item['value'][] {
|
|
99
110
|
if (!Array.isArray(value)) {
|
|
100
111
|
return false
|
|
@@ -109,19 +109,24 @@ export class DatePartsField extends FormComponent {
|
|
|
109
109
|
return this.isState(value) ? value : undefined
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (!value) {
|
|
112
|
+
getDisplayStringFromFormValue(formValue: DatePartsState | undefined) {
|
|
113
|
+
if (!formValue) {
|
|
116
114
|
return ''
|
|
117
115
|
}
|
|
118
116
|
|
|
119
|
-
return format(
|
|
117
|
+
return format(
|
|
118
|
+
`${formValue.year}-${formValue.month}-${formValue.day}`,
|
|
119
|
+
'd MMMM yyyy'
|
|
120
|
+
)
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
|
|
123
|
+
getDisplayStringFromState(state: FormSubmissionState) {
|
|
123
124
|
const value = this.getFormValueFromState(state)
|
|
124
125
|
|
|
126
|
+
return this.getDisplayStringFromFormValue(value)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
getContextValueFromFormValue(value: DatePartsState | undefined) {
|
|
125
130
|
if (
|
|
126
131
|
!value ||
|
|
127
132
|
!isValid(
|
|
@@ -139,6 +144,12 @@ export class DatePartsField extends FormComponent {
|
|
|
139
144
|
return format(`${value.year}-${value.month}-${value.day}`, 'yyyy-MM-dd')
|
|
140
145
|
}
|
|
141
146
|
|
|
147
|
+
getContextValueFromState(state: FormSubmissionState) {
|
|
148
|
+
const value = this.getFormValueFromState(state)
|
|
149
|
+
|
|
150
|
+
return this.getContextValueFromFormValue(value)
|
|
151
|
+
}
|
|
152
|
+
|
|
142
153
|
getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {
|
|
143
154
|
const { collection, name } = this
|
|
144
155
|
|
|
@@ -149,8 +149,7 @@ export class FileUploadField extends FormComponent {
|
|
|
149
149
|
return this.isValue(value) ? value : undefined
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
const files = this.getFormValueFromState(state)
|
|
152
|
+
getDisplayStringFromFormValue(files: FileState[] | undefined): string {
|
|
154
153
|
if (!files?.length) {
|
|
155
154
|
return ''
|
|
156
155
|
}
|
|
@@ -159,11 +158,23 @@ export class FileUploadField extends FormComponent {
|
|
|
159
158
|
return `Uploaded ${files.length} ${unit}`
|
|
160
159
|
}
|
|
161
160
|
|
|
162
|
-
|
|
161
|
+
getDisplayStringFromState(state: FormSubmissionState) {
|
|
163
162
|
const files = this.getFormValueFromState(state)
|
|
163
|
+
|
|
164
|
+
return this.getDisplayStringFromFormValue(files)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
getContextValueFromFormValue(
|
|
168
|
+
files: UploadState | undefined
|
|
169
|
+
): string[] | null {
|
|
164
170
|
return files?.map(({ status }) => status.form.file.fileId) ?? null
|
|
165
171
|
}
|
|
166
172
|
|
|
173
|
+
getContextValueFromState(state: FormSubmissionState) {
|
|
174
|
+
const files = this.getFormValueFromState(state)
|
|
175
|
+
return this.getContextValueFromFormValue(files)
|
|
176
|
+
}
|
|
177
|
+
|
|
167
178
|
getViewModel(
|
|
168
179
|
payload: FormPayload,
|
|
169
180
|
errors?: FormSubmissionError[],
|
|
@@ -157,17 +157,21 @@ export class FormComponent extends ComponentBase {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
getDisplayStringFromFormValue(value: FormValue | FormPayload): string {
|
|
161
|
+
// Map selected values to text
|
|
162
162
|
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
163
163
|
return this.isValue(value) ? value.toString() : ''
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
state: FormSubmissionState
|
|
168
|
-
): Item['value'] | Item['value'][] | null {
|
|
166
|
+
getDisplayStringFromState(state: FormSubmissionState): string {
|
|
169
167
|
const value = this.getFormValueFromState(state)
|
|
168
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
169
|
+
return this.getDisplayStringFromFormValue(value)
|
|
170
|
+
}
|
|
170
171
|
|
|
172
|
+
getContextValueFromFormValue(
|
|
173
|
+
value: FormValue | FormPayload
|
|
174
|
+
): Item['value'] | Item['value'][] | null {
|
|
171
175
|
// Filter object field values
|
|
172
176
|
if (this.isState(value)) {
|
|
173
177
|
const values = Object.values(value).filter(isFormValue)
|
|
@@ -182,6 +186,14 @@ export class FormComponent extends ComponentBase {
|
|
|
182
186
|
return this.isValue(value) ? value : null
|
|
183
187
|
}
|
|
184
188
|
|
|
189
|
+
getContextValueFromState(
|
|
190
|
+
state: FormSubmissionState
|
|
191
|
+
): Item['value'] | Item['value'][] | null {
|
|
192
|
+
const value = this.getFormValueFromState(state)
|
|
193
|
+
|
|
194
|
+
return this.getContextValueFromFormValue(value)
|
|
195
|
+
}
|
|
196
|
+
|
|
185
197
|
isValue(
|
|
186
198
|
value?: FormStateValue | FormState
|
|
187
199
|
): value is NonNullable<FormStateValue> {
|
|
@@ -99,11 +99,11 @@ export class ListFormComponent extends FormComponent {
|
|
|
99
99
|
return selected.at(0)?.value
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
getDisplayStringFromFormValue(
|
|
103
|
+
value: string | number | boolean | Item['value'][] | undefined
|
|
104
|
+
): string {
|
|
103
105
|
const { items } = this
|
|
104
106
|
|
|
105
|
-
// Allow for array values via subclass
|
|
106
|
-
const value = this.getFormValueFromState(state)
|
|
107
107
|
const values = [value ?? []].flat()
|
|
108
108
|
|
|
109
109
|
return items
|
|
@@ -112,6 +112,13 @@ export class ListFormComponent extends FormComponent {
|
|
|
112
112
|
.join(', ')
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
getDisplayStringFromState(state: FormSubmissionState) {
|
|
116
|
+
// Allow for array values via subclass
|
|
117
|
+
const value = this.getFormValueFromState(state)
|
|
118
|
+
|
|
119
|
+
return this.getDisplayStringFromFormValue(value)
|
|
120
|
+
}
|
|
121
|
+
|
|
115
122
|
getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {
|
|
116
123
|
const { items: listItems } = this
|
|
117
124
|
|
|
@@ -103,9 +103,7 @@ export class MonthYearField extends FormComponent {
|
|
|
103
103
|
return MonthYearField.isMonthYear(value) ? value : undefined
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
const value = this.getFormValueFromState(state)
|
|
108
|
-
|
|
106
|
+
getDisplayStringFromFormValue(value: MonthYearState | undefined): string {
|
|
109
107
|
if (!value) {
|
|
110
108
|
return ''
|
|
111
109
|
}
|
|
@@ -117,9 +115,15 @@ export class MonthYearField extends FormComponent {
|
|
|
117
115
|
return `${monthString} ${value.year}`
|
|
118
116
|
}
|
|
119
117
|
|
|
120
|
-
|
|
118
|
+
getDisplayStringFromState(state: FormSubmissionState) {
|
|
121
119
|
const value = this.getFormValueFromState(state)
|
|
122
120
|
|
|
121
|
+
return this.getDisplayStringFromFormValue(value)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
getContextValueFromFormValue(
|
|
125
|
+
value: MonthYearState | undefined
|
|
126
|
+
): string | null {
|
|
123
127
|
if (
|
|
124
128
|
!value ||
|
|
125
129
|
!isValid(
|
|
@@ -137,6 +141,12 @@ export class MonthYearField extends FormComponent {
|
|
|
137
141
|
return format(`${value.year}-${value.month}-01`, 'yyyy-MM')
|
|
138
142
|
}
|
|
139
143
|
|
|
144
|
+
getContextValueFromState(state: FormSubmissionState) {
|
|
145
|
+
const value = this.getFormValueFromState(state)
|
|
146
|
+
|
|
147
|
+
return this.getContextValueFromFormValue(value)
|
|
148
|
+
}
|
|
149
|
+
|
|
140
150
|
getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {
|
|
141
151
|
const { collection, name } = this
|
|
142
152
|
|
|
@@ -110,18 +110,28 @@ export class UkAddressField extends FormComponent {
|
|
|
110
110
|
return this.isState(value) ? value : undefined
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
getContextValueFromFormValue(value: UkAddressState | undefined) {
|
|
114
|
+
if (!value) {
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return Object.values(value).filter(Boolean)
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
getContextValueFromState(state: FormSubmissionState) {
|
|
118
122
|
const value = this.getFormValueFromState(state)
|
|
119
123
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
124
|
+
return this.getContextValueFromFormValue(value)
|
|
125
|
+
}
|
|
123
126
|
|
|
124
|
-
|
|
127
|
+
getDisplayStringFromFormValue(value: UkAddressState | undefined): string {
|
|
128
|
+
return this.getContextValueFromFormValue(value)?.join(', ') ?? ''
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getDisplayStringFromState(state: FormSubmissionState) {
|
|
132
|
+
const value = this.getFormValueFromState(state)
|
|
133
|
+
|
|
134
|
+
return this.getDisplayStringFromFormValue(value)
|
|
125
135
|
}
|
|
126
136
|
|
|
127
137
|
/**
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type FormMetadata,
|
|
3
|
+
type SubmitResponsePayload
|
|
4
|
+
} from '@defra/forms-model'
|
|
2
5
|
|
|
3
6
|
import { FileUploadField } from '~/src/server/plugins/engine/components/FileUploadField.js'
|
|
4
7
|
import { type Field } from '~/src/server/plugins/engine/components/helpers/components.js'
|
|
@@ -26,7 +29,7 @@ const submitResponse = {
|
|
|
26
29
|
files: {
|
|
27
30
|
main: '00000000-0000-0000-0000-000000000000',
|
|
28
31
|
repeaters: {
|
|
29
|
-
|
|
32
|
+
exampleRepeat: '11111111-1111-1111-1111-111111111111'
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
}
|
|
@@ -258,15 +261,26 @@ describe('Adapter v1 formatter', () => {
|
|
|
258
261
|
exampleFile1: [
|
|
259
262
|
{
|
|
260
263
|
fileId: '123-456-789',
|
|
264
|
+
fileName: 'foobar.txt',
|
|
261
265
|
userDownloadLink: 'https://forms-designer/file-download/123-456-789'
|
|
262
266
|
},
|
|
263
267
|
{
|
|
264
268
|
fileId: '456-789-123',
|
|
269
|
+
fileName: 'bazbuzz.txt',
|
|
265
270
|
userDownloadLink: 'https://forms-designer/file-download/456-789-123'
|
|
266
271
|
}
|
|
267
272
|
]
|
|
268
273
|
}
|
|
269
274
|
})
|
|
275
|
+
|
|
276
|
+
expect(parsedBody.result).toEqual({
|
|
277
|
+
files: {
|
|
278
|
+
main: '00000000-0000-0000-0000-000000000000',
|
|
279
|
+
repeaters: {
|
|
280
|
+
exampleRepeat: '11111111-1111-1111-1111-111111111111'
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
})
|
|
270
284
|
})
|
|
271
285
|
|
|
272
286
|
it('should handle preview form status correctly', () => {
|
|
@@ -385,6 +399,14 @@ describe('Adapter v1 formatter', () => {
|
|
|
385
399
|
expect(parsedBody.data.main).toEqual({})
|
|
386
400
|
expect(parsedBody.data.repeaters).toEqual({})
|
|
387
401
|
expect(parsedBody.data.files).toEqual({})
|
|
402
|
+
expect(parsedBody.result).toEqual({
|
|
403
|
+
files: {
|
|
404
|
+
main: '00000000-0000-0000-0000-000000000000',
|
|
405
|
+
repeaters: {
|
|
406
|
+
exampleRepeat: '11111111-1111-1111-1111-111111111111'
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
})
|
|
388
410
|
})
|
|
389
411
|
|
|
390
412
|
it('should handle different form statuses', () => {
|
|
@@ -503,4 +525,172 @@ describe('Adapter v1 formatter', () => {
|
|
|
503
525
|
expect(parsedBody.meta.formSlug).toBe('')
|
|
504
526
|
expect(parsedBody.meta.notificationEmail).toBe('only-email@example.com')
|
|
505
527
|
})
|
|
528
|
+
|
|
529
|
+
it('should include CSV file IDs from submitResponse.result.files', () => {
|
|
530
|
+
const formStatus = {
|
|
531
|
+
isPreview: false,
|
|
532
|
+
state: FormStatus.Live
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const body = format(context, items, model, submitResponse, formStatus)
|
|
536
|
+
const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
|
|
537
|
+
|
|
538
|
+
expect(parsedBody.data.main).toEqual({
|
|
539
|
+
exampleField: 'hello world',
|
|
540
|
+
exampleField2: 'hello world'
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
expect(parsedBody.data.repeaters.exampleRepeat).toEqual([
|
|
544
|
+
{
|
|
545
|
+
subItem1_1: 'hello world',
|
|
546
|
+
subItem1_2: 'hello world'
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
subItem2_1: 'hello world'
|
|
550
|
+
}
|
|
551
|
+
])
|
|
552
|
+
|
|
553
|
+
expect(parsedBody.data.files.exampleFile1).toEqual([
|
|
554
|
+
{
|
|
555
|
+
fileId: '123-456-789',
|
|
556
|
+
fileName: 'foobar.txt',
|
|
557
|
+
userDownloadLink: 'https://forms-designer/file-download/123-456-789'
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
fileId: '456-789-123',
|
|
561
|
+
fileName: 'bazbuzz.txt',
|
|
562
|
+
userDownloadLink: 'https://forms-designer/file-download/456-789-123'
|
|
563
|
+
}
|
|
564
|
+
])
|
|
565
|
+
|
|
566
|
+
expect(parsedBody.result).toEqual({
|
|
567
|
+
files: {
|
|
568
|
+
main: '00000000-0000-0000-0000-000000000000',
|
|
569
|
+
repeaters: {
|
|
570
|
+
exampleRepeat: '11111111-1111-1111-1111-111111111111'
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
})
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
it('should handle submitResponse without CSV file IDs gracefully', () => {
|
|
577
|
+
const submitResponseWithoutFiles = {
|
|
578
|
+
message: 'Submit completed',
|
|
579
|
+
result: {
|
|
580
|
+
files: {
|
|
581
|
+
main: '',
|
|
582
|
+
repeaters: {}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const formStatus = {
|
|
588
|
+
isPreview: false,
|
|
589
|
+
state: FormStatus.Live
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const body = format(
|
|
593
|
+
context,
|
|
594
|
+
items,
|
|
595
|
+
model,
|
|
596
|
+
submitResponseWithoutFiles,
|
|
597
|
+
formStatus
|
|
598
|
+
)
|
|
599
|
+
const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
|
|
600
|
+
|
|
601
|
+
expect(parsedBody.data.main).toEqual({
|
|
602
|
+
exampleField: 'hello world',
|
|
603
|
+
exampleField2: 'hello world'
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
expect(parsedBody.data.repeaters.exampleRepeat).toEqual([
|
|
607
|
+
{
|
|
608
|
+
subItem1_1: 'hello world',
|
|
609
|
+
subItem1_2: 'hello world'
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
subItem2_1: 'hello world'
|
|
613
|
+
}
|
|
614
|
+
])
|
|
615
|
+
})
|
|
616
|
+
|
|
617
|
+
it('should handle submitResponse with only main CSV file ID', () => {
|
|
618
|
+
const submitResponseWithMainOnly = {
|
|
619
|
+
message: 'Submit completed',
|
|
620
|
+
result: {
|
|
621
|
+
files: {
|
|
622
|
+
main: 'main-only-file-id',
|
|
623
|
+
repeaters: {}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const formStatus = {
|
|
629
|
+
isPreview: false,
|
|
630
|
+
state: FormStatus.Live
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const body = format(
|
|
634
|
+
context,
|
|
635
|
+
items,
|
|
636
|
+
model,
|
|
637
|
+
submitResponseWithMainOnly,
|
|
638
|
+
formStatus
|
|
639
|
+
)
|
|
640
|
+
const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
|
|
641
|
+
|
|
642
|
+
expect(parsedBody.data.main).toEqual({
|
|
643
|
+
exampleField: 'hello world',
|
|
644
|
+
exampleField2: 'hello world'
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
expect(parsedBody.data.repeaters.exampleRepeat).toEqual([
|
|
648
|
+
{
|
|
649
|
+
subItem1_1: 'hello world',
|
|
650
|
+
subItem1_2: 'hello world'
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
subItem2_1: 'hello world'
|
|
654
|
+
}
|
|
655
|
+
])
|
|
656
|
+
|
|
657
|
+
expect(parsedBody.result).toEqual({
|
|
658
|
+
files: {
|
|
659
|
+
main: 'main-only-file-id',
|
|
660
|
+
repeaters: {}
|
|
661
|
+
}
|
|
662
|
+
})
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
it('should handle submitResponse with missing repeaters property', () => {
|
|
666
|
+
const submitResponseWithoutRepeaters = {
|
|
667
|
+
message: 'Submit completed',
|
|
668
|
+
result: {
|
|
669
|
+
files: {
|
|
670
|
+
main: 'main-only-file-id'
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const formStatus = {
|
|
676
|
+
isPreview: false,
|
|
677
|
+
state: FormStatus.Live
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const body = format(
|
|
681
|
+
context,
|
|
682
|
+
items,
|
|
683
|
+
model,
|
|
684
|
+
submitResponseWithoutRepeaters as unknown as SubmitResponsePayload,
|
|
685
|
+
formStatus
|
|
686
|
+
)
|
|
687
|
+
const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
|
|
688
|
+
|
|
689
|
+
expect(parsedBody.result).toEqual({
|
|
690
|
+
files: {
|
|
691
|
+
main: 'main-only-file-id',
|
|
692
|
+
repeaters: {}
|
|
693
|
+
}
|
|
694
|
+
})
|
|
695
|
+
})
|
|
506
696
|
})
|
|
@@ -10,7 +10,9 @@ import { format as machineV2 } from '~/src/server/plugins/engine/outputFormatter
|
|
|
10
10
|
import { FormAdapterSubmissionSchemaVersion } from '~/src/server/plugins/engine/types/enums.js'
|
|
11
11
|
import {
|
|
12
12
|
type FormAdapterSubmissionMessageData,
|
|
13
|
+
type FormAdapterSubmissionMessageMeta,
|
|
13
14
|
type FormAdapterSubmissionMessagePayload,
|
|
15
|
+
type FormAdapterSubmissionMessageResult,
|
|
14
16
|
type FormContext
|
|
15
17
|
} from '~/src/server/plugins/engine/types.js'
|
|
16
18
|
import { FormStatus } from '~/src/server/routes/types.js'
|
|
@@ -34,20 +36,44 @@ export function format(
|
|
|
34
36
|
data: FormAdapterSubmissionMessageData
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
const csvFiles = extractCsvFiles(submitResponse)
|
|
40
|
+
|
|
41
|
+
const transformedData = v2DataParsed.data
|
|
42
|
+
|
|
43
|
+
const meta: FormAdapterSubmissionMessageMeta = {
|
|
44
|
+
schemaVersion: FormAdapterSubmissionSchemaVersion.V1,
|
|
45
|
+
timestamp: new Date(),
|
|
46
|
+
referenceNumber: context.referenceNumber,
|
|
47
|
+
formName: model.name,
|
|
48
|
+
formId: formMetadata?.id ?? '',
|
|
49
|
+
formSlug: formMetadata?.slug ?? '',
|
|
50
|
+
status: formStatus.isPreview ? FormStatus.Draft : FormStatus.Live,
|
|
51
|
+
isPreview: formStatus.isPreview,
|
|
52
|
+
notificationEmail: formMetadata?.notificationEmail ?? ''
|
|
53
|
+
}
|
|
54
|
+
const data: FormAdapterSubmissionMessageData = transformedData
|
|
55
|
+
|
|
56
|
+
const result: FormAdapterSubmissionMessageResult = {
|
|
57
|
+
files: csvFiles
|
|
58
|
+
}
|
|
59
|
+
|
|
37
60
|
const payload: FormAdapterSubmissionMessagePayload = {
|
|
38
|
-
meta
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
referenceNumber: context.referenceNumber,
|
|
42
|
-
formName: model.name,
|
|
43
|
-
formId: formMetadata?.id ?? '',
|
|
44
|
-
formSlug: formMetadata?.slug ?? '',
|
|
45
|
-
status: formStatus.isPreview ? FormStatus.Draft : FormStatus.Live,
|
|
46
|
-
isPreview: formStatus.isPreview,
|
|
47
|
-
notificationEmail: formMetadata?.notificationEmail ?? ''
|
|
48
|
-
},
|
|
49
|
-
data: v2DataParsed.data
|
|
61
|
+
meta,
|
|
62
|
+
data,
|
|
63
|
+
result
|
|
50
64
|
}
|
|
51
65
|
|
|
52
66
|
return JSON.stringify(payload)
|
|
53
67
|
}
|
|
68
|
+
|
|
69
|
+
function extractCsvFiles(
|
|
70
|
+
submitResponse: SubmitResponsePayload
|
|
71
|
+
): FormAdapterSubmissionMessageResult['files'] {
|
|
72
|
+
const result =
|
|
73
|
+
submitResponse.result as Partial<FormAdapterSubmissionMessageResult>
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
main: result.files?.main ?? '',
|
|
77
|
+
repeaters: result.files?.repeaters ?? {}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -249,10 +249,12 @@ describe('getPersonalisation', () => {
|
|
|
249
249
|
exampleFile1: [
|
|
250
250
|
{
|
|
251
251
|
fileId: '123-456-789',
|
|
252
|
+
fileName: 'foobar.txt',
|
|
252
253
|
userDownloadLink: 'https://forms-designer/file-download/123-456-789'
|
|
253
254
|
},
|
|
254
255
|
{
|
|
255
256
|
fileId: '456-789-123',
|
|
257
|
+
fileName: 'bazbuzz.txt',
|
|
256
258
|
userDownloadLink: 'https://forms-designer/file-download/456-789-123'
|
|
257
259
|
}
|
|
258
260
|
]
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { type SubmitResponsePayload } from '@defra/forms-model'
|
|
2
2
|
|
|
3
3
|
import { config } from '~/src/config/index.js'
|
|
4
|
-
import { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'
|
|
5
4
|
import { FileUploadField } from '~/src/server/plugins/engine/components/index.js'
|
|
6
|
-
import {
|
|
7
|
-
type DatePartsState,
|
|
8
|
-
type MonthYearState
|
|
9
|
-
} from '~/src/server/plugins/engine/components/types.js'
|
|
10
5
|
import { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'
|
|
11
6
|
import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
|
|
12
7
|
import {
|
|
@@ -15,9 +10,10 @@ import {
|
|
|
15
10
|
type DetailItemRepeat
|
|
16
11
|
} from '~/src/server/plugins/engine/models/types.js'
|
|
17
12
|
import {
|
|
13
|
+
type FileUploadFieldDetailitem,
|
|
14
|
+
type FormAdapterFile,
|
|
18
15
|
type FormContext,
|
|
19
|
-
type
|
|
20
|
-
type FormValue
|
|
16
|
+
type RichFormValue
|
|
21
17
|
} from '~/src/server/plugins/engine/types.js'
|
|
22
18
|
|
|
23
19
|
const designerUrl = config.get('designerUrl')
|
|
@@ -69,7 +65,8 @@ export function format(
|
|
|
69
65
|
* fileComponentName: [
|
|
70
66
|
* {
|
|
71
67
|
* fileId: '123-456-789',
|
|
72
|
-
*
|
|
68
|
+
* fileName: 'example.pdf',
|
|
69
|
+
* userDownloadLink: 'https://forms-designer/file-download/123-456-789'
|
|
73
70
|
* }
|
|
74
71
|
* ]
|
|
75
72
|
* }
|
|
@@ -79,7 +76,10 @@ function categoriseData(items: DetailItem[]) {
|
|
|
79
76
|
const output: {
|
|
80
77
|
main: Record<string, RichFormValue>
|
|
81
78
|
repeaters: Record<string, Record<string, RichFormValue>[]>
|
|
82
|
-
files: Record<
|
|
79
|
+
files: Record<
|
|
80
|
+
string,
|
|
81
|
+
{ fileId: string; fileName: string; userDownloadLink: string }[]
|
|
82
|
+
>
|
|
83
83
|
} = { main: {}, repeaters: {}, files: {} }
|
|
84
84
|
|
|
85
85
|
items.forEach((item) => {
|
|
@@ -126,13 +126,17 @@ function extractRepeaters(item: DetailItemRepeat) {
|
|
|
126
126
|
* @param item - the file upload item in the form
|
|
127
127
|
* @returns the file upload data
|
|
128
128
|
*/
|
|
129
|
-
function extractFileUploads(
|
|
130
|
-
|
|
129
|
+
function extractFileUploads(
|
|
130
|
+
item: FileUploadFieldDetailitem
|
|
131
|
+
): FormAdapterFile[] {
|
|
132
|
+
const fileUploadState = item.field.getFormValueFromState(item.state) ?? []
|
|
131
133
|
|
|
132
|
-
return fileUploadState.map((
|
|
134
|
+
return fileUploadState.map((fileState) => {
|
|
135
|
+
const { file } = fileState.status.form
|
|
133
136
|
return {
|
|
134
|
-
fileId,
|
|
135
|
-
|
|
137
|
+
fileId: file.fileId,
|
|
138
|
+
fileName: file.filename,
|
|
139
|
+
userDownloadLink: `${designerUrl}/file-download/${file.fileId}`
|
|
136
140
|
}
|
|
137
141
|
})
|
|
138
142
|
}
|
|
@@ -142,17 +146,3 @@ function isFileUploadFieldItem(
|
|
|
142
146
|
): item is FileUploadFieldDetailitem {
|
|
143
147
|
return item.field instanceof FileUploadField
|
|
144
148
|
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* A detail item specifically for files
|
|
148
|
-
*/
|
|
149
|
-
type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {
|
|
150
|
-
field: FileUploadField
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export type RichFormValue =
|
|
154
|
-
| FormValue
|
|
155
|
-
| FormPayload
|
|
156
|
-
| DatePartsState
|
|
157
|
-
| MonthYearState
|
|
158
|
-
| UkAddressState
|
|
@@ -87,7 +87,5 @@ export type {
|
|
|
87
87
|
Services
|
|
88
88
|
} from '~/src/server/types.js'
|
|
89
89
|
|
|
90
|
-
export type { RichFormValue } from '~/src/server/plugins/engine/outputFormatters/machine/v2.js'
|
|
91
|
-
|
|
92
90
|
export * from '~/src/server/plugins/engine/types/schema.js'
|
|
93
91
|
export { FormAdapterSubmissionSchemaVersion } from '~/src/server/plugins/engine/types/enums.js'
|