@defra/forms-engine-plugin 2.0.3 → 2.1.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.
- package/.server/server/forms/page-events.yaml +87 -0
- package/.server/server/index.js +2 -1
- package/.server/server/index.js.map +1 -1
- package/.server/server/plugins/engine/routes/questions.d.ts +4 -2
- package/.server/server/plugins/engine/routes/questions.js +67 -43
- package/.server/server/plugins/engine/routes/questions.js.map +1 -1
- package/.server/server/plugins/engine/services/localFormsService.js +7 -9
- package/.server/server/plugins/engine/services/localFormsService.js.map +1 -1
- package/.server/server/routes/dummy-api.d.ts +38 -0
- package/.server/server/routes/dummy-api.js +33 -0
- package/.server/server/routes/dummy-api.js.map +1 -0
- package/.server/server/routes/index.d.ts +1 -0
- package/.server/server/routes/index.js +1 -0
- package/.server/server/routes/index.js.map +1 -1
- package/package.json +3 -1
- package/src/server/forms/page-events.yaml +87 -0
- package/src/server/index.ts +4 -2
- package/src/server/plugins/engine/routes/questions.test.ts +416 -0
- package/src/server/plugins/engine/routes/questions.ts +96 -40
- package/src/server/plugins/engine/services/localFormsService.js +7 -8
- package/src/server/routes/dummy-api.test.ts +96 -0
- package/src/server/routes/dummy-api.ts +62 -0
- package/src/server/routes/index.ts +1 -0
- package/.server/server/forms/register-as-a-unicorn-breeder.json +0 -393
- package/src/server/forms/register-as-a-unicorn-breeder.json +0 -393
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Page events
|
|
3
|
+
engine: V2
|
|
4
|
+
schema: 2
|
|
5
|
+
startPage: '/summary'
|
|
6
|
+
pages:
|
|
7
|
+
- title: Your name
|
|
8
|
+
path: '/your-name'
|
|
9
|
+
components:
|
|
10
|
+
- type: TextField
|
|
11
|
+
title: What is your first name?
|
|
12
|
+
name: applicantFirstName
|
|
13
|
+
shortDescription: Your first name
|
|
14
|
+
hint: ''
|
|
15
|
+
options:
|
|
16
|
+
required: true
|
|
17
|
+
schema: {}
|
|
18
|
+
id: 1fb8e182-c709-4792-8f83-e01d8b1fee1a
|
|
19
|
+
- type: TextField
|
|
20
|
+
title: What is your last name?
|
|
21
|
+
name: applicantLastName
|
|
22
|
+
shortDescription: Your last name
|
|
23
|
+
hint: ''
|
|
24
|
+
options:
|
|
25
|
+
required: true
|
|
26
|
+
schema: {}
|
|
27
|
+
id: b68df7f1-d4f4-4c17-83c8-402f584906c9
|
|
28
|
+
next: []
|
|
29
|
+
id: 622a35ec-3795-418a-81f3-a45746959045
|
|
30
|
+
- title: ''
|
|
31
|
+
path: '/date-of-birth'
|
|
32
|
+
events:
|
|
33
|
+
onLoad:
|
|
34
|
+
type: http
|
|
35
|
+
options:
|
|
36
|
+
method: 'POST'
|
|
37
|
+
url: http://localhost:3009/api/example/on-load-page
|
|
38
|
+
components:
|
|
39
|
+
- type: Html
|
|
40
|
+
# technically context.data.submissionReferenceNumber is redundant as the reference number is available locally as context.referenceNumber
|
|
41
|
+
# but this is to demonstrate that the server can send data back to the client
|
|
42
|
+
content: >
|
|
43
|
+
<p class="govuk-body">
|
|
44
|
+
The backend received a full copy of the form state even though you haven't submitted the form yet.
|
|
45
|
+
</p>
|
|
46
|
+
<p class="govuk-body">
|
|
47
|
+
Your submission was received with the reference: <strong>{{ context.data.submissionReferenceNumber }}</strong>.
|
|
48
|
+
</p>
|
|
49
|
+
id: 334b10dc-3373-4928-8fed-575578a67de8
|
|
50
|
+
- type: DatePartsField
|
|
51
|
+
title: When is {{ applicantFirstName }} {{ applicantLastName }}'s birthday?
|
|
52
|
+
name: dateOfBirth
|
|
53
|
+
shortDescription: Your birthday
|
|
54
|
+
hint: ''
|
|
55
|
+
options:
|
|
56
|
+
required: true
|
|
57
|
+
schema: {}
|
|
58
|
+
id: '00738799-3489-4ab2-a57b-542eecb31bfa'
|
|
59
|
+
next: []
|
|
60
|
+
id: da0fbdb4-a2de-4650-be16-9ba552af135f
|
|
61
|
+
- id: 449a45f6-4541-4a46-91bd-8b8931b07b50
|
|
62
|
+
title: Summary
|
|
63
|
+
path: '/summary'
|
|
64
|
+
controller: SummaryPageController
|
|
65
|
+
events:
|
|
66
|
+
onLoad:
|
|
67
|
+
type: http
|
|
68
|
+
options:
|
|
69
|
+
method: 'POST'
|
|
70
|
+
url: http://localhost:3009/api/example/on-summary
|
|
71
|
+
onSave:
|
|
72
|
+
type: http
|
|
73
|
+
options:
|
|
74
|
+
method: 'POST'
|
|
75
|
+
url: http://localhost:3009/api/example/on-summary
|
|
76
|
+
components:
|
|
77
|
+
- type: Html
|
|
78
|
+
content: >
|
|
79
|
+
<h2 class="govuk-heading-m">Your age</h1>
|
|
80
|
+
<p class="govuk-body">
|
|
81
|
+
We've calculated that you are {{ context.data.calculatedAge }} years old. Only proceed if this is correct.
|
|
82
|
+
</p>
|
|
83
|
+
id: c42ea488-a38c-4b67-b8fa-4cf543a4f82d
|
|
84
|
+
next: []
|
|
85
|
+
conditions: []
|
|
86
|
+
sections: []
|
|
87
|
+
lists: []
|
package/.server/server/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import pluginErrorPages from "./plugins/errorPages.js";
|
|
|
16
16
|
import { plugin as pluginViews } from "./plugins/nunjucks/index.js";
|
|
17
17
|
import pluginPulse from "./plugins/pulse.js";
|
|
18
18
|
import pluginSession from "./plugins/session.js";
|
|
19
|
-
import { publicRoutes } from "./routes/index.js";
|
|
19
|
+
import { dummyApiRoutes, publicRoutes } from "./routes/index.js";
|
|
20
20
|
import { prepareSecureContext } from "./secure-context.js";
|
|
21
21
|
const proxyAgent = new ProxyAgent();
|
|
22
22
|
Wreck.agents = {
|
|
@@ -96,6 +96,7 @@ export async function createServer(routeConfig) {
|
|
|
96
96
|
name: 'router',
|
|
97
97
|
register: server => {
|
|
98
98
|
server.route(publicRoutes);
|
|
99
|
+
server.route(dummyApiRoutes);
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["Engine","CatboxMemory","CatboxRedis","hapi","inert","Scooter","Wreck","blipp","ProxyAgent","config","requestLogger","requestTracing","buildRedisClient","configureCrumbPlugin","configureEnginePlugin","pluginErrorPages","plugin","pluginViews","pluginPulse","pluginSession","publicRoutes","prepareSecureContext","proxyAgent","agents","https","http","httpsAllowUnauthorized","serverOptions","debug","request","get","port","router","stripTrailingSlash","routes","validate","options","abortEarly","security","hsts","maxAge","includeSubDomains","preload","xss","noSniff","xframe","cache","name","engine","client","createServer","routeConfig","server","register","pluginCrumb","pluginEngine","ext","h","response","continue","header","path","startsWith","route"],"sources":["../../src/server/index.ts"],"sourcesContent":["import { Engine as CatboxMemory } from '@hapi/catbox-memory'\nimport { Engine as CatboxRedis } from '@hapi/catbox-redis'\nimport hapi, {\n type Request,\n type ResponseToolkit,\n type ServerOptions\n} from '@hapi/hapi'\nimport inert from '@hapi/inert'\nimport Scooter from '@hapi/scooter'\nimport Wreck from '@hapi/wreck'\nimport blipp from 'blipp'\nimport { ProxyAgent } from 'proxy-agent'\n\nimport { config } from '~/src/config/index.js'\nimport { requestLogger } from '~/src/server/common/helpers/logging/request-logger.js'\nimport { requestTracing } from '~/src/server/common/helpers/logging/request-tracing.js'\nimport { buildRedisClient } from '~/src/server/common/helpers/redis-client.js'\nimport { configureCrumbPlugin } from '~/src/server/plugins/crumb.js'\nimport { configureEnginePlugin } from '~/src/server/plugins/engine/configureEnginePlugin.js'\nimport pluginErrorPages from '~/src/server/plugins/errorPages.js'\nimport { plugin as pluginViews } from '~/src/server/plugins/nunjucks/index.js'\nimport pluginPulse from '~/src/server/plugins/pulse.js'\nimport pluginSession from '~/src/server/plugins/session.js'\nimport { publicRoutes } from '~/src/server/routes/index.js'\nimport { prepareSecureContext } from '~/src/server/secure-context.js'\nimport { type RouteConfig } from '~/src/server/types.js'\n\nconst proxyAgent = new ProxyAgent()\n\nWreck.agents = {\n https: proxyAgent,\n http: proxyAgent,\n httpsAllowUnauthorized: proxyAgent\n}\n\nconst serverOptions = (): ServerOptions => {\n const serverOptions: ServerOptions = {\n debug: { request: [`${config.get('isDevelopment')}`] },\n port: config.get('port'),\n router: {\n stripTrailingSlash: true\n },\n routes: {\n validate: {\n options: {\n abortEarly: false\n }\n },\n security: {\n hsts: {\n maxAge: 31536000,\n includeSubDomains: true,\n preload: false\n },\n xss: 'enabled',\n noSniff: true,\n xframe: true\n }\n },\n cache: [\n {\n name: 'session',\n engine: config.get('isTest')\n ? new CatboxMemory()\n : new CatboxRedis({\n client: buildRedisClient()\n })\n }\n ]\n }\n\n return serverOptions\n}\n\nexport async function createServer(routeConfig?: RouteConfig) {\n const server = hapi.server(serverOptions())\n\n await server.register(requestLogger)\n\n if (config.get('isProduction')) {\n prepareSecureContext(server)\n }\n\n const pluginCrumb = configureCrumbPlugin(routeConfig)\n const pluginEngine = await configureEnginePlugin(routeConfig)\n\n await server.register(pluginSession)\n await server.register(pluginPulse)\n await server.register(inert)\n await server.register(Scooter)\n await server.register(pluginCrumb)\n await server.register(pluginEngine)\n\n server.ext('onPreResponse', (request: Request, h: ResponseToolkit) => {\n const { response } = request\n\n if ('isBoom' in response) {\n return h.continue\n }\n\n // Prevent search engine indexing\n response.header('x-robots-tag', 'noindex, nofollow')\n\n // Disable cache to ensure back/foward navigation updates progress\n if (\n !request.path.startsWith('/javascripts/') &&\n !request.path.startsWith('/stylesheets/') &&\n !request.path.startsWith('/assets/')\n ) {\n response.header('cache-control', 'no-store')\n }\n\n return h.continue\n })\n\n await server.register(pluginViews)\n\n await server.register({\n plugin: {\n name: 'router',\n register: (server) => {\n server.route(publicRoutes)\n }\n }\n })\n\n await server.register(pluginErrorPages)\n\n if (config.get('cdpEnvironment') === 'local') {\n await server.register(blipp)\n }\n\n await server.register(requestTracing)\n\n return server\n}\n"],"mappings":"AAAA,SAASA,MAAM,IAAIC,YAAY,QAAQ,qBAAqB;AAC5D,SAASD,MAAM,IAAIE,WAAW,QAAQ,oBAAoB;AAC1D,OAAOC,IAAI,
|
|
1
|
+
{"version":3,"file":"index.js","names":["Engine","CatboxMemory","CatboxRedis","hapi","inert","Scooter","Wreck","blipp","ProxyAgent","config","requestLogger","requestTracing","buildRedisClient","configureCrumbPlugin","configureEnginePlugin","pluginErrorPages","plugin","pluginViews","pluginPulse","pluginSession","dummyApiRoutes","publicRoutes","prepareSecureContext","proxyAgent","agents","https","http","httpsAllowUnauthorized","serverOptions","debug","request","get","port","router","stripTrailingSlash","routes","validate","options","abortEarly","security","hsts","maxAge","includeSubDomains","preload","xss","noSniff","xframe","cache","name","engine","client","createServer","routeConfig","server","register","pluginCrumb","pluginEngine","ext","h","response","continue","header","path","startsWith","route"],"sources":["../../src/server/index.ts"],"sourcesContent":["import { Engine as CatboxMemory } from '@hapi/catbox-memory'\nimport { Engine as CatboxRedis } from '@hapi/catbox-redis'\nimport hapi, {\n type Request,\n type ResponseToolkit,\n type ServerOptions,\n type ServerRoute\n} from '@hapi/hapi'\nimport inert from '@hapi/inert'\nimport Scooter from '@hapi/scooter'\nimport Wreck from '@hapi/wreck'\nimport blipp from 'blipp'\nimport { ProxyAgent } from 'proxy-agent'\n\nimport { config } from '~/src/config/index.js'\nimport { requestLogger } from '~/src/server/common/helpers/logging/request-logger.js'\nimport { requestTracing } from '~/src/server/common/helpers/logging/request-tracing.js'\nimport { buildRedisClient } from '~/src/server/common/helpers/redis-client.js'\nimport { configureCrumbPlugin } from '~/src/server/plugins/crumb.js'\nimport { configureEnginePlugin } from '~/src/server/plugins/engine/configureEnginePlugin.js'\nimport pluginErrorPages from '~/src/server/plugins/errorPages.js'\nimport { plugin as pluginViews } from '~/src/server/plugins/nunjucks/index.js'\nimport pluginPulse from '~/src/server/plugins/pulse.js'\nimport pluginSession from '~/src/server/plugins/session.js'\nimport { dummyApiRoutes, publicRoutes } from '~/src/server/routes/index.js'\nimport { prepareSecureContext } from '~/src/server/secure-context.js'\nimport { type RouteConfig } from '~/src/server/types.js'\n\nconst proxyAgent = new ProxyAgent()\n\nWreck.agents = {\n https: proxyAgent,\n http: proxyAgent,\n httpsAllowUnauthorized: proxyAgent\n}\n\nconst serverOptions = (): ServerOptions => {\n const serverOptions: ServerOptions = {\n debug: { request: [`${config.get('isDevelopment')}`] },\n port: config.get('port'),\n router: {\n stripTrailingSlash: true\n },\n routes: {\n validate: {\n options: {\n abortEarly: false\n }\n },\n security: {\n hsts: {\n maxAge: 31536000,\n includeSubDomains: true,\n preload: false\n },\n xss: 'enabled',\n noSniff: true,\n xframe: true\n }\n },\n cache: [\n {\n name: 'session',\n engine: config.get('isTest')\n ? new CatboxMemory()\n : new CatboxRedis({\n client: buildRedisClient()\n })\n }\n ]\n }\n\n return serverOptions\n}\n\nexport async function createServer(routeConfig?: RouteConfig) {\n const server = hapi.server(serverOptions())\n\n await server.register(requestLogger)\n\n if (config.get('isProduction')) {\n prepareSecureContext(server)\n }\n\n const pluginCrumb = configureCrumbPlugin(routeConfig)\n const pluginEngine = await configureEnginePlugin(routeConfig)\n\n await server.register(pluginSession)\n await server.register(pluginPulse)\n await server.register(inert)\n await server.register(Scooter)\n await server.register(pluginCrumb)\n await server.register(pluginEngine)\n\n server.ext('onPreResponse', (request: Request, h: ResponseToolkit) => {\n const { response } = request\n\n if ('isBoom' in response) {\n return h.continue\n }\n\n // Prevent search engine indexing\n response.header('x-robots-tag', 'noindex, nofollow')\n\n // Disable cache to ensure back/foward navigation updates progress\n if (\n !request.path.startsWith('/javascripts/') &&\n !request.path.startsWith('/stylesheets/') &&\n !request.path.startsWith('/assets/')\n ) {\n response.header('cache-control', 'no-store')\n }\n\n return h.continue\n })\n\n await server.register(pluginViews)\n\n await server.register({\n plugin: {\n name: 'router',\n register: (server) => {\n server.route(publicRoutes)\n server.route(dummyApiRoutes as ServerRoute[])\n }\n }\n })\n\n await server.register(pluginErrorPages)\n\n if (config.get('cdpEnvironment') === 'local') {\n await server.register(blipp)\n }\n\n await server.register(requestTracing)\n\n return server\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { type RouteOptions, type ServerRoute } from '@hapi/hapi';
|
|
1
|
+
import { type ResponseObject, type ResponseToolkit, type RouteOptions, type ServerRoute } from '@hapi/hapi';
|
|
2
2
|
import { type PreparePageEventRequestOptions } from '~/src/server/plugins/engine/types.js';
|
|
3
|
-
import { type FormRequestPayloadRefs, type FormRequestRefs } from '~/src/server/routes/types.js';
|
|
3
|
+
import { type FormRequest, type FormRequestPayload, type FormRequestPayloadRefs, type FormRequestRefs } from '~/src/server/routes/types.js';
|
|
4
|
+
export declare function makeGetHandler(preparePageEventRequestOptions?: PreparePageEventRequestOptions): (request: FormRequest, h: Pick<ResponseToolkit, "redirect" | "view">) => ResponseObject | Promise<ResponseObject>;
|
|
5
|
+
export declare function makePostHandler(preparePageEventRequestOptions?: PreparePageEventRequestOptions): (request: FormRequestPayload, h: Pick<ResponseToolkit, "redirect" | "view">) => Promise<ResponseObject>;
|
|
4
6
|
export declare function getRoutes(getRouteOptions: RouteOptions<FormRequestRefs>, postRouteOptions: RouteOptions<FormRequestPayloadRefs>, preparePageEventRequestOptions?: PreparePageEventRequestOptions): (ServerRoute<FormRequestRefs> | ServerRoute<FormRequestPayloadRefs>)[];
|
|
@@ -8,7 +8,33 @@ import { getFormSubmissionData } from "../pageControllers/SummaryPageController.
|
|
|
8
8
|
import { dispatchHandler, redirectOrMakeHandler } from "./index.js";
|
|
9
9
|
import { actionSchema, crumbSchema, itemIdSchema, pathSchema, stateSchema } from "../../../schemas/index.js";
|
|
10
10
|
import * as httpService from "../../../services/httpService.js";
|
|
11
|
-
function
|
|
11
|
+
async function handleHttpEvent(request, page, context, event, model, preparePageEventRequestOptions) {
|
|
12
|
+
const {
|
|
13
|
+
options
|
|
14
|
+
} = event;
|
|
15
|
+
const {
|
|
16
|
+
url
|
|
17
|
+
} = options;
|
|
18
|
+
|
|
19
|
+
// TODO: Update structured data POST payload with when helper
|
|
20
|
+
// is updated to removing the dependency on `SummaryViewModel` etc.
|
|
21
|
+
const viewModel = new SummaryViewModel(request, page, context);
|
|
22
|
+
const items = getFormSubmissionData(viewModel.context, viewModel.details);
|
|
23
|
+
|
|
24
|
+
// @ts-expect-error - function signature will be refactored in the next iteration of the formatter
|
|
25
|
+
const payload = format(context, items, model, undefined, undefined);
|
|
26
|
+
const opts = {
|
|
27
|
+
payload
|
|
28
|
+
};
|
|
29
|
+
if (preparePageEventRequestOptions) {
|
|
30
|
+
preparePageEventRequestOptions(opts, event, page, context);
|
|
31
|
+
}
|
|
32
|
+
const {
|
|
33
|
+
payload: response
|
|
34
|
+
} = await httpService.postJson(url, opts);
|
|
35
|
+
Object.assign(context.data, response);
|
|
36
|
+
}
|
|
37
|
+
export function makeGetHandler(preparePageEventRequestOptions) {
|
|
12
38
|
return function getHandler(request, h) {
|
|
13
39
|
const {
|
|
14
40
|
params
|
|
@@ -29,53 +55,51 @@ function makeGetHandler(preparePageEventRequestOptions) {
|
|
|
29
55
|
throw Boom.notFound(`No model found for /${params.path}`);
|
|
30
56
|
}
|
|
31
57
|
if (events?.onLoad && events.onLoad.type === 'http') {
|
|
32
|
-
|
|
33
|
-
options
|
|
34
|
-
} = events.onLoad;
|
|
35
|
-
const {
|
|
36
|
-
url
|
|
37
|
-
} = options;
|
|
38
|
-
|
|
39
|
-
// TODO: Update structured data POST payload with when helper
|
|
40
|
-
// is updated to removing the dependency on `SummaryViewModel` etc.
|
|
41
|
-
const viewModel = new SummaryViewModel(request, page, context);
|
|
42
|
-
const items = getFormSubmissionData(viewModel.context, viewModel.details);
|
|
43
|
-
|
|
44
|
-
// @ts-expect-error - function signature will be refactored in the next iteration of the formatter
|
|
45
|
-
const payload = format(context, items, model, undefined, undefined);
|
|
46
|
-
const opts = {
|
|
47
|
-
payload
|
|
48
|
-
};
|
|
49
|
-
if (preparePageEventRequestOptions) {
|
|
50
|
-
preparePageEventRequestOptions(opts, events.onLoad, page, context);
|
|
51
|
-
}
|
|
52
|
-
const {
|
|
53
|
-
payload: response
|
|
54
|
-
} = await httpService.postJson(url, opts);
|
|
55
|
-
Object.assign(context.data, response);
|
|
58
|
+
await handleHttpEvent(request, page, context, events.onLoad, model, preparePageEventRequestOptions);
|
|
56
59
|
}
|
|
57
60
|
return page.makeGetRouteHandler()(request, context, h);
|
|
58
61
|
});
|
|
59
62
|
};
|
|
60
63
|
}
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
query
|
|
64
|
-
} = request;
|
|
65
|
-
return redirectOrMakeHandler(request, h, (page, context) => {
|
|
64
|
+
export function makePostHandler(preparePageEventRequestOptions) {
|
|
65
|
+
return function postHandler(request, h) {
|
|
66
66
|
const {
|
|
67
|
-
|
|
68
|
-
} =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
query
|
|
68
|
+
} = request;
|
|
69
|
+
return redirectOrMakeHandler(request, h, async (page, context) => {
|
|
70
|
+
const {
|
|
71
|
+
pageDef
|
|
72
|
+
} = page;
|
|
73
|
+
const {
|
|
74
|
+
isForceAccess
|
|
75
|
+
} = context;
|
|
76
|
+
const {
|
|
77
|
+
model
|
|
78
|
+
} = request.app;
|
|
79
|
+
const {
|
|
80
|
+
events
|
|
81
|
+
} = page;
|
|
72
82
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
// Redirect to GET for preview URL direct access
|
|
84
|
+
if (isForceAccess && !hasFormComponents(pageDef)) {
|
|
85
|
+
return proceed(request, h, redirectPath(page.href, query));
|
|
86
|
+
}
|
|
87
|
+
if (!model) {
|
|
88
|
+
throw Boom.notFound(`No model found for /${request.params.path}`);
|
|
89
|
+
}
|
|
90
|
+
const response = await page.makePostRouteHandler()(request, context, h);
|
|
91
|
+
if (events?.onSave && events.onSave.type === 'http' && isSuccessful(response)) {
|
|
92
|
+
await handleHttpEvent(request, page, context, events.onSave, model, preparePageEventRequestOptions);
|
|
93
|
+
}
|
|
94
|
+
return response;
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function isSuccessful(response) {
|
|
99
|
+
const {
|
|
100
|
+
statusCode
|
|
101
|
+
} = response;
|
|
102
|
+
return !Boom.isBoom(response) && statusCode >= 200 && statusCode < 400;
|
|
79
103
|
}
|
|
80
104
|
export function getRoutes(getRouteOptions, postRouteOptions, preparePageEventRequestOptions) {
|
|
81
105
|
return [{
|
|
@@ -135,7 +159,7 @@ export function getRoutes(getRouteOptions, postRouteOptions, preparePageEventReq
|
|
|
135
159
|
}, {
|
|
136
160
|
method: 'post',
|
|
137
161
|
path: '/{slug}/{path}/{itemId?}',
|
|
138
|
-
handler:
|
|
162
|
+
handler: makePostHandler(preparePageEventRequestOptions),
|
|
139
163
|
options: {
|
|
140
164
|
...postRouteOptions,
|
|
141
165
|
validate: {
|
|
@@ -153,7 +177,7 @@ export function getRoutes(getRouteOptions, postRouteOptions, preparePageEventReq
|
|
|
153
177
|
}, {
|
|
154
178
|
method: 'post',
|
|
155
179
|
path: '/preview/{state}/{slug}/{path}/{itemId?}',
|
|
156
|
-
handler:
|
|
180
|
+
handler: makePostHandler(preparePageEventRequestOptions),
|
|
157
181
|
options: {
|
|
158
182
|
...postRouteOptions,
|
|
159
183
|
validate: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"questions.js","names":["hasFormComponents","slugSchema","Boom","Joi","normalisePath","proceed","redirectPath","SummaryViewModel","format","getFormSubmissionData","dispatchHandler","redirectOrMakeHandler","actionSchema","crumbSchema","itemIdSchema","pathSchema","stateSchema","httpService","makeGetHandler","preparePageEventRequestOptions","getHandler","request","h","params","path","page","context","events","model","app","notFound","onLoad","type","options","url","viewModel","items","details","payload","undefined","opts","response","postJson","Object","assign","data","makeGetRouteHandler","postHandler","query","pageDef","isForceAccess","href","makePostRouteHandler","getRoutes","getRouteOptions","postRouteOptions","method","handler","validate","object","keys","slug","state","itemId","optional","crumb","action","unknown","required"],"sources":["../../../../../src/server/plugins/engine/routes/questions.ts"],"sourcesContent":["import { hasFormComponents, slugSchema } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport {\n type ResponseToolkit,\n type RouteOptions,\n type ServerRoute\n} from '@hapi/hapi'\nimport Joi from 'joi'\n\nimport {\n normalisePath,\n proceed,\n redirectPath\n} from '~/src/server/plugins/engine/helpers.js'\nimport { SummaryViewModel } from '~/src/server/plugins/engine/models/index.js'\nimport { format } from '~/src/server/plugins/engine/outputFormatters/machine/v1.js'\nimport { getFormSubmissionData } from '~/src/server/plugins/engine/pageControllers/SummaryPageController.js'\nimport {\n dispatchHandler,\n redirectOrMakeHandler\n} from '~/src/server/plugins/engine/routes/index.js'\nimport { type PreparePageEventRequestOptions } from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport {\n actionSchema,\n crumbSchema,\n itemIdSchema,\n pathSchema,\n stateSchema\n} from '~/src/server/schemas/index.js'\nimport * as httpService from '~/src/server/services/httpService.js'\n\nfunction makeGetHandler(\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n) {\n return function getHandler(\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) {\n const { params } = request\n\n if (normalisePath(params.path) === '') {\n return dispatchHandler(request, h)\n }\n\n return redirectOrMakeHandler(request, h, async (page, context) => {\n // Check for a page onLoad HTTP event and if one exists,\n // call it and assign the response to the context data\n const { events } = page\n const { model } = request.app\n\n if (!model) {\n throw Boom.notFound(`No model found for /${params.path}`)\n }\n\n if (events?.onLoad && events.onLoad.type === 'http') {\n const { options } = events.onLoad\n const { url } = options\n\n // TODO: Update structured data POST payload with when helper\n // is updated to removing the dependency on `SummaryViewModel` etc.\n const viewModel = new SummaryViewModel(request, page, context)\n const items = getFormSubmissionData(\n viewModel.context,\n viewModel.details\n )\n\n // @ts-expect-error - function signature will be refactored in the next iteration of the formatter\n const payload = format(context, items, model, undefined, undefined)\n const opts = { payload }\n\n if (preparePageEventRequestOptions) {\n preparePageEventRequestOptions(opts, events.onLoad, page, context)\n }\n\n const { payload: response } = await httpService.postJson(url, opts)\n\n Object.assign(context.data, response)\n }\n\n return page.makeGetRouteHandler()(request, context, h)\n })\n }\n}\n\nfunction postHandler(\n request: FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n) {\n const { query } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n const { pageDef } = page\n const { isForceAccess } = context\n\n // Redirect to GET for preview URL direct access\n if (isForceAccess && !hasFormComponents(pageDef)) {\n return proceed(request, h, redirectPath(page.href, query))\n }\n\n return page.makePostRouteHandler()(request, context, h)\n })\n}\n\nexport function getRoutes(\n getRouteOptions: RouteOptions<FormRequestRefs>,\n postRouteOptions: RouteOptions<FormRequestPayloadRefs>,\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n): (ServerRoute<FormRequestRefs> | ServerRoute<FormRequestPayloadRefs>)[] {\n return [\n {\n method: 'get',\n path: '/{slug}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema\n })\n }\n }\n },\n {\n method: 'get',\n path: '/preview/{state}/{slug}',\n handler: dispatchHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema\n })\n }\n }\n },\n {\n method: 'get',\n path: '/{slug}/{path}/{itemId?}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n },\n {\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n },\n {\n method: 'post',\n path: '/{slug}/{path}/{itemId?}',\n handler: postHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n },\n {\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: postHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n }\n ]\n}\n"],"mappings":"AAAA,SAASA,iBAAiB,EAAEC,UAAU,QAAQ,oBAAoB;AAClE,OAAOC,IAAI,MAAM,YAAY;AAM7B,OAAOC,GAAG,MAAM,KAAK;AAErB,SACEC,aAAa,EACbC,OAAO,EACPC,YAAY;AAEd,SAASC,gBAAgB;AACzB,SAASC,MAAM;AACf,SAASC,qBAAqB;AAC9B,SACEC,eAAe,EACfC,qBAAqB;AASvB,SACEC,YAAY,EACZC,WAAW,EACXC,YAAY,EACZC,UAAU,EACVC,WAAW;AAEb,OAAO,KAAKC,WAAW;AAEvB,SAASC,cAAcA,CACrBC,8BAA+D,EAC/D;EACA,OAAO,SAASC,UAAUA,CACxBC,OAAoB,EACpBC,CAA6C,EAC7C;IACA,MAAM;MAAEC;IAAO,CAAC,GAAGF,OAAO;IAE1B,IAAIjB,aAAa,CAACmB,MAAM,CAACC,IAAI,CAAC,KAAK,EAAE,EAAE;MACrC,OAAOd,eAAe,CAACW,OAAO,EAAEC,CAAC,CAAC;IACpC;IAEA,OAAOX,qBAAqB,CAACU,OAAO,EAAEC,CAAC,EAAE,OAAOG,IAAI,EAAEC,OAAO,KAAK;MAChE;MACA;MACA,MAAM;QAAEC;MAAO,CAAC,GAAGF,IAAI;MACvB,MAAM;QAAEG;MAAM,CAAC,GAAGP,OAAO,CAACQ,GAAG;MAE7B,IAAI,CAACD,KAAK,EAAE;QACV,MAAM1B,IAAI,CAAC4B,QAAQ,CAAC,uBAAuBP,MAAM,CAACC,IAAI,EAAE,CAAC;MAC3D;MAEA,IAAIG,MAAM,EAAEI,MAAM,IAAIJ,MAAM,CAACI,MAAM,CAACC,IAAI,KAAK,MAAM,EAAE;QACnD,MAAM;UAAEC;QAAQ,CAAC,GAAGN,MAAM,CAACI,MAAM;QACjC,MAAM;UAAEG;QAAI,CAAC,GAAGD,OAAO;;QAEvB;QACA;QACA,MAAME,SAAS,GAAG,IAAI5B,gBAAgB,CAACc,OAAO,EAAEI,IAAI,EAAEC,OAAO,CAAC;QAC9D,MAAMU,KAAK,GAAG3B,qBAAqB,CACjC0B,SAAS,CAACT,OAAO,EACjBS,SAAS,CAACE,OACZ,CAAC;;QAED;QACA,MAAMC,OAAO,GAAG9B,MAAM,CAACkB,OAAO,EAAEU,KAAK,EAAER,KAAK,EAAEW,SAAS,EAAEA,SAAS,CAAC;QACnE,MAAMC,IAAI,GAAG;UAAEF;QAAQ,CAAC;QAExB,IAAInB,8BAA8B,EAAE;UAClCA,8BAA8B,CAACqB,IAAI,EAAEb,MAAM,CAACI,MAAM,EAAEN,IAAI,EAAEC,OAAO,CAAC;QACpE;QAEA,MAAM;UAAEY,OAAO,EAAEG;QAAS,CAAC,GAAG,MAAMxB,WAAW,CAACyB,QAAQ,CAACR,GAAG,EAAEM,IAAI,CAAC;QAEnEG,MAAM,CAACC,MAAM,CAAClB,OAAO,CAACmB,IAAI,EAAEJ,QAAQ,CAAC;MACvC;MAEA,OAAOhB,IAAI,CAACqB,mBAAmB,CAAC,CAAC,CAACzB,OAAO,EAAEK,OAAO,EAAEJ,CAAC,CAAC;IACxD,CAAC,CAAC;EACJ,CAAC;AACH;AAEA,SAASyB,WAAWA,CAClB1B,OAA2B,EAC3BC,CAA6C,EAC7C;EACA,MAAM;IAAE0B;EAAM,CAAC,GAAG3B,OAAO;EAEzB,OAAOV,qBAAqB,CAACU,OAAO,EAAEC,CAAC,EAAE,CAACG,IAAI,EAAEC,OAAO,KAAK;IAC1D,MAAM;MAAEuB;IAAQ,CAAC,GAAGxB,IAAI;IACxB,MAAM;MAAEyB;IAAc,CAAC,GAAGxB,OAAO;;IAEjC;IACA,IAAIwB,aAAa,IAAI,CAAClD,iBAAiB,CAACiD,OAAO,CAAC,EAAE;MAChD,OAAO5C,OAAO,CAACgB,OAAO,EAAEC,CAAC,EAAEhB,YAAY,CAACmB,IAAI,CAAC0B,IAAI,EAAEH,KAAK,CAAC,CAAC;IAC5D;IAEA,OAAOvB,IAAI,CAAC2B,oBAAoB,CAAC,CAAC,CAAC/B,OAAO,EAAEK,OAAO,EAAEJ,CAAC,CAAC;EACzD,CAAC,CAAC;AACJ;AAEA,OAAO,SAAS+B,SAASA,CACvBC,eAA8C,EAC9CC,gBAAsD,EACtDpC,8BAA+D,EACS;EACxE,OAAO,CACL;IACEqC,MAAM,EAAE,KAAK;IACbhC,IAAI,EAAE,SAAS;IACfiC,OAAO,EAAEvC,cAAc,CAACC,8BAA8B,CAAC;IACvDc,OAAO,EAAE;MACP,GAAGqB,eAAe;MAClBI,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBC,IAAI,EAAE5D;QACR,CAAC;MACH;IACF;EACF,CAAC,EACD;IACEuD,MAAM,EAAE,KAAK;IACbhC,IAAI,EAAE,yBAAyB;IAC/BiC,OAAO,EAAE/C,eAAe;IACxBuB,OAAO,EAAE;MACP,GAAGqB,eAAe;MAClBI,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBE,KAAK,EAAE9C,WAAW;UAClB6C,IAAI,EAAE5D;QACR,CAAC;MACH;IACF;EACF,CAAC,EACD;IACEuD,MAAM,EAAE,KAAK;IACbhC,IAAI,EAAE,0BAA0B;IAChCiC,OAAO,EAAEvC,cAAc,CAACC,8BAA8B,CAAC;IACvDc,OAAO,EAAE;MACP,GAAGqB,eAAe;MAClBI,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBC,IAAI,EAAE5D,UAAU;UAChBuB,IAAI,EAAET,UAAU;UAChBgD,MAAM,EAAEjD,YAAY,CAACkD,QAAQ,CAAC;QAChC,CAAC;MACH;IACF;EACF,CAAC,EACD;IACER,MAAM,EAAE,KAAK;IACbhC,IAAI,EAAE,0CAA0C;IAChDiC,OAAO,EAAEvC,cAAc,CAACC,8BAA8B,CAAC;IACvDc,OAAO,EAAE;MACP,GAAGqB,eAAe;MAClBI,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBE,KAAK,EAAE9C,WAAW;UAClB6C,IAAI,EAAE5D,UAAU;UAChBuB,IAAI,EAAET,UAAU;UAChBgD,MAAM,EAAEjD,YAAY,CAACkD,QAAQ,CAAC;QAChC,CAAC;MACH;IACF;EACF,CAAC,EACD;IACER,MAAM,EAAE,MAAM;IACdhC,IAAI,EAAE,0BAA0B;IAChCiC,OAAO,EAAEV,WAAW;IACpBd,OAAO,EAAE;MACP,GAAGsB,gBAAgB;MACnBG,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBC,IAAI,EAAE5D,UAAU;UAChBuB,IAAI,EAAET,UAAU;UAChBgD,MAAM,EAAEjD,YAAY,CAACkD,QAAQ,CAAC;QAChC,CAAC,CAAC;QACF1B,OAAO,EAAEnC,GAAG,CAACwD,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;UACJK,KAAK,EAAEpD,WAAW;UAClBqD,MAAM,EAAEtD;QACV,CAAC,CAAC,CACDuD,OAAO,CAAC,IAAI,CAAC,CACbC,QAAQ,CAAC;MACd;IACF;EACF,CAAC,EACD;IACEZ,MAAM,EAAE,MAAM;IACdhC,IAAI,EAAE,0CAA0C;IAChDiC,OAAO,EAAEV,WAAW;IACpBd,OAAO,EAAE;MACP,GAAGsB,gBAAgB;MACnBG,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBE,KAAK,EAAE9C,WAAW;UAClB6C,IAAI,EAAE5D,UAAU;UAChBuB,IAAI,EAAET,UAAU;UAChBgD,MAAM,EAAEjD,YAAY,CAACkD,QAAQ,CAAC;QAChC,CAAC,CAAC;QACF1B,OAAO,EAAEnC,GAAG,CAACwD,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;UACJK,KAAK,EAAEpD,WAAW;UAClBqD,MAAM,EAAEtD;QACV,CAAC,CAAC,CACDuD,OAAO,CAAC,IAAI,CAAC,CACbC,QAAQ,CAAC;MACd;IACF;EACF,CAAC,CACF;AACH","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"questions.js","names":["hasFormComponents","slugSchema","Boom","Joi","normalisePath","proceed","redirectPath","SummaryViewModel","format","getFormSubmissionData","dispatchHandler","redirectOrMakeHandler","actionSchema","crumbSchema","itemIdSchema","pathSchema","stateSchema","httpService","handleHttpEvent","request","page","context","event","model","preparePageEventRequestOptions","options","url","viewModel","items","details","payload","undefined","opts","response","postJson","Object","assign","data","makeGetHandler","getHandler","h","params","path","events","app","notFound","onLoad","type","makeGetRouteHandler","makePostHandler","postHandler","query","pageDef","isForceAccess","href","makePostRouteHandler","onSave","isSuccessful","statusCode","isBoom","getRoutes","getRouteOptions","postRouteOptions","method","handler","validate","object","keys","slug","state","itemId","optional","crumb","action","unknown","required"],"sources":["../../../../../src/server/plugins/engine/routes/questions.ts"],"sourcesContent":["import { hasFormComponents, slugSchema, type Event } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport {\n type ResponseObject,\n type ResponseToolkit,\n type RouteOptions,\n type ServerRoute\n} from '@hapi/hapi'\nimport Joi from 'joi'\n\nimport {\n normalisePath,\n proceed,\n redirectPath\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n SummaryViewModel,\n type FormModel\n} from '~/src/server/plugins/engine/models/index.js'\nimport { format } from '~/src/server/plugins/engine/outputFormatters/machine/v1.js'\nimport { getFormSubmissionData } from '~/src/server/plugins/engine/pageControllers/SummaryPageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport {\n dispatchHandler,\n redirectOrMakeHandler\n} from '~/src/server/plugins/engine/routes/index.js'\nimport {\n type FormContext,\n type PreparePageEventRequestOptions\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport {\n actionSchema,\n crumbSchema,\n itemIdSchema,\n pathSchema,\n stateSchema\n} from '~/src/server/schemas/index.js'\nimport * as httpService from '~/src/server/services/httpService.js'\n\nasync function handleHttpEvent(\n request: FormRequest | FormRequestPayload,\n page: PageControllerClass,\n context: FormContext,\n event: Event,\n model: FormModel,\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n) {\n const { options } = event\n const { url } = options\n\n // TODO: Update structured data POST payload with when helper\n // is updated to removing the dependency on `SummaryViewModel` etc.\n const viewModel = new SummaryViewModel(request, page, context)\n const items = getFormSubmissionData(viewModel.context, viewModel.details)\n\n // @ts-expect-error - function signature will be refactored in the next iteration of the formatter\n const payload = format(context, items, model, undefined, undefined)\n const opts = { payload }\n\n if (preparePageEventRequestOptions) {\n preparePageEventRequestOptions(opts, event, page, context)\n }\n\n const { payload: response } = await httpService.postJson(url, opts)\n\n Object.assign(context.data, response)\n}\n\nexport function makeGetHandler(\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n) {\n return function getHandler(\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) {\n const { params } = request\n\n if (normalisePath(params.path) === '') {\n return dispatchHandler(request, h)\n }\n\n return redirectOrMakeHandler(request, h, async (page, context) => {\n // Check for a page onLoad HTTP event and if one exists,\n // call it and assign the response to the context data\n const { events } = page\n const { model } = request.app\n\n if (!model) {\n throw Boom.notFound(`No model found for /${params.path}`)\n }\n\n if (events?.onLoad && events.onLoad.type === 'http') {\n await handleHttpEvent(\n request,\n page,\n context,\n events.onLoad,\n model,\n preparePageEventRequestOptions\n )\n }\n\n return page.makeGetRouteHandler()(request, context, h)\n })\n }\n}\n\nexport function makePostHandler(\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n) {\n return function postHandler(\n request: FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) {\n const { query } = request\n\n return redirectOrMakeHandler(request, h, async (page, context) => {\n const { pageDef } = page\n const { isForceAccess } = context\n const { model } = request.app\n const { events } = page\n\n // Redirect to GET for preview URL direct access\n if (isForceAccess && !hasFormComponents(pageDef)) {\n return proceed(request, h, redirectPath(page.href, query))\n }\n\n if (!model) {\n throw Boom.notFound(`No model found for /${request.params.path}`)\n }\n\n const response = await page.makePostRouteHandler()(request, context, h)\n\n if (\n events?.onSave &&\n events.onSave.type === 'http' &&\n isSuccessful(response)\n ) {\n await handleHttpEvent(\n request,\n page,\n context,\n events.onSave,\n model,\n preparePageEventRequestOptions\n )\n }\n\n return response\n })\n }\n}\n\nfunction isSuccessful(response: ResponseObject): boolean {\n const { statusCode } = response\n\n return !Boom.isBoom(response) && statusCode >= 200 && statusCode < 400\n}\n\nexport function getRoutes(\n getRouteOptions: RouteOptions<FormRequestRefs>,\n postRouteOptions: RouteOptions<FormRequestPayloadRefs>,\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n): (ServerRoute<FormRequestRefs> | ServerRoute<FormRequestPayloadRefs>)[] {\n return [\n {\n method: 'get',\n path: '/{slug}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema\n })\n }\n }\n },\n {\n method: 'get',\n path: '/preview/{state}/{slug}',\n handler: dispatchHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema\n })\n }\n }\n },\n {\n method: 'get',\n path: '/{slug}/{path}/{itemId?}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n },\n {\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n },\n {\n method: 'post',\n path: '/{slug}/{path}/{itemId?}',\n handler: makePostHandler(preparePageEventRequestOptions),\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n },\n {\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: makePostHandler(preparePageEventRequestOptions),\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n }\n ]\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -32,20 +32,18 @@ export const formsService = async () => {
|
|
|
32
32
|
// Instantiate the file loader form service
|
|
33
33
|
const loader = new FileFormService();
|
|
34
34
|
|
|
35
|
-
// Add a
|
|
36
|
-
await loader.addForm('src/server/forms/register-as-a-unicorn-breeder.
|
|
35
|
+
// Add a Yaml form
|
|
36
|
+
await loader.addForm('src/server/forms/register-as-a-unicorn-breeder.yaml', {
|
|
37
37
|
...metadata,
|
|
38
|
-
id: '
|
|
38
|
+
id: '641aeafd-13dd-40fa-9186-001703800efb',
|
|
39
39
|
title: 'Register as a unicorn breeder',
|
|
40
40
|
slug: 'register-as-a-unicorn-breeder'
|
|
41
41
|
});
|
|
42
|
-
|
|
43
|
-
// Add a Yaml form
|
|
44
|
-
await loader.addForm('src/server/forms/register-as-a-unicorn-breeder.yaml', {
|
|
42
|
+
await loader.addForm('src/server/forms/page-events.yaml', {
|
|
45
43
|
...metadata,
|
|
46
|
-
id: '
|
|
47
|
-
title: '
|
|
48
|
-
slug: '
|
|
44
|
+
id: '511db05e-ebbd-42e8-8270-5fe93f5c9762',
|
|
45
|
+
title: 'Page events demo',
|
|
46
|
+
slug: 'page-events-demo'
|
|
49
47
|
});
|
|
50
48
|
await loader.addForm('src/server/forms/components.json', {
|
|
51
49
|
...metadata,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localFormsService.js","names":["config","FileFormService","now","Date","user","id","displayName","author","createdAt","createdBy","updatedAt","updatedBy","metadata","organisation","teamName","teamEmail","submissionGuidance","notificationEmail","get","live","formsService","loader","addForm","title","slug","toFormsService"],"sources":["../../../../../src/server/plugins/engine/services/localFormsService.js"],"sourcesContent":["import { config } from '~/src/config/index.js'\nimport { FileFormService } from '~/src/server/utils/file-form-service.js'\n\n// Create shared form metadata\nconst now = new Date()\nconst user = { id: 'user', displayName: 'Username' }\nconst author = {\n createdAt: now,\n createdBy: user,\n updatedAt: now,\n updatedBy: user\n}\nconst metadata = {\n organisation: 'Defra',\n teamName: 'Team name',\n teamEmail: 'team@defra.gov.uk',\n submissionGuidance: \"Thanks for your submission, we'll be in touch\",\n notificationEmail: config.get('submissionEmailAddress'),\n ...author,\n live: author\n}\n\n/**\n * Return an function rather than the service directly. This is to prevent consumer applications\n * blowing up as they won't have these files on disk. We can defer the execution until when it's\n * needed, i.e. the createServer function of the devtool.\n */\nexport const formsService = async () => {\n // Instantiate the file loader form service\n const loader = new FileFormService()\n\n // Add a
|
|
1
|
+
{"version":3,"file":"localFormsService.js","names":["config","FileFormService","now","Date","user","id","displayName","author","createdAt","createdBy","updatedAt","updatedBy","metadata","organisation","teamName","teamEmail","submissionGuidance","notificationEmail","get","live","formsService","loader","addForm","title","slug","toFormsService"],"sources":["../../../../../src/server/plugins/engine/services/localFormsService.js"],"sourcesContent":["import { config } from '~/src/config/index.js'\nimport { FileFormService } from '~/src/server/utils/file-form-service.js'\n\n// Create shared form metadata\nconst now = new Date()\nconst user = { id: 'user', displayName: 'Username' }\nconst author = {\n createdAt: now,\n createdBy: user,\n updatedAt: now,\n updatedBy: user\n}\nconst metadata = {\n organisation: 'Defra',\n teamName: 'Team name',\n teamEmail: 'team@defra.gov.uk',\n submissionGuidance: \"Thanks for your submission, we'll be in touch\",\n notificationEmail: config.get('submissionEmailAddress'),\n ...author,\n live: author\n}\n\n/**\n * Return an function rather than the service directly. This is to prevent consumer applications\n * blowing up as they won't have these files on disk. We can defer the execution until when it's\n * needed, i.e. the createServer function of the devtool.\n */\nexport const formsService = async () => {\n // Instantiate the file loader form service\n const loader = new FileFormService()\n\n // Add a Yaml form\n await loader.addForm('src/server/forms/register-as-a-unicorn-breeder.yaml', {\n ...metadata,\n id: '641aeafd-13dd-40fa-9186-001703800efb',\n title: 'Register as a unicorn breeder',\n slug: 'register-as-a-unicorn-breeder'\n })\n\n await loader.addForm('src/server/forms/page-events.yaml', {\n ...metadata,\n id: '511db05e-ebbd-42e8-8270-5fe93f5c9762',\n title: 'Page events demo',\n slug: 'page-events-demo'\n })\n\n await loader.addForm('src/server/forms/components.json', {\n ...metadata,\n id: '6a872d3b-13f9e-804ce3e-4830-5c45fb32',\n title: 'Components',\n slug: 'components'\n })\n\n return loader.toFormsService()\n}\n"],"mappings":"AAAA,SAASA,MAAM;AACf,SAASC,eAAe;;AAExB;AACA,MAAMC,GAAG,GAAG,IAAIC,IAAI,CAAC,CAAC;AACtB,MAAMC,IAAI,GAAG;EAAEC,EAAE,EAAE,MAAM;EAAEC,WAAW,EAAE;AAAW,CAAC;AACpD,MAAMC,MAAM,GAAG;EACbC,SAAS,EAAEN,GAAG;EACdO,SAAS,EAAEL,IAAI;EACfM,SAAS,EAAER,GAAG;EACdS,SAAS,EAAEP;AACb,CAAC;AACD,MAAMQ,QAAQ,GAAG;EACfC,YAAY,EAAE,OAAO;EACrBC,QAAQ,EAAE,WAAW;EACrBC,SAAS,EAAE,mBAAmB;EAC9BC,kBAAkB,EAAE,+CAA+C;EACnEC,iBAAiB,EAAEjB,MAAM,CAACkB,GAAG,CAAC,wBAAwB,CAAC;EACvD,GAAGX,MAAM;EACTY,IAAI,EAAEZ;AACR,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMa,YAAY,GAAG,MAAAA,CAAA,KAAY;EACtC;EACA,MAAMC,MAAM,GAAG,IAAIpB,eAAe,CAAC,CAAC;;EAEpC;EACA,MAAMoB,MAAM,CAACC,OAAO,CAAC,qDAAqD,EAAE;IAC1E,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,+BAA+B;IACtCC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,MAAMH,MAAM,CAACC,OAAO,CAAC,mCAAmC,EAAE;IACxD,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,kBAAkB;IACzBC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,MAAMH,MAAM,CAACC,OAAO,CAAC,kCAAkC,EAAE;IACvD,GAAGV,QAAQ;IACXP,EAAE,EAAE,sCAAsC;IAC1CkB,KAAK,EAAE,YAAY;IACnBC,IAAI,EAAE;EACR,CAAC,CAAC;EAEF,OAAOH,MAAM,CAACI,cAAc,CAAC,CAAC;AAChC,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type Request, type ResponseToolkit } from '@hapi/hapi';
|
|
2
|
+
declare const _default: ({
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
handler(request: Request<{
|
|
6
|
+
Payload: {
|
|
7
|
+
meta: {
|
|
8
|
+
referenceNumber: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
}>, _h: ResponseToolkit): {
|
|
12
|
+
submissionEvent: string;
|
|
13
|
+
submissionReferenceNumber: string;
|
|
14
|
+
};
|
|
15
|
+
} | {
|
|
16
|
+
method: string;
|
|
17
|
+
path: string;
|
|
18
|
+
handler(request: Request<{
|
|
19
|
+
Payload: {
|
|
20
|
+
data: {
|
|
21
|
+
main: {
|
|
22
|
+
applicantFirstName: string;
|
|
23
|
+
applicantLastName: string;
|
|
24
|
+
dateOfBirth: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
meta: {
|
|
28
|
+
event: string;
|
|
29
|
+
referenceNumber: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
}>, _h: ResponseToolkit): {
|
|
33
|
+
calculatedAge: number;
|
|
34
|
+
submissionEvent: string;
|
|
35
|
+
submissionReferenceNumber: string;
|
|
36
|
+
};
|
|
37
|
+
})[];
|
|
38
|
+
export default _default;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
function calculateAge(day, month, year) {
|
|
2
|
+
const dobDate = new Date(Number(day), Number(month) - 1, Number(year));
|
|
3
|
+
const today = new Date();
|
|
4
|
+
let age = today.getFullYear() - dobDate.getFullYear();
|
|
5
|
+
const m = today.getMonth() - dobDate.getMonth();
|
|
6
|
+
if (m < 0 || m === 0 && today.getDate() < dobDate.getDate()) {
|
|
7
|
+
age--;
|
|
8
|
+
}
|
|
9
|
+
return age;
|
|
10
|
+
}
|
|
11
|
+
export default [{
|
|
12
|
+
method: 'POST',
|
|
13
|
+
path: '/api/example/on-load-page',
|
|
14
|
+
handler(request, _h) {
|
|
15
|
+
return {
|
|
16
|
+
submissionEvent: 'GET',
|
|
17
|
+
submissionReferenceNumber: request.payload.meta.referenceNumber
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
path: '/api/example/on-summary',
|
|
23
|
+
handler(request, _h) {
|
|
24
|
+
const [day, month, year] = request.payload.data.main.dateOfBirth.split('-');
|
|
25
|
+
const age = calculateAge(day, month, year);
|
|
26
|
+
return {
|
|
27
|
+
calculatedAge: age,
|
|
28
|
+
submissionEvent: 'POST',
|
|
29
|
+
submissionReferenceNumber: request.payload.meta.referenceNumber // example of receiving a payload from DXT
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}];
|
|
33
|
+
//# sourceMappingURL=dummy-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dummy-api.js","names":["calculateAge","day","month","year","dobDate","Date","Number","today","age","getFullYear","m","getMonth","getDate","method","path","handler","request","_h","submissionEvent","submissionReferenceNumber","payload","meta","referenceNumber","data","main","dateOfBirth","split","calculatedAge"],"sources":["../../../src/server/routes/dummy-api.ts"],"sourcesContent":["import { type Request, type ResponseToolkit } from '@hapi/hapi'\n\nfunction calculateAge(day: string, month: string, year: string) {\n const dobDate = new Date(Number(day), Number(month) - 1, Number(year))\n\n const today = new Date()\n\n let age = today.getFullYear() - dobDate.getFullYear()\n const m = today.getMonth() - dobDate.getMonth()\n\n if (m < 0 || (m === 0 && today.getDate() < dobDate.getDate())) {\n age--\n }\n\n return age\n}\n\nexport default [\n {\n method: 'POST',\n path: '/api/example/on-load-page',\n handler(\n request: Request<{ Payload: { meta: { referenceNumber: string } } }>,\n _h: ResponseToolkit\n ) {\n return {\n submissionEvent: 'GET',\n submissionReferenceNumber: request.payload.meta.referenceNumber\n }\n }\n },\n {\n method: 'POST',\n path: '/api/example/on-summary',\n handler(\n request: Request<{\n Payload: {\n data: {\n main: {\n applicantFirstName: string\n applicantLastName: string\n dateOfBirth: string\n }\n }\n meta: { event: string; referenceNumber: string }\n }\n }>,\n _h: ResponseToolkit\n ) {\n const [day, month, year] =\n request.payload.data.main.dateOfBirth.split('-')\n\n const age = calculateAge(day, month, year)\n\n return {\n calculatedAge: age,\n submissionEvent: 'POST',\n submissionReferenceNumber: request.payload.meta.referenceNumber // example of receiving a payload from DXT\n }\n }\n }\n]\n"],"mappings":"AAEA,SAASA,YAAYA,CAACC,GAAW,EAAEC,KAAa,EAAEC,IAAY,EAAE;EAC9D,MAAMC,OAAO,GAAG,IAAIC,IAAI,CAACC,MAAM,CAACL,GAAG,CAAC,EAAEK,MAAM,CAACJ,KAAK,CAAC,GAAG,CAAC,EAAEI,MAAM,CAACH,IAAI,CAAC,CAAC;EAEtE,MAAMI,KAAK,GAAG,IAAIF,IAAI,CAAC,CAAC;EAExB,IAAIG,GAAG,GAAGD,KAAK,CAACE,WAAW,CAAC,CAAC,GAAGL,OAAO,CAACK,WAAW,CAAC,CAAC;EACrD,MAAMC,CAAC,GAAGH,KAAK,CAACI,QAAQ,CAAC,CAAC,GAAGP,OAAO,CAACO,QAAQ,CAAC,CAAC;EAE/C,IAAID,CAAC,GAAG,CAAC,IAAKA,CAAC,KAAK,CAAC,IAAIH,KAAK,CAACK,OAAO,CAAC,CAAC,GAAGR,OAAO,CAACQ,OAAO,CAAC,CAAE,EAAE;IAC7DJ,GAAG,EAAE;EACP;EAEA,OAAOA,GAAG;AACZ;AAEA,eAAe,CACb;EACEK,MAAM,EAAE,MAAM;EACdC,IAAI,EAAE,2BAA2B;EACjCC,OAAOA,CACLC,OAAoE,EACpEC,EAAmB,EACnB;IACA,OAAO;MACLC,eAAe,EAAE,KAAK;MACtBC,yBAAyB,EAAEH,OAAO,CAACI,OAAO,CAACC,IAAI,CAACC;IAClD,CAAC;EACH;AACF,CAAC,EACD;EACET,MAAM,EAAE,MAAM;EACdC,IAAI,EAAE,yBAAyB;EAC/BC,OAAOA,CACLC,OAWE,EACFC,EAAmB,EACnB;IACA,MAAM,CAAChB,GAAG,EAAEC,KAAK,EAAEC,IAAI,CAAC,GACtBa,OAAO,CAACI,OAAO,CAACG,IAAI,CAACC,IAAI,CAACC,WAAW,CAACC,KAAK,CAAC,GAAG,CAAC;IAElD,MAAMlB,GAAG,GAAGR,YAAY,CAACC,GAAG,EAAEC,KAAK,EAAEC,IAAI,CAAC;IAE1C,OAAO;MACLwB,aAAa,EAAEnB,GAAG;MAClBU,eAAe,EAAE,MAAM;MACvBC,yBAAyB,EAAEH,OAAO,CAACI,OAAO,CAACC,IAAI,CAACC,eAAe,CAAC;IAClE,CAAC;EACH;AACF,CAAC,CACF","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["default","publicRoutes"],"sources":["../../../src/server/routes/index.ts"],"sourcesContent":["export { default as publicRoutes } from '~/src/server/routes/public.js'\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,YAAY","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"index.js","names":["default","publicRoutes","dummyApiRoutes"],"sources":["../../../src/server/routes/index.ts"],"sourcesContent":["export { default as publicRoutes } from '~/src/server/routes/public.js'\nexport { default as dummyApiRoutes } from '~/src/server/routes/dummy-api.js'\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,YAAY;AAChC,SAASD,OAAO,IAAIE,cAAc","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra/forms-engine-plugin",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Defra forms engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -168,6 +168,8 @@
|
|
|
168
168
|
"jest-extended": "^4.0.2",
|
|
169
169
|
"jsdom": "^26.1.0",
|
|
170
170
|
"lint-staged": "^15.3.0",
|
|
171
|
+
"mockdate": "^3.0.5",
|
|
172
|
+
"nock": "^14.0.8",
|
|
171
173
|
"postcss": "^8.5.6",
|
|
172
174
|
"postcss-load-config": "^6.0.1",
|
|
173
175
|
"postcss-loader": "^8.1.1",
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Page events
|
|
3
|
+
engine: V2
|
|
4
|
+
schema: 2
|
|
5
|
+
startPage: '/summary'
|
|
6
|
+
pages:
|
|
7
|
+
- title: Your name
|
|
8
|
+
path: '/your-name'
|
|
9
|
+
components:
|
|
10
|
+
- type: TextField
|
|
11
|
+
title: What is your first name?
|
|
12
|
+
name: applicantFirstName
|
|
13
|
+
shortDescription: Your first name
|
|
14
|
+
hint: ''
|
|
15
|
+
options:
|
|
16
|
+
required: true
|
|
17
|
+
schema: {}
|
|
18
|
+
id: 1fb8e182-c709-4792-8f83-e01d8b1fee1a
|
|
19
|
+
- type: TextField
|
|
20
|
+
title: What is your last name?
|
|
21
|
+
name: applicantLastName
|
|
22
|
+
shortDescription: Your last name
|
|
23
|
+
hint: ''
|
|
24
|
+
options:
|
|
25
|
+
required: true
|
|
26
|
+
schema: {}
|
|
27
|
+
id: b68df7f1-d4f4-4c17-83c8-402f584906c9
|
|
28
|
+
next: []
|
|
29
|
+
id: 622a35ec-3795-418a-81f3-a45746959045
|
|
30
|
+
- title: ''
|
|
31
|
+
path: '/date-of-birth'
|
|
32
|
+
events:
|
|
33
|
+
onLoad:
|
|
34
|
+
type: http
|
|
35
|
+
options:
|
|
36
|
+
method: 'POST'
|
|
37
|
+
url: http://localhost:3009/api/example/on-load-page
|
|
38
|
+
components:
|
|
39
|
+
- type: Html
|
|
40
|
+
# technically context.data.submissionReferenceNumber is redundant as the reference number is available locally as context.referenceNumber
|
|
41
|
+
# but this is to demonstrate that the server can send data back to the client
|
|
42
|
+
content: >
|
|
43
|
+
<p class="govuk-body">
|
|
44
|
+
The backend received a full copy of the form state even though you haven't submitted the form yet.
|
|
45
|
+
</p>
|
|
46
|
+
<p class="govuk-body">
|
|
47
|
+
Your submission was received with the reference: <strong>{{ context.data.submissionReferenceNumber }}</strong>.
|
|
48
|
+
</p>
|
|
49
|
+
id: 334b10dc-3373-4928-8fed-575578a67de8
|
|
50
|
+
- type: DatePartsField
|
|
51
|
+
title: When is {{ applicantFirstName }} {{ applicantLastName }}'s birthday?
|
|
52
|
+
name: dateOfBirth
|
|
53
|
+
shortDescription: Your birthday
|
|
54
|
+
hint: ''
|
|
55
|
+
options:
|
|
56
|
+
required: true
|
|
57
|
+
schema: {}
|
|
58
|
+
id: '00738799-3489-4ab2-a57b-542eecb31bfa'
|
|
59
|
+
next: []
|
|
60
|
+
id: da0fbdb4-a2de-4650-be16-9ba552af135f
|
|
61
|
+
- id: 449a45f6-4541-4a46-91bd-8b8931b07b50
|
|
62
|
+
title: Summary
|
|
63
|
+
path: '/summary'
|
|
64
|
+
controller: SummaryPageController
|
|
65
|
+
events:
|
|
66
|
+
onLoad:
|
|
67
|
+
type: http
|
|
68
|
+
options:
|
|
69
|
+
method: 'POST'
|
|
70
|
+
url: http://localhost:3009/api/example/on-summary
|
|
71
|
+
onSave:
|
|
72
|
+
type: http
|
|
73
|
+
options:
|
|
74
|
+
method: 'POST'
|
|
75
|
+
url: http://localhost:3009/api/example/on-summary
|
|
76
|
+
components:
|
|
77
|
+
- type: Html
|
|
78
|
+
content: >
|
|
79
|
+
<h2 class="govuk-heading-m">Your age</h1>
|
|
80
|
+
<p class="govuk-body">
|
|
81
|
+
We've calculated that you are {{ context.data.calculatedAge }} years old. Only proceed if this is correct.
|
|
82
|
+
</p>
|
|
83
|
+
id: c42ea488-a38c-4b67-b8fa-4cf543a4f82d
|
|
84
|
+
next: []
|
|
85
|
+
conditions: []
|
|
86
|
+
sections: []
|
|
87
|
+
lists: []
|