@defra/forms-engine-plugin 4.4.0 → 4.5.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 (35) hide show
  1. package/.public/javascripts/application.min.js +1 -1
  2. package/.public/javascripts/application.min.js.map +1 -1
  3. package/.public/javascripts/shared.min.js +1 -1
  4. package/.public/javascripts/shared.min.js.map +1 -1
  5. package/.server/client/javascripts/file-upload.js +13 -8
  6. package/.server/client/javascripts/file-upload.js.map +1 -1
  7. package/.server/server/constants.d.ts +1 -0
  8. package/.server/server/constants.js +1 -0
  9. package/.server/server/constants.js.map +1 -1
  10. package/.server/server/plugins/engine/beta/form-context.d.ts +0 -1
  11. package/.server/server/plugins/engine/beta/form-context.js +4 -3
  12. package/.server/server/plugins/engine/beta/form-context.js.map +1 -1
  13. package/.server/server/plugins/engine/components/FileUploadField.d.ts +3 -2
  14. package/.server/server/plugins/engine/components/FileUploadField.js +11 -3
  15. package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -1
  16. package/.server/server/plugins/engine/helpers.d.ts +8 -0
  17. package/.server/server/plugins/engine/helpers.js +8 -0
  18. package/.server/server/plugins/engine/helpers.js.map +1 -1
  19. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +2 -1
  20. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
  21. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +11 -0
  22. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +65 -28
  23. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  24. package/package.json +1 -1
  25. package/src/client/javascripts/file-upload.js +12 -8
  26. package/src/server/constants.js +1 -0
  27. package/src/server/plugins/engine/beta/form-context.test.ts +22 -8
  28. package/src/server/plugins/engine/beta/form-context.ts +7 -6
  29. package/src/server/plugins/engine/components/FileUploadField.test.ts +11 -8
  30. package/src/server/plugins/engine/components/FileUploadField.ts +14 -5
  31. package/src/server/plugins/engine/helpers.ts +17 -0
  32. package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +54 -0
  33. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +7 -5
  34. package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +109 -5
  35. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +69 -21
