@defra/forms-engine-plugin 2.1.9 → 3.0.0

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 (126) hide show
  1. package/.server/server/index.js +2 -1
  2. package/.server/server/index.js.map +1 -1
  3. package/.server/server/plugins/engine/README.md +2 -2
  4. package/.server/server/plugins/engine/components/helpers/components.js +1 -1
  5. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  6. package/.server/server/plugins/engine/configureEnginePlugin.d.ts +2 -1
  7. package/.server/server/plugins/engine/configureEnginePlugin.js +4 -4
  8. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
  9. package/.server/server/plugins/engine/helpers.d.ts +7 -11
  10. package/.server/server/plugins/engine/helpers.js +2 -2
  11. package/.server/server/plugins/engine/helpers.js.map +1 -1
  12. package/.server/server/plugins/engine/models/FormModel.js +1 -1
  13. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  14. package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +1 -1
  15. package/.server/server/plugins/engine/models/SummaryViewModel.js +1 -1
  16. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  17. package/.server/server/plugins/engine/options.js +3 -6
  18. package/.server/server/plugins/engine/options.js.map +1 -1
  19. package/.server/server/plugins/engine/options.test.js +2 -8
  20. package/.server/server/plugins/engine/options.test.js.map +1 -1
  21. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +5 -6
  22. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  23. package/.server/server/plugins/engine/pageControllers/PageController.d.ts +6 -6
  24. package/.server/server/plugins/engine/pageControllers/PageController.js +4 -4
  25. package/.server/server/plugins/engine/pageControllers/PageController.js.map +1 -1
  26. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +13 -13
  27. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +12 -20
  28. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  29. package/.server/server/plugins/engine/pageControllers/RepeatPageController.d.ts +7 -8
  30. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
  31. package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +2 -2
  32. package/.server/server/plugins/engine/pageControllers/StartPageController.js +1 -1
  33. package/.server/server/plugins/engine/pageControllers/StartPageController.js.map +1 -1
  34. package/.server/server/plugins/engine/pageControllers/StatusPageController.d.ts +3 -4
  35. package/.server/server/plugins/engine/pageControllers/StatusPageController.js +1 -1
  36. package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
  37. package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +5 -4
  38. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +12 -1
  39. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  40. package/.server/server/plugins/engine/pageControllers/TerminalPageController.d.ts +4 -4
  41. package/.server/server/plugins/engine/pageControllers/TerminalPageController.js +1 -1
  42. package/.server/server/plugins/engine/pageControllers/TerminalPageController.js.map +1 -1
  43. package/.server/server/plugins/engine/pageControllers/__stubs__/server.d.ts +1 -1
  44. package/.server/server/plugins/engine/pageControllers/__stubs__/server.js +2 -6
  45. package/.server/server/plugins/engine/pageControllers/__stubs__/server.js.map +1 -1
  46. package/.server/server/plugins/engine/plugin.js +7 -12
  47. package/.server/server/plugins/engine/plugin.js.map +1 -1
  48. package/.server/server/plugins/engine/routes/index.d.ts +5 -5
  49. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  50. package/.server/server/plugins/engine/routes/questions.d.ts +4 -4
  51. package/.server/server/plugins/engine/routes/questions.js.map +1 -1
  52. package/.server/server/plugins/engine/routes/repeaters/item-delete.js.map +1 -1
  53. package/.server/server/plugins/engine/routes/repeaters/summary.js.map +1 -1
  54. package/.server/server/plugins/engine/types/index.d.ts +2 -2
  55. package/.server/server/plugins/engine/types/index.js.map +1 -1
  56. package/.server/server/plugins/engine/types.d.ts +10 -11
  57. package/.server/server/plugins/engine/types.js.map +1 -1
  58. package/.server/server/plugins/engine/views/partials/form.html +3 -3
  59. package/.server/server/plugins/engine/views/summary.html +21 -5
  60. package/.server/server/plugins/nunjucks/context.d.ts +5 -6
  61. package/.server/server/plugins/nunjucks/context.js +3 -3
  62. package/.server/server/plugins/nunjucks/context.js.map +1 -1
  63. package/.server/server/routes/types.d.ts +3 -2
  64. package/.server/server/routes/types.js +1 -1
  65. package/.server/server/routes/types.js.map +1 -1
  66. package/.server/server/schemas/index.js +1 -1
  67. package/.server/server/schemas/index.js.map +1 -1
  68. package/.server/server/services/cacheService.d.ts +11 -19
  69. package/.server/server/services/cacheService.js +9 -30
  70. package/.server/server/services/cacheService.js.map +1 -1
  71. package/.server/server/types.d.ts +4 -1
  72. package/.server/server/types.js.map +1 -1
  73. package/.server/typings/hapi/index.d.js.map +1 -1
  74. package/package.json +3 -1
  75. package/src/server/index.test.ts +0 -39
  76. package/src/server/index.ts +4 -1
  77. package/src/server/plugins/engine/README.md +2 -2
  78. package/src/server/plugins/engine/components/MultilineTextField.test.ts +11 -0
  79. package/src/server/plugins/engine/components/helpers/components.ts +1 -1
  80. package/src/server/plugins/engine/components/helpers/helpers.test.ts +1 -1
  81. package/src/server/plugins/engine/configureEnginePlugin.ts +15 -11
  82. package/src/server/plugins/engine/helpers.test.ts +3 -2
  83. package/src/server/plugins/engine/helpers.ts +6 -6
  84. package/src/server/plugins/engine/models/FormModel.test.ts +2 -2
  85. package/src/server/plugins/engine/models/FormModel.ts +1 -2
  86. package/src/server/plugins/engine/models/SummaryViewModel.test.ts +7 -7
  87. package/src/server/plugins/engine/models/SummaryViewModel.ts +1 -1
  88. package/src/server/plugins/engine/options.js +6 -6
  89. package/src/server/plugins/engine/options.test.js +2 -6
  90. package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +8 -10
  91. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +9 -8
  92. package/src/server/plugins/engine/pageControllers/PageController.test.ts +11 -8
  93. package/src/server/plugins/engine/pageControllers/PageController.ts +9 -15
  94. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +35 -102
  95. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +24 -36
  96. package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +4 -6
  97. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +8 -11
  98. package/src/server/plugins/engine/pageControllers/StartPageController.test.ts +4 -4
  99. package/src/server/plugins/engine/pageControllers/StartPageController.ts +1 -1
  100. package/src/server/plugins/engine/pageControllers/StatusPageController.test.ts +4 -4
  101. package/src/server/plugins/engine/pageControllers/StatusPageController.ts +6 -4
  102. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +15 -5
  103. package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +4 -4
  104. package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +7 -4
  105. package/src/server/plugins/engine/pageControllers/__stubs__/server.ts +5 -6
  106. package/src/server/plugins/engine/plugin.ts +7 -13
  107. package/src/server/plugins/engine/routes/index.ts +6 -11
  108. package/src/server/plugins/engine/routes/questions.test.ts +29 -53
  109. package/src/server/plugins/engine/routes/questions.ts +6 -8
  110. package/src/server/plugins/engine/routes/repeaters/item-delete.ts +5 -14
  111. package/src/server/plugins/engine/routes/repeaters/summary.ts +5 -14
  112. package/src/server/plugins/engine/types/index.ts +4 -1
  113. package/src/server/plugins/engine/types.ts +19 -13
  114. package/src/server/plugins/engine/views/partials/form.html +3 -3
  115. package/src/server/plugins/engine/views/summary.html +21 -5
  116. package/src/server/plugins/nunjucks/context.js +3 -3
  117. package/src/server/routes/types.ts +7 -2
  118. package/src/server/schemas/index.ts +1 -1
  119. package/src/server/services/cacheService.test.ts +1 -117
  120. package/src/server/services/cacheService.ts +22 -73
  121. package/src/server/types.ts +4 -1
  122. package/src/typings/hapi/index.d.ts +6 -7
  123. package/.server/server/plugins/engine/routes/exit.d.ts +0 -46
  124. package/.server/server/plugins/engine/routes/exit.js +0 -36
  125. package/.server/server/plugins/engine/routes/exit.js.map +0 -1
  126. package/src/server/plugins/engine/routes/exit.ts +0 -47
