@defra/forms-engine-plugin 4.6.0 → 4.7.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/plugins/engine/beta/form-context.js +1 -5
- package/.server/server/plugins/engine/beta/form-context.js.map +1 -1
- package/.server/server/plugins/engine/components/CheckboxesField.d.ts +10 -1
- package/.server/server/plugins/engine/components/CheckboxesField.js +39 -0
- package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
- package/.server/server/plugins/engine/models/FormModel.d.ts +0 -2
- package/.server/server/plugins/engine/models/FormModel.js +1 -4
- package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.d.ts +0 -4
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +1 -22
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/validationOptions.js +4 -1
- package/.server/server/plugins/engine/pageControllers/validationOptions.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +0 -1
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/package.json +2 -2
- package/src/server/plugins/engine/beta/form-context.test.ts +0 -2
- package/src/server/plugins/engine/beta/form-context.ts +1 -8
- package/src/server/plugins/engine/components/CheckboxesField.test.ts +56 -1
- package/src/server/plugins/engine/components/CheckboxesField.ts +40 -0
- package/src/server/plugins/engine/models/FormModel.test.ts +0 -64
- package/src/server/plugins/engine/models/FormModel.ts +1 -5
- package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +4 -356
- package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +1 -31
- package/src/server/plugins/engine/pageControllers/validationOptions.ts +4 -1
- package/src/server/plugins/engine/types.ts +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Boom from '@hapi/boom';
|
|
2
2
|
import { isEqual } from 'date-fns';
|
|
3
3
|
import { PREVIEW_PATH_PREFIX } from "../../../constants.js";
|
|
4
|
-
import { checkEmailAddressForLiveFormSubmission, getCacheService
|
|
4
|
+
import { checkEmailAddressForLiveFormSubmission, getCacheService } from "../helpers.js";
|
|
5
5
|
import { FormModel } from "../models/index.js";
|
|
6
6
|
import { TerminalPageController } from "../pageControllers/index.js";
|
|
7
7
|
import * as defaultServices from "../services/index.js";
|
|
@@ -18,10 +18,8 @@ export async function getFormModel(slug, state, options = {}) {
|
|
|
18
18
|
if (!definition) {
|
|
19
19
|
throw Boom.notFound(`No definition found for form metadata ${metadata.id} (${slug}) ${state}`);
|
|
20
20
|
}
|
|
21
|
-
const versionNumber = getFormVersion(definition)?.versionNumber;
|
|
22
21
|
return new FormModel(definition, {
|
|
23
22
|
basePath: options.basePath ?? buildBasePath(options.routePrefix ?? '', slug, formState, isPreview),
|
|
24
|
-
versionNumber,
|
|
25
23
|
ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,
|
|
26
24
|
formId: options.formId ?? metadata.id
|
|
27
25
|
}, services, options.controllers);
|
|
@@ -83,10 +81,8 @@ export async function resolveFormModel(server, slug, state, options = {}) {
|
|
|
83
81
|
}
|
|
84
82
|
checkEmailAddressForLiveFormSubmission(metadata.notificationEmail, isPreview);
|
|
85
83
|
const routePrefix = options.routePrefix ?? server.realm.modifiers.route.prefix;
|
|
86
|
-
const versionNumber = getFormVersion(definition)?.versionNumber;
|
|
87
84
|
const model = new FormModel(definition, {
|
|
88
85
|
basePath: options.basePath ?? buildBasePath(routePrefix, slug, formState, isPreview),
|
|
89
|
-
versionNumber,
|
|
90
86
|
ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,
|
|
91
87
|
formId: options.formId ?? metadata.id
|
|
92
88
|
}, services, options.controllers);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-context.js","names":["Boom","isEqual","PREVIEW_PATH_PREFIX","checkEmailAddressForLiveFormSubmission","getCacheService","getFormVersion","FormModel","TerminalPageController","defaultServices","FormStatus","getFormModel","slug","state","options","services","formsService","isPreview","isPreviewState","formState","resolveState","metadata","getFormMetadata","definition","getFormDefinition","id","notFound","versionNumber","basePath","buildBasePath","routePrefix","ordnanceSurveyApiKey","formId","controllers","getFormContext","server","yar","Live","formModel","resolveFormModel","cacheService","summaryRequest","app","method","params","path","query","url","URL","cachedState","getState","$$__referenceNumber","errors","stateMetadata","models","Map","cache","cacheKey","entry","get","updatedAt","notificationEmail","realm","modifiers","route","prefix","model","set","base","replace","startsWith","slice","getFirstJourneyPage","context","relevantPages","undefined","lastPageReached","at","penultimatePageReached"],"sources":["../../../../../src/server/plugins/engine/beta/form-context.ts"],"sourcesContent":["import Boom from '@hapi/boom'\nimport { type Request, type Server } from '@hapi/hapi'\nimport { isEqual } from 'date-fns'\n\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n getCacheService,\n getFormVersion\n} from '~/src/server/plugins/engine/helpers.js'\nimport { FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { TerminalPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type AnyRequest,\n type FormContext,\n type FormContextRequest,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormStatus } from '~/src/server/routes/types.js'\nimport { type Services } from '~/src/server/types.js'\n\ntype JourneyState = FormStatus | 'preview'\n\nexport interface FormModelOptions {\n services?: Services\n controllers?: Record<string, typeof PageController>\n basePath?: string\n ordnanceSurveyApiKey?: string\n formId?: string\n routePrefix?: string\n isPreview?: boolean\n}\n\nexport interface FormContextOptions extends FormModelOptions {\n errors?: FormSubmissionError[]\n}\n\ntype SummaryRequest = FormContextRequest & {\n yar: Request['yar']\n}\n\nexport async function getFormModel(\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n const isPreview = isPreviewState(state, options)\n const formState = resolveState(state)\n\n const metadata = await formsService.getFormMetadata(slug)\n\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n const versionNumber = getFormVersion(definition)?.versionNumber\n\n return new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(options.routePrefix ?? '', slug, formState, isPreview),\n versionNumber,\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n}\n\nexport async function getFormContext(\n { server, yar }: Pick<Request, 'server' | 'yar'>,\n slug: string,\n state: JourneyState = FormStatus.Live,\n options: FormContextOptions = {}\n): Promise<FormContext> {\n const formModel = await resolveFormModel(server, slug, state, options)\n\n const cacheService = getCacheService(server)\n\n const summaryRequest: SummaryRequest = {\n app: {},\n method: 'get',\n params: {\n path: 'summary',\n slug,\n ...(isPreviewState(state, options) && {\n state: resolveState(state)\n })\n },\n path: `/${formModel.basePath}/summary`,\n query: {},\n url: new URL(\n `/${formModel.basePath}/summary`,\n 'https://form-context.local'\n ),\n server,\n yar\n }\n\n const cachedState = await cacheService.getState(\n summaryRequest as unknown as AnyRequest\n )\n\n const formState = {\n ...cachedState,\n $$__referenceNumber: cachedState.$$__referenceNumber\n } as unknown as FormSubmissionState\n\n return formModel.getFormContext(\n summaryRequest,\n formState,\n options.errors ?? []\n )\n}\n\nexport async function resolveFormModel(\n server: Server,\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n\n const metadata = await formsService.getFormMetadata(slug)\n const formState = resolveState(state)\n const isPreview = options.isPreview ?? isPreviewState(state, options)\n const stateMetadata = metadata[formState]\n\n if (!stateMetadata) {\n throw Boom.notFound(\n `No '${formState}' state for form metadata ${metadata.id}`\n )\n }\n\n // The models cache is created lazily per server instance\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!server.app.models) {\n server.app.models = new Map<string, { model: FormModel; updatedAt: Date }>()\n }\n\n const cache = server.app.models as Map<\n string,\n { model: FormModel; updatedAt: Date }\n >\n\n const cacheKey = `${metadata.id}_${formState}_${isPreview}`\n let entry = cache.get(cacheKey)\n\n if (!entry || !isEqual(entry.updatedAt, stateMetadata.updatedAt)) {\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n checkEmailAddressForLiveFormSubmission(\n metadata.notificationEmail,\n isPreview\n )\n\n const routePrefix =\n options.routePrefix ?? server.realm.modifiers.route.prefix\n\n const versionNumber = getFormVersion(definition)?.versionNumber\n\n const model = new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(routePrefix, slug, formState, isPreview),\n versionNumber,\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n\n entry = { model, updatedAt: stateMetadata.updatedAt }\n cache.set(cacheKey, entry)\n }\n\n return entry.model\n}\n\nfunction buildBasePath(\n routePrefix: string,\n slug: string,\n state: FormStatus,\n isPreview: boolean\n) {\n const base = (\n isPreview\n ? `${routePrefix}${PREVIEW_PATH_PREFIX}/${state}/${slug}`\n : `${routePrefix}/${slug}`\n ).replace(/\\/{2,}/g, '/')\n\n return base.startsWith('/') ? base.slice(1) : base\n}\n\nexport function getFirstJourneyPage(\n context?: Pick<FormContext, 'relevantPages'>\n) {\n if (!context?.relevantPages) {\n return undefined\n }\n\n const lastPageReached = context.relevantPages.at(-1)\n const penultimatePageReached = context.relevantPages.at(-2)\n\n if (\n lastPageReached instanceof TerminalPageController &&\n penultimatePageReached\n ) {\n return penultimatePageReached\n }\n\n return lastPageReached\n}\n\nfunction resolveState(state: JourneyState): FormStatus {\n return state === 'preview' ? FormStatus.Live : state\n}\n\nfunction isPreviewState(\n state: JourneyState,\n options: FormModelOptions = {}\n): boolean {\n return options.isPreview ?? state === 'preview'\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"form-context.js","names":["Boom","isEqual","PREVIEW_PATH_PREFIX","checkEmailAddressForLiveFormSubmission","getCacheService","FormModel","TerminalPageController","defaultServices","FormStatus","getFormModel","slug","state","options","services","formsService","isPreview","isPreviewState","formState","resolveState","metadata","getFormMetadata","definition","getFormDefinition","id","notFound","basePath","buildBasePath","routePrefix","ordnanceSurveyApiKey","formId","controllers","getFormContext","server","yar","Live","formModel","resolveFormModel","cacheService","summaryRequest","app","method","params","path","query","url","URL","cachedState","getState","$$__referenceNumber","errors","stateMetadata","models","Map","cache","cacheKey","entry","get","updatedAt","notificationEmail","realm","modifiers","route","prefix","model","set","base","replace","startsWith","slice","getFirstJourneyPage","context","relevantPages","undefined","lastPageReached","at","penultimatePageReached"],"sources":["../../../../../src/server/plugins/engine/beta/form-context.ts"],"sourcesContent":["import Boom from '@hapi/boom'\nimport { type Request, type Server } from '@hapi/hapi'\nimport { isEqual } from 'date-fns'\n\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport { FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { TerminalPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type AnyRequest,\n type FormContext,\n type FormContextRequest,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormStatus } from '~/src/server/routes/types.js'\nimport { type Services } from '~/src/server/types.js'\n\ntype JourneyState = FormStatus | 'preview'\n\nexport interface FormModelOptions {\n services?: Services\n controllers?: Record<string, typeof PageController>\n basePath?: string\n ordnanceSurveyApiKey?: string\n formId?: string\n routePrefix?: string\n isPreview?: boolean\n}\n\nexport interface FormContextOptions extends FormModelOptions {\n errors?: FormSubmissionError[]\n}\n\ntype SummaryRequest = FormContextRequest & {\n yar: Request['yar']\n}\n\nexport async function getFormModel(\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n const isPreview = isPreviewState(state, options)\n const formState = resolveState(state)\n\n const metadata = await formsService.getFormMetadata(slug)\n\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n return new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(options.routePrefix ?? '', slug, formState, isPreview),\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n}\n\nexport async function getFormContext(\n { server, yar }: Pick<Request, 'server' | 'yar'>,\n slug: string,\n state: JourneyState = FormStatus.Live,\n options: FormContextOptions = {}\n): Promise<FormContext> {\n const formModel = await resolveFormModel(server, slug, state, options)\n\n const cacheService = getCacheService(server)\n\n const summaryRequest: SummaryRequest = {\n app: {},\n method: 'get',\n params: {\n path: 'summary',\n slug,\n ...(isPreviewState(state, options) && {\n state: resolveState(state)\n })\n },\n path: `/${formModel.basePath}/summary`,\n query: {},\n url: new URL(\n `/${formModel.basePath}/summary`,\n 'https://form-context.local'\n ),\n server,\n yar\n }\n\n const cachedState = await cacheService.getState(\n summaryRequest as unknown as AnyRequest\n )\n\n const formState = {\n ...cachedState,\n $$__referenceNumber: cachedState.$$__referenceNumber\n } as unknown as FormSubmissionState\n\n return formModel.getFormContext(\n summaryRequest,\n formState,\n options.errors ?? []\n )\n}\n\nexport async function resolveFormModel(\n server: Server,\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n\n const metadata = await formsService.getFormMetadata(slug)\n const formState = resolveState(state)\n const isPreview = options.isPreview ?? isPreviewState(state, options)\n const stateMetadata = metadata[formState]\n\n if (!stateMetadata) {\n throw Boom.notFound(\n `No '${formState}' state for form metadata ${metadata.id}`\n )\n }\n\n // The models cache is created lazily per server instance\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!server.app.models) {\n server.app.models = new Map<string, { model: FormModel; updatedAt: Date }>()\n }\n\n const cache = server.app.models as Map<\n string,\n { model: FormModel; updatedAt: Date }\n >\n\n const cacheKey = `${metadata.id}_${formState}_${isPreview}`\n let entry = cache.get(cacheKey)\n\n if (!entry || !isEqual(entry.updatedAt, stateMetadata.updatedAt)) {\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n checkEmailAddressForLiveFormSubmission(\n metadata.notificationEmail,\n isPreview\n )\n\n const routePrefix =\n options.routePrefix ?? server.realm.modifiers.route.prefix\n\n const model = new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(routePrefix, slug, formState, isPreview),\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n\n entry = { model, updatedAt: stateMetadata.updatedAt }\n cache.set(cacheKey, entry)\n }\n\n return entry.model\n}\n\nfunction buildBasePath(\n routePrefix: string,\n slug: string,\n state: FormStatus,\n isPreview: boolean\n) {\n const base = (\n isPreview\n ? `${routePrefix}${PREVIEW_PATH_PREFIX}/${state}/${slug}`\n : `${routePrefix}/${slug}`\n ).replace(/\\/{2,}/g, '/')\n\n return base.startsWith('/') ? base.slice(1) : base\n}\n\nexport function getFirstJourneyPage(\n context?: Pick<FormContext, 'relevantPages'>\n) {\n if (!context?.relevantPages) {\n return undefined\n }\n\n const lastPageReached = context.relevantPages.at(-1)\n const penultimatePageReached = context.relevantPages.at(-2)\n\n if (\n lastPageReached instanceof TerminalPageController &&\n penultimatePageReached\n ) {\n return penultimatePageReached\n }\n\n return lastPageReached\n}\n\nfunction resolveState(state: JourneyState): FormStatus {\n return state === 'preview' ? FormStatus.Live : state\n}\n\nfunction isPreviewState(\n state: JourneyState,\n options: FormModelOptions = {}\n): boolean {\n return options.isPreview ?? state === 'preview'\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type CheckboxesFieldComponent, type Item } from '@defra/forms-model';
|
|
2
2
|
import { type ArraySchema } from 'joi';
|
|
3
3
|
import { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js';
|
|
4
|
-
import { type FormState, type FormStateValue, type FormSubmissionState } from '~/src/server/plugins/engine/types.js';
|
|
4
|
+
import { type ErrorMessageTemplateList, type FormState, type FormStateValue, type FormSubmissionState } from '~/src/server/plugins/engine/types.js';
|
|
5
5
|
export declare class CheckboxesField extends SelectionControlField {
|
|
6
6
|
options: CheckboxesFieldComponent['options'];
|
|
7
|
+
schema: CheckboxesFieldComponent['schema'];
|
|
7
8
|
formSchema: ArraySchema<string> | ArraySchema<number>;
|
|
8
9
|
stateSchema: ArraySchema<string> | ArraySchema<number>;
|
|
9
10
|
constructor(def: CheckboxesFieldComponent, props: ConstructorParameters<typeof SelectionControlField>[1]);
|
|
@@ -13,5 +14,13 @@ export declare class CheckboxesField extends SelectionControlField {
|
|
|
13
14
|
getContextValueFromFormValue(values: (string | number | boolean)[] | undefined): (string | number | boolean)[];
|
|
14
15
|
getDisplayStringFromState(state: FormSubmissionState): string;
|
|
15
16
|
getContextValueFromState(state: FormSubmissionState): (string | number | boolean)[];
|
|
17
|
+
/**
|
|
18
|
+
* For error preview page that shows all possible errors on a component
|
|
19
|
+
*/
|
|
20
|
+
getAllPossibleErrors(): ErrorMessageTemplateList;
|
|
21
|
+
/**
|
|
22
|
+
* Static version of getAllPossibleErrors that doesn't require a component instance.
|
|
23
|
+
*/
|
|
24
|
+
static getAllPossibleErrors(): ErrorMessageTemplateList;
|
|
16
25
|
isValue(value?: FormStateValue | FormState): value is Item['value'][];
|
|
17
26
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import joi from 'joi';
|
|
2
2
|
import { isFormValue } from "./FormComponent.js";
|
|
3
3
|
import { SelectionControlField } from "./SelectionControlField.js";
|
|
4
|
+
import { messageTemplate } from "../pageControllers/validationOptions.js";
|
|
4
5
|
export class CheckboxesField extends SelectionControlField {
|
|
5
6
|
constructor(def, props) {
|
|
6
7
|
super(def, props);
|
|
@@ -10,12 +11,23 @@ export class CheckboxesField extends SelectionControlField {
|
|
|
10
11
|
const {
|
|
11
12
|
options
|
|
12
13
|
} = def;
|
|
14
|
+
const schema = 'schema' in def ? def.schema : {};
|
|
13
15
|
let formSchema = type === 'string' ? joi.array() : joi.array();
|
|
14
16
|
const itemsSchema = joi[type]().valid(...this.values).label(this.label);
|
|
15
17
|
formSchema = formSchema.items(itemsSchema).single().label(this.label).required();
|
|
16
18
|
if (options.required === false) {
|
|
17
19
|
formSchema = formSchema.optional();
|
|
18
20
|
}
|
|
21
|
+
if (typeof schema?.length === 'number') {
|
|
22
|
+
formSchema = formSchema.length(schema.length);
|
|
23
|
+
} else {
|
|
24
|
+
if (typeof schema?.min === 'number') {
|
|
25
|
+
formSchema = formSchema.min(schema.min);
|
|
26
|
+
}
|
|
27
|
+
if (typeof schema?.max === 'number') {
|
|
28
|
+
formSchema = formSchema.max(schema.max);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
19
31
|
this.formSchema = formSchema.default([]);
|
|
20
32
|
this.stateSchema = formSchema.default(null).allow(null);
|
|
21
33
|
this.options = options;
|
|
@@ -72,6 +84,33 @@ export class CheckboxesField extends SelectionControlField {
|
|
|
72
84
|
const values = this.getFormValueFromState(state);
|
|
73
85
|
return this.getContextValueFromFormValue(values);
|
|
74
86
|
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* For error preview page that shows all possible errors on a component
|
|
90
|
+
*/
|
|
91
|
+
getAllPossibleErrors() {
|
|
92
|
+
return CheckboxesField.getAllPossibleErrors();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Static version of getAllPossibleErrors that doesn't require a component instance.
|
|
97
|
+
*/
|
|
98
|
+
static getAllPossibleErrors() {
|
|
99
|
+
const parentErrors = SelectionControlField.getAllPossibleErrors();
|
|
100
|
+
return {
|
|
101
|
+
...parentErrors,
|
|
102
|
+
advancedSettingsErrors: [...parentErrors.advancedSettingsErrors, {
|
|
103
|
+
type: 'array.min',
|
|
104
|
+
template: messageTemplate.arrayMin
|
|
105
|
+
}, {
|
|
106
|
+
type: 'array.max',
|
|
107
|
+
template: messageTemplate.arrayMax
|
|
108
|
+
}, {
|
|
109
|
+
type: 'array.length',
|
|
110
|
+
template: messageTemplate.arrayLength
|
|
111
|
+
}]
|
|
112
|
+
};
|
|
113
|
+
}
|
|
75
114
|
isValue(value) {
|
|
76
115
|
if (!Array.isArray(value)) {
|
|
77
116
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckboxesField.js","names":["joi","isFormValue","SelectionControlField","CheckboxesField","constructor","def","props","listType","type","options","formSchema","array","itemsSchema","valid","values","label","items","single","required","optional","default","stateSchema","allow","getFormValueFromState","state","name","getFormValue","selected","filter","item","includes","value","map","
|
|
1
|
+
{"version":3,"file":"CheckboxesField.js","names":["joi","isFormValue","SelectionControlField","messageTemplate","CheckboxesField","constructor","def","props","listType","type","options","schema","formSchema","array","itemsSchema","valid","values","label","items","single","required","optional","length","min","max","default","stateSchema","allow","getFormValueFromState","state","name","getFormValue","selected","filter","item","includes","value","map","undefined","isValue","getDisplayStringFromFormValue","text","join","getContextValueFromFormValue","getDisplayStringFromState","getContextValueFromState","getAllPossibleErrors","parentErrors","advancedSettingsErrors","template","arrayMin","arrayMax","arrayLength","Array","isArray","every"],"sources":["../../../../../src/server/plugins/engine/components/CheckboxesField.ts"],"sourcesContent":["import { type CheckboxesFieldComponent, type Item } from '@defra/forms-model'\nimport joi, { type ArraySchema } from 'joi'\n\nimport { isFormValue } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormState,\n type FormStateValue,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class CheckboxesField extends SelectionControlField {\n declare options: CheckboxesFieldComponent['options']\n declare schema: CheckboxesFieldComponent['schema']\n declare formSchema: ArraySchema<string> | ArraySchema<number>\n declare stateSchema: ArraySchema<string> | ArraySchema<number>\n\n constructor(\n def: CheckboxesFieldComponent,\n props: ConstructorParameters<typeof SelectionControlField>[1]\n ) {\n super(def, props)\n\n const { listType: type } = this\n const { options } = def\n const schema = 'schema' in def ? def.schema : {}\n\n let formSchema =\n type === 'string' ? joi.array<string>() : joi.array<number>()\n\n const itemsSchema = joi[type]()\n .valid(...this.values)\n .label(this.label)\n\n formSchema = formSchema\n .items(itemsSchema)\n .single()\n .label(this.label)\n .required()\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n if (typeof schema?.length === 'number') {\n formSchema = formSchema.length(schema.length)\n } else {\n if (typeof schema?.min === 'number') {\n formSchema = formSchema.min(schema.min)\n }\n\n if (typeof schema?.max === 'number') {\n formSchema = formSchema.max(schema.max)\n }\n }\n\n this.formSchema = formSchema.default([])\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { items, name } = this\n\n // State checkbox values\n const values = this.getFormValue(state[name]) ?? []\n\n // Map (or discard) state values to item values\n const selected = items\n .filter((item) => values.includes(item.value))\n .map((item) => item.value)\n\n return selected.length ? selected : undefined\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(\n selected: (string | number | boolean)[] | undefined\n ) {\n const { items } = this\n\n if (!selected) {\n return ''\n }\n\n // Map selected values to text\n return items\n .filter((item) => selected.includes(item.value))\n .map((item) => item.text)\n .join(', ')\n }\n\n getContextValueFromFormValue(\n values: (string | number | boolean)[] | undefined\n ): (string | number | boolean)[] {\n /**\n * For evaluation context purposes, optional {@link CheckboxesField}\n * with an undefined value (i.e. nothing selected) should default to [].\n * This way conditions are not evaluated against `undefined` which throws errors.\n * Currently these errors are caught and the evaluation returns default `false`.\n * @see {@link QuestionPageController.getNextPath} for `undefined` return value\n * @see {@link FormModel.makeCondition} for try/catch block with default `false`\n * For negative conditions this is a problem because E.g.\n * The condition: 'selectedchecks' does not contain 'someval'\n * should return true IF 'selectedchecks' is undefined, not throw and return false.\n */\n return values ?? []\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n // Selected checkbox values\n const selected = this.getFormValueFromState(state) ?? []\n\n // Map selected values to text\n return this.getDisplayStringFromFormValue(selected)\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const values = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(values)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return CheckboxesField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n const parentErrors = SelectionControlField.getAllPossibleErrors()\n\n return {\n ...parentErrors,\n advancedSettingsErrors: [\n ...parentErrors.advancedSettingsErrors,\n { type: 'array.min', template: messageTemplate.arrayMin },\n { type: 'array.max', template: messageTemplate.arrayMax },\n { type: 'array.length', template: messageTemplate.arrayLength }\n ]\n }\n }\n\n isValue(value?: FormStateValue | FormState): value is Item['value'][] {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isFormValue)\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -19,7 +19,6 @@ export declare class FormModel {
|
|
|
19
19
|
formId: string;
|
|
20
20
|
values: FormDefinition;
|
|
21
21
|
basePath: string;
|
|
22
|
-
versionNumber?: number;
|
|
23
22
|
ordnanceSurveyApiKey?: string;
|
|
24
23
|
conditions: Partial<Record<string, ExecutableCondition>>;
|
|
25
24
|
pages: PageControllerClass[];
|
|
@@ -34,7 +33,6 @@ export declare class FormModel {
|
|
|
34
33
|
componentMap: Map<string, Component>;
|
|
35
34
|
constructor(def: typeof this.def, options: {
|
|
36
35
|
basePath: string;
|
|
37
|
-
versionNumber?: number;
|
|
38
36
|
ordnanceSurveyApiKey?: string;
|
|
39
37
|
formId?: string;
|
|
40
38
|
}, services?: Services, controllers?: Record<string, typeof PageController>);
|
|
@@ -26,7 +26,6 @@ export class FormModel {
|
|
|
26
26
|
formId;
|
|
27
27
|
values;
|
|
28
28
|
basePath;
|
|
29
|
-
versionNumber;
|
|
30
29
|
ordnanceSurveyApiKey;
|
|
31
30
|
conditions;
|
|
32
31
|
pages;
|
|
@@ -84,7 +83,6 @@ export class FormModel {
|
|
|
84
83
|
this.formId = options.formId ?? '';
|
|
85
84
|
this.values = result.value;
|
|
86
85
|
this.basePath = options.basePath;
|
|
87
|
-
this.versionNumber = options.versionNumber;
|
|
88
86
|
this.ordnanceSurveyApiKey = options.ordnanceSurveyApiKey;
|
|
89
87
|
this.conditions = {};
|
|
90
88
|
this.services = services;
|
|
@@ -232,8 +230,7 @@ export class FormModel {
|
|
|
232
230
|
componentDefMap: this.componentDefMap,
|
|
233
231
|
pageMap: this.pageMap,
|
|
234
232
|
componentMap: this.componentMap,
|
|
235
|
-
referenceNumber: getReferenceNumber(state)
|
|
236
|
-
submittedVersionNumber: this.versionNumber
|
|
233
|
+
referenceNumber: getReferenceNumber(state)
|
|
237
234
|
};
|
|
238
235
|
|
|
239
236
|
// Validate current page
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormModel.js","names":["ComponentType","ConditionsModel","ControllerPath","ControllerType","SchemaVersion","convertConditionWrapperFromV2","formDefinitionSchema","formDefinitionV2Schema","generateConditionAlias","hasComponents","hasRepeater","isConditionWrapperV2","yesNoListId","yesNoListName","add","format","Parser","joi","createLogger","hasListFormField","todayAsDateOnly","findPage","getError","getPage","setPageTitles","createPage","validationOptions","opts","defaultServices","FormAction","merge","logger","FormModel","engine","schemaVersion","def","lists","sections","name","formId","values","basePath","versionNumber","ordnanceSurveyApiKey","conditions","pages","services","controllers","pageDefMap","listDefMap","listDefIdMap","componentDefMap","componentDefIdMap","pageMap","componentMap","constructor","options","schema","V1","warn","result","validate","abortEarly","error","structuredClone","value","push","id","title","type","items","text","Map","map","page","path","list","filter","flatMap","components","component","forEach","conditionDef","condition","makeCondition","pageDef","some","controller","Status","collection","makeFilteredSchema","relevantPages","object","required","concat","stateSchema","parser","operators","logical","Object","assign","functions","dateForComparison","timePeriod","timeUnit","displayName","expr","toConditionExpression","fn","evaluationState","ctx","toConditionContext","evaluate","context","conditionId","propertyName","V2","defineProperty","get","from","parse","toExpression","getList","nameOrId","find","getSection","section","getFormContext","request","state","errors","query","currentPath","startPath","getStartPath","isForceAccess","relevantState","payload","getFormDataFromState","paths","data","referenceNumber","getReferenceNumber","submittedVersionNumber","validateFormPayload","nextPage","initialiseContext","assignEvaluationState","assignRelevantState","pageStateIsInvalid","getNextPath","validateFormState","assignPaths","emptyState","freeze","getContextValueFromState","key","keys","listFields","fields","field","undefined","YesNoField","hasOptionalItems","item","length","fieldStateIsInvalid","validValues","fieldState","getFormValueFromState","isInvalid","isArray","Array","every","includes","href","getComponentById","componentId","getListById","listId","getConditionById","action","getFormParams","Validate","startsWith","External","update","CheckboxesField","formState","getStateFromValidForm","previousPages","relevantPage","model","stripUnknown","errorsState","details","$$__referenceNumber","Error"],"sources":["../../../../../src/server/plugins/engine/models/FormModel.ts"],"sourcesContent":["import {\n ComponentType,\n ConditionsModel,\n ControllerPath,\n ControllerType,\n SchemaVersion,\n convertConditionWrapperFromV2,\n formDefinitionSchema,\n formDefinitionV2Schema,\n generateConditionAlias,\n hasComponents,\n hasRepeater,\n isConditionWrapperV2,\n yesNoListId,\n yesNoListName,\n type ComponentDef,\n type ConditionWrapper,\n type ConditionWrapperV2,\n type ConditionsModelData,\n type DateUnits,\n type Engine,\n type FormDefinition,\n type List,\n type Page,\n type Section\n} from '@defra/forms-model'\nimport { add, format } from 'date-fns'\nimport { Parser, type Value } from 'expr-eval-fork'\nimport joi from 'joi'\n\nimport { createLogger } from '~/src/server/common/helpers/logging/logger.js'\nimport { type ListFormComponent } from '~/src/server/plugins/engine/components/ListFormComponent.js'\nimport {} from '~/src/server/plugins/engine/components/YesNoField.js'\nimport {\n hasListFormField,\n type Component\n} from '~/src/server/plugins/engine/components/helpers/components.js'\nimport { todayAsDateOnly } from '~/src/server/plugins/engine/date-helper.js'\nimport {\n findPage,\n getError,\n getPage,\n setPageTitles\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type ExecutableCondition } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n createPage,\n type PageControllerClass\n} from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormAction } from '~/src/server/routes/types.js'\nimport { merge } from '~/src/server/services/cacheService.js'\nimport { type Services } from '~/src/server/types.js'\n\nconst logger = createLogger()\n\nexport class FormModel {\n /** The runtime engine that should be used */\n engine?: Engine\n\n schemaVersion: SchemaVersion\n\n /** the entire form JSON as an object */\n def: FormDefinition\n\n lists: FormDefinition['lists']\n sections: FormDefinition['sections'] = []\n name: string\n formId: string\n values: FormDefinition\n basePath: string\n versionNumber?: number\n ordnanceSurveyApiKey?: string\n conditions: Partial<Record<string, ExecutableCondition>>\n pages: PageControllerClass[]\n services: Services\n\n controllers?: Record<string, typeof PageController>\n pageDefMap: Map<string, Page>\n\n listDefMap: Map<string, List>\n listDefIdMap: Map<string, List>\n\n componentDefMap: Map<string, ComponentDef>\n componentDefIdMap: Map<string, ComponentDef>\n\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n\n constructor(\n def: typeof this.def,\n options: {\n basePath: string\n versionNumber?: number\n ordnanceSurveyApiKey?: string\n formId?: string\n },\n services: Services = defaultServices,\n controllers?: Record<string, typeof PageController>\n ) {\n let schema = formDefinitionV2Schema\n\n if (!def.schema || def.schema === SchemaVersion.V1) {\n logger.warn(\n `[DEPRECATION NOTICE] Form \"${def.name}\" constructed with legacy V1 schema. See https://defra.github.io/forms-engine-plugin/schemas/form-definition-schema.html.`\n )\n schema = formDefinitionSchema\n }\n\n const result = schema.validate(def, { abortEarly: false })\n\n if (result.error) {\n throw result.error\n }\n\n // Make a clone of the shallow copy returned\n // by joi so as not to change the source data.\n def = structuredClone(result.value)\n\n // Add default lists\n def.lists.push({\n id: def.schema === SchemaVersion.V1 ? yesNoListName : yesNoListId,\n name: '__yesNo',\n title: 'Yes/No',\n type: 'boolean',\n items: [\n {\n id: '02900d42-83d1-4c72-a719-c4e8228952fa',\n text: 'Yes',\n value: true\n },\n {\n id: 'f39000eb-c51b-4019-8f82-bbda0423f04d',\n text: 'No',\n value: false\n }\n ]\n })\n\n // Fix up page titles\n setPageTitles(def)\n\n this.engine = def.engine\n this.schemaVersion = def.schema ?? SchemaVersion.V1\n this.def = def\n this.lists = def.lists\n this.sections = def.sections\n this.name = def.name ?? ''\n this.formId = options.formId ?? ''\n this.values = result.value\n this.basePath = options.basePath\n this.versionNumber = options.versionNumber\n this.ordnanceSurveyApiKey = options.ordnanceSurveyApiKey\n this.conditions = {}\n this.services = services\n this.controllers = controllers\n\n this.pageDefMap = new Map(def.pages.map((page) => [page.path, page]))\n this.listDefMap = new Map(def.lists.map((list) => [list.name, list]))\n this.listDefIdMap = new Map(\n def.lists\n .filter((list) => list.id) // Skip lists without an ID\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n .map((list) => [list.id as string, list])\n )\n this.componentDefMap = new Map(\n def.pages\n .filter(hasComponents)\n .flatMap((page) =>\n page.components.map((component) => [component.name, component])\n )\n )\n this.componentDefIdMap = new Map(\n def.pages.filter(hasComponents).flatMap((page) =>\n page.components\n .filter((component) => component.id) // Skip components without an ID\n .map((component) => {\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n return [component.id as string, component]\n })\n )\n )\n\n def.conditions.forEach((conditionDef) => {\n const condition = this.makeCondition(\n isConditionWrapperV2(conditionDef)\n ? convertConditionWrapperFromV2(conditionDef, this)\n : conditionDef\n )\n this.conditions[condition.name] = condition\n })\n\n this.pages = def.pages.map((pageDef) => createPage(this, pageDef))\n\n if (\n !def.pages.some(\n ({ controller }) =>\n // Check for user-provided status page (optional)\n controller === ControllerType.Status\n )\n ) {\n this.pages.push(\n createPage(this, {\n title: 'Form submitted',\n path: ControllerPath.Status,\n controller: ControllerType.Status\n })\n )\n }\n\n this.pageMap = new Map(this.pages.map((page) => [page.path, page]))\n this.componentMap = new Map(\n this.pages.flatMap((page) =>\n page.collection.components.map((component) => [\n component.name,\n component\n ])\n )\n )\n }\n\n /**\n * build the entire model schema from individual pages/sections and filter out answers\n * for pages which are no longer accessible due to an answer that has been changed\n */\n makeFilteredSchema(relevantPages: PageControllerClass[]) {\n // Build the entire model schema\n // from the individual pages/sections\n let schema = joi.object<FormSubmissionState>().required()\n\n relevantPages.forEach((page) => {\n schema = schema.concat(page.collection.stateSchema)\n })\n\n return schema\n }\n\n /**\n * Instantiates a Condition based on {@link ConditionWrapper}\n * @param condition\n */\n makeCondition(condition: ConditionWrapper): ExecutableCondition {\n const parser = new Parser({\n operators: {\n logical: true\n }\n })\n\n Object.assign(parser.functions, {\n dateForComparison(timePeriod: number, timeUnit: DateUnits) {\n // The time element must be stripped (hence using startOfDay() which has no time element),\n // then formatted as YYYY-MM-DD otherwise we can hit time element and BST issues giving the\n // wrong date to compare against.\n // Do not use .toISOString() to format the date as that introduces BST errors.\n return format(\n add(todayAsDateOnly(), { [timeUnit]: timePeriod }),\n 'yyyy-MM-dd'\n )\n }\n })\n\n const { name, displayName, value } = condition\n const expr = this.toConditionExpression(value, parser)\n\n const fn = (evaluationState: FormState) => {\n const ctx = this.toConditionContext(evaluationState, this.conditions)\n try {\n return expr.evaluate(ctx) as boolean\n } catch {\n return false\n }\n }\n\n return {\n name,\n displayName,\n value,\n expr,\n fn\n }\n }\n\n toConditionContext(\n evaluationState: FormState,\n conditions: Partial<Record<string, ExecutableCondition>>\n ) {\n const context = { ...evaluationState }\n\n for (const conditionId in conditions) {\n const propertyName =\n this.schemaVersion === SchemaVersion.V2\n ? generateConditionAlias(conditionId)\n : conditionId\n\n Object.defineProperty(context, propertyName, {\n get() {\n return conditions[conditionId]?.fn(evaluationState)\n }\n })\n }\n\n return context as Extract<Value, Record<string, Value>>\n }\n\n toConditionExpression(value: ConditionsModelData, parser: Parser) {\n const conditions = ConditionsModel.from(value)\n return parser.parse(conditions.toExpression())\n }\n\n getList(nameOrId: string): List | undefined {\n return this.schemaVersion === SchemaVersion.V1\n ? this.lists.find((list) => list.name === nameOrId)\n : this.lists.find((list) => list.id === nameOrId)\n }\n\n getSection(nameOrId: string): Section | undefined {\n return this.schemaVersion === SchemaVersion.V1\n ? this.sections.find((section) => section.name === nameOrId)\n : this.sections.find((section) => section.id === nameOrId)\n }\n\n /**\n * Form context for the current page\n */\n getFormContext(\n request: FormContextRequest,\n state: FormSubmissionState,\n errors?: FormSubmissionError[]\n ): FormContext {\n const { query } = request\n\n const page = getPage(this, request)\n\n // Determine form paths\n const currentPath = page.path\n const startPath = page.getStartPath()\n\n // Preview URL direct access is allowed\n const isForceAccess = 'force' in query\n\n let context: FormContext = {\n evaluationState: {},\n relevantState: {},\n relevantPages: [],\n payload: page.getFormDataFromState(request, state),\n state,\n paths: [],\n errors,\n isForceAccess,\n data: {},\n pageDefMap: this.pageDefMap,\n listDefMap: this.listDefMap,\n componentDefMap: this.componentDefMap,\n pageMap: this.pageMap,\n componentMap: this.componentMap,\n referenceNumber: getReferenceNumber(state),\n submittedVersionNumber: this.versionNumber\n }\n\n // Validate current page\n context = validateFormPayload(request, page, context)\n\n // Find start page\n let nextPage = findPage(this, startPath)\n\n this.initialiseContext(context)\n\n // Walk form pages from start\n while (nextPage) {\n // Add page to context\n context.relevantPages.push(nextPage)\n\n this.assignEvaluationState(context, nextPage)\n\n this.assignRelevantState(context, nextPage)\n\n // Stop at current page\n if (\n this.pageStateIsInvalid(context, nextPage) ||\n nextPage.path === currentPath\n ) {\n break\n }\n\n // Apply conditions to determine next page\n nextPage = findPage(this, nextPage.getNextPath(context))\n }\n\n // Validate form state\n context = validateFormState(request, page, context)\n\n // Add paths for navigation\n this.assignPaths(context)\n\n return context\n }\n\n private initialiseContext(context: FormContext) {\n // Initialise `evaluationState` for all keys using empty state.\n // This is because the current condition evaluation library (eval-expr)\n // will throw if an expression uses a key that is undefined.\n const emptyState = Object.freeze({})\n\n for (const page of this.pages) {\n const { collection, pageDef } = page\n\n if (!hasRepeater(pageDef)) {\n Object.assign(\n context.evaluationState,\n collection.getContextValueFromState(emptyState)\n )\n }\n }\n }\n\n private assignEvaluationState(\n context: FormContext,\n page: PageControllerClass\n ) {\n const { collection, pageDef } = page\n // Skip evaluation state for repeater pages\n\n if (!hasRepeater(pageDef)) {\n Object.assign(\n context.evaluationState,\n collection.getContextValueFromState(context.state)\n )\n }\n }\n\n private assignRelevantState(context: FormContext, page: PageControllerClass) {\n // Copy relevant state by expected keys\n for (const key of page.keys) {\n if (typeof context.state[key] !== 'undefined') {\n context.relevantState[key] = context.state[key]\n }\n }\n }\n\n private pageStateIsInvalid(context: FormContext, page: PageControllerClass) {\n // Get any list-bound fields on the page\n const listFields = page.collection.fields.filter(hasListFormField)\n\n // For each list field that is bound to a list that contains any conditional items,\n // we need to check any answers are still valid. Do this by evaluating the conditions\n // and ensuring any current answers are all included in the set of valid answers\n for (const field of listFields) {\n const list = field.list\n\n // Filter out YesNo as they can't be conditional\n if (list !== undefined && field.type !== ComponentType.YesNoField) {\n const hasOptionalItems =\n list.items.filter((item) => item.condition).length > 0\n\n if (hasOptionalItems) {\n return this.fieldStateIsInvalid(context, field, list)\n }\n }\n }\n }\n\n private fieldStateIsInvalid(\n context: FormContext,\n field: ListFormComponent,\n list: List\n ) {\n const { evaluationState, state } = context\n\n const validValues = list.items\n .filter((item) =>\n item.condition\n ? this.conditions[item.condition]?.fn(evaluationState)\n : true\n )\n .map((item) => item.value)\n\n // Get the field state\n const fieldState = field.getFormValueFromState(state)\n\n if (fieldState !== undefined) {\n let isInvalid = false\n const isArray = Array.isArray(fieldState)\n\n // Check if any saved state value(s) are still valid\n // and return true if any are invalid\n if (isArray) {\n isInvalid = !fieldState.every((item) => validValues.includes(item))\n } else {\n isInvalid = !validValues.includes(fieldState)\n }\n\n if (isInvalid) {\n context.errors ??= []\n\n const text =\n 'Options are different because you changed a previous answer'\n\n context.errors.push({\n text,\n name: field.name,\n href: `#${field.name}`,\n path: [`#${field.name}`]\n })\n }\n\n return isInvalid\n }\n }\n\n private assignPaths(context: FormContext) {\n for (const { keys, path } of context.relevantPages) {\n context.paths.push(path)\n\n // Stop at page with errors\n if (\n context.errors?.some(({ name, path }) => {\n return keys.includes(name) || keys.some((key) => path.includes(key))\n })\n ) {\n break\n }\n }\n }\n\n getComponentById(componentId: string): ComponentDef | undefined {\n return this.componentDefIdMap.get(componentId)\n }\n\n getListById(listId: string): List | undefined {\n return this.listDefIdMap.get(listId)\n }\n\n /**\n * Returns a condition by its ID. O(n) lookup time.\n * @param conditionId\n * @returns\n */\n getConditionById(conditionId: string): ConditionWrapperV2 | undefined {\n return this.def.conditions\n .filter(isConditionWrapperV2)\n .find((condition) => condition.id === conditionId)\n }\n}\n\n/**\n * Validate current page only\n */\nfunction validateFormPayload(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { collection } = page\n const { payload, state } = context\n\n const { action } = page.getFormParams(request)\n\n // Skip validation GET requests or other actions\n if (\n !request.payload ||\n (action &&\n action !== FormAction.Validate &&\n !action.startsWith(FormAction.External))\n ) {\n return context\n }\n\n // For checkbox fields missing in the payload (i.e. unchecked),\n // explicitly set their value to undefined so that any previously\n // stored value is cleared and required field validation is enforced.\n const update = { ...request.payload }\n collection.fields.forEach((field) => {\n if (\n field.type === ComponentType.CheckboxesField &&\n !(field.name in update)\n ) {\n update[field.name] = undefined\n }\n })\n\n const { value, errors } = collection.validate({\n ...payload,\n ...update\n })\n\n // Add sanitised payload (ready to save)\n const formState = page.getStateFromValidForm(request, state, value)\n\n return {\n ...context,\n payload: merge(payload, value),\n state: merge(state, formState),\n errors\n }\n}\n\n/**\n * Validate entire form state\n */\nfunction validateFormState(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { errors = [], relevantPages, relevantState } = context\n\n // Exclude current page\n const previousPages = relevantPages.filter(\n (relevantPage) => relevantPage !== page\n )\n\n // Validate relevant state\n const { error } = page.model\n .makeFilteredSchema(previousPages)\n .validate(relevantState, { ...opts, stripUnknown: true })\n\n // Add relevant state errors\n if (error) {\n const errorsState = error.details.map(getError)\n return { ...context, errors: errors.concat(errorsState) }\n }\n\n return context\n}\n\nfunction getReferenceNumber(state: FormSubmissionState): string {\n if (\n !state.$$__referenceNumber ||\n typeof state.$$__referenceNumber !== 'string'\n ) {\n throw Error('Reference number not found in form state')\n }\n\n return state.$$__referenceNumber\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"FormModel.js","names":["ComponentType","ConditionsModel","ControllerPath","ControllerType","SchemaVersion","convertConditionWrapperFromV2","formDefinitionSchema","formDefinitionV2Schema","generateConditionAlias","hasComponents","hasRepeater","isConditionWrapperV2","yesNoListId","yesNoListName","add","format","Parser","joi","createLogger","hasListFormField","todayAsDateOnly","findPage","getError","getPage","setPageTitles","createPage","validationOptions","opts","defaultServices","FormAction","merge","logger","FormModel","engine","schemaVersion","def","lists","sections","name","formId","values","basePath","ordnanceSurveyApiKey","conditions","pages","services","controllers","pageDefMap","listDefMap","listDefIdMap","componentDefMap","componentDefIdMap","pageMap","componentMap","constructor","options","schema","V1","warn","result","validate","abortEarly","error","structuredClone","value","push","id","title","type","items","text","Map","map","page","path","list","filter","flatMap","components","component","forEach","conditionDef","condition","makeCondition","pageDef","some","controller","Status","collection","makeFilteredSchema","relevantPages","object","required","concat","stateSchema","parser","operators","logical","Object","assign","functions","dateForComparison","timePeriod","timeUnit","displayName","expr","toConditionExpression","fn","evaluationState","ctx","toConditionContext","evaluate","context","conditionId","propertyName","V2","defineProperty","get","from","parse","toExpression","getList","nameOrId","find","getSection","section","getFormContext","request","state","errors","query","currentPath","startPath","getStartPath","isForceAccess","relevantState","payload","getFormDataFromState","paths","data","referenceNumber","getReferenceNumber","validateFormPayload","nextPage","initialiseContext","assignEvaluationState","assignRelevantState","pageStateIsInvalid","getNextPath","validateFormState","assignPaths","emptyState","freeze","getContextValueFromState","key","keys","listFields","fields","field","undefined","YesNoField","hasOptionalItems","item","length","fieldStateIsInvalid","validValues","fieldState","getFormValueFromState","isInvalid","isArray","Array","every","includes","href","getComponentById","componentId","getListById","listId","getConditionById","action","getFormParams","Validate","startsWith","External","update","CheckboxesField","formState","getStateFromValidForm","previousPages","relevantPage","model","stripUnknown","errorsState","details","$$__referenceNumber","Error"],"sources":["../../../../../src/server/plugins/engine/models/FormModel.ts"],"sourcesContent":["import {\n ComponentType,\n ConditionsModel,\n ControllerPath,\n ControllerType,\n SchemaVersion,\n convertConditionWrapperFromV2,\n formDefinitionSchema,\n formDefinitionV2Schema,\n generateConditionAlias,\n hasComponents,\n hasRepeater,\n isConditionWrapperV2,\n yesNoListId,\n yesNoListName,\n type ComponentDef,\n type ConditionWrapper,\n type ConditionWrapperV2,\n type ConditionsModelData,\n type DateUnits,\n type Engine,\n type FormDefinition,\n type List,\n type Page,\n type Section\n} from '@defra/forms-model'\nimport { add, format } from 'date-fns'\nimport { Parser, type Value } from 'expr-eval-fork'\nimport joi from 'joi'\n\nimport { createLogger } from '~/src/server/common/helpers/logging/logger.js'\nimport { type ListFormComponent } from '~/src/server/plugins/engine/components/ListFormComponent.js'\nimport {} from '~/src/server/plugins/engine/components/YesNoField.js'\nimport {\n hasListFormField,\n type Component\n} from '~/src/server/plugins/engine/components/helpers/components.js'\nimport { todayAsDateOnly } from '~/src/server/plugins/engine/date-helper.js'\nimport {\n findPage,\n getError,\n getPage,\n setPageTitles\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type ExecutableCondition } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n createPage,\n type PageControllerClass\n} from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormAction } from '~/src/server/routes/types.js'\nimport { merge } from '~/src/server/services/cacheService.js'\nimport { type Services } from '~/src/server/types.js'\n\nconst logger = createLogger()\n\nexport class FormModel {\n /** The runtime engine that should be used */\n engine?: Engine\n\n schemaVersion: SchemaVersion\n\n /** the entire form JSON as an object */\n def: FormDefinition\n\n lists: FormDefinition['lists']\n sections: FormDefinition['sections'] = []\n name: string\n formId: string\n values: FormDefinition\n basePath: string\n ordnanceSurveyApiKey?: string\n conditions: Partial<Record<string, ExecutableCondition>>\n pages: PageControllerClass[]\n services: Services\n\n controllers?: Record<string, typeof PageController>\n pageDefMap: Map<string, Page>\n\n listDefMap: Map<string, List>\n listDefIdMap: Map<string, List>\n\n componentDefMap: Map<string, ComponentDef>\n componentDefIdMap: Map<string, ComponentDef>\n\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n\n constructor(\n def: typeof this.def,\n options: {\n basePath: string\n ordnanceSurveyApiKey?: string\n formId?: string\n },\n services: Services = defaultServices,\n controllers?: Record<string, typeof PageController>\n ) {\n let schema = formDefinitionV2Schema\n\n if (!def.schema || def.schema === SchemaVersion.V1) {\n logger.warn(\n `[DEPRECATION NOTICE] Form \"${def.name}\" constructed with legacy V1 schema. See https://defra.github.io/forms-engine-plugin/schemas/form-definition-schema.html.`\n )\n schema = formDefinitionSchema\n }\n\n const result = schema.validate(def, { abortEarly: false })\n\n if (result.error) {\n throw result.error\n }\n\n // Make a clone of the shallow copy returned\n // by joi so as not to change the source data.\n def = structuredClone(result.value)\n\n // Add default lists\n def.lists.push({\n id: def.schema === SchemaVersion.V1 ? yesNoListName : yesNoListId,\n name: '__yesNo',\n title: 'Yes/No',\n type: 'boolean',\n items: [\n {\n id: '02900d42-83d1-4c72-a719-c4e8228952fa',\n text: 'Yes',\n value: true\n },\n {\n id: 'f39000eb-c51b-4019-8f82-bbda0423f04d',\n text: 'No',\n value: false\n }\n ]\n })\n\n // Fix up page titles\n setPageTitles(def)\n\n this.engine = def.engine\n this.schemaVersion = def.schema ?? SchemaVersion.V1\n this.def = def\n this.lists = def.lists\n this.sections = def.sections\n this.name = def.name ?? ''\n this.formId = options.formId ?? ''\n this.values = result.value\n this.basePath = options.basePath\n this.ordnanceSurveyApiKey = options.ordnanceSurveyApiKey\n this.conditions = {}\n this.services = services\n this.controllers = controllers\n\n this.pageDefMap = new Map(def.pages.map((page) => [page.path, page]))\n this.listDefMap = new Map(def.lists.map((list) => [list.name, list]))\n this.listDefIdMap = new Map(\n def.lists\n .filter((list) => list.id) // Skip lists without an ID\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n .map((list) => [list.id as string, list])\n )\n this.componentDefMap = new Map(\n def.pages\n .filter(hasComponents)\n .flatMap((page) =>\n page.components.map((component) => [component.name, component])\n )\n )\n this.componentDefIdMap = new Map(\n def.pages.filter(hasComponents).flatMap((page) =>\n page.components\n .filter((component) => component.id) // Skip components without an ID\n .map((component) => {\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n return [component.id as string, component]\n })\n )\n )\n\n def.conditions.forEach((conditionDef) => {\n const condition = this.makeCondition(\n isConditionWrapperV2(conditionDef)\n ? convertConditionWrapperFromV2(conditionDef, this)\n : conditionDef\n )\n this.conditions[condition.name] = condition\n })\n\n this.pages = def.pages.map((pageDef) => createPage(this, pageDef))\n\n if (\n !def.pages.some(\n ({ controller }) =>\n // Check for user-provided status page (optional)\n controller === ControllerType.Status\n )\n ) {\n this.pages.push(\n createPage(this, {\n title: 'Form submitted',\n path: ControllerPath.Status,\n controller: ControllerType.Status\n })\n )\n }\n\n this.pageMap = new Map(this.pages.map((page) => [page.path, page]))\n this.componentMap = new Map(\n this.pages.flatMap((page) =>\n page.collection.components.map((component) => [\n component.name,\n component\n ])\n )\n )\n }\n\n /**\n * build the entire model schema from individual pages/sections and filter out answers\n * for pages which are no longer accessible due to an answer that has been changed\n */\n makeFilteredSchema(relevantPages: PageControllerClass[]) {\n // Build the entire model schema\n // from the individual pages/sections\n let schema = joi.object<FormSubmissionState>().required()\n\n relevantPages.forEach((page) => {\n schema = schema.concat(page.collection.stateSchema)\n })\n\n return schema\n }\n\n /**\n * Instantiates a Condition based on {@link ConditionWrapper}\n * @param condition\n */\n makeCondition(condition: ConditionWrapper): ExecutableCondition {\n const parser = new Parser({\n operators: {\n logical: true\n }\n })\n\n Object.assign(parser.functions, {\n dateForComparison(timePeriod: number, timeUnit: DateUnits) {\n // The time element must be stripped (hence using startOfDay() which has no time element),\n // then formatted as YYYY-MM-DD otherwise we can hit time element and BST issues giving the\n // wrong date to compare against.\n // Do not use .toISOString() to format the date as that introduces BST errors.\n return format(\n add(todayAsDateOnly(), { [timeUnit]: timePeriod }),\n 'yyyy-MM-dd'\n )\n }\n })\n\n const { name, displayName, value } = condition\n const expr = this.toConditionExpression(value, parser)\n\n const fn = (evaluationState: FormState) => {\n const ctx = this.toConditionContext(evaluationState, this.conditions)\n try {\n return expr.evaluate(ctx) as boolean\n } catch {\n return false\n }\n }\n\n return {\n name,\n displayName,\n value,\n expr,\n fn\n }\n }\n\n toConditionContext(\n evaluationState: FormState,\n conditions: Partial<Record<string, ExecutableCondition>>\n ) {\n const context = { ...evaluationState }\n\n for (const conditionId in conditions) {\n const propertyName =\n this.schemaVersion === SchemaVersion.V2\n ? generateConditionAlias(conditionId)\n : conditionId\n\n Object.defineProperty(context, propertyName, {\n get() {\n return conditions[conditionId]?.fn(evaluationState)\n }\n })\n }\n\n return context as Extract<Value, Record<string, Value>>\n }\n\n toConditionExpression(value: ConditionsModelData, parser: Parser) {\n const conditions = ConditionsModel.from(value)\n return parser.parse(conditions.toExpression())\n }\n\n getList(nameOrId: string): List | undefined {\n return this.schemaVersion === SchemaVersion.V1\n ? this.lists.find((list) => list.name === nameOrId)\n : this.lists.find((list) => list.id === nameOrId)\n }\n\n getSection(nameOrId: string): Section | undefined {\n return this.schemaVersion === SchemaVersion.V1\n ? this.sections.find((section) => section.name === nameOrId)\n : this.sections.find((section) => section.id === nameOrId)\n }\n\n /**\n * Form context for the current page\n */\n getFormContext(\n request: FormContextRequest,\n state: FormSubmissionState,\n errors?: FormSubmissionError[]\n ): FormContext {\n const { query } = request\n\n const page = getPage(this, request)\n\n // Determine form paths\n const currentPath = page.path\n const startPath = page.getStartPath()\n\n // Preview URL direct access is allowed\n const isForceAccess = 'force' in query\n\n let context: FormContext = {\n evaluationState: {},\n relevantState: {},\n relevantPages: [],\n payload: page.getFormDataFromState(request, state),\n state,\n paths: [],\n errors,\n isForceAccess,\n data: {},\n pageDefMap: this.pageDefMap,\n listDefMap: this.listDefMap,\n componentDefMap: this.componentDefMap,\n pageMap: this.pageMap,\n componentMap: this.componentMap,\n referenceNumber: getReferenceNumber(state)\n }\n\n // Validate current page\n context = validateFormPayload(request, page, context)\n\n // Find start page\n let nextPage = findPage(this, startPath)\n\n this.initialiseContext(context)\n\n // Walk form pages from start\n while (nextPage) {\n // Add page to context\n context.relevantPages.push(nextPage)\n\n this.assignEvaluationState(context, nextPage)\n\n this.assignRelevantState(context, nextPage)\n\n // Stop at current page\n if (\n this.pageStateIsInvalid(context, nextPage) ||\n nextPage.path === currentPath\n ) {\n break\n }\n\n // Apply conditions to determine next page\n nextPage = findPage(this, nextPage.getNextPath(context))\n }\n\n // Validate form state\n context = validateFormState(request, page, context)\n\n // Add paths for navigation\n this.assignPaths(context)\n\n return context\n }\n\n private initialiseContext(context: FormContext) {\n // Initialise `evaluationState` for all keys using empty state.\n // This is because the current condition evaluation library (eval-expr)\n // will throw if an expression uses a key that is undefined.\n const emptyState = Object.freeze({})\n\n for (const page of this.pages) {\n const { collection, pageDef } = page\n\n if (!hasRepeater(pageDef)) {\n Object.assign(\n context.evaluationState,\n collection.getContextValueFromState(emptyState)\n )\n }\n }\n }\n\n private assignEvaluationState(\n context: FormContext,\n page: PageControllerClass\n ) {\n const { collection, pageDef } = page\n // Skip evaluation state for repeater pages\n\n if (!hasRepeater(pageDef)) {\n Object.assign(\n context.evaluationState,\n collection.getContextValueFromState(context.state)\n )\n }\n }\n\n private assignRelevantState(context: FormContext, page: PageControllerClass) {\n // Copy relevant state by expected keys\n for (const key of page.keys) {\n if (typeof context.state[key] !== 'undefined') {\n context.relevantState[key] = context.state[key]\n }\n }\n }\n\n private pageStateIsInvalid(context: FormContext, page: PageControllerClass) {\n // Get any list-bound fields on the page\n const listFields = page.collection.fields.filter(hasListFormField)\n\n // For each list field that is bound to a list that contains any conditional items,\n // we need to check any answers are still valid. Do this by evaluating the conditions\n // and ensuring any current answers are all included in the set of valid answers\n for (const field of listFields) {\n const list = field.list\n\n // Filter out YesNo as they can't be conditional\n if (list !== undefined && field.type !== ComponentType.YesNoField) {\n const hasOptionalItems =\n list.items.filter((item) => item.condition).length > 0\n\n if (hasOptionalItems) {\n return this.fieldStateIsInvalid(context, field, list)\n }\n }\n }\n }\n\n private fieldStateIsInvalid(\n context: FormContext,\n field: ListFormComponent,\n list: List\n ) {\n const { evaluationState, state } = context\n\n const validValues = list.items\n .filter((item) =>\n item.condition\n ? this.conditions[item.condition]?.fn(evaluationState)\n : true\n )\n .map((item) => item.value)\n\n // Get the field state\n const fieldState = field.getFormValueFromState(state)\n\n if (fieldState !== undefined) {\n let isInvalid = false\n const isArray = Array.isArray(fieldState)\n\n // Check if any saved state value(s) are still valid\n // and return true if any are invalid\n if (isArray) {\n isInvalid = !fieldState.every((item) => validValues.includes(item))\n } else {\n isInvalid = !validValues.includes(fieldState)\n }\n\n if (isInvalid) {\n context.errors ??= []\n\n const text =\n 'Options are different because you changed a previous answer'\n\n context.errors.push({\n text,\n name: field.name,\n href: `#${field.name}`,\n path: [`#${field.name}`]\n })\n }\n\n return isInvalid\n }\n }\n\n private assignPaths(context: FormContext) {\n for (const { keys, path } of context.relevantPages) {\n context.paths.push(path)\n\n // Stop at page with errors\n if (\n context.errors?.some(({ name, path }) => {\n return keys.includes(name) || keys.some((key) => path.includes(key))\n })\n ) {\n break\n }\n }\n }\n\n getComponentById(componentId: string): ComponentDef | undefined {\n return this.componentDefIdMap.get(componentId)\n }\n\n getListById(listId: string): List | undefined {\n return this.listDefIdMap.get(listId)\n }\n\n /**\n * Returns a condition by its ID. O(n) lookup time.\n * @param conditionId\n * @returns\n */\n getConditionById(conditionId: string): ConditionWrapperV2 | undefined {\n return this.def.conditions\n .filter(isConditionWrapperV2)\n .find((condition) => condition.id === conditionId)\n }\n}\n\n/**\n * Validate current page only\n */\nfunction validateFormPayload(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { collection } = page\n const { payload, state } = context\n\n const { action } = page.getFormParams(request)\n\n // Skip validation GET requests or other actions\n if (\n !request.payload ||\n (action &&\n action !== FormAction.Validate &&\n !action.startsWith(FormAction.External))\n ) {\n return context\n }\n\n // For checkbox fields missing in the payload (i.e. unchecked),\n // explicitly set their value to undefined so that any previously\n // stored value is cleared and required field validation is enforced.\n const update = { ...request.payload }\n collection.fields.forEach((field) => {\n if (\n field.type === ComponentType.CheckboxesField &&\n !(field.name in update)\n ) {\n update[field.name] = undefined\n }\n })\n\n const { value, errors } = collection.validate({\n ...payload,\n ...update\n })\n\n // Add sanitised payload (ready to save)\n const formState = page.getStateFromValidForm(request, state, value)\n\n return {\n ...context,\n payload: merge(payload, value),\n state: merge(state, formState),\n errors\n }\n}\n\n/**\n * Validate entire form state\n */\nfunction validateFormState(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n): FormContext {\n const { errors = [], relevantPages, relevantState } = context\n\n // Exclude current page\n const previousPages = relevantPages.filter(\n (relevantPage) => relevantPage !== page\n )\n\n // Validate relevant state\n const { error } = page.model\n .makeFilteredSchema(previousPages)\n .validate(relevantState, { ...opts, stripUnknown: true })\n\n // Add relevant state errors\n if (error) {\n const errorsState = error.details.map(getError)\n return { ...context, errors: errors.concat(errorsState) }\n }\n\n return context\n}\n\nfunction getReferenceNumber(state: FormSubmissionState): string {\n if (\n !state.$$__referenceNumber ||\n typeof state.$$__referenceNumber !== 'string'\n ) {\n throw Error('Reference number not found in form state')\n }\n\n return state.$$__referenceNumber\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -4,7 +4,3 @@ import { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
|
4
4
|
import { type DetailItem } from '~/src/server/plugins/engine/models/types.js';
|
|
5
5
|
import { type FormContext } from '~/src/server/plugins/engine/types.js';
|
|
6
6
|
export declare function format(context: FormContext, items: DetailItem[], model: FormModel, submitResponse: SubmitResponsePayload, formStatus: ReturnType<typeof checkFormStatus>, formMetadata?: FormMetadata): string;
|
|
7
|
-
export declare function getVersionMetadata(submittedVersionNumber: number | undefined, formMetadata?: FormMetadata): {
|
|
8
|
-
versionNumber: number;
|
|
9
|
-
createdAt: Date;
|
|
10
|
-
} | undefined;
|
|
@@ -7,7 +7,7 @@ export function format(context, items, model, submitResponse, formStatus, formMe
|
|
|
7
7
|
main: v2Main,
|
|
8
8
|
...v2Data
|
|
9
9
|
} = categoriseData(items);
|
|
10
|
-
const versionMetadata = getFormVersion(model.def)
|
|
10
|
+
const versionMetadata = getFormVersion(model.def);
|
|
11
11
|
const meta = {
|
|
12
12
|
schemaVersion: FormAdapterSubmissionSchemaVersion.V1,
|
|
13
13
|
timestamp: new Date(),
|
|
@@ -42,27 +42,6 @@ export function format(context, items, model, submitResponse, formStatus, formMe
|
|
|
42
42
|
};
|
|
43
43
|
return JSON.stringify(payload);
|
|
44
44
|
}
|
|
45
|
-
export function getVersionMetadata(submittedVersionNumber, formMetadata) {
|
|
46
|
-
if (!formMetadata?.versions?.length) {
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
if (submittedVersionNumber !== undefined) {
|
|
50
|
-
const submittedVersion = formMetadata.versions.find(v => v.versionNumber === submittedVersionNumber);
|
|
51
|
-
if (submittedVersion) {
|
|
52
|
-
return {
|
|
53
|
-
versionNumber: submittedVersion.versionNumber,
|
|
54
|
-
createdAt: submittedVersion.createdAt
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// fallback to first available version
|
|
60
|
-
const firstVersion = formMetadata.versions[0];
|
|
61
|
-
return {
|
|
62
|
-
versionNumber: firstVersion.versionNumber,
|
|
63
|
-
createdAt: firstVersion.createdAt
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
45
|
function extractCsvFiles(submitResponse) {
|
|
67
46
|
const result = submitResponse.result;
|
|
68
47
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v1.js","names":["getFormVersion","categoriseData","FormAdapterSubmissionSchemaVersion","format","context","items","model","submitResponse","formStatus","formMetadata","csvFiles","extractCsvFiles","main","v2Main","v2Data","versionMetadata","def","
|
|
1
|
+
{"version":3,"file":"v1.js","names":["getFormVersion","categoriseData","FormAdapterSubmissionSchemaVersion","format","context","items","model","submitResponse","formStatus","formMetadata","csvFiles","extractCsvFiles","main","v2Main","v2Data","versionMetadata","def","meta","schemaVersion","V1","timestamp","Date","referenceNumber","formName","name","formId","id","formSlug","slug","status","state","isPreview","notificationEmail","Object","fromEntries","entries","map","key","value","undefined","data","result","files","payload","JSON","stringify","repeaters"],"sources":["../../../../../../src/server/plugins/engine/outputFormatters/adapter/v1.ts"],"sourcesContent":["import {\n type FormMetadata,\n type SubmitResponsePayload\n} from '@defra/forms-model'\n\nimport {\n getFormVersion,\n type checkFormStatus\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { categoriseData } from '~/src/server/plugins/engine/outputFormatters/machine/v2.js'\nimport { FormAdapterSubmissionSchemaVersion } from '~/src/server/plugins/engine/types/enums.js'\nimport {\n type FormAdapterSubmissionMessageData,\n type FormAdapterSubmissionMessageMeta,\n type FormAdapterSubmissionMessagePayload,\n type FormAdapterSubmissionMessageResult,\n type FormContext\n} from '~/src/server/plugins/engine/types.js'\n\nexport function format(\n context: FormContext,\n items: DetailItem[],\n model: FormModel,\n submitResponse: SubmitResponsePayload,\n formStatus: ReturnType<typeof checkFormStatus>,\n formMetadata?: FormMetadata\n): string {\n const csvFiles = extractCsvFiles(submitResponse)\n\n const { main: v2Main, ...v2Data } = categoriseData(items)\n\n const versionMetadata = getFormVersion(model.def)\n\n const meta: FormAdapterSubmissionMessageMeta = {\n schemaVersion: FormAdapterSubmissionSchemaVersion.V1,\n timestamp: new Date(),\n referenceNumber: context.referenceNumber,\n formName: model.name,\n formId: formMetadata?.id ?? '',\n formSlug: formMetadata?.slug ?? '',\n status: formStatus.state,\n isPreview: formStatus.isPreview,\n notificationEmail: formMetadata?.notificationEmail ?? ''\n }\n\n if (versionMetadata) {\n meta.versionMetadata = versionMetadata\n }\n\n const main = Object.fromEntries(\n Object.entries(v2Main).map(([key, value]) => {\n if (value === undefined) {\n return [key, null]\n }\n\n return [key, value]\n })\n )\n\n const data: FormAdapterSubmissionMessageData = {\n main,\n ...v2Data\n }\n\n const result: FormAdapterSubmissionMessageResult = {\n files: csvFiles\n }\n\n const payload: FormAdapterSubmissionMessagePayload = {\n meta,\n data,\n result\n }\n\n return JSON.stringify(payload)\n}\n\nfunction extractCsvFiles(\n submitResponse: SubmitResponsePayload\n): FormAdapterSubmissionMessageResult['files'] {\n const result =\n submitResponse.result as Partial<FormAdapterSubmissionMessageResult>\n\n return {\n main: result.files?.main ?? '',\n repeaters: result.files?.repeaters ?? {}\n }\n}\n"],"mappings":"AAKA,SACEA,cAAc;AAKhB,SAASC,cAAc;AACvB,SAASC,kCAAkC;AAS3C,OAAO,SAASC,MAAMA,CACpBC,OAAoB,EACpBC,KAAmB,EACnBC,KAAgB,EAChBC,cAAqC,EACrCC,UAA8C,EAC9CC,YAA2B,EACnB;EACR,MAAMC,QAAQ,GAAGC,eAAe,CAACJ,cAAc,CAAC;EAEhD,MAAM;IAAEK,IAAI,EAAEC,MAAM;IAAE,GAAGC;EAAO,CAAC,GAAGb,cAAc,CAACI,KAAK,CAAC;EAEzD,MAAMU,eAAe,GAAGf,cAAc,CAACM,KAAK,CAACU,GAAG,CAAC;EAEjD,MAAMC,IAAsC,GAAG;IAC7CC,aAAa,EAAEhB,kCAAkC,CAACiB,EAAE;IACpDC,SAAS,EAAE,IAAIC,IAAI,CAAC,CAAC;IACrBC,eAAe,EAAElB,OAAO,CAACkB,eAAe;IACxCC,QAAQ,EAAEjB,KAAK,CAACkB,IAAI;IACpBC,MAAM,EAAEhB,YAAY,EAAEiB,EAAE,IAAI,EAAE;IAC9BC,QAAQ,EAAElB,YAAY,EAAEmB,IAAI,IAAI,EAAE;IAClCC,MAAM,EAAErB,UAAU,CAACsB,KAAK;IACxBC,SAAS,EAAEvB,UAAU,CAACuB,SAAS;IAC/BC,iBAAiB,EAAEvB,YAAY,EAAEuB,iBAAiB,IAAI;EACxD,CAAC;EAED,IAAIjB,eAAe,EAAE;IACnBE,IAAI,CAACF,eAAe,GAAGA,eAAe;EACxC;EAEA,MAAMH,IAAI,GAAGqB,MAAM,CAACC,WAAW,CAC7BD,MAAM,CAACE,OAAO,CAACtB,MAAM,CAAC,CAACuB,GAAG,CAAC,CAAC,CAACC,GAAG,EAAEC,KAAK,CAAC,KAAK;IAC3C,IAAIA,KAAK,KAAKC,SAAS,EAAE;MACvB,OAAO,CAACF,GAAG,EAAE,IAAI,CAAC;IACpB;IAEA,OAAO,CAACA,GAAG,EAAEC,KAAK,CAAC;EACrB,CAAC,CACH,CAAC;EAED,MAAME,IAAsC,GAAG;IAC7C5B,IAAI;IACJ,GAAGE;EACL,CAAC;EAED,MAAM2B,MAA0C,GAAG;IACjDC,KAAK,EAAEhC;EACT,CAAC;EAED,MAAMiC,OAA4C,GAAG;IACnD1B,IAAI;IACJuB,IAAI;IACJC;EACF,CAAC;EAED,OAAOG,IAAI,CAACC,SAAS,CAACF,OAAO,CAAC;AAChC;AAEA,SAAShC,eAAeA,CACtBJ,cAAqC,EACQ;EAC7C,MAAMkC,MAAM,GACVlC,cAAc,CAACkC,MAAqD;EAEtE,OAAO;IACL7B,IAAI,EAAE6B,MAAM,CAACC,KAAK,EAAE9B,IAAI,IAAI,EAAE;IAC9BkC,SAAS,EAAEL,MAAM,CAACC,KAAK,EAAEI,SAAS,IAAI,CAAC;EACzC,CAAC;AACH","ignoreList":[]}
|
|
@@ -34,7 +34,10 @@ export const messageTemplate = {
|
|
|
34
34
|
objectMissing: joi.expression('{{#title}} must include a {{lowerFirst(#label)}}', opts),
|
|
35
35
|
dateFormat: '{{#title}} must be a real date',
|
|
36
36
|
dateMin: '{{#title}} must be the same as or after {{#limit}}',
|
|
37
|
-
dateMax: '{{#title}} must be the same as or before {{#limit}}'
|
|
37
|
+
dateMax: '{{#title}} must be the same as or before {{#limit}}',
|
|
38
|
+
arrayMax: 'Only {{#limit}} can be selected from the list',
|
|
39
|
+
arrayMin: 'Select at least {{#limit}} options from the list',
|
|
40
|
+
arrayLength: 'Select only {{#limit}} options from the list'
|
|
38
41
|
};
|
|
39
42
|
export const messages = {
|
|
40
43
|
'string.base': messageTemplate.required,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validationOptions.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","messageTemplate","declarationRequired","expression","required","selectRequired","selectYesNoRequired","max","min","minMax","pattern","format","unicode","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","objectRequired","objectMissing","dateFormat","dateMin","dateMax","messages","messagesPre","validationOptions","abortEarly","errors","wrap","array","label"],"sources":["../../../../../src/server/plugins/engine/pageControllers/validationOptions.ts"],"sourcesContent":["// Declaration above is needed for: https://github.com/hapijs/joi/issues/3064\n\nimport joi, {\n type JoiExpression,\n type LanguageMessages,\n type LanguageMessagesExt,\n type ReferenceOptions,\n type ValidationOptions\n} from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nconst opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax\n */\nexport const messageTemplate: Record<string, JoiExpression> = {\n declarationRequired: joi.expression(\n 'You must confirm you understand and agree with the {{lowerFirst(#label)}} to continue',\n opts\n ) as JoiExpression,\n required: joi.expression(\n 'Enter {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectRequired: joi.expression(\n 'Select {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectYesNoRequired: '{{#label}} - select yes or no',\n max: '{{#label}} must be {{#limit}} characters or less',\n min: '{{#label}} must be {{#limit}} characters or more',\n minMax: '{{#label}} must be between {{#min}} and {{#max}} characters',\n pattern: joi.expression(\n 'Enter a valid {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n format: joi.expression(\n 'Enter {{lowerFirst(#label)}} in the correct format',\n opts\n ) as JoiExpression,\n unicode: '{{#label}} includes invalid characters, for example, long dashes',\n number: '{{#label}} must be a number',\n numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',\n numberInteger: '{{#label}} must be a whole number',\n numberMin: '{{#label}} must be {{#limit}} or higher',\n numberMax: '{{#label}} must be {{#limit}} or lower',\n maxWords: '{{#label}} must be {{#limit}} words or fewer',\n\n // Nested fields use component title\n\n objectRequired: joi.expression('Enter {{#label}}', opts) as JoiExpression,\n objectMissing: joi.expression(\n '{{#title}} must include a {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n dateFormat: '{{#title}} must be a real date',\n dateMin: '{{#title}} must be the same as or after {{#limit}}',\n dateMax: '{{#title}} must be the same as or before {{#limit}}'\n}\n\nexport const messages: LanguageMessagesExt = {\n 'string.base': messageTemplate.required,\n 'string.min': messageTemplate.min,\n 'string.empty': messageTemplate.required,\n 'string.max': messageTemplate.max,\n 'string.email': messageTemplate.format,\n 'string.unicode': messageTemplate.unicode,\n 'string.pattern.base': messageTemplate.pattern,\n 'string.maxWords': messageTemplate.maxWords,\n\n 'number.base': messageTemplate.number,\n 'number.precision': messageTemplate.numberPrecision,\n 'number.integer': messageTemplate.numberInteger,\n 'number.unsafe': messageTemplate.format,\n 'number.min': messageTemplate.numberMin,\n 'number.max': messageTemplate.numberMax,\n\n 'object.required': messageTemplate.objectRequired,\n 'object.and': messageTemplate.objectMissing,\n\n 'any.only': messageTemplate.selectRequired,\n 'any.required': messageTemplate.selectRequired,\n 'any.empty': messageTemplate.required,\n\n 'date.base': messageTemplate.dateFormat,\n 'date.format': messageTemplate.dateFormat,\n 'date.min': messageTemplate.dateMin,\n 'date.max': messageTemplate.dateMax,\n\n 'object.invalidjson': messageTemplate.format\n}\n\nexport const messagesPre: LanguageMessages =\n messages as unknown as LanguageMessages\n\nexport const validationOptions: ValidationOptions = {\n abortEarly: false,\n messages: messagesPre,\n errors: {\n wrap: {\n array: false,\n label: false\n }\n }\n}\n"],"mappings":"AAAA;;AAEA,OAAOA,GAAG,MAMH,KAAK;AAEZ,SAASC,6BAA6B;AAEtC,MAAMC,IAAI,GAAG;EACXC,SAAS,EAAE;IACTC,UAAU,EAAEH;EACd;AACF,CAAqB;;AAErB;AACA;AACA;AACA,OAAO,MAAMI,eAA8C,GAAG;EAC5DC,mBAAmB,EAAEN,GAAG,CAACO,UAAU,CACjC,uFAAuF,EACvFL,IACF,CAAkB;EAClBM,QAAQ,EAAER,GAAG,CAACO,UAAU,CACtB,8BAA8B,EAC9BL,IACF,CAAkB;EAClBO,cAAc,EAAET,GAAG,CAACO,UAAU,CAC5B,+BAA+B,EAC/BL,IACF,CAAkB;EAClBQ,mBAAmB,EAAE,+BAA+B;EACpDC,GAAG,EAAE,kDAAkD;EACvDC,GAAG,EAAE,kDAAkD;EACvDC,MAAM,EAAE,6DAA6D;EACrEC,OAAO,EAAEd,GAAG,CAACO,UAAU,CACrB,sCAAsC,EACtCL,IACF,CAAkB;EAClBa,MAAM,EAAEf,GAAG,CAACO,UAAU,CACpB,oDAAoD,EACpDL,IACF,CAAkB;EAClBc,OAAO,EAAE,kEAAkE;EAC3EC,MAAM,EAAE,6BAA6B;EACrCC,eAAe,EAAE,yDAAyD;EAC1EC,aAAa,EAAE,mCAAmC;EAClDC,SAAS,EAAE,yCAAyC;EACpDC,SAAS,EAAE,wCAAwC;EACnDC,QAAQ,EAAE,8CAA8C;EAExD;;EAEAC,cAAc,EAAEvB,GAAG,CAACO,UAAU,CAAC,kBAAkB,EAAEL,IAAI,CAAkB;EACzEsB,aAAa,EAAExB,GAAG,CAACO,UAAU,CAC3B,kDAAkD,EAClDL,IACF,CAAkB;EAClBuB,UAAU,EAAE,gCAAgC;EAC5CC,OAAO,EAAE,oDAAoD;EAC7DC,OAAO,EAAE;
|
|
1
|
+
{"version":3,"file":"validationOptions.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","messageTemplate","declarationRequired","expression","required","selectRequired","selectYesNoRequired","max","min","minMax","pattern","format","unicode","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","objectRequired","objectMissing","dateFormat","dateMin","dateMax","arrayMax","arrayMin","arrayLength","messages","messagesPre","validationOptions","abortEarly","errors","wrap","array","label"],"sources":["../../../../../src/server/plugins/engine/pageControllers/validationOptions.ts"],"sourcesContent":["// Declaration above is needed for: https://github.com/hapijs/joi/issues/3064\n\nimport joi, {\n type JoiExpression,\n type LanguageMessages,\n type LanguageMessagesExt,\n type ReferenceOptions,\n type ValidationOptions\n} from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nconst opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax\n */\nexport const messageTemplate: Record<string, JoiExpression> = {\n declarationRequired: joi.expression(\n 'You must confirm you understand and agree with the {{lowerFirst(#label)}} to continue',\n opts\n ) as JoiExpression,\n required: joi.expression(\n 'Enter {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectRequired: joi.expression(\n 'Select {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectYesNoRequired: '{{#label}} - select yes or no',\n max: '{{#label}} must be {{#limit}} characters or less',\n min: '{{#label}} must be {{#limit}} characters or more',\n minMax: '{{#label}} must be between {{#min}} and {{#max}} characters',\n pattern: joi.expression(\n 'Enter a valid {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n format: joi.expression(\n 'Enter {{lowerFirst(#label)}} in the correct format',\n opts\n ) as JoiExpression,\n unicode: '{{#label}} includes invalid characters, for example, long dashes',\n number: '{{#label}} must be a number',\n numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',\n numberInteger: '{{#label}} must be a whole number',\n numberMin: '{{#label}} must be {{#limit}} or higher',\n numberMax: '{{#label}} must be {{#limit}} or lower',\n maxWords: '{{#label}} must be {{#limit}} words or fewer',\n\n // Nested fields use component title\n\n objectRequired: joi.expression('Enter {{#label}}', opts) as JoiExpression,\n objectMissing: joi.expression(\n '{{#title}} must include a {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n dateFormat: '{{#title}} must be a real date',\n dateMin: '{{#title}} must be the same as or after {{#limit}}',\n dateMax: '{{#title}} must be the same as or before {{#limit}}',\n arrayMax: 'Only {{#limit}} can be selected from the list',\n arrayMin: 'Select at least {{#limit}} options from the list',\n arrayLength: 'Select only {{#limit}} options from the list'\n}\n\nexport const messages: LanguageMessagesExt = {\n 'string.base': messageTemplate.required,\n 'string.min': messageTemplate.min,\n 'string.empty': messageTemplate.required,\n 'string.max': messageTemplate.max,\n 'string.email': messageTemplate.format,\n 'string.unicode': messageTemplate.unicode,\n 'string.pattern.base': messageTemplate.pattern,\n 'string.maxWords': messageTemplate.maxWords,\n\n 'number.base': messageTemplate.number,\n 'number.precision': messageTemplate.numberPrecision,\n 'number.integer': messageTemplate.numberInteger,\n 'number.unsafe': messageTemplate.format,\n 'number.min': messageTemplate.numberMin,\n 'number.max': messageTemplate.numberMax,\n\n 'object.required': messageTemplate.objectRequired,\n 'object.and': messageTemplate.objectMissing,\n\n 'any.only': messageTemplate.selectRequired,\n 'any.required': messageTemplate.selectRequired,\n 'any.empty': messageTemplate.required,\n\n 'date.base': messageTemplate.dateFormat,\n 'date.format': messageTemplate.dateFormat,\n 'date.min': messageTemplate.dateMin,\n 'date.max': messageTemplate.dateMax,\n\n 'object.invalidjson': messageTemplate.format\n}\n\nexport const messagesPre: LanguageMessages =\n messages as unknown as LanguageMessages\n\nexport const validationOptions: ValidationOptions = {\n abortEarly: false,\n messages: messagesPre,\n errors: {\n wrap: {\n array: false,\n label: false\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|