@@ -5,7 +5,8 @@ import { isEqual } from 'date-fns'
5
5
  import { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'
6
6
  import {
7
7
  checkEmailAddressForLiveFormSubmission,
8
- getCacheService
8
+ getCacheService,
9
+ getFormVersion
9
10
  } from '~/src/server/plugins/engine/helpers.js'
10
11
  import { FormModel } from '~/src/server/plugins/engine/models/index.js'
11
12
  import { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'
@@ -27,7 +28,6 @@ export interface FormModelOptions {
27
28
  services?: Services
28
29
  controllers?: Record<string, typeof PageController>
29
30
  basePath?: string
30
- versionNumber?: number
31
31
  ordnanceSurveyApiKey?: string
32
32
  formId?: string
33
33
  routePrefix?: string
@@ -53,8 +53,6 @@ export async function getFormModel(
53
53
  const formState = resolveState(state)
54
54
 
55
55
  const metadata = await formsService.getFormMetadata(slug)
56
- const versionNumber =
57
- options.versionNumber ?? metadata.versions?.[0]?.versionNumber
58
56
 
59
57
  const definition = await formsService.getFormDefinition(
60
58
  metadata.id,
@@ -67,6 +65,8 @@ export async function getFormModel(
67
65
  )
68
66
  }
69
67
 
68
+ const versionNumber = getFormVersion(definition)?.versionNumber
69
+
70
70
  return new FormModel(
71
71
  definition,
72
72
  {
@@ -182,14 +182,15 @@ export async function resolveFormModel(
182
182
  const routePrefix =
183
183
  options.routePrefix ?? server.realm.modifiers.route.prefix
184
184
 
185
+ const versionNumber = getFormVersion(definition)?.versionNumber
186
+
185
187
  const model = new FormModel(
186
188
  definition,
187
189
  {
188
190
  basePath:
189
191
  options.basePath ??
190
192
  buildBasePath(routePrefix, slug, formState, isPreview),
191
- versionNumber:
192
- options.versionNumber ?? metadata.versions?.[0]?.versionNumber,
193
+ versionNumber,
193
194
  ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,
194
195
  formId: options.formId ?? metadata.id
195
196
  },
@@ -405,7 +405,7 @@ describe('FileUploadField', () => {
405
405
  actions: {
406
406
  items: [
407
407
  {
408
- href: `/test/file-upload-component/${validState[0].uploadId}/confirm-delete`,
408
+ href: `/test/file-upload-component/${validState[0].status.form.file.fileId}/confirm-delete`,
409
409
  text: 'Remove',
410
410
  attributes: { id: 'myComponent__0' },
411
411
  classes: 'govuk-link--no-visited-state',
@@ -424,7 +424,7 @@ describe('FileUploadField', () => {
424
424
  actions: {
425
425
  items: [
426
426
  {
427
- href: `/test/file-upload-component/${validState[1].uploadId}/confirm-delete`,
427
+ href: `/test/file-upload-component/${validState[1].status.form.file.fileId}/confirm-delete`,
428
428
  text: 'Remove',
429
429
  attributes: { id: 'myComponent__1' },
430
430
  classes: 'govuk-link--no-visited-state',
@@ -443,7 +443,7 @@ describe('FileUploadField', () => {
443
443
  actions: {
444
444
  items: [
445
445
  {
446
- href: `/test/file-upload-component/${validState[2].uploadId}/confirm-delete`,
446
+ href: `/test/file-upload-component/${validState[2].status.form.file.fileId}/confirm-delete`,
447
447
  text: 'Remove',
448
448
  attributes: { id: 'myComponent__2' },
449
449
  classes: 'govuk-link--no-visited-state',
@@ -454,7 +454,8 @@ describe('FileUploadField', () => {
454
454
  }
455
455
  ]
456
456
  }
457
- }
457
+ },
458
+ multiple: true
458
459
  })
459
460
  )
460
461
  })
@@ -543,7 +544,7 @@ describe('FileUploadField', () => {
543
544
  actions: {
544
545
  items: [
545
546
  {
546
- href: `/test/file-upload-component/${validState[2].uploadId}/confirm-delete`,
547
+ href: `/test/file-upload-component/${validState[2].status.form.file.fileId}/confirm-delete`,
547
548
  text: 'Remove',
548
549
  attributes: { id: 'myComponent__0' },
549
550
  classes: 'govuk-link--no-visited-state',
@@ -554,7 +555,8 @@ describe('FileUploadField', () => {
554
555
  }
555
556
  ]
556
557
  }
557
- }
558
+ },
559
+ multiple: true
558
560
  })
559
561
  )
560
562
  })
@@ -583,7 +585,7 @@ describe('FileUploadField', () => {
583
585
  actions: {
584
586
  items: [
585
587
  {
586
- href: `/test/file-upload-component/${validState[2].uploadId}/confirm-delete`,
588
+ href: `/test/file-upload-component/${validState[2].status.form.file.fileId}/confirm-delete`,
587
589
  text: 'Remove',
588
590
  attributes: { id: 'myComponent__0' },
589
591
  classes: 'govuk-link--no-visited-state',
@@ -594,7 +596,8 @@ describe('FileUploadField', () => {
594
596
  }
595
597
  ]
596
598
  }
597
- }
599
+ },
600
+ multiple: true
598
601
  })
599
602
  )
600
603
  })
@@ -73,9 +73,12 @@ export const tempStatusSchema = joi
73
73
  .valid(UploadStatus.ready, UploadStatus.pending)
74
74
  .required(),
75
75
  metadata: metadataSchema,
76
- form: joi.object().required().keys({
77
- file: tempFileSchema
78
- }),
76
+ form: joi
77
+ .object()
78
+ .required()
79
+ .keys({
80
+ file: joi.array().items(tempFileSchema).single().required()
81
+ }),
79
82
  numberOfRejectedFiles: joi.number().optional()
80
83
  })
81
84
  .required()
@@ -191,7 +194,7 @@ export class FileUploadField extends FormComponent {
191
194
  errors?: FormSubmissionError[],
192
195
  query: FormQuery = {}
193
196
  ) {
194
- const { options, page } = this
197
+ const { options, page, schema } = this
195
198
 
196
199
  // Allow preview URL direct access
197
200
  const isForceAccess = 'force' in query
@@ -233,7 +236,7 @@ export class FileUploadField extends FormComponent {
233
236
 
234
237
  // Remove summary list actions from previews
235
238
  if (!isForceAccess) {
236
- const path = `/${item.uploadId}/confirm-delete`
239
+ const path = `/${file.fileId}/confirm-delete`
237
240
  const href = page?.getHref(`${page.path}${path}`) ?? '#'
238
241
 
239
242
  items.push({
@@ -263,6 +266,9 @@ export class FileUploadField extends FormComponent {
263
266
  attributes.accept = options.accept
264
267
  }
265
268
 
269
+ // Allow multiple file selection when schema permits more than 1 file
270
+ const allowsMultiple = schema.max !== 1 && schema.length !== 1
271
+
266
272
  const summaryList: SummaryList = {
267
273
  classes: 'govuk-summary-list--long-key',
268
274
  rows
@@ -277,6 +283,9 @@ export class FileUploadField extends FormComponent {
277
283
  // Override the component name we send to CDP
278
284
  name: 'file',
279
285
 
286
+ // Enable multi-file selection in the file picker
287
+ ...(allowsMultiple && { multiple: true }),
288
+
280
289
  upload: {
281
290
  count,
282
291
  summaryList
@@ -16,6 +16,7 @@ import { type Schema, type ValidationErrorItem } from 'joi'
16
16
  import { Liquid } from 'liquidjs'
17
17
 
18
18
  import { createLogger } from '~/src/server/common/helpers/logging/logger.js'
19
+ import { FORM_VERSION_METADATA_KEY } from '~/src/server/constants.js'
19
20
  import {
20
21
  getAnswer,
21
22
  type Field
@@ -416,6 +417,22 @@ export function handleLegacyRedirect(h: ResponseToolkit, targetUrl: string) {
416
417
  * If the page doesn't have a title, set it from the title of the first form component
417
418
  * @param def - the form definition
418
419
  */
420
+ export interface FormVersionMetadata {
421
+ versionNumber: number
422
+ createdAt: Date
423
+ }
424
+
425
+ /**
426
+ * Extracts form version metadata from a form definition
427
+ */
428
+ export function getFormVersion(
429
+ definition: Pick<FormDefinition, 'metadata'>
430
+ ): FormVersionMetadata | undefined {
431
+ return definition.metadata?.[FORM_VERSION_METADATA_KEY] as
432
+ | FormVersionMetadata
433
+ | undefined
434
+ }
435
+
419
436
  export function setPageTitles(def: FormDefinition) {
420
437
  def.pages.forEach((page) => {
421
438
  if (!page.title) {
@@ -764,6 +764,60 @@ describe('Adapter v1 formatter', () => {
764
764
  })
765
765
 
766
766
  describe('version metadata handling', () => {
767
+ it('should prefer $$__formVersion from definition metadata over formMetadata.versions', () => {
768
+ const definitionWithFormVersion = {
769
+ ...definition,
770
+ metadata: {
771
+ $$__formVersion: {
772
+ versionNumber: 42,
773
+ createdAt: new Date('2024-06-01T00:00:00.000Z')
774
+ }
775
+ }
776
+ }
777
+
778
+ const modelWithFormVersion = new FormModel(definitionWithFormVersion, {
779
+ basePath: 'test'
780
+ })
781
+
782
+ const contextWithFormVersion = modelWithFormVersion.getFormContext(
783
+ request,
784
+ state
785
+ )
786
+
787
+ const formMetadata: Partial<FormMetadata> = {
788
+ id: 'form-123',
789
+ slug: 'test-form',
790
+ title: 'Test Form',
791
+ notificationEmail: 'test@example.com',
792
+ versions: [
793
+ {
794
+ versionNumber: 1,
795
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
796
+ }
797
+ ]
798
+ }
799
+
800
+ const formStatus = {
801
+ isPreview: false,
802
+ state: FormStatus.Live
803
+ }
804
+
805
+ const body = format(
806
+ contextWithFormVersion,
807
+ items,
808
+ modelWithFormVersion,
809
+ submitResponse,
810
+ formStatus,
811
+ formMetadata as FormMetadata
812
+ )
813
+ const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
814
+
815
+ expect(parsedBody.meta.versionMetadata).toEqual({
816
+ versionNumber: 42,
817
+ createdAt: '2024-06-01T00:00:00.000Z'
818
+ })
819
+ })
820
+
767
821
  it('should include versionMetadata when context has submittedVersionNumber and formMetadata has versions', () => {
768
822
  const formMetadata: Partial<FormMetadata> = {
769
823
  id: 'form-123',
@@ -3,7 +3,10 @@ import {
3
3
  type SubmitResponsePayload
4
4
  } from '@defra/forms-model'
5
5
 
6
- import { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'
6
+ import {
7
+ getFormVersion,
8
+ type checkFormStatus
9
+ } from '~/src/server/plugins/engine/helpers.js'
7
10
  import { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
8
11
  import { type DetailItem } from '~/src/server/plugins/engine/models/types.js'
9
12
  import { categoriseData } from '~/src/server/plugins/engine/outputFormatters/machine/v2.js'
@@ -28,10 +31,9 @@ export function format(
28
31
 
29
32
  const { main: v2Main, ...v2Data } = categoriseData(items)
30
33
 
31
- const versionMetadata = getVersionMetadata(
32
- context.submittedVersionNumber,
33
- formMetadata
34
- )
34
+ const versionMetadata =
35
+ getFormVersion(model.def) ??
36
+ getVersionMetadata(context.submittedVersionNumber, formMetadata)
35
37
 
36
38
  const meta: FormAdapterSubmissionMessageMeta = {
37
39
  schemaVersion: FormAdapterSubmissionSchemaVersion.V1,
@@ -795,6 +795,83 @@ describe('FileUploadPageController', () => {
795
795
  })
796
796
  })
797
797
 
798
+ it('collects all file errors into a single flash when multiple files fail', async () => {
799
+ const state = {
800
+ upload: {
801
+ [controller.path]: {
802
+ upload: {
803
+ uploadId: 'some-id',
804
+ uploadUrl: 'some-url',
805
+ statusUrl: 'some-status-url'
806
+ },
807
+ files: []
808
+ }
809
+ }
810
+ } as unknown as FormSubmissionState
811
+
812
+ const errorStatus = {
813
+ uploadStatus: UploadStatus.ready,
814
+ form: {
815
+ file: [
816
+ {
817
+ fileStatus: FileStatus.rejected,
818
+ errorMessage: 'File too large'
819
+ },
820
+ {
821
+ fileStatus: FileStatus.rejected,
822
+ errorMessage: 'Invalid file type'
823
+ }
824
+ ]
825
+ }
826
+ }
827
+
828
+ jest
829
+ .spyOn(uploadService, 'getUploadStatus')
830
+ .mockResolvedValue(errorStatus as unknown as UploadStatusResponse)
831
+
832
+ jest.spyOn(tempItemSchema, 'validate').mockReturnValue({
833
+ value: {
834
+ status: errorStatus,
835
+ uploadId: 'some-id'
836
+ },
837
+ error: undefined
838
+ } as ValidationResult)
839
+
840
+ const testController = controller as TestableFileUploadPageController
841
+
842
+ const initiateSpy = jest.spyOn(
843
+ testController,
844
+ 'initiateAndStoreNewUpload'
845
+ ) as jest.SpyInstance<
846
+ Promise<FormSubmissionState>,
847
+ [FormRequest, FormSubmissionState]
848
+ >
849
+
850
+ initiateSpy.mockResolvedValue(state)
851
+
852
+ const cacheService = getCacheService(request.server)
853
+
854
+ await controller['checkUploadStatus'](request, state, 1)
855
+
856
+ expect(cacheService.setFlash).toHaveBeenCalledTimes(1)
857
+ expect(cacheService.setFlash).toHaveBeenCalledWith(request, {
858
+ errors: [
859
+ {
860
+ path: ['fileUpload'],
861
+ href: '#fileUpload',
862
+ name: 'fileUpload',
863
+ text: 'File too large'
864
+ },
865
+ {
866
+ path: ['fileUpload'],
867
+ href: '#fileUpload',
868
+ name: 'fileUpload',
869
+ text: 'Invalid file type'
870
+ }
871
+ ]
872
+ })
873
+ })
874
+
798
875
  it('sets default error message when none provided', async () => {
799
876
  const state = {
800
877
  upload: {
@@ -859,7 +936,16 @@ describe('FileUploadPageController', () => {
859
936
 
860
937
  describe('file removal', () => {
861
938
  it('returns early when no file is removed', async () => {
862
- const files = [{ uploadId: 'file1' }, { uploadId: 'file2' }]
939
+ const files = [
940
+ {
941
+ uploadId: 'upload1',
942
+ status: { form: { file: { fileId: 'file1' } } }
943
+ },
944
+ {
945
+ uploadId: 'upload2',
946
+ status: { form: { file: { fileId: 'file2' } } }
947
+ }
948
+ ]
863
949
 
864
950
  Object.defineProperty(request, 'params', {
865
951
  value: { itemId: 'nonexistent-file' },
@@ -892,7 +978,16 @@ describe('FileUploadPageController', () => {
892
978
  })
893
979
 
894
980
  it('merges state when file is removed', async () => {
895
- const files = [{ uploadId: 'file1' }, { uploadId: 'file2' }]
981
+ const files = [
982
+ {
983
+ uploadId: 'upload1',
984
+ status: { form: { file: { fileId: 'file1' } } }
985
+ },
986
+ {
987
+ uploadId: 'upload2',
988
+ status: { form: { file: { fileId: 'file2' } } }
989
+ }
990
+ ]
896
991
 
897
992
  Object.defineProperty(request, 'params', {
898
993
  value: { itemId: 'file1' },
@@ -924,7 +1019,12 @@ describe('FileUploadPageController', () => {
924
1019
  expect(mergeStateSpy).toHaveBeenCalledWith(request, state, {
925
1020
  upload: {
926
1021
  [controller.path]: {
927
- files: [{ uploadId: 'file2' }],
1022
+ files: [
1023
+ {
1024
+ uploadId: 'upload2',
1025
+ status: { form: { file: { fileId: 'file2' } } }
1026
+ }
1027
+ ],
928
1028
  upload: {
929
1029
  uploadId: 'upload-123',
930
1030
  uploadUrl: 'some-url',
@@ -1121,11 +1221,15 @@ describe('FileUploadPageController', () => {
1121
1221
  files: [
1122
1222
  {
1123
1223
  uploadId: 'file-1',
1124
- status: { form: { file: { filename: 'file-1.pdf' } } }
1224
+ status: {
1225
+ form: { file: { fileId: 'file-1', filename: 'file-1.pdf' } }
1226
+ }
1125
1227
  },
1126
1228
  {
1127
1229
  uploadId: 'file-2',
1128
- status: { form: { file: { filename: 'file-2.pdf' } } }
1230
+ status: {
1231
+ form: { file: { fileId: 'file-2', filename: 'file-2.pdf' } }
1232
+ }
1129
1233
  }
1130
1234
  ]
1131
1235
  }
@@ -27,6 +27,7 @@ import {
27
27
  type AnyFormRequest,
28
28
  type FeaturedFormPageViewModel,
29
29
  type FileState,
30
+ type FileUpload,
30
31
  type FormContext,
31
32
  type FormContextRequest,
32
33
  type FormSubmissionError,
@@ -177,7 +178,7 @@ export class FileUploadPageController extends QuestionPageController {
177
178
  const files = this.getFilesFromState(state)
178
179
 
179
180
  const fileToRemove = files.find(
180
- ({ uploadId }) => uploadId === params.itemId
181
+ ({ status }) => status.form.file.fileId === params.itemId
181
182
  )
182
183
 
183
184
  if (!fileToRemove) {
@@ -385,39 +386,86 @@ export class FileUploadPageController extends QuestionPageController {
385
386
 
386
387
  // Only add to files state if the file validates.
387
388
  // This secures against html tampering of the file input
388
- // by adding a 'multiple' attribute or it being
389
- // changed to a simple text field or similar.
389
+ // (e.g. changing it to a simple text field or similar).
390
390
  const validationResult = tempItemSchema.validate(
391
391
  { uploadId, status: statusResponse },
392
392
  { stripUnknown: true }
393
393
  )
394
394
  const error = validationResult.error
395
- const fileState = validationResult.value as FileState
396
395
 
397
396
  if (error) {
398
397
  return this.initiateAndStoreNewUpload(request, state)
399
398
  }
400
399
 
401
- const file = fileState.status.form.file
402
- if (file.fileStatus === FileStatus.complete) {
403
- files.unshift(prepareFileState(fileState))
400
+ // CDP returns form.file as a single object for one file,
401
+ // or an array for multiple files. The Joi schema normalises
402
+ // both to an array via .single().
403
+ await this.processUploadedFiles(
404
+ request,
405
+ state,
406
+ validationResult.value,
407
+ files,
408
+ upload
409
+ )
410
+
411
+ return this.initiateAndStoreNewUpload(request, state)
412
+ }
413
+
414
+ /**
415
+ * Processes the uploaded files from a CDP status response.
416
+ * Complete files are added to state, rejected/pending files
417
+ * have their error messages flashed.
418
+ * @param request - the hapi request
419
+ * @param state - the form state
420
+ * @param validatedItem - the Joi-validated upload item
421
+ * @param files - the current files array from state
422
+ * @param upload - the current upload initiation response
423
+ */
424
+ private async processUploadedFiles(
425
+ request: AnyFormRequest,
426
+ state: FormSubmissionState,
427
+ validatedItem: FileState,
428
+ files: FileState[],
429
+ upload: UploadInitiateResponse | undefined
430
+ ) {
431
+ const { uploadId } = validatedItem
432
+ const validatedStatus = validatedItem.status
433
+ const rawFile = validatedStatus.form.file as unknown as
434
+ | FileUpload
435
+ | FileUpload[]
436
+ const uploadedFiles = Array.isArray(rawFile) ? rawFile : [rawFile]
437
+
438
+ const allErrors: FormSubmissionError[] = []
439
+
440
+ for (const file of uploadedFiles) {
441
+ if (file.fileStatus === FileStatus.complete) {
442
+ const perFileState: FileState = {
443
+ uploadId,
444
+ status: {
445
+ ...validatedStatus,
446
+ form: { file }
447
+ } as FileState['status']
448
+ }
449
+ files.unshift(prepareFileState(perFileState))
450
+ } else {
451
+ // Collect the error for rejected/pending files.
452
+ const { fileUpload } = this
453
+ const name = fileUpload.name
454
+ const text = file.errorMessage ?? 'Unknown error'
455
+ allErrors.push({ path: [name], href: `#${name}`, name, text })
456
+ }
457
+ }
458
+
459
+ if (allErrors.length) {
460
+ const cacheService = getCacheService(request.server)
461
+ cacheService.setFlash(request, { errors: allErrors })
462
+ }
463
+
464
+ if (uploadedFiles.some((f) => f.fileStatus === FileStatus.complete)) {
404
465
  await this.mergeState(request, state, {
405
466
  upload: { [this.path]: { files, upload } }
406
467
  })
407
- } else {
408
- // Flash the error message.
409
- const { fileUpload } = this
410
- const cacheService = getCacheService(request.server)
411
-
412
- const name = fileUpload.name
413
- const text = file.errorMessage ?? 'Unknown error'
414
- const errors: FormSubmissionError[] = [
415
- { path: [name], href: `#${name}`, name, text }
416
- ]
417
- cacheService.setFlash(request, { errors })
418
468
  }
419
-
420
- return this.initiateAndStoreNewUpload(request, state)
421
469
  }
422
470
 
423
471
  /**
@@ -438,7 +486,7 @@ export class FileUploadPageController extends QuestionPageController {
438
486
  const files = this.getFilesFromState(state)
439
487
 
440
488
  const filesUpdated = files.filter(
441
- ({ uploadId }) => uploadId !== params.itemId
489
+ ({ status }) => status.form.file.fileId !== params.itemId
442
490
  )
443
491
 
444
492
  if (filesUpdated.length === files.length) {