@defra/forms-engine-plugin 0.0.5 → 0.0.6
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/index.js +0 -4
- package/.server/server/index.js.map +1 -1
- package/.server/server/plugins/engine/helpers.js +3 -0
- package/.server/server/plugins/engine/helpers.js.map +1 -1
- package/.server/server/plugins/engine/index.js +27 -1
- package/.server/server/plugins/engine/index.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +2 -4
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +4 -10
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js +2 -3
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +2 -4
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
- package/.server/server/plugins/engine/plugin.js +65 -6
- package/.server/server/plugins/engine/plugin.js.map +1 -1
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/{views → plugins/engine/views}/components/service-banner/template.test.js +1 -1
- package/.server/server/plugins/engine/views/components/service-banner/template.test.js.map +1 -0
- package/.server/server/{views → plugins/engine/views}/components/tag-env/template.test.js +1 -1
- package/.server/server/plugins/engine/views/components/tag-env/template.test.js.map +1 -0
- package/.server/server/services/cacheService.js +5 -2
- package/.server/server/services/cacheService.js.map +1 -1
- package/.server/typings/hapi/index.d.js.map +1 -1
- package/README.md +215 -4
- package/package.json +1 -2
- package/src/server/index.ts +0 -5
- package/src/server/plugins/engine/helpers.ts +6 -1
- package/src/server/plugins/engine/index.ts +41 -1
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +20 -12
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +2 -1
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +7 -3
- package/src/server/plugins/engine/pageControllers/StatusPageController.ts +2 -1
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +3 -2
- package/src/server/plugins/engine/plugin.ts +84 -4
- package/src/server/plugins/engine/types.ts +2 -0
- package/src/server/services/cacheService.test.ts +1 -0
- package/src/server/services/cacheService.ts +9 -2
- package/src/typings/hapi/index.d.ts +3 -11
- package/.server/server/views/components/service-banner/template.test.js.map +0 -1
- package/.server/server/views/components/tag-env/template.test.js.map +0 -1
- /package/.server/server/{views → plugins/engine/views}/components/debug/macro.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/components/debug/template.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/components/service-banner/macro.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/components/service-banner/template.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/components/tag-env/macro.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/components/tag-env/template.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/confirmation.html +0 -0
- /package/.server/server/{views → plugins/engine/views}/layout.html +0 -0
- /package/.server/server/{views → plugins/engine/views}/summary.html +0 -0
- /package/src/server/{views → plugins/engine/views}/components/debug/macro.njk +0 -0
- /package/src/server/{views → plugins/engine/views}/components/debug/template.njk +0 -0
- /package/src/server/{views → plugins/engine/views}/components/service-banner/macro.njk +0 -0
- /package/src/server/{views → plugins/engine/views}/components/service-banner/template.njk +0 -0
- /package/src/server/{views → plugins/engine/views}/components/service-banner/template.test.js +0 -0
- /package/src/server/{views → plugins/engine/views}/components/tag-env/macro.njk +0 -0
- /package/src/server/{views → plugins/engine/views}/components/tag-env/template.njk +0 -0
- /package/src/server/{views → plugins/engine/views}/components/tag-env/template.test.js +0 -0
- /package/src/server/{views → plugins/engine/views}/confirmation.html +0 -0
- /package/src/server/{views → plugins/engine/views}/layout.html +0 -0
- /package/src/server/{views → plugins/engine/views}/summary.html +0 -0
package/README.md
CHANGED
|
@@ -1,15 +1,209 @@
|
|
|
1
|
-
# forms-engine
|
|
1
|
+
# @defra/forms-engine-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `@defra/forms-engine-plugin` is a [plugin](https://hapi.dev/tutorials/plugins/?lang=en_US) for [hapi](https://hapi.dev/) used to serve GOV.UK-based form journeys.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
It is designed to be embedded in the frontend of a digital service and provide a convenient, configuration driven approach to building forms that are aligned to [GDS Design System](https://design-system.service.gov.uk/) guidelines.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
`npm install @defra/forms-engine-plugin --save`
|
|
10
|
+
|
|
11
|
+
## Dependencies
|
|
12
|
+
|
|
13
|
+
The following are [plugin dependencies](<https://hapi.dev/api/?v=21.4.0#server.dependency()>) that are required to be registered with hapi:
|
|
14
|
+
|
|
15
|
+
`npm install hapi-pino @hapi/crumb @hapi/yar @hapi/vision --save`
|
|
16
|
+
|
|
17
|
+
- [hapi-pino](https://github.com/hapijs/hapi-pino) - [Pino](https://github.com/pinojs/pino) logger for hapi
|
|
18
|
+
- [@hapi/crumb](https://github.com/hapijs/crumb) - CSRF crumb generation and validation
|
|
19
|
+
- [@hapi/yar](https://github.com/hapijs/yar) - Session manager
|
|
20
|
+
- [@hapi/vision](https://github.com/hapijs/vision) - Template rendering support
|
|
21
|
+
|
|
22
|
+
Additional npm dependencies that you will need are:
|
|
23
|
+
|
|
24
|
+
`npm install nunjucks govuk-frontend --save`
|
|
25
|
+
|
|
26
|
+
- [nunjucks](https://www.npmjs.com/package/nunjucks) - [templating engine](https://mozilla.github.io/nunjucks/) used by GOV.UK design system
|
|
27
|
+
- [govuk-frontend](https://www.npmjs.com/package/govuk-frontend) - [code](https://github.com/alphagov/govuk-frontend) you need to build a user interface for government platforms and services
|
|
28
|
+
|
|
29
|
+
Optional dependencies
|
|
30
|
+
|
|
31
|
+
`npm install @hapi/inert --save`
|
|
32
|
+
|
|
33
|
+
- [@hapi/inert](https://www.npmjs.com/package/@hapi/inert) - static file and directory handlers for serving GOV.UK assets and styles
|
|
34
|
+
|
|
35
|
+
## Setup
|
|
36
|
+
|
|
37
|
+
### Form config
|
|
38
|
+
|
|
39
|
+
The `form-engine-plugin` uses JSON configuration files to serve form journeys.
|
|
40
|
+
These files are called `Form definitions` and are built up of:
|
|
41
|
+
|
|
42
|
+
- `pages` - includes a `path`, `title`
|
|
43
|
+
- `components` - one or more questions on a page
|
|
44
|
+
- `conditions` - used to conditionally show and hide pages and
|
|
45
|
+
- `lists` - data used to in selection fields like [Select](https://design-system.service.gov.uk/components/select/), [Checkboxes](https://design-system.service.gov.uk/components/checkboxes/) and [Radios](https://design-system.service.gov.uk/components/radios/)
|
|
46
|
+
|
|
47
|
+
The [types](https://github.com/DEFRA/forms-designer/blob/main/model/src/form/form-definition/types.ts), `joi` [schema](https://github.com/DEFRA/forms-designer/blob/main/model/src/form/form-definition/index.ts) and the [examples](test/form/definitions) folder are a good place to learn about the structure of these files.
|
|
48
|
+
|
|
49
|
+
TODO - Link to wiki for `Form metadata`
|
|
50
|
+
TODO - Link to wiki for `Form definition`
|
|
51
|
+
|
|
52
|
+
#### Providing form config to the engine
|
|
53
|
+
|
|
54
|
+
The engine plugin registers several [routes](https://hapi.dev/tutorials/routing/?lang=en_US) on the hapi server.
|
|
55
|
+
|
|
56
|
+
They look like this:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
GET /{slug}/{path}
|
|
60
|
+
POST /{slug}/{path}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
A unique `slug` is used to route the user to the correct form, and the `path` used to identify the correct page within the form to show.
|
|
64
|
+
The [plugin registration options](#options) have a `services` setting to provide a `formsService` that is responsible for returning `form definition` data.
|
|
65
|
+
|
|
66
|
+
WARNING: This below is subject to change
|
|
67
|
+
|
|
68
|
+
A `formsService` has two methods, one for returning `formMetadata` and another to return `formDefinition`s.
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
const formsService = {
|
|
72
|
+
getFormMetadata: async function (slug) {
|
|
73
|
+
// Returns the metadata for the slug
|
|
74
|
+
},
|
|
75
|
+
getFormDefinition: async function (id, state) {
|
|
76
|
+
// Returns the form definition for the given id
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The reason for the two separate methods is caching.
|
|
82
|
+
`formMetadata` is a lightweight record designed to give top level information about a form.
|
|
83
|
+
This method is invoked for every page request.
|
|
84
|
+
|
|
85
|
+
Only when the `formMetadata` indicates that the definition has changed is a call to `getFormDefinition` is made.
|
|
86
|
+
The response from this can be quite big as it contains the entire form definition.
|
|
87
|
+
|
|
88
|
+
See [example](#example) below for more detail
|
|
89
|
+
|
|
90
|
+
### Static assets and styles
|
|
91
|
+
|
|
92
|
+
TODO
|
|
93
|
+
|
|
94
|
+
## Example
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
import hapi from '@hapi/hapi'
|
|
98
|
+
import yar from '@hapi/yar'
|
|
99
|
+
import crumb from '@hapi/crumb'
|
|
100
|
+
import inert from '@hapi/inert'
|
|
101
|
+
import pino from 'hapi-pino'
|
|
102
|
+
import plugin from '@defra/forms-engine-plugin'
|
|
103
|
+
|
|
104
|
+
const server = hapi.server({
|
|
105
|
+
port: 3000
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
// Register the dependent plugins
|
|
109
|
+
await server.register(pino)
|
|
110
|
+
await server.register(inert)
|
|
111
|
+
await server.register(crumb)
|
|
112
|
+
await server.register({
|
|
113
|
+
plugin: yar,
|
|
114
|
+
options: {
|
|
115
|
+
cookieOptions: {
|
|
116
|
+
password: 'ENTER_YOUR_SESSION_COOKIE_PASSWORD_HERE' // Must be > 32 chars
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Register the `forms-engine-plugin`
|
|
122
|
+
await server.register({
|
|
123
|
+
plugin
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
await server.start()
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Environment variables
|
|
130
|
+
|
|
131
|
+
## Options
|
|
132
|
+
|
|
133
|
+
The forms plugin is configured with [registration options](https://hapi.dev/api/?v=21.4.0#plugins)
|
|
134
|
+
|
|
135
|
+
- `services` (optional) - object containing `formsService`, `formSubmissionService` and `outputService`
|
|
136
|
+
- `formsService` - used to load `formMetadata` and `formDefinition`
|
|
137
|
+
- `formSubmissionService` - used prepare the form during submission (ignore - subject to change)
|
|
138
|
+
- `outputService` - used to save the submission
|
|
139
|
+
- `controllers` (optional) - Object map of custom page controllers used to override the default. See [custom controllers](#custom-controllers)
|
|
140
|
+
- `filters` (optional) - A map of custom template filters to include
|
|
141
|
+
- `cacheName` (optional) - The cache name to use. Defaults to hapi's [default server cache]. Recommended for production. See [here]
|
|
142
|
+
(#custom-cache) for more details
|
|
143
|
+
- `pluginPath` (optional) - The location of the plugin (defaults to `node_modules/@defra/forms-engine-plugin`)
|
|
144
|
+
|
|
145
|
+
### Services
|
|
146
|
+
|
|
147
|
+
TODO
|
|
148
|
+
|
|
149
|
+
### Custom controllers
|
|
150
|
+
|
|
151
|
+
TODO
|
|
152
|
+
|
|
153
|
+
### Custom filters
|
|
154
|
+
|
|
155
|
+
Use the `filter` plugin option to provide custom template filters.
|
|
156
|
+
Filters are available in both [nunjucks](https://mozilla.github.io/nunjucks/templating.html#filters) and [liquid](https://liquidjs.com/filters/overview.html) templates.
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
const formatter = new Intl.NumberFormat('en-GB')
|
|
160
|
+
|
|
161
|
+
await server.register({
|
|
162
|
+
plugin,
|
|
163
|
+
options: {
|
|
164
|
+
filters: {
|
|
165
|
+
money: value => formatter.format(value),
|
|
166
|
+
upper: value => typeof value === 'string' ? value.toUpperCase() : value
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Custom cache
|
|
173
|
+
|
|
174
|
+
The plugin will use the [default server cache](https://hapi.dev/api/?v=21.4.0#-serveroptionscache) to store form answers on the server.
|
|
175
|
+
This is just an in-memory cache which is fine for development.
|
|
176
|
+
|
|
177
|
+
In production you should create a custom cache one of the available `@hapi/catbox` adapters.
|
|
178
|
+
|
|
179
|
+
E.g. [Redis](https://github.com/hapijs/catbox-redis)
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
import { Engine as CatboxRedis } from '@hapi/catbox-redis'
|
|
183
|
+
|
|
184
|
+
const server = new Hapi.Server({
|
|
185
|
+
cache : [
|
|
186
|
+
{
|
|
187
|
+
name: 'my_cache',
|
|
188
|
+
provider: {
|
|
189
|
+
constructor: CatboxRedis,
|
|
190
|
+
options: {}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Exemplar
|
|
198
|
+
|
|
199
|
+
TODO: Link to CDP exemplar
|
|
6
200
|
|
|
7
201
|
## Templates
|
|
8
202
|
|
|
9
203
|
The following elements support [LiquidJS templates](https://liquidjs.com/):
|
|
10
204
|
|
|
11
205
|
- Page **title**
|
|
12
|
-
- Form component **
|
|
206
|
+
- Form component **title**
|
|
13
207
|
- Support for fieldset legend text or label text
|
|
14
208
|
- This includes when the title is used in **error messages**
|
|
15
209
|
- Html (guidance) component **content**
|
|
@@ -85,3 +279,20 @@ There are a number of `LiquidJS` filters available to you from within the templa
|
|
|
85
279
|
}
|
|
86
280
|
]
|
|
87
281
|
```
|
|
282
|
+
|
|
283
|
+
## Templates and views: Extending the default layout
|
|
284
|
+
|
|
285
|
+
TODO
|
|
286
|
+
|
|
287
|
+
To override the default page template, vision and nunjucks both need to be configured to search in the `forms-engine-plugin` views directory when looking for template files.
|
|
288
|
+
|
|
289
|
+
For vision this is done through the `path` [plugin option](https://github.com/hapijs/vision/blob/master/API.md#options)
|
|
290
|
+
For nunjucks it is configured through the environment [configure options](https://mozilla.github.io/nunjucks/api.html#configure).
|
|
291
|
+
|
|
292
|
+
The `forms-engine-plugin` path to add can be imported from:
|
|
293
|
+
|
|
294
|
+
`import { VIEW_PATH } from '@defra/forms-engine-plugin'`
|
|
295
|
+
|
|
296
|
+
Which can then be appended to the `node_modules` path `node_modules/@defra/forms-engine`.
|
|
297
|
+
|
|
298
|
+
The main template layout is `govuk-frontend`'s `template.njk` file, this also needs to be added to the `path`s that nunjucks can look in.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra/forms-engine-plugin",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Defra forms engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": ".server/server/plugins/engine/index.js",
|
|
@@ -60,7 +60,6 @@
|
|
|
60
60
|
"@hapi/vision": "^7.0.3",
|
|
61
61
|
"@hapi/wreck": "^18.1.0",
|
|
62
62
|
"@hapi/yar": "^11.0.2",
|
|
63
|
-
"@hapipal/schmervice": "^3.0.0",
|
|
64
63
|
"@types/humanize-duration": "^3.27.4",
|
|
65
64
|
"accessible-autocomplete": "^3.0.1",
|
|
66
65
|
"atob": "^2.1.2",
|
package/src/server/index.ts
CHANGED
|
@@ -8,7 +8,6 @@ import hapi, {
|
|
|
8
8
|
import inert from '@hapi/inert'
|
|
9
9
|
import Scooter from '@hapi/scooter'
|
|
10
10
|
import Wreck from '@hapi/wreck'
|
|
11
|
-
import Schmervice from '@hapipal/schmervice'
|
|
12
11
|
import blipp from 'blipp'
|
|
13
12
|
import { ProxyAgent } from 'proxy-agent'
|
|
14
13
|
|
|
@@ -25,7 +24,6 @@ import pluginPulse from '~/src/server/plugins/pulse.js'
|
|
|
25
24
|
import pluginRouter from '~/src/server/plugins/router.js'
|
|
26
25
|
import pluginSession from '~/src/server/plugins/session.js'
|
|
27
26
|
import { prepareSecureContext } from '~/src/server/secure-context.js'
|
|
28
|
-
import { CacheService } from '~/src/server/services/index.js'
|
|
29
27
|
import { type RouteConfig } from '~/src/server/types.js'
|
|
30
28
|
|
|
31
29
|
const proxyAgent = new ProxyAgent()
|
|
@@ -94,9 +92,6 @@ export async function createServer(routeConfig?: RouteConfig) {
|
|
|
94
92
|
await server.register(Scooter)
|
|
95
93
|
await server.register(pluginBlankie)
|
|
96
94
|
await server.register(pluginCrumb)
|
|
97
|
-
await server.register(Schmervice)
|
|
98
|
-
|
|
99
|
-
server.registerService(CacheService)
|
|
100
95
|
|
|
101
96
|
server.ext('onPreResponse', (request: Request, h: ResponseToolkit) => {
|
|
102
97
|
const { response } = request
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
type Page
|
|
6
6
|
} from '@defra/forms-model'
|
|
7
7
|
import Boom from '@hapi/boom'
|
|
8
|
-
import { type ResponseToolkit } from '@hapi/hapi'
|
|
8
|
+
import { type ResponseToolkit, type Server } from '@hapi/hapi'
|
|
9
9
|
import { format, parseISO } from 'date-fns'
|
|
10
10
|
import { StatusCodes } from 'http-status-codes'
|
|
11
11
|
import { type Schema, type ValidationErrorItem } from 'joi'
|
|
@@ -362,6 +362,7 @@ export function getExponentialBackoffDelay(depth: number): number {
|
|
|
362
362
|
const delay = BASE_DELAY_MS * 2 ** (depth - 1)
|
|
363
363
|
return Math.min(delay, CAP_DELAY_MS)
|
|
364
364
|
}
|
|
365
|
+
|
|
365
366
|
export function evaluateTemplate(
|
|
366
367
|
template: string,
|
|
367
368
|
context: FormContext
|
|
@@ -377,3 +378,7 @@ export function evaluateTemplate(
|
|
|
377
378
|
globals
|
|
378
379
|
})
|
|
379
380
|
}
|
|
381
|
+
|
|
382
|
+
export function getCacheService(server: Server) {
|
|
383
|
+
return server.plugins['forms-engine-plugin'].cacheService
|
|
384
|
+
}
|
|
@@ -1,7 +1,47 @@
|
|
|
1
|
+
import { type Environment } from 'nunjucks'
|
|
2
|
+
|
|
3
|
+
import { engine } from '~/src/server/plugins/engine/helpers.js'
|
|
1
4
|
import { plugin } from '~/src/server/plugins/engine/plugin.js'
|
|
5
|
+
import { type FilterFunction } from '~/src/server/plugins/engine/types.js'
|
|
6
|
+
import {
|
|
7
|
+
checkComponentTemplates,
|
|
8
|
+
checkErrorTemplates,
|
|
9
|
+
evaluate
|
|
10
|
+
} from '~/src/server/plugins/nunjucks/environment.js'
|
|
11
|
+
import * as filters from '~/src/server/plugins/nunjucks/filters/index.js'
|
|
2
12
|
|
|
3
13
|
export { getPageHref } from '~/src/server/plugins/engine/helpers.js'
|
|
4
14
|
export { configureEnginePlugin } from '~/src/server/plugins/engine/configureEnginePlugin.js'
|
|
5
|
-
export {
|
|
15
|
+
export { context } from '~/src/server/plugins/nunjucks/context.js'
|
|
16
|
+
|
|
17
|
+
const globals = {
|
|
18
|
+
checkComponentTemplates,
|
|
19
|
+
checkErrorTemplates,
|
|
20
|
+
evaluate
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const VIEW_PATH = 'src/server/plugins/engine/views'
|
|
24
|
+
export const PLUGIN_PATH = 'node_modules/@defra/forms-engine-plugin'
|
|
25
|
+
|
|
26
|
+
export const prepareNunjucksEnvironment = function (
|
|
27
|
+
env: Environment,
|
|
28
|
+
additionalFilters?: Record<string, FilterFunction>
|
|
29
|
+
) {
|
|
30
|
+
for (const [name, nunjucksFilter] of Object.entries(filters)) {
|
|
31
|
+
env.addFilter(name, nunjucksFilter)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const [name, nunjucksGlobal] of Object.entries(globals)) {
|
|
35
|
+
env.addGlobal(name, nunjucksGlobal)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Apply any additional filters to both the liquid and nunjucks engines
|
|
39
|
+
if (additionalFilters) {
|
|
40
|
+
for (const [name, filter] of Object.entries(additionalFilters)) {
|
|
41
|
+
env.addFilter(name, filter)
|
|
42
|
+
engine.registerFilter(name, filter)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
6
46
|
|
|
7
47
|
export default plugin
|
|
@@ -4,7 +4,10 @@ import { type ResponseToolkit } from '@hapi/hapi'
|
|
|
4
4
|
import { type ValidationErrorItem, type ValidationResult } from 'joi'
|
|
5
5
|
|
|
6
6
|
import { tempItemSchema } from '~/src/server/plugins/engine/components/FileUploadField.js'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
getCacheService,
|
|
9
|
+
getError
|
|
10
|
+
} from '~/src/server/plugins/engine/helpers.js'
|
|
8
11
|
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
9
12
|
import {
|
|
10
13
|
FileUploadPageController,
|
|
@@ -71,14 +74,18 @@ describe('FileUploadPageController', () => {
|
|
|
71
74
|
trace: jest.fn(),
|
|
72
75
|
level: 'info'
|
|
73
76
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
server: {
|
|
78
|
+
plugins: {
|
|
79
|
+
'forms-engine-plugin': {
|
|
80
|
+
cacheService: {
|
|
81
|
+
setFlash: jest.fn(),
|
|
82
|
+
setState: jest
|
|
83
|
+
.fn()
|
|
84
|
+
.mockImplementation((req, updated) => Promise.resolve(updated))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
80
87
|
}
|
|
81
|
-
}
|
|
88
|
+
},
|
|
82
89
|
query: {}
|
|
83
90
|
} as unknown as FormRequest
|
|
84
91
|
})
|
|
@@ -316,7 +323,7 @@ describe('FileUploadPageController', () => {
|
|
|
316
323
|
)
|
|
317
324
|
initiateSpy.mockResolvedValue(state as never)
|
|
318
325
|
|
|
319
|
-
const
|
|
326
|
+
const cacheService = getCacheService(request.server)
|
|
320
327
|
await controller['checkUploadStatus'](request, state, 1)
|
|
321
328
|
|
|
322
329
|
expect(cacheService.setFlash).toHaveBeenCalledWith(request, {
|
|
@@ -608,7 +615,8 @@ describe('FileUploadPageController', () => {
|
|
|
608
615
|
Promise.resolve(Object.assign({}, s, { newUpload: true }))
|
|
609
616
|
)
|
|
610
617
|
|
|
611
|
-
const
|
|
618
|
+
const cacheService = getCacheService(request.server)
|
|
619
|
+
|
|
612
620
|
await controller['checkUploadStatus'](request, state, 1)
|
|
613
621
|
|
|
614
622
|
expect(cacheService.setFlash).toHaveBeenCalledWith(request, {
|
|
@@ -670,7 +678,7 @@ describe('FileUploadPageController', () => {
|
|
|
670
678
|
|
|
671
679
|
initiateSpy.mockResolvedValue(state)
|
|
672
680
|
|
|
673
|
-
const
|
|
681
|
+
const cacheService = getCacheService(request.server)
|
|
674
682
|
await controller['checkUploadStatus'](request, state, 1)
|
|
675
683
|
|
|
676
684
|
expect(cacheService.setFlash).toHaveBeenCalledWith(request, {
|
|
@@ -729,7 +737,7 @@ describe('FileUploadPageController', () => {
|
|
|
729
737
|
|
|
730
738
|
initiateSpy.mockResolvedValue(state)
|
|
731
739
|
|
|
732
|
-
const
|
|
740
|
+
const cacheService = getCacheService(request.server)
|
|
733
741
|
|
|
734
742
|
await controller['checkUploadStatus'](request, state, 1)
|
|
735
743
|
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type FileUploadField
|
|
10
10
|
} from '~/src/server/plugins/engine/components/FileUploadField.js'
|
|
11
11
|
import {
|
|
12
|
+
getCacheService,
|
|
12
13
|
getError,
|
|
13
14
|
getExponentialBackoffDelay
|
|
14
15
|
} from '~/src/server/plugins/engine/helpers.js'
|
|
@@ -364,7 +365,7 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
364
365
|
} else {
|
|
365
366
|
// Flash the error message.
|
|
366
367
|
const { fileUpload } = this
|
|
367
|
-
const
|
|
368
|
+
const cacheService = getCacheService(request.server)
|
|
368
369
|
const name = fileUpload.name
|
|
369
370
|
const text = file.errorMessage ?? 'Unknown error'
|
|
370
371
|
const errors: FormSubmissionError[] = [
|
|
@@ -15,6 +15,7 @@ import { ComponentCollection } from '~/src/server/plugins/engine/components/Comp
|
|
|
15
15
|
import { optionalText } from '~/src/server/plugins/engine/components/constants.js'
|
|
16
16
|
import { type BackLink } from '~/src/server/plugins/engine/components/types.js'
|
|
17
17
|
import {
|
|
18
|
+
getCacheService,
|
|
18
19
|
getErrors,
|
|
19
20
|
normalisePath,
|
|
20
21
|
proceed
|
|
@@ -298,7 +299,8 @@ export class QuestionPageController extends PageController {
|
|
|
298
299
|
return {}
|
|
299
300
|
}
|
|
300
301
|
|
|
301
|
-
const
|
|
302
|
+
const cacheService = getCacheService(request.server)
|
|
303
|
+
|
|
302
304
|
return cacheService.getState(request)
|
|
303
305
|
}
|
|
304
306
|
|
|
@@ -313,7 +315,8 @@ export class QuestionPageController extends PageController {
|
|
|
313
315
|
return state
|
|
314
316
|
}
|
|
315
317
|
|
|
316
|
-
const
|
|
318
|
+
const cacheService = getCacheService(request.server)
|
|
319
|
+
|
|
317
320
|
return cacheService.setState(request, state)
|
|
318
321
|
}
|
|
319
322
|
|
|
@@ -332,7 +335,8 @@ export class QuestionPageController extends PageController {
|
|
|
332
335
|
return updated
|
|
333
336
|
}
|
|
334
337
|
|
|
335
|
-
const
|
|
338
|
+
const cacheService = getCacheService(request.server)
|
|
339
|
+
|
|
336
340
|
return cacheService.setState(request, updated)
|
|
337
341
|
}
|
|
338
342
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type PageStatus } from '@defra/forms-model'
|
|
2
2
|
import { type ResponseToolkit } from '@hapi/hapi'
|
|
3
3
|
|
|
4
|
+
import { getCacheService } from '~/src/server/plugins/engine/helpers.js'
|
|
4
5
|
import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
|
|
5
6
|
import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
|
|
6
7
|
import { type FormContext } from '~/src/server/plugins/engine/types.js'
|
|
@@ -26,7 +27,7 @@ export class StatusPageController extends QuestionPageController {
|
|
|
26
27
|
) => {
|
|
27
28
|
const { viewModel, viewName } = this
|
|
28
29
|
|
|
29
|
-
const
|
|
30
|
+
const cacheService = getCacheService(request.server)
|
|
30
31
|
const confirmationState = await cacheService.getConfirmationState(request)
|
|
31
32
|
|
|
32
33
|
// If there's no confirmation state, then
|
|
@@ -6,7 +6,8 @@ import { FileUploadField } from '~/src/server/plugins/engine/components/FileUplo
|
|
|
6
6
|
import { getAnswer } from '~/src/server/plugins/engine/components/helpers.js'
|
|
7
7
|
import {
|
|
8
8
|
checkEmailAddressForLiveFormSubmission,
|
|
9
|
-
checkFormStatus
|
|
9
|
+
checkFormStatus,
|
|
10
|
+
getCacheService
|
|
10
11
|
} from '~/src/server/plugins/engine/helpers.js'
|
|
11
12
|
import {
|
|
12
13
|
SummaryViewModel,
|
|
@@ -88,8 +89,8 @@ export class SummaryPageController extends QuestionPageController {
|
|
|
88
89
|
const { model } = this
|
|
89
90
|
const { params } = request
|
|
90
91
|
const { state } = context
|
|
92
|
+
const cacheService = getCacheService(request.server)
|
|
91
93
|
|
|
92
|
-
const { cacheService } = request.services([])
|
|
93
94
|
const { formsService } = this.model.services
|
|
94
95
|
const { getFormMetadata } = formsService
|
|
95
96
|
|
|
@@ -6,20 +6,30 @@ import {
|
|
|
6
6
|
type ResponseToolkit,
|
|
7
7
|
type RouteOptions
|
|
8
8
|
} from '@hapi/hapi'
|
|
9
|
+
// import vision from '@hapi/vision'
|
|
9
10
|
import { isEqual } from 'date-fns'
|
|
10
11
|
import Joi from 'joi'
|
|
12
|
+
import { type Environment } from 'nunjucks'
|
|
13
|
+
// import nunjucks, { type Environment } from 'nunjucks'
|
|
11
14
|
|
|
12
15
|
import { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'
|
|
13
16
|
import {
|
|
14
17
|
checkEmailAddressForLiveFormSubmission,
|
|
15
18
|
checkFormStatus,
|
|
16
19
|
findPage,
|
|
20
|
+
getCacheService,
|
|
17
21
|
getPage,
|
|
18
22
|
getStartPath,
|
|
19
23
|
normalisePath,
|
|
20
24
|
proceed,
|
|
21
25
|
redirectPath
|
|
22
26
|
} from '~/src/server/plugins/engine/helpers.js'
|
|
27
|
+
// import {
|
|
28
|
+
// PLUGIN_PATH,
|
|
29
|
+
// VIEW_PATH,
|
|
30
|
+
// context,
|
|
31
|
+
// prepareNunjucksEnvironment
|
|
32
|
+
// } from '~/src/server/plugins/engine/index.js'
|
|
23
33
|
import {
|
|
24
34
|
FormModel,
|
|
25
35
|
SummaryViewModel
|
|
@@ -32,7 +42,10 @@ import { getFormSubmissionData } from '~/src/server/plugins/engine/pageControlle
|
|
|
32
42
|
import { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'
|
|
33
43
|
import * as defaultServices from '~/src/server/plugins/engine/services/index.js'
|
|
34
44
|
import { getUploadStatus } from '~/src/server/plugins/engine/services/uploadService.js'
|
|
35
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
type FilterFunction,
|
|
47
|
+
type FormContext
|
|
48
|
+
} from '~/src/server/plugins/engine/types.js'
|
|
36
49
|
import {
|
|
37
50
|
type FormRequest,
|
|
38
51
|
type FormRequestPayload,
|
|
@@ -48,21 +61,80 @@ import {
|
|
|
48
61
|
stateSchema
|
|
49
62
|
} from '~/src/server/schemas/index.js'
|
|
50
63
|
import * as httpService from '~/src/server/services/httpService.js'
|
|
64
|
+
import { CacheService } from '~/src/server/services/index.js'
|
|
51
65
|
import { type Services } from '~/src/server/types.js'
|
|
52
66
|
|
|
53
67
|
export interface PluginOptions {
|
|
54
68
|
model?: FormModel
|
|
55
69
|
services?: Services
|
|
56
70
|
controllers?: Record<string, typeof PageController>
|
|
71
|
+
cacheName?: string
|
|
72
|
+
pluginPath?: string
|
|
73
|
+
filters?: Record<string, FilterFunction>
|
|
57
74
|
}
|
|
58
75
|
|
|
59
76
|
export const plugin = {
|
|
60
77
|
name: '@defra/forms-engine-plugin',
|
|
61
|
-
dependencies: ['@hapi/
|
|
78
|
+
dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],
|
|
62
79
|
multiple: true,
|
|
63
80
|
register(server, options) {
|
|
64
|
-
const {
|
|
81
|
+
const {
|
|
82
|
+
model,
|
|
83
|
+
services = defaultServices,
|
|
84
|
+
controllers,
|
|
85
|
+
cacheName
|
|
86
|
+
// pluginPath = PLUGIN_PATH,
|
|
87
|
+
// filters
|
|
88
|
+
} = options
|
|
65
89
|
const { formsService } = services
|
|
90
|
+
const cacheService = new CacheService(server, cacheName)
|
|
91
|
+
|
|
92
|
+
// Paths array to tell `vision` and `nunjucks` where template files are stored.
|
|
93
|
+
// const path = [`${pluginPath}/${VIEW_PATH}`]
|
|
94
|
+
|
|
95
|
+
// await server.register({
|
|
96
|
+
// plugin: vision,
|
|
97
|
+
// options: {
|
|
98
|
+
// engines: {
|
|
99
|
+
// html: {
|
|
100
|
+
// compile: (
|
|
101
|
+
// path: string,
|
|
102
|
+
// compileOptions: { environment: Environment }
|
|
103
|
+
// ) => {
|
|
104
|
+
// const template = nunjucks.compile(
|
|
105
|
+
// path,
|
|
106
|
+
// compileOptions.environment
|
|
107
|
+
// )
|
|
108
|
+
|
|
109
|
+
// return (context: object | undefined) => {
|
|
110
|
+
// return template.render(context)
|
|
111
|
+
// }
|
|
112
|
+
// },
|
|
113
|
+
// prepare: (options: EngineConfigurationObject, next) => {
|
|
114
|
+
// // Nunjucks also needs an additional path configuration
|
|
115
|
+
// // to use the templates and macros from `govuk-frontend`
|
|
116
|
+
// const environment = nunjucks.configure([
|
|
117
|
+
// ...path,
|
|
118
|
+
// 'node_modules/govuk-frontend/dist'
|
|
119
|
+
// ])
|
|
120
|
+
|
|
121
|
+
// // Applies custom filters and globals for nunjucks
|
|
122
|
+
// // that are required by the `forms-engine-plugin`
|
|
123
|
+
// prepareNunjucksEnvironment(environment, filters)
|
|
124
|
+
|
|
125
|
+
// options.compileOptions.environment = environment
|
|
126
|
+
|
|
127
|
+
// next()
|
|
128
|
+
// }
|
|
129
|
+
// }
|
|
130
|
+
// },
|
|
131
|
+
// path,
|
|
132
|
+
// // Provides global context used with all templates
|
|
133
|
+
// context
|
|
134
|
+
// }
|
|
135
|
+
// })
|
|
136
|
+
|
|
137
|
+
server.expose('cacheService', cacheService)
|
|
66
138
|
|
|
67
139
|
server.app.model = model
|
|
68
140
|
|
|
@@ -178,7 +250,7 @@ export const plugin = {
|
|
|
178
250
|
throw Boom.notFound(`No model found for /${params.path}`)
|
|
179
251
|
}
|
|
180
252
|
|
|
181
|
-
const
|
|
253
|
+
const cacheService = getCacheService(request.server)
|
|
182
254
|
const page = getPage(model, request)
|
|
183
255
|
const state = await page.getState(request)
|
|
184
256
|
const flash = cacheService.getFlash(request)
|
|
@@ -671,3 +743,11 @@ export const plugin = {
|
|
|
671
743
|
})
|
|
672
744
|
}
|
|
673
745
|
} satisfies Plugin<PluginOptions>
|
|
746
|
+
|
|
747
|
+
interface CompileOptions {
|
|
748
|
+
environment: Environment
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
export interface EngineConfigurationObject {
|
|
752
|
+
compileOptions: CompileOptions
|
|
753
|
+
}
|