@defra/forms-engine-plugin 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -173,8 +173,8 @@ export class FormModel {
|
|
|
173
173
|
...evaluationState
|
|
174
174
|
};
|
|
175
175
|
for (const conditionId in conditions) {
|
|
176
|
-
const
|
|
177
|
-
Object.defineProperty(context,
|
|
176
|
+
const propertyName = this.schemaVersion === SchemaVersion.V2 ? generateConditionAlias(conditionId) : conditionId;
|
|
177
|
+
Object.defineProperty(context, propertyName, {
|
|
178
178
|
get() {
|
|
179
179
|
return conditions[conditionId]?.fn(evaluationState);
|
|
180
180
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormModel.js","names":["ComponentType","ConditionsModel","ControllerPath","ControllerType","Engine","SchemaVersion","convertConditionWrapperFromV2","formDefinitionSchema","formDefinitionV2Schema","generateConditionAlias","hasComponents","hasRepeater","isConditionWrapperV2","yesNoListId","yesNoListName","add","format","Parser","joi","hasListFormField","todayAsDateOnly","findPage","getError","getPage","setPageTitles","createPage","validationOptions","opts","defaultServices","FormAction","merge","FormModel","engine","schemaVersion","def","lists","sections","name","values","basePath","conditions","pages","services","controllers","pageDefMap","listDefMap","listDefIdMap","componentDefMap","componentDefIdMap","pageMap","componentMap","constructor","options","schema","V2","result","validate","abortEarly","error","structuredClone","value","push","id","V1","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","alias","defineProperty","get","from","parse","toExpression","getList","nameOrId","find","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","key","keys","getContextValueFromState","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","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 Engine,\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 FormDefinition,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { add, format } from 'date-fns'\nimport { Parser, type Value } from 'expr-eval'\nimport joi from 'joi'\n\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.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.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\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 values: FormDefinition\n basePath: 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: { basePath: string },\n services: Services = defaultServices,\n controllers?: Record<string, typeof PageController>\n ) {\n let schema = formDefinitionSchema\n\n if (def.schema === SchemaVersion.V2) {\n schema = formDefinitionV2Schema\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.values = result.value\n this.basePath = options.basePath\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 alias = generateConditionAlias(conditionId)\n\n Object.defineProperty(context, alias, {\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 /**\n * Form context for the current page\n */\n getFormContext(\n request: FormContextRequest,\n state: FormState,\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 // For the V2 engine, we need to initialise `evaluationState` to null\n // for all keys. This is because the current condition evaluation\n // library (eval-expr) will throw if an expression uses a key that is undefined.\n if (this.engine === Engine.V2) {\n for (const page of this.pages) {\n for (const key of page.keys) {\n context.evaluationState[key] = null\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 (!request.payload || action !== FormAction.Validate) {\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: FormState): 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","Engine","SchemaVersion","convertConditionWrapperFromV2","formDefinitionSchema","formDefinitionV2Schema","generateConditionAlias","hasComponents","hasRepeater","isConditionWrapperV2","yesNoListId","yesNoListName","add","format","Parser","joi","hasListFormField","todayAsDateOnly","findPage","getError","getPage","setPageTitles","createPage","validationOptions","opts","defaultServices","FormAction","merge","FormModel","engine","schemaVersion","def","lists","sections","name","values","basePath","conditions","pages","services","controllers","pageDefMap","listDefMap","listDefIdMap","componentDefMap","componentDefIdMap","pageMap","componentMap","constructor","options","schema","V2","result","validate","abortEarly","error","structuredClone","value","push","id","V1","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","defineProperty","get","from","parse","toExpression","getList","nameOrId","find","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","key","keys","getContextValueFromState","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","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 Engine,\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 FormDefinition,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { add, format } from 'date-fns'\nimport { Parser, type Value } from 'expr-eval'\nimport joi from 'joi'\n\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.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.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\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 values: FormDefinition\n basePath: 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: { basePath: string },\n services: Services = defaultServices,\n controllers?: Record<string, typeof PageController>\n ) {\n let schema = formDefinitionSchema\n\n if (def.schema === SchemaVersion.V2) {\n schema = formDefinitionV2Schema\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.values = result.value\n this.basePath = options.basePath\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 /**\n * Form context for the current page\n */\n getFormContext(\n request: FormContextRequest,\n state: FormState,\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 // For the V2 engine, we need to initialise `evaluationState` to null\n // for all keys. This is because the current condition evaluation\n // library (eval-expr) will throw if an expression uses a key that is undefined.\n if (this.engine === Engine.V2) {\n for (const page of this.pages) {\n for (const key of page.keys) {\n context.evaluationState[key] = null\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 (!request.payload || action !== FormAction.Validate) {\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: FormState): 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":[]}
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@ import definition from '~/test/form/definitions/conditions-escaping.js'
|
|
|
12
12
|
import conditionsListDefinition from '~/test/form/definitions/conditions-list.js'
|
|
13
13
|
import relativeDatesDefinition from '~/test/form/definitions/conditions-relative-dates-v2.js'
|
|
14
14
|
import fieldsRequiredDefinition from '~/test/form/definitions/fields-required.js'
|
|
15
|
-
import joinedConditionsDefinition from '~/test/form/definitions/joined-conditions-
|
|
15
|
+
import joinedConditionsDefinition from '~/test/form/definitions/joined-conditions-simple-v2.js'
|
|
16
16
|
|
|
17
17
|
jest.mock('~/src/server/plugins/engine/date-helper.ts')
|
|
18
18
|
|
|
@@ -357,15 +357,15 @@ describe('FormModel - Joined Conditions', () => {
|
|
|
357
357
|
const joinedCondition =
|
|
358
358
|
model.conditions['db43c6bc-9ce6-478b-8345-4fff5eff2ba3']
|
|
359
359
|
expect(joinedCondition).toBeDefined()
|
|
360
|
-
expect(joinedCondition?.displayName).toBe('
|
|
360
|
+
expect(joinedCondition?.displayName).toBe('is Bob AND over 18')
|
|
361
361
|
|
|
362
|
-
const stateAllTrue = {
|
|
362
|
+
const stateAllTrue = { userName: 'Bob', isOverEighteen: true }
|
|
363
363
|
expect(joinedCondition?.fn(stateAllTrue)).toBe(true)
|
|
364
364
|
|
|
365
|
-
const statePartialTrue = {
|
|
365
|
+
const statePartialTrue = { userName: 'Alice', isOverEighteen: true }
|
|
366
366
|
expect(joinedCondition?.fn(statePartialTrue)).toBe(false)
|
|
367
367
|
|
|
368
|
-
const stateFalse = {
|
|
368
|
+
const stateFalse = { userName: 'Alice', isOverEighteen: false }
|
|
369
369
|
expect(joinedCondition?.fn(stateFalse)).toBe(false)
|
|
370
370
|
})
|
|
371
371
|
|
|
@@ -379,18 +379,92 @@ describe('FormModel - Joined Conditions', () => {
|
|
|
379
379
|
})
|
|
380
380
|
|
|
381
381
|
const joinedConditionPage = model.pages.find(
|
|
382
|
-
(page) => page.path === '/
|
|
382
|
+
(page) => page.path === '/simple-and-page'
|
|
383
383
|
)
|
|
384
384
|
|
|
385
385
|
expect(joinedConditionPage?.condition).toBeDefined()
|
|
386
386
|
|
|
387
|
-
const trueState = {
|
|
387
|
+
const trueState = { userName: 'Bob', isOverEighteen: true }
|
|
388
388
|
expect(joinedConditionPage?.condition?.fn(trueState)).toBe(true)
|
|
389
389
|
|
|
390
|
-
const falseState = {
|
|
390
|
+
const falseState = { userName: 'Bob', isOverEighteen: false }
|
|
391
391
|
expect(joinedConditionPage?.condition?.fn(falseState)).toBe(false)
|
|
392
392
|
})
|
|
393
393
|
|
|
394
|
+
it('should handle V1 joined conditions without aliases', () => {
|
|
395
|
+
formDefinitionV2Schema.validate = jest
|
|
396
|
+
.fn()
|
|
397
|
+
.mockReturnValue({ value: definition })
|
|
398
|
+
|
|
399
|
+
const model = new FormModel(definition, {
|
|
400
|
+
basePath: 'test'
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
expect(model.conditions).toBeDefined()
|
|
404
|
+
expect(Object.keys(model.conditions)).toHaveLength(1)
|
|
405
|
+
|
|
406
|
+
const joinedCondition = model.conditions.ZCXeMz
|
|
407
|
+
expect(joinedCondition).toBeDefined()
|
|
408
|
+
expect(joinedCondition?.displayName).toBe('test')
|
|
409
|
+
|
|
410
|
+
const testState = { NIJphU: "ap'ostrophe's", iraEpG: "shouldn't've" }
|
|
411
|
+
expect(joinedCondition?.fn(testState)).toBe(true)
|
|
412
|
+
|
|
413
|
+
const testStateFalse = { NIJphU: 'other', iraEpG: "shouldn't've" }
|
|
414
|
+
expect(joinedCondition?.fn(testStateFalse)).toBe(false)
|
|
415
|
+
|
|
416
|
+
const context = model.toConditionContext(testState, model.conditions)
|
|
417
|
+
|
|
418
|
+
expect(context).not.toHaveProperty('cond_ZCXeMz')
|
|
419
|
+
|
|
420
|
+
expect(context).toHaveProperty('ZCXeMz')
|
|
421
|
+
|
|
422
|
+
expect(context).toHaveProperty('NIJphU', "ap'ostrophe's")
|
|
423
|
+
expect(context).toHaveProperty('iraEpG', "shouldn't've")
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
it('should use schema version to determine condition aliases', () => {
|
|
427
|
+
const v1Definition = { ...definition, schema: SchemaVersion.V1 }
|
|
428
|
+
formDefinitionV2Schema.validate = jest
|
|
429
|
+
.fn()
|
|
430
|
+
.mockReturnValue({ value: v1Definition })
|
|
431
|
+
|
|
432
|
+
const v1Model = new FormModel(v1Definition, { basePath: 'test' })
|
|
433
|
+
expect(v1Model.schemaVersion).toBe(SchemaVersion.V1)
|
|
434
|
+
|
|
435
|
+
const v1TestState = { NIJphU: "ap'ostrophe's", iraEpG: "shouldn't've" }
|
|
436
|
+
const v1Context = v1Model.toConditionContext(
|
|
437
|
+
v1TestState,
|
|
438
|
+
v1Model.conditions
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
expect(v1Context).toHaveProperty('ZCXeMz')
|
|
442
|
+
expect(v1Context).not.toHaveProperty('cond_ZCXeMz')
|
|
443
|
+
|
|
444
|
+
formDefinitionV2Schema.validate = jest
|
|
445
|
+
.fn()
|
|
446
|
+
.mockReturnValue({ value: joinedConditionsDefinition })
|
|
447
|
+
|
|
448
|
+
const v2Model = new FormModel(joinedConditionsDefinition, {
|
|
449
|
+
basePath: 'test'
|
|
450
|
+
})
|
|
451
|
+
expect(v2Model.schemaVersion).toBe(SchemaVersion.V2)
|
|
452
|
+
|
|
453
|
+
const v2TestState = { userName: 'Bob', isOverEighteen: true }
|
|
454
|
+
const v2Context = v2Model.toConditionContext(
|
|
455
|
+
v2TestState,
|
|
456
|
+
v2Model.conditions
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
expect(v2Context).toHaveProperty('cond_d15aff7a622440a28e5f51a5af2f7910')
|
|
460
|
+
expect(v2Context).toHaveProperty('cond_d1f9fcc7f09847e79d314f5ee57ba985')
|
|
461
|
+
expect(v2Context).toHaveProperty('cond_db43c6bc9ce6478b83454fff5eff2ba3')
|
|
462
|
+
|
|
463
|
+
expect(v2Context).not.toHaveProperty('d15aff7a-6224-40a2-8e5f-51a5af2f7910')
|
|
464
|
+
expect(v2Context).not.toHaveProperty('d1f9fcc7-f098-47e7-9d31-4f5ee57ba985')
|
|
465
|
+
expect(v2Context).not.toHaveProperty('db43c6bc-9ce6-478b-8345-4fff5eff2ba3')
|
|
466
|
+
})
|
|
467
|
+
|
|
394
468
|
describe('generateConditionAlias', () => {
|
|
395
469
|
it('should generate valid JavaScript identifiers from condition IDs', () => {
|
|
396
470
|
formDefinitionV2Schema.validate = jest
|
|
@@ -401,7 +475,7 @@ describe('FormModel - Joined Conditions', () => {
|
|
|
401
475
|
basePath: 'test'
|
|
402
476
|
})
|
|
403
477
|
|
|
404
|
-
const evaluationState = {
|
|
478
|
+
const evaluationState = { userName: 'Bob', isOverEighteen: true }
|
|
405
479
|
|
|
406
480
|
const context = model.toConditionContext(
|
|
407
481
|
evaluationState,
|
|
@@ -428,8 +502,8 @@ describe('FormModel - Joined Conditions', () => {
|
|
|
428
502
|
model.conditions['db43c6bc-9ce6-478b-8345-4fff5eff2ba3']
|
|
429
503
|
expect(joinedCondition).toBeDefined()
|
|
430
504
|
|
|
431
|
-
const stateTrue = {
|
|
432
|
-
const stateFalse = {
|
|
505
|
+
const stateTrue = { userName: 'Bob', isOverEighteen: true }
|
|
506
|
+
const stateFalse = { userName: 'Alice', isOverEighteen: false }
|
|
433
507
|
|
|
434
508
|
expect(joinedCondition?.fn(stateTrue)).toBe(true)
|
|
435
509
|
expect(joinedCondition?.fn(stateFalse)).toBe(false)
|
|
@@ -278,9 +278,12 @@ export class FormModel {
|
|
|
278
278
|
const context = { ...evaluationState }
|
|
279
279
|
|
|
280
280
|
for (const conditionId in conditions) {
|
|
281
|
-
const
|
|
281
|
+
const propertyName =
|
|
282
|
+
this.schemaVersion === SchemaVersion.V2
|
|
283
|
+
? generateConditionAlias(conditionId)
|
|
284
|
+
: conditionId
|
|
282
285
|
|
|
283
|
-
Object.defineProperty(context,
|
|
286
|
+
Object.defineProperty(context, propertyName, {
|
|
284
287
|
get() {
|
|
285
288
|
return conditions[conditionId]?.fn(evaluationState)
|
|
286
289
|
}
|