@@ -5,7 +5,7 @@ import {
5
5
  type SubmitPayload
6
6
  } from '@defra/forms-model'
7
7
  import Boom from '@hapi/boom'
8
- import { type ResponseToolkit, type RouteOptions } from '@hapi/hapi'
8
+ import { type RouteOptions } from '@hapi/hapi'
9
9
 
10
10
  import { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
11
11
  import { FileUploadField } from '~/src/server/plugins/engine/components/FileUploadField.js'
@@ -30,13 +30,16 @@ import {
30
30
  type FormSubmissionState
31
31
  } from '~/src/server/plugins/engine/types.js'
32
32
  import {
33
+ FormAction,
33
34
  type FormRequest,
34
35
  type FormRequestPayload,
35
- type FormRequestPayloadRefs
36
+ type FormRequestPayloadRefs,
37
+ type FormResponseToolkit
36
38
  } from '~/src/server/routes/types.js'
37
39
 
38
40
  export class SummaryPageController extends QuestionPageController {
39
41
  declare pageDef: Page
42
+ allowSaveAndExit = true
40
43
 
41
44
  /**
42
45
  * The controller which is used when Page["controller"] is defined as "./pages/summary.js"
@@ -69,7 +72,7 @@ export class SummaryPageController extends QuestionPageController {
69
72
  viewModel.feedbackLink = this.feedbackLink
70
73
  viewModel.phaseTag = this.phaseTag
71
74
  viewModel.components = components
72
- viewModel.allowSaveAndReturn = this.shouldShowSaveAndReturn(request.server)
75
+ viewModel.allowSaveAndExit = this.shouldShowSaveAndExit(request.server)
73
76
 
74
77
  return viewModel
75
78
  }
@@ -81,7 +84,7 @@ export class SummaryPageController extends QuestionPageController {
81
84
  return async (
82
85
  request: FormRequest,
83
86
  context: FormContext,
84
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
87
+ h: FormResponseToolkit
85
88
  ) => {
86
89
  const { viewName } = this
87
90
 
@@ -102,10 +105,17 @@ export class SummaryPageController extends QuestionPageController {
102
105
  return async (
103
106
  request: FormRequestPayload,
104
107
  context: FormContext,
105
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
108
+ h: FormResponseToolkit
106
109
  ) => {
107
110
  const { model } = this
108
111
  const { params } = request
112
+
113
+ // Check if this is a save-and-exit action
114
+ const { action } = request.payload
115
+ if (action === FormAction.SaveAndExit) {
116
+ return this.handleSaveAndExit(request, context, h)
117
+ }
118
+
109
119
  const cacheService = getCacheService(request.server)
110
120
 
111
121
  const { formsService } = this.model.services
@@ -1,6 +1,6 @@
1
1
  import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
2
2
  import { TerminalPageController } from '~/src/server/plugins/engine/pageControllers/TerminalPageController.js'
3
- import { serverWithSaveAndReturn } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
3
+ import { serverWithSaveAndExit } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
4
4
  import definition from '~/test/form/definitions/basic.js'
5
5
 
6
6
  describe('TerminalController', () => {
@@ -27,9 +27,9 @@ describe('TerminalController', () => {
27
27
  })
28
28
  })
29
29
 
30
- describe('shouldShowSaveAndReturn', () => {
31
- it('should return false (TerminalPageController does not allow save and return)', () => {
32
- expect(controller1.shouldShowSaveAndReturn(serverWithSaveAndReturn)).toBe(
30
+ describe('shouldShowSaveAndExit', () => {
31
+ it('should return false (TerminalPageController does not allow save and exit)', () => {
32
+ expect(controller1.shouldShowSaveAndExit(serverWithSaveAndExit)).toBe(
33
33
  false
34
34
  )
35
35
  })
@@ -1,19 +1,22 @@
1
1
  import { type PageTerminal } from '@defra/forms-model'
2
2
  import Boom from '@hapi/boom'
3
- import { type ResponseObject, type ResponseToolkit } from '@hapi/hapi'
3
+ import { type ResponseObject } from '@hapi/hapi'
4
4
 
5
5
  import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
6
6
  import { type FormContext } from '~/src/server/plugins/engine/types.js'
7
- import { type FormRequestPayload } from '~/src/server/routes/types.js'
7
+ import {
8
+ type FormRequestPayload,
9
+ type FormResponseToolkit
10
+ } from '~/src/server/routes/types.js'
8
11
 
9
12
  export class TerminalPageController extends QuestionPageController {
10
13
  declare pageDef: PageTerminal
11
- allowSaveAndReturn = false
14
+ allowSaveAndExit = false
12
15
 
13
16
  makePostRouteHandler(): (
14
17
  request: FormRequestPayload,
15
18
  context: FormContext,
16
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
19
+ h: FormResponseToolkit
17
20
  ) => Promise<ResponseObject> {
18
21
  throw Boom.methodNotAllowed('POST method not allowed for terminal pages')
19
22
  }
@@ -12,16 +12,15 @@ export const server: Server = {
12
12
  }
13
13
  } as Server // only mocking out properties we care about;
14
14
 
15
- export const serverWithSaveAndReturn: Server = {
15
+ export const serverWithSaveAndExit: Server = {
16
16
  plugins: {
17
17
  ...server.plugins,
18
18
  'forms-engine-plugin': {
19
19
  ...server.plugins['forms-engine-plugin'],
20
- saveAndReturn: {
21
- keyGenerator: jest.fn().mockReturnValue('foobar'),
22
- sessionHydrator: jest.fn().mockReturnValue({}),
23
- sessionPersister: jest.fn().mockImplementation(() => Promise.resolve())
24
- } as Pick<PluginOptions, 'saveAndReturn'>
20
+ saveAndExit: jest.fn().mockReturnValue({}) as Pick<
21
+ PluginOptions,
22
+ 'saveAndExit'
23
+ >
25
24
  }
26
25
  }
27
26
  } as Server // only mocking out properties we care about
@@ -8,7 +8,6 @@ import {
8
8
 
9
9
  import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
10
10
  import { validatePluginOptions } from '~/src/server/plugins/engine/options.js'
11
- import { getRoutes as getSaveAndReturnExitRoutes } from '~/src/server/plugins/engine/routes/exit.js'
12
11
  import { getRoutes as getFileUploadStatusRoutes } from '~/src/server/plugins/engine/routes/file-upload.js'
13
12
  import { makeLoadFormPreHandler } from '~/src/server/plugins/engine/routes/index.js'
14
13
  import { getRoutes as getQuestionRoutes } from '~/src/server/plugins/engine/routes/questions.js'
@@ -31,28 +30,24 @@ export const plugin = {
31
30
 
32
31
  const {
33
32
  model,
34
- cacheName,
35
- saveAndReturn,
33
+ cache,
34
+ saveAndExit,
36
35
  nunjucks: nunjucksOptions,
37
36
  viewContext,
38
37
  preparePageEventRequestOptions
39
38
  } = options
40
39
 
41
- const cacheService = new CacheService({
42
- server,
43
- cacheName,
44
- options: {
45
- keyGenerator: saveAndReturn?.keyGenerator,
46
- sessionHydrator: saveAndReturn?.sessionHydrator
47
- }
48
- })
40
+ const cacheService =
41
+ typeof cache === 'string'
42
+ ? new CacheService({ server, cacheName: cache })
43
+ : cache
49
44
 
50
45
  await registerVision(server, options)
51
46
 
52
47
  server.expose('baseLayoutPath', nunjucksOptions.baseLayoutPath)
53
48
  server.expose('viewContext', viewContext)
54
49
  server.expose('cacheService', cacheService)
55
- server.expose('saveAndReturn', saveAndReturn)
50
+ server.expose('saveAndExit', saveAndExit)
56
51
 
57
52
  server.app.model = model
58
53
 
@@ -92,7 +87,6 @@ export const plugin = {
92
87
  ),
93
88
  ...getRepeaterSummaryRoutes(getRouteOptions, postRouteOptions),
94
89
  ...getRepeaterItemDeleteRoutes(getRouteOptions, postRouteOptions),
95
- ...getSaveAndReturnExitRoutes(getRouteOptions),
96
90
  ...getFileUploadStatusRoutes()
97
91
  ]
98
92
 
@@ -21,17 +21,18 @@ import { type PageControllerClass } from '~/src/server/plugins/engine/pageContro
21
21
  import { generateUniqueReference } from '~/src/server/plugins/engine/referenceNumbers.js'
22
22
  import * as defaultServices from '~/src/server/plugins/engine/services/index.js'
23
23
  import {
24
+ type AnyFormRequest,
24
25
  type FormContext,
25
26
  type PluginOptions
26
27
  } from '~/src/server/plugins/engine/types.js'
27
28
  import {
28
29
  type FormRequest,
29
- type FormRequestPayload
30
+ type FormResponseToolkit
30
31
  } from '~/src/server/routes/types.js'
31
32
 
32
33
  export async function redirectOrMakeHandler(
33
- request: FormRequest | FormRequestPayload,
34
- h: Pick<ResponseToolkit, 'redirect' | 'view'>,
34
+ request: AnyFormRequest,
35
+ h: FormResponseToolkit,
35
36
  makeHandler: (
36
37
  page: PageControllerClass,
37
38
  context: FormContext
@@ -92,10 +93,7 @@ export function makeLoadFormPreHandler(server: Server, options: PluginOptions) {
92
93
 
93
94
  const { formsService } = services
94
95
 
95
- async function handler(
96
- request: FormRequest | FormRequestPayload,
97
- h: ResponseToolkit
98
- ) {
96
+ async function handler(request: AnyFormRequest, h: ResponseToolkit) {
99
97
  if (server.app.model) {
100
98
  request.app.model = server.app.model
101
99
 
@@ -181,10 +179,7 @@ export function makeLoadFormPreHandler(server: Server, options: PluginOptions) {
181
179
  return handler
182
180
  }
183
181
 
184
- export function dispatchHandler(
185
- request: FormRequest,
186
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
187
- ) {
182
+ export function dispatchHandler(request: FormRequest, h: FormResponseToolkit) {
188
183
  const { model } = request.app
189
184
 
190
185
  const servicePath = model ? `/${model.basePath}` : ''
@@ -1,5 +1,5 @@
1
1
  import Boom from '@hapi/boom'
2
- import { type ResponseObject, type ResponseToolkit } from '@hapi/hapi'
2
+ import { type ResponseObject } from '@hapi/hapi'
3
3
  // eslint-disable-next-line n/no-unpublished-import
4
4
  import nock from 'nock'
5
5
 
@@ -10,10 +10,14 @@ import {
10
10
  makeGetHandler,
11
11
  makePostHandler
12
12
  } from '~/src/server/plugins/engine/routes/questions.js'
13
- import { type FormContext } from '~/src/server/plugins/engine/types.js'
13
+ import {
14
+ type AnyFormRequest,
15
+ type FormContext
16
+ } from '~/src/server/plugins/engine/types.js'
14
17
  import {
15
18
  type FormRequest,
16
- type FormRequestPayload
19
+ type FormRequestPayload,
20
+ type FormResponseToolkit
17
21
  } from '~/src/server/routes/types.js'
18
22
  jest.mock('~/src/server/plugins/engine/models/SummaryViewModel', () => ({
19
23
  SummaryViewModel: class {
@@ -35,7 +39,7 @@ jest.mock('~/src/server/plugins/engine/outputFormatters/machine/v1', () => ({
35
39
  jest.mock('~/src/server/plugins/engine/routes/index')
36
40
 
37
41
  describe('makeGetHandler', () => {
38
- const hMock: Pick<ResponseToolkit, 'redirect' | 'view'> = {
42
+ const hMock: FormResponseToolkit = {
39
43
  redirect: jest.fn(),
40
44
  view: jest.fn()
41
45
  }
@@ -64,7 +68,7 @@ describe('makeGetHandler', () => {
64
68
  (
65
69
  _request: FormRequest,
66
70
  context: FormContext,
67
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>
71
+ _h: FormResponseToolkit
68
72
  ) => {
69
73
  data = context.data
70
74
  return Promise.resolve({} as unknown as ResponseObject)
@@ -80,12 +84,8 @@ describe('makeGetHandler', () => {
80
84
 
81
85
  jest
82
86
  .mocked(redirectOrMakeHandler)
83
- .mockImplementation(
84
- (
85
- _req: FormRequest | FormRequestPayload,
86
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
87
- fn
88
- ) => Promise.resolve(fn(pageMock, contextMock))
87
+ .mockImplementation((_req: AnyFormRequest, _h: FormResponseToolkit, fn) =>
88
+ Promise.resolve(fn(pageMock, contextMock))
89
89
  )
90
90
 
91
91
  await makeGetHandler()(requestMock, hMock)
@@ -108,7 +108,7 @@ describe('makeGetHandler', () => {
108
108
  (
109
109
  _request: FormRequest,
110
110
  context: FormContext,
111
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>
111
+ _h: FormResponseToolkit
112
112
  ) => {
113
113
  data = context.data
114
114
  return Promise.resolve({} as unknown as ResponseObject)
@@ -126,12 +126,8 @@ describe('makeGetHandler', () => {
126
126
 
127
127
  jest
128
128
  .mocked(redirectOrMakeHandler)
129
- .mockImplementation(
130
- (
131
- _req: FormRequest | FormRequestPayload,
132
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
133
- fn
134
- ) => Promise.resolve(fn(pageMock, contextMock))
129
+ .mockImplementation((_req: AnyFormRequest, _h: FormResponseToolkit, fn) =>
130
+ Promise.resolve(fn(pageMock, contextMock))
135
131
  )
136
132
 
137
133
  await makeGetHandler()(requestMock, hMock)
@@ -152,7 +148,7 @@ describe('makeGetHandler', () => {
152
148
  (
153
149
  _request: FormRequest,
154
150
  _context: FormContext,
155
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>
151
+ _h: FormResponseToolkit
156
152
  ) => {
157
153
  return Promise.resolve({} as unknown as ResponseObject)
158
154
  }
@@ -168,11 +164,7 @@ describe('makeGetHandler', () => {
168
164
  jest
169
165
  .mocked(redirectOrMakeHandler)
170
166
  .mockImplementation(
171
- async (
172
- _req: FormRequest | FormRequestPayload,
173
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
174
- fn
175
- ) => {
167
+ async (_req: AnyFormRequest, _h: FormResponseToolkit, fn) => {
176
168
  try {
177
169
  await fn(pageMock, contextMock)
178
170
  } catch (err) {
@@ -190,7 +182,7 @@ describe('makeGetHandler', () => {
190
182
  })
191
183
 
192
184
  describe('makePostHandler', () => {
193
- const hMock: Pick<ResponseToolkit, 'redirect' | 'view'> = {
185
+ const hMock: FormResponseToolkit = {
194
186
  redirect: jest.fn(),
195
187
  view: jest.fn()
196
188
  }
@@ -221,7 +213,7 @@ describe('makePostHandler', () => {
221
213
  (
222
214
  _request: FormRequest,
223
215
  _context: FormContext,
224
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>
216
+ _h: FormResponseToolkit
225
217
  ) => {
226
218
  // do return a valid ResponseObject wrapped in Promise.resolve
227
219
  return mockPostResponse
@@ -238,12 +230,8 @@ describe('makePostHandler', () => {
238
230
 
239
231
  jest
240
232
  .mocked(redirectOrMakeHandler)
241
- .mockImplementation(
242
- (
243
- _req: FormRequest | FormRequestPayload,
244
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
245
- fn
246
- ) => Promise.resolve(fn(pageMock, contextMock))
233
+ .mockImplementation((_req: AnyFormRequest, _h: FormResponseToolkit, fn) =>
234
+ Promise.resolve(fn(pageMock, contextMock))
247
235
  )
248
236
 
249
237
  const response = await makePostHandler()(requestMock, hMock)
@@ -263,7 +251,7 @@ describe('makePostHandler', () => {
263
251
  (
264
252
  _request: FormRequest,
265
253
  _context: FormContext,
266
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>
254
+ _h: FormResponseToolkit
267
255
  ) => {
268
256
  return Promise.resolve({} as unknown as ResponseObject)
269
257
  }
@@ -281,12 +269,8 @@ describe('makePostHandler', () => {
281
269
 
282
270
  jest
283
271
  .mocked(redirectOrMakeHandler)
284
- .mockImplementation(
285
- (
286
- _req: FormRequest | FormRequestPayload,
287
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
288
- fn
289
- ) => Promise.resolve(fn(pageMock, contextMock))
272
+ .mockImplementation((_req: AnyFormRequest, _h: FormResponseToolkit, fn) =>
273
+ Promise.resolve(fn(pageMock, contextMock))
290
274
  )
291
275
 
292
276
  await makePostHandler()(requestMock, hMock)
@@ -309,7 +293,7 @@ describe('makePostHandler', () => {
309
293
  (
310
294
  _request: FormRequest,
311
295
  _context: FormContext,
312
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>
296
+ _h: FormResponseToolkit
313
297
  ) => {
314
298
  // do return a valid ResponseObject wrapped in Promise.resolve
315
299
  return mockPostResponse
@@ -326,12 +310,8 @@ describe('makePostHandler', () => {
326
310
 
327
311
  jest
328
312
  .mocked(redirectOrMakeHandler)
329
- .mockImplementation(
330
- (
331
- _req: FormRequest | FormRequestPayload,
332
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
333
- fn
334
- ) => Promise.resolve(fn(pageMock, contextMock))
313
+ .mockImplementation((_req: AnyFormRequest, _h: FormResponseToolkit, fn) =>
314
+ Promise.resolve(fn(pageMock, contextMock))
335
315
  )
336
316
 
337
317
  await makePostHandler()(requestMock, hMock)
@@ -352,7 +332,7 @@ describe('makePostHandler', () => {
352
332
  (
353
333
  _request: FormRequest,
354
334
  _context: FormContext,
355
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>
335
+ _h: FormResponseToolkit
356
336
  ) => {
357
337
  return Promise.resolve({} as unknown as ResponseObject)
358
338
  }
@@ -369,11 +349,7 @@ describe('makePostHandler', () => {
369
349
  jest
370
350
  .mocked(redirectOrMakeHandler)
371
351
  .mockImplementation(
372
- async (
373
- _req: FormRequest | FormRequestPayload,
374
- _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
375
- fn
376
- ) => {
352
+ async (_req: AnyFormRequest, _h: FormResponseToolkit, fn) => {
377
353
  try {
378
354
  await fn(pageMock, contextMock)
379
355
  } catch (err) {
@@ -395,7 +371,7 @@ function createMockPageController(
395
371
  routeHandler: (
396
372
  request: FormRequest,
397
373
  context: FormContext,
398
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
374
+ h: FormResponseToolkit
399
375
  ) => ResponseObject | Promise<ResponseObject>
400
376
  ): PageControllerClass {
401
377
  return {
@@ -2,7 +2,6 @@ import { hasFormComponents, slugSchema, type Event } from '@defra/forms-model'
2
2
  import Boom from '@hapi/boom'
3
3
  import {
4
4
  type ResponseObject,
5
- type ResponseToolkit,
6
5
  type RouteOptions,
7
6
  type ServerRoute
8
7
  } from '@hapi/hapi'
@@ -25,6 +24,7 @@ import {
25
24
  redirectOrMakeHandler
26
25
  } from '~/src/server/plugins/engine/routes/index.js'
27
26
  import {
27
+ type AnyFormRequest,
28
28
  type FormContext,
29
29
  type PreparePageEventRequestOptions
30
30
  } from '~/src/server/plugins/engine/types.js'
@@ -32,7 +32,8 @@ import {
32
32
  type FormRequest,
33
33
  type FormRequestPayload,
34
34
  type FormRequestPayloadRefs,
35
- type FormRequestRefs
35
+ type FormRequestRefs,
36
+ type FormResponseToolkit
36
37
  } from '~/src/server/routes/types.js'
37
38
  import {
38
39
  actionSchema,
@@ -44,7 +45,7 @@ import {
44
45
  import * as httpService from '~/src/server/services/httpService.js'
45
46
 
46
47
  async function handleHttpEvent(
47
- request: FormRequest | FormRequestPayload,
48
+ request: AnyFormRequest,
48
49
  page: PageControllerClass,
49
50
  context: FormContext,
50
51
  event: Event,
@@ -75,10 +76,7 @@ async function handleHttpEvent(
75
76
  export function makeGetHandler(
76
77
  preparePageEventRequestOptions?: PreparePageEventRequestOptions
77
78
  ) {
78
- return function getHandler(
79
- request: FormRequest,
80
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
81
- ) {
79
+ return function getHandler(request: FormRequest, h: FormResponseToolkit) {
82
80
  const { params } = request
83
81
 
84
82
  if (normalisePath(params.path) === '') {
@@ -116,7 +114,7 @@ export function makePostHandler(
116
114
  ) {
117
115
  return function postHandler(
118
116
  request: FormRequestPayload,
119
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
117
+ h: FormResponseToolkit
120
118
  ) {
121
119
  const { query } = request
122
120
 
@@ -1,10 +1,6 @@
1
1
  import { slugSchema } from '@defra/forms-model'
2
2
  import Boom from '@hapi/boom'
3
- import {
4
- type ResponseToolkit,
5
- type RouteOptions,
6
- type ServerRoute
7
- } from '@hapi/hapi'
3
+ import { type RouteOptions, type ServerRoute } from '@hapi/hapi'
8
4
  import Joi from 'joi'
9
5
 
10
6
  import { FileUploadPageController } from '~/src/server/plugins/engine/pageControllers/FileUploadPageController.js'
@@ -14,7 +10,8 @@ import {
14
10
  type FormRequest,
15
11
  type FormRequestPayload,
16
12
  type FormRequestPayloadRefs,
17
- type FormRequestRefs
13
+ type FormRequestRefs,
14
+ type FormResponseToolkit
18
15
  } from '~/src/server/routes/types.js'
19
16
  import {
20
17
  actionSchema,
@@ -26,10 +23,7 @@ import {
26
23
  } from '~/src/server/schemas/index.js'
27
24
 
28
25
  // Item delete GET route
29
- function getHandler(
30
- request: FormRequest,
31
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
32
- ) {
26
+ function getHandler(request: FormRequest, h: FormResponseToolkit) {
33
27
  const { params } = request
34
28
 
35
29
  return redirectOrMakeHandler(request, h, (page, context) => {
@@ -46,10 +40,7 @@ function getHandler(
46
40
  })
47
41
  }
48
42
 
49
- function postHandler(
50
- request: FormRequestPayload,
51
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
52
- ) {
43
+ function postHandler(request: FormRequestPayload, h: FormResponseToolkit) {
53
44
  const { params } = request
54
45
 
55
46
  return redirectOrMakeHandler(request, h, (page, context) => {
@@ -1,11 +1,7 @@
1
1
  // List summary GET route
2
2
  import { slugSchema } from '@defra/forms-model'
3
3
  import Boom from '@hapi/boom'
4
- import {
5
- type ResponseToolkit,
6
- type RouteOptions,
7
- type ServerRoute
8
- } from '@hapi/hapi'
4
+ import { type RouteOptions, type ServerRoute } from '@hapi/hapi'
9
5
  import Joi from 'joi'
10
6
 
11
7
  import { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'
@@ -14,7 +10,8 @@ import {
14
10
  type FormRequest,
15
11
  type FormRequestPayload,
16
12
  type FormRequestPayloadRefs,
17
- type FormRequestRefs
13
+ type FormRequestRefs,
14
+ type FormResponseToolkit
18
15
  } from '~/src/server/routes/types.js'
19
16
  import {
20
17
  actionSchema,
@@ -23,10 +20,7 @@ import {
23
20
  stateSchema
24
21
  } from '~/src/server/schemas/index.js'
25
22
 
26
- function getHandler(
27
- request: FormRequest,
28
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
29
- ) {
23
+ function getHandler(request: FormRequest, h: FormResponseToolkit) {
30
24
  const { params } = request
31
25
 
32
26
  return redirectOrMakeHandler(request, h, (page, context) => {
@@ -38,10 +32,7 @@ function getHandler(
38
32
  })
39
33
  }
40
34
 
41
- function postHandler(
42
- request: FormRequestPayload,
43
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
44
- ) {
35
+ function postHandler(request: FormRequestPayload, h: FormResponseToolkit) {
45
36
  const { params } = request
46
37
 
47
38
  return redirectOrMakeHandler(request, h, (page, context) => {
@@ -1,4 +1,6 @@
1
1
  export type {
2
+ AnyFormRequest,
3
+ AnyRequest,
2
4
  CheckAnswers,
3
5
  ErrorMessageTemplate,
4
6
  ErrorMessageTemplateList,
@@ -74,7 +76,8 @@ export type {
74
76
  FormRequest,
75
77
  FormRequestPayload,
76
78
  FormRequestPayloadRefs,
77
- FormRequestRefs
79
+ FormRequestRefs,
80
+ FormResponseToolkit
78
81
  } from '~/src/server/routes/types.js'
79
82
 
80
83
  export { FormAction, FormStatus } from '~/src/server/routes/types.js'
@@ -7,7 +7,11 @@ import {
7
7
  type List,
8
8
  type Page
9
9
  } from '@defra/forms-model'
10
- import { type PluginProperties, type Request } from '@hapi/hapi'
10
+ import {
11
+ type PluginProperties,
12
+ type Request,
13
+ type ResponseObject
14
+ } from '@hapi/hapi'
11
15
  import { type JoiExpression, type ValidationErrorItem } from 'joi'
12
16
 
13
17
  import { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'
@@ -36,12 +40,15 @@ import {
36
40
  type FormParams,
37
41
  type FormRequest,
38
42
  type FormRequestPayload,
43
+ type FormResponseToolkit,
39
44
  type FormStatus
40
45
  } from '~/src/server/routes/types.js'
46
+ import { type CacheService } from '~/src/server/services/cacheService.js'
41
47
  import { type RequestOptions } from '~/src/server/services/httpService.js'
42
48
  import { type Services } from '~/src/server/types.js'
43
49
 
44
- type RequestType = Request | FormRequest | FormRequestPayload
50
+ export type AnyFormRequest = FormRequest | FormRequestPayload
51
+ export type AnyRequest = Request | AnyFormRequest
45
52
 
46
53
  /**
47
54
  * Form submission state stores the following in Redis:
@@ -312,7 +319,7 @@ export interface FormPageViewModel extends PageViewModelBase {
312
319
  context: FormContext
313
320
  errors?: FormSubmissionError[]
314
321
  hasMissingNotificationEmail?: boolean
315
- allowSaveAndReturn: boolean
322
+ allowSaveAndExit: boolean
316
323
  }
317
324
 
318
325
  export interface RepeaterSummaryPageViewModel extends PageViewModelBase {
@@ -357,27 +364,26 @@ export type PreparePageEventRequestOptions = (
357
364
  ) => void
358
365
 
359
366
  export type OnRequestCallback = (
360
- request: FormRequest | FormRequestPayload,
367
+ request: AnyFormRequest,
361
368
  params: FormParams,
362
369
  definition: FormDefinition,
363
370
  metadata: FormMetadata
364
371
  ) => void
365
372
 
373
+ export type SaveAndExitHandler = (
374
+ request: FormRequestPayload,
375
+ h: FormResponseToolkit,
376
+ context: FormContext
377
+ ) => ResponseObject
378
+
366
379
  export interface PluginOptions {
367
380
  model?: FormModel
368
381
  services?: Services
369
382
  controllers?: Record<string, typeof PageController>
370
- cacheName?: string
383
+ cache?: CacheService | string
371
384
  globals?: Record<string, GlobalFunction>
372
385
  filters?: Record<string, FilterFunction>
373
- saveAndReturn?: {
374
- keyGenerator: (request: RequestType) => string
375
- sessionHydrator: (request: RequestType) => Promise<FormSubmissionState>
376
- sessionPersister: (
377
- state: FormSubmissionState,
378
- request: RequestType
379
- ) => Promise<void>
380
- }
386
+ saveAndExit?: SaveAndExitHandler
381
387
  pluginPath?: string
382
388
  nunjucks: {
383
389
  baseLayoutPath: string