@defra/forms-engine-plugin 2.1.3 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.server/server/plugins/engine/models/FormModel.js +1 -1
  2. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  3. package/.server/server/plugins/engine/outputFormatters/adapter/v1.d.ts +6 -0
  4. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +23 -0
  5. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -0
  6. package/.server/server/plugins/engine/outputFormatters/human/v1.d.ts +2 -2
  7. package/.server/server/plugins/engine/outputFormatters/human/v1.js +1 -1
  8. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  9. package/.server/server/plugins/engine/outputFormatters/index.d.ts +4 -3
  10. package/.server/server/plugins/engine/outputFormatters/index.js +4 -0
  11. package/.server/server/plugins/engine/outputFormatters/index.js.map +1 -1
  12. package/.server/server/plugins/engine/outputFormatters/machine/v1.d.ts +2 -2
  13. package/.server/server/plugins/engine/outputFormatters/machine/v1.js +1 -1
  14. package/.server/server/plugins/engine/outputFormatters/machine/v1.js.map +1 -1
  15. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +4 -1
  16. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  17. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +5 -4
  18. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  19. package/.server/server/plugins/engine/services/localFormsService.d.ts +1 -1
  20. package/.server/server/plugins/engine/services/notifyService.d.ts +7 -2
  21. package/.server/server/plugins/engine/services/notifyService.js +11 -2
  22. package/.server/server/plugins/engine/services/notifyService.js.map +1 -1
  23. package/.server/server/plugins/engine/types/index.d.ts +10 -0
  24. package/.server/server/plugins/engine/types/index.js +4 -0
  25. package/.server/server/plugins/engine/types/index.js.map +1 -0
  26. package/.server/server/plugins/engine/types/schema.d.ts +5 -0
  27. package/.server/server/plugins/engine/types/schema.js +24 -0
  28. package/.server/server/plugins/engine/types/schema.js.map +1 -0
  29. package/.server/server/plugins/engine/types.d.ts +37 -1
  30. package/.server/server/plugins/engine/types.js +4 -0
  31. package/.server/server/plugins/engine/types.js.map +1 -1
  32. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  33. package/.server/server/plugins/nunjucks/filters/page.d.ts +1 -1
  34. package/.server/server/types.d.ts +1 -1
  35. package/.server/server/types.js.map +1 -1
  36. package/package.json +6 -1
  37. package/src/server/plugins/engine/models/FormModel.test.ts +64 -0
  38. package/src/server/plugins/engine/models/FormModel.ts +5 -1
  39. package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +506 -0
  40. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +53 -0
  41. package/src/server/plugins/engine/outputFormatters/human/v1.ts +6 -2
  42. package/src/server/plugins/engine/outputFormatters/index.ts +11 -3
  43. package/src/server/plugins/engine/outputFormatters/machine/v1.ts +6 -2
  44. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +1 -1
  45. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +15 -4
  46. package/src/server/plugins/engine/services/notifyService.test.ts +156 -1
  47. package/src/server/plugins/engine/services/notifyService.ts +24 -3
  48. package/src/server/plugins/engine/types/index.ts +96 -0
  49. package/src/server/plugins/engine/types/schema.test.ts +152 -0
  50. package/src/server/plugins/engine/types/schema.ts +45 -0
  51. package/src/server/plugins/engine/types.ts +51 -1
  52. package/src/server/types.ts +2 -1
@@ -390,7 +390,7 @@ function validateFormPayload(request, page, context) {
390
390
  } = page.getFormParams(request);
391
391
 
392
392
  // Skip validation GET requests or other actions
393
- if (!request.payload || action !== FormAction.Validate) {
393
+ if (!request.payload || action && ![FormAction.Validate, FormAction.SaveAndReturn].includes(action)) {
394
394
  return context;
395
395
  }
396
396
 
@@ -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","createLogger","hasListFormField","todayAsDateOnly","findPage","getError","getPage","setPageTitles","createPage","validationOptions","opts","defaultServices","FormAction","merge","logger","FormModel","engine","schemaVersion","def","lists","sections","name","values","basePath","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","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 { 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.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\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 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 = 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.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":[]}
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","createLogger","hasListFormField","todayAsDateOnly","findPage","getError","getPage","setPageTitles","createPage","validationOptions","opts","defaultServices","FormAction","merge","logger","FormModel","engine","schemaVersion","def","lists","sections","name","values","basePath","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","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","SaveAndReturn","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 { 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.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\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 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 = 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.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 (\n !request.payload ||\n (action &&\n ![FormAction.Validate, FormAction.SaveAndReturn].includes(action))\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: 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":[]}
@@ -0,0 +1,6 @@
1
+ import { type FormMetadata, type SubmitResponsePayload } from '@defra/forms-model';
2
+ import { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js';
3
+ import { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js';
4
+ import { type DetailItem } from '~/src/server/plugins/engine/models/types.js';
5
+ import { type FormContext } from '~/src/server/plugins/engine/types.js';
6
+ export declare function format(context: FormContext, items: DetailItem[], model: FormModel, submitResponse: SubmitResponsePayload, formStatus: ReturnType<typeof checkFormStatus>, formMetadata?: FormMetadata): string;
@@ -0,0 +1,23 @@
1
+ import { format as machineV2 } from "../machine/v2.js";
2
+ import { FormAdapterSubmissionSchemaVersion } from "../../types.js";
3
+ import { FormStatus } from "../../../../routes/types.js";
4
+ export function format(context, items, model, submitResponse, formStatus, formMetadata) {
5
+ const v2DataString = machineV2(context, items, model, submitResponse, formStatus);
6
+ const v2DataParsed = JSON.parse(v2DataString);
7
+ const payload = {
8
+ meta: {
9
+ schemaVersion: FormAdapterSubmissionSchemaVersion.V1,
10
+ timestamp: new Date(),
11
+ referenceNumber: context.referenceNumber,
12
+ formName: model.name,
13
+ formId: formMetadata?.id ?? '',
14
+ formSlug: formMetadata?.slug ?? '',
15
+ status: formStatus.isPreview ? FormStatus.Draft : FormStatus.Live,
16
+ isPreview: formStatus.isPreview,
17
+ notificationEmail: formMetadata?.notificationEmail ?? ''
18
+ },
19
+ data: v2DataParsed.data
20
+ };
21
+ return JSON.stringify(payload);
22
+ }
23
+ //# sourceMappingURL=v1.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v1.js","names":["format","machineV2","FormAdapterSubmissionSchemaVersion","FormStatus","context","items","model","submitResponse","formStatus","formMetadata","v2DataString","v2DataParsed","JSON","parse","payload","meta","schemaVersion","V1","timestamp","Date","referenceNumber","formName","name","formId","id","formSlug","slug","status","isPreview","Draft","Live","notificationEmail","data","stringify"],"sources":["../../../../../../src/server/plugins/engine/outputFormatters/adapter/v1.ts"],"sourcesContent":["import {\n type FormMetadata,\n type SubmitResponsePayload\n} from '@defra/forms-model'\n\nimport { type checkFormStatus } 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 { format as machineV2 } from '~/src/server/plugins/engine/outputFormatters/machine/v2.js'\nimport {\n FormAdapterSubmissionSchemaVersion,\n type FormAdapterSubmissionMessageData,\n type FormAdapterSubmissionMessagePayload,\n type FormContext\n} from '~/src/server/plugins/engine/types.js'\nimport { FormStatus } from '~/src/server/routes/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 v2DataString = machineV2(\n context,\n items,\n model,\n submitResponse,\n formStatus\n )\n const v2DataParsed = JSON.parse(v2DataString) as {\n data: FormAdapterSubmissionMessageData\n }\n\n const payload: FormAdapterSubmissionMessagePayload = {\n meta: {\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.isPreview ? FormStatus.Draft : FormStatus.Live,\n isPreview: formStatus.isPreview,\n notificationEmail: formMetadata?.notificationEmail ?? ''\n },\n data: v2DataParsed.data\n }\n\n return JSON.stringify(payload)\n}\n"],"mappings":"AAQA,SAASA,MAAM,IAAIC,SAAS;AAC5B,SACEC,kCAAkC;AAKpC,SAASC,UAAU;AAEnB,OAAO,SAASH,MAAMA,CACpBI,OAAoB,EACpBC,KAAmB,EACnBC,KAAgB,EAChBC,cAAqC,EACrCC,UAA8C,EAC9CC,YAA2B,EACnB;EACR,MAAMC,YAAY,GAAGT,SAAS,CAC5BG,OAAO,EACPC,KAAK,EACLC,KAAK,EACLC,cAAc,EACdC,UACF,CAAC;EACD,MAAMG,YAAY,GAAGC,IAAI,CAACC,KAAK,CAACH,YAAY,CAE3C;EAED,MAAMI,OAA4C,GAAG;IACnDC,IAAI,EAAE;MACJC,aAAa,EAAEd,kCAAkC,CAACe,EAAE;MACpDC,SAAS,EAAE,IAAIC,IAAI,CAAC,CAAC;MACrBC,eAAe,EAAEhB,OAAO,CAACgB,eAAe;MACxCC,QAAQ,EAAEf,KAAK,CAACgB,IAAI;MACpBC,MAAM,EAAEd,YAAY,EAAEe,EAAE,IAAI,EAAE;MAC9BC,QAAQ,EAAEhB,YAAY,EAAEiB,IAAI,IAAI,EAAE;MAClCC,MAAM,EAAEnB,UAAU,CAACoB,SAAS,GAAGzB,UAAU,CAAC0B,KAAK,GAAG1B,UAAU,CAAC2B,IAAI;MACjEF,SAAS,EAAEpB,UAAU,CAACoB,SAAS;MAC/BG,iBAAiB,EAAEtB,YAAY,EAAEsB,iBAAiB,IAAI;IACxD,CAAC;IACDC,IAAI,EAAErB,YAAY,CAACqB;EACrB,CAAC;EAED,OAAOpB,IAAI,CAACqB,SAAS,CAACnB,OAAO,CAAC;AAChC","ignoreList":[]}
@@ -1,6 +1,6 @@
1
- import { type SubmitResponsePayload } from '@defra/forms-model';
1
+ import { type FormMetadata, type SubmitResponsePayload } from '@defra/forms-model';
2
2
  import { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js';
3
3
  import { type FormModel } from '~/src/server/plugins/engine/models/index.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
- export declare function format(_context: FormContext, items: DetailItem[], model: FormModel, submitResponse: SubmitResponsePayload, formStatus: ReturnType<typeof checkFormStatus>): string;
6
+ export declare function format(_context: FormContext, items: DetailItem[], model: FormModel, submitResponse: SubmitResponsePayload, formStatus: ReturnType<typeof checkFormStatus>, _formMetadata?: FormMetadata): string;
@@ -2,7 +2,7 @@ import { addDays, format as dateFormat } from 'date-fns';
2
2
  import { config } from "../../../../../config/index.js";
3
3
  import { escapeMarkdown, getAnswer } from "../../components/helpers.js";
4
4
  const designerUrl = config.get('designerUrl');
5
- export function format(_context, items, model, submitResponse, formStatus) {
5
+ export function format(_context, items, model, submitResponse, formStatus, _formMetadata) {
6
6
  const {
7
7
  files
8
8
  } = submitResponse.result;
@@ -1 +1 @@
1
- {"version":3,"file":"v1.js","names":["addDays","format","dateFormat","config","escapeMarkdown","getAnswer","designerUrl","get","_context","items","model","submitResponse","formStatus","files","result","formName","name","now","Date","formattedNow","fileExpiryDate","formattedExpiryDate","lines","push","isPreview","state","forEach","item","label","filename","fileId","repeaters","field","main","join"],"sources":["../../../../../../src/server/plugins/engine/outputFormatters/human/v1.ts"],"sourcesContent":["import { type SubmitResponsePayload } from '@defra/forms-model'\nimport { addDays, format as dateFormat } from 'date-fns'\n\nimport { config } from '~/src/config/index.js'\nimport {\n escapeMarkdown,\n getAnswer\n} from '~/src/server/plugins/engine/components/helpers.js'\nimport { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\n\nconst designerUrl = config.get('designerUrl')\n\nexport function format(\n _context: FormContext,\n items: DetailItem[],\n model: FormModel,\n submitResponse: SubmitResponsePayload,\n formStatus: ReturnType<typeof checkFormStatus>\n) {\n const { files } = submitResponse.result\n\n const formName = escapeMarkdown(model.name)\n\n /**\n * @todo Refactor this below but the code to\n * generate the question and answers works for now\n */\n const now = new Date()\n const formattedNow = `${dateFormat(now, 'h:mmaaa')} on ${dateFormat(now, 'd MMMM yyyy')}`\n\n const fileExpiryDate = addDays(now, 90)\n const formattedExpiryDate = `${dateFormat(fileExpiryDate, 'h:mmaaa')} on ${dateFormat(fileExpiryDate, 'eeee d MMMM yyyy')}`\n\n const lines: string[] = []\n\n lines.push(\n `^ For security reasons, the links in this email expire at ${escapeMarkdown(formattedExpiryDate)}\\n`\n )\n\n if (formStatus.isPreview) {\n lines.push(`This is a test of the ${formName} ${formStatus.state} form.\\n`)\n }\n\n lines.push(`${formName} form received at ${escapeMarkdown(formattedNow)}.\\n`)\n lines.push('---\\n')\n\n items.forEach((item) => {\n const label = escapeMarkdown(item.label)\n\n lines.push(`## ${label}\\n`)\n\n if ('subItems' in item) {\n const filename = escapeMarkdown(`Download ${label} (CSV)`)\n const fileId = files.repeaters[item.name]\n\n lines.push(`[${filename}](${designerUrl}/file-download/${fileId})\\n`)\n } else {\n lines.push(\n getAnswer(item.field, item.state, {\n format: 'email'\n })\n )\n }\n\n lines.push('---\\n')\n })\n\n const filename = escapeMarkdown('Download main form (CSV)')\n lines.push(`[${filename}](${designerUrl}/file-download/${files.main})\\n`)\n\n return lines.join('\\n')\n}\n"],"mappings":"AACA,SAASA,OAAO,EAAEC,MAAM,IAAIC,UAAU,QAAQ,UAAU;AAExD,SAASC,MAAM;AACf,SACEC,cAAc,EACdC,SAAS;AAOX,MAAMC,WAAW,GAAGH,MAAM,CAACI,GAAG,CAAC,aAAa,CAAC;AAE7C,OAAO,SAASN,MAAMA,CACpBO,QAAqB,EACrBC,KAAmB,EACnBC,KAAgB,EAChBC,cAAqC,EACrCC,UAA8C,EAC9C;EACA,MAAM;IAAEC;EAAM,CAAC,GAAGF,cAAc,CAACG,MAAM;EAEvC,MAAMC,QAAQ,GAAGX,cAAc,CAACM,KAAK,CAACM,IAAI,CAAC;;EAE3C;AACF;AACA;AACA;EACE,MAAMC,GAAG,GAAG,IAAIC,IAAI,CAAC,CAAC;EACtB,MAAMC,YAAY,GAAG,GAAGjB,UAAU,CAACe,GAAG,EAAE,SAAS,CAAC,OAAOf,UAAU,CAACe,GAAG,EAAE,aAAa,CAAC,EAAE;EAEzF,MAAMG,cAAc,GAAGpB,OAAO,CAACiB,GAAG,EAAE,EAAE,CAAC;EACvC,MAAMI,mBAAmB,GAAG,GAAGnB,UAAU,CAACkB,cAAc,EAAE,SAAS,CAAC,OAAOlB,UAAU,CAACkB,cAAc,EAAE,kBAAkB,CAAC,EAAE;EAE3H,MAAME,KAAe,GAAG,EAAE;EAE1BA,KAAK,CAACC,IAAI,CACR,6DAA6DnB,cAAc,CAACiB,mBAAmB,CAAC,IAClG,CAAC;EAED,IAAIT,UAAU,CAACY,SAAS,EAAE;IACxBF,KAAK,CAACC,IAAI,CAAC,yBAAyBR,QAAQ,IAAIH,UAAU,CAACa,KAAK,UAAU,CAAC;EAC7E;EAEAH,KAAK,CAACC,IAAI,CAAC,GAAGR,QAAQ,qBAAqBX,cAAc,CAACe,YAAY,CAAC,KAAK,CAAC;EAC7EG,KAAK,CAACC,IAAI,CAAC,OAAO,CAAC;EAEnBd,KAAK,CAACiB,OAAO,CAAEC,IAAI,IAAK;IACtB,MAAMC,KAAK,GAAGxB,cAAc,CAACuB,IAAI,CAACC,KAAK,CAAC;IAExCN,KAAK,CAACC,IAAI,CAAC,MAAMK,KAAK,IAAI,CAAC;IAE3B,IAAI,UAAU,IAAID,IAAI,EAAE;MACtB,MAAME,QAAQ,GAAGzB,cAAc,CAAC,YAAYwB,KAAK,QAAQ,CAAC;MAC1D,MAAME,MAAM,GAAGjB,KAAK,CAACkB,SAAS,CAACJ,IAAI,CAACX,IAAI,CAAC;MAEzCM,KAAK,CAACC,IAAI,CAAC,IAAIM,QAAQ,KAAKvB,WAAW,kBAAkBwB,MAAM,KAAK,CAAC;IACvE,CAAC,MAAM;MACLR,KAAK,CAACC,IAAI,CACRlB,SAAS,CAACsB,IAAI,CAACK,KAAK,EAAEL,IAAI,CAACF,KAAK,EAAE;QAChCxB,MAAM,EAAE;MACV,CAAC,CACH,CAAC;IACH;IAEAqB,KAAK,CAACC,IAAI,CAAC,OAAO,CAAC;EACrB,CAAC,CAAC;EAEF,MAAMM,QAAQ,GAAGzB,cAAc,CAAC,0BAA0B,CAAC;EAC3DkB,KAAK,CAACC,IAAI,CAAC,IAAIM,QAAQ,KAAKvB,WAAW,kBAAkBO,KAAK,CAACoB,IAAI,KAAK,CAAC;EAEzE,OAAOX,KAAK,CAACY,IAAI,CAAC,IAAI,CAAC;AACzB","ignoreList":[]}
1
+ {"version":3,"file":"v1.js","names":["addDays","format","dateFormat","config","escapeMarkdown","getAnswer","designerUrl","get","_context","items","model","submitResponse","formStatus","_formMetadata","files","result","formName","name","now","Date","formattedNow","fileExpiryDate","formattedExpiryDate","lines","push","isPreview","state","forEach","item","label","filename","fileId","repeaters","field","main","join"],"sources":["../../../../../../src/server/plugins/engine/outputFormatters/human/v1.ts"],"sourcesContent":["import {\n type FormMetadata,\n type SubmitResponsePayload\n} from '@defra/forms-model'\nimport { addDays, format as dateFormat } from 'date-fns'\n\nimport { config } from '~/src/config/index.js'\nimport {\n escapeMarkdown,\n getAnswer\n} from '~/src/server/plugins/engine/components/helpers.js'\nimport { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\n\nconst designerUrl = config.get('designerUrl')\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) {\n const { files } = submitResponse.result\n\n const formName = escapeMarkdown(model.name)\n\n /**\n * @todo Refactor this below but the code to\n * generate the question and answers works for now\n */\n const now = new Date()\n const formattedNow = `${dateFormat(now, 'h:mmaaa')} on ${dateFormat(now, 'd MMMM yyyy')}`\n\n const fileExpiryDate = addDays(now, 90)\n const formattedExpiryDate = `${dateFormat(fileExpiryDate, 'h:mmaaa')} on ${dateFormat(fileExpiryDate, 'eeee d MMMM yyyy')}`\n\n const lines: string[] = []\n\n lines.push(\n `^ For security reasons, the links in this email expire at ${escapeMarkdown(formattedExpiryDate)}\\n`\n )\n\n if (formStatus.isPreview) {\n lines.push(`This is a test of the ${formName} ${formStatus.state} form.\\n`)\n }\n\n lines.push(`${formName} form received at ${escapeMarkdown(formattedNow)}.\\n`)\n lines.push('---\\n')\n\n items.forEach((item) => {\n const label = escapeMarkdown(item.label)\n\n lines.push(`## ${label}\\n`)\n\n if ('subItems' in item) {\n const filename = escapeMarkdown(`Download ${label} (CSV)`)\n const fileId = files.repeaters[item.name]\n\n lines.push(`[${filename}](${designerUrl}/file-download/${fileId})\\n`)\n } else {\n lines.push(\n getAnswer(item.field, item.state, {\n format: 'email'\n })\n )\n }\n\n lines.push('---\\n')\n })\n\n const filename = escapeMarkdown('Download main form (CSV)')\n lines.push(`[${filename}](${designerUrl}/file-download/${files.main})\\n`)\n\n return lines.join('\\n')\n}\n"],"mappings":"AAIA,SAASA,OAAO,EAAEC,MAAM,IAAIC,UAAU,QAAQ,UAAU;AAExD,SAASC,MAAM;AACf,SACEC,cAAc,EACdC,SAAS;AAOX,MAAMC,WAAW,GAAGH,MAAM,CAACI,GAAG,CAAC,aAAa,CAAC;AAE7C,OAAO,SAASN,MAAMA,CACpBO,QAAqB,EACrBC,KAAmB,EACnBC,KAAgB,EAChBC,cAAqC,EACrCC,UAA8C,EAC9CC,aAA4B,EAC5B;EACA,MAAM;IAAEC;EAAM,CAAC,GAAGH,cAAc,CAACI,MAAM;EAEvC,MAAMC,QAAQ,GAAGZ,cAAc,CAACM,KAAK,CAACO,IAAI,CAAC;;EAE3C;AACF;AACA;AACA;EACE,MAAMC,GAAG,GAAG,IAAIC,IAAI,CAAC,CAAC;EACtB,MAAMC,YAAY,GAAG,GAAGlB,UAAU,CAACgB,GAAG,EAAE,SAAS,CAAC,OAAOhB,UAAU,CAACgB,GAAG,EAAE,aAAa,CAAC,EAAE;EAEzF,MAAMG,cAAc,GAAGrB,OAAO,CAACkB,GAAG,EAAE,EAAE,CAAC;EACvC,MAAMI,mBAAmB,GAAG,GAAGpB,UAAU,CAACmB,cAAc,EAAE,SAAS,CAAC,OAAOnB,UAAU,CAACmB,cAAc,EAAE,kBAAkB,CAAC,EAAE;EAE3H,MAAME,KAAe,GAAG,EAAE;EAE1BA,KAAK,CAACC,IAAI,CACR,6DAA6DpB,cAAc,CAACkB,mBAAmB,CAAC,IAClG,CAAC;EAED,IAAIV,UAAU,CAACa,SAAS,EAAE;IACxBF,KAAK,CAACC,IAAI,CAAC,yBAAyBR,QAAQ,IAAIJ,UAAU,CAACc,KAAK,UAAU,CAAC;EAC7E;EAEAH,KAAK,CAACC,IAAI,CAAC,GAAGR,QAAQ,qBAAqBZ,cAAc,CAACgB,YAAY,CAAC,KAAK,CAAC;EAC7EG,KAAK,CAACC,IAAI,CAAC,OAAO,CAAC;EAEnBf,KAAK,CAACkB,OAAO,CAAEC,IAAI,IAAK;IACtB,MAAMC,KAAK,GAAGzB,cAAc,CAACwB,IAAI,CAACC,KAAK,CAAC;IAExCN,KAAK,CAACC,IAAI,CAAC,MAAMK,KAAK,IAAI,CAAC;IAE3B,IAAI,UAAU,IAAID,IAAI,EAAE;MACtB,MAAME,QAAQ,GAAG1B,cAAc,CAAC,YAAYyB,KAAK,QAAQ,CAAC;MAC1D,MAAME,MAAM,GAAGjB,KAAK,CAACkB,SAAS,CAACJ,IAAI,CAACX,IAAI,CAAC;MAEzCM,KAAK,CAACC,IAAI,CAAC,IAAIM,QAAQ,KAAKxB,WAAW,kBAAkByB,MAAM,KAAK,CAAC;IACvE,CAAC,MAAM;MACLR,KAAK,CAACC,IAAI,CACRnB,SAAS,CAACuB,IAAI,CAACK,KAAK,EAAEL,IAAI,CAACF,KAAK,EAAE;QAChCzB,MAAM,EAAE;MACV,CAAC,CACH,CAAC;IACH;IAEAsB,KAAK,CAACC,IAAI,CAAC,OAAO,CAAC;EACrB,CAAC,CAAC;EAEF,MAAMM,QAAQ,GAAG1B,cAAc,CAAC,0BAA0B,CAAC;EAC3DmB,KAAK,CAACC,IAAI,CAAC,IAAIM,QAAQ,KAAKxB,WAAW,kBAAkBQ,KAAK,CAACoB,IAAI,KAAK,CAAC;EAEzE,OAAOX,KAAK,CAACY,IAAI,CAAC,IAAI,CAAC;AACzB","ignoreList":[]}
@@ -1,8 +1,9 @@
1
- import { type SubmitResponsePayload } from '@defra/forms-model';
1
+ import { type FormMetadata, type SubmitResponsePayload } from '@defra/forms-model';
2
2
  import { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js';
3
3
  import { type FormModel } from '~/src/server/plugins/engine/models/index.js';
4
4
  import { type DetailItem } from '~/src/server/plugins/engine/models/types.js';
5
+ import { format as formatAdapterV1 } from '~/src/server/plugins/engine/outputFormatters/adapter/v1.js';
5
6
  import { type FormContext } from '~/src/server/plugins/engine/types.js';
6
- type Formatter = (context: FormContext, items: DetailItem[], model: FormModel, submitResponse: SubmitResponsePayload, formStatus: ReturnType<typeof checkFormStatus>) => string;
7
- export declare function getFormatter(audience: string, version: string): Formatter;
7
+ type Formatter = (context: FormContext, items: DetailItem[], model: FormModel, submitResponse: SubmitResponsePayload, formStatus: ReturnType<typeof checkFormStatus>, formMetadata?: FormMetadata) => string;
8
+ export declare function getFormatter(audience: string, version: string): Formatter | typeof formatAdapterV1;
8
9
  export {};
@@ -1,3 +1,4 @@
1
+ import { format as formatAdapterV1 } from "./adapter/v1.js";
1
2
  import { format as formatHumanV1 } from "./human/v1.js";
2
3
  import { format as formatMachineV1 } from "./machine/v1.js";
3
4
  import { format as formatMachineV2 } from "./machine/v2.js";
@@ -8,6 +9,9 @@ const formatters = {
8
9
  machine: {
9
10
  '1': formatMachineV1,
10
11
  '2': formatMachineV2
12
+ },
13
+ adapter: {
14
+ '1': formatAdapterV1
11
15
  }
12
16
  };
13
17
  export function getFormatter(audience, version) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["format","formatHumanV1","formatMachineV1","formatMachineV2","formatters","human","machine","getFormatter","audience","version","versions","Error","formatter"],"sources":["../../../../../src/server/plugins/engine/outputFormatters/index.ts"],"sourcesContent":["import { type SubmitResponsePayload } from '@defra/forms-model'\n\nimport { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { format as formatHumanV1 } from '~/src/server/plugins/engine/outputFormatters/human/v1.js'\nimport { format as formatMachineV1 } from '~/src/server/plugins/engine/outputFormatters/machine/v1.js'\nimport { format as formatMachineV2 } from '~/src/server/plugins/engine/outputFormatters/machine/v2.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\n\ntype Formatter = (\n context: FormContext,\n items: DetailItem[],\n model: FormModel,\n submitResponse: SubmitResponsePayload,\n formStatus: ReturnType<typeof checkFormStatus>\n) => string\n\nconst formatters: Record<\n string,\n Record<string, Formatter | undefined> | undefined\n> = {\n human: {\n '1': formatHumanV1\n },\n machine: {\n '1': formatMachineV1,\n '2': formatMachineV2\n }\n}\n\nexport function getFormatter(audience: string, version: string) {\n const versions = formatters[audience]\n\n if (!versions) {\n throw new Error('Unknown audience')\n }\n\n const formatter = versions[version]\n\n if (!formatter) {\n throw new Error('Unknown version')\n }\n\n return formatter\n}\n"],"mappings":"AAKA,SAASA,MAAM,IAAIC,aAAa;AAChC,SAASD,MAAM,IAAIE,eAAe;AAClC,SAASF,MAAM,IAAIG,eAAe;AAWlC,MAAMC,UAGL,GAAG;EACFC,KAAK,EAAE;IACL,GAAG,EAAEJ;EACP,CAAC;EACDK,OAAO,EAAE;IACP,GAAG,EAAEJ,eAAe;IACpB,GAAG,EAAEC;EACP;AACF,CAAC;AAED,OAAO,SAASI,YAAYA,CAACC,QAAgB,EAAEC,OAAe,EAAE;EAC9D,MAAMC,QAAQ,GAAGN,UAAU,CAACI,QAAQ,CAAC;EAErC,IAAI,CAACE,QAAQ,EAAE;IACb,MAAM,IAAIC,KAAK,CAAC,kBAAkB,CAAC;EACrC;EAEA,MAAMC,SAAS,GAAGF,QAAQ,CAACD,OAAO,CAAC;EAEnC,IAAI,CAACG,SAAS,EAAE;IACd,MAAM,IAAID,KAAK,CAAC,iBAAiB,CAAC;EACpC;EAEA,OAAOC,SAAS;AAClB","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["format","formatAdapterV1","formatHumanV1","formatMachineV1","formatMachineV2","formatters","human","machine","adapter","getFormatter","audience","version","versions","Error","formatter"],"sources":["../../../../../src/server/plugins/engine/outputFormatters/index.ts"],"sourcesContent":["import {\n type FormMetadata,\n type SubmitResponsePayload\n} from '@defra/forms-model'\n\nimport { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { format as formatAdapterV1 } from '~/src/server/plugins/engine/outputFormatters/adapter/v1.js'\nimport { format as formatHumanV1 } from '~/src/server/plugins/engine/outputFormatters/human/v1.js'\nimport { format as formatMachineV1 } from '~/src/server/plugins/engine/outputFormatters/machine/v1.js'\nimport { format as formatMachineV2 } from '~/src/server/plugins/engine/outputFormatters/machine/v2.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\n\ntype Formatter = (\n context: FormContext,\n items: DetailItem[],\n model: FormModel,\n submitResponse: SubmitResponsePayload,\n formStatus: ReturnType<typeof checkFormStatus>,\n formMetadata?: FormMetadata\n) => string\n\nconst formatters: Record<\n string,\n Record<string, Formatter | typeof formatAdapterV1 | undefined> | undefined\n> = {\n human: {\n '1': formatHumanV1\n },\n machine: {\n '1': formatMachineV1,\n '2': formatMachineV2\n },\n adapter: {\n '1': formatAdapterV1\n }\n}\n\nexport function getFormatter(audience: string, version: string) {\n const versions = formatters[audience]\n\n if (!versions) {\n throw new Error('Unknown audience')\n }\n\n const formatter = versions[version]\n\n if (!formatter) {\n throw new Error('Unknown version')\n }\n\n return formatter\n}\n"],"mappings":"AAQA,SAASA,MAAM,IAAIC,eAAe;AAClC,SAASD,MAAM,IAAIE,aAAa;AAChC,SAASF,MAAM,IAAIG,eAAe;AAClC,SAASH,MAAM,IAAII,eAAe;AAYlC,MAAMC,UAGL,GAAG;EACFC,KAAK,EAAE;IACL,GAAG,EAAEJ;EACP,CAAC;EACDK,OAAO,EAAE;IACP,GAAG,EAAEJ,eAAe;IACpB,GAAG,EAAEC;EACP,CAAC;EACDI,OAAO,EAAE;IACP,GAAG,EAAEP;EACP;AACF,CAAC;AAED,OAAO,SAASQ,YAAYA,CAACC,QAAgB,EAAEC,OAAe,EAAE;EAC9D,MAAMC,QAAQ,GAAGP,UAAU,CAACK,QAAQ,CAAC;EAErC,IAAI,CAACE,QAAQ,EAAE;IACb,MAAM,IAAIC,KAAK,CAAC,kBAAkB,CAAC;EACrC;EAEA,MAAMC,SAAS,GAAGF,QAAQ,CAACD,OAAO,CAAC;EAEnC,IAAI,CAACG,SAAS,EAAE;IACd,MAAM,IAAID,KAAK,CAAC,iBAAiB,CAAC;EACpC;EAEA,OAAOC,SAAS;AAClB","ignoreList":[]}
@@ -1,6 +1,6 @@
1
- import { type SubmitResponsePayload } from '@defra/forms-model';
1
+ import { type FormMetadata, type SubmitResponsePayload } from '@defra/forms-model';
2
2
  import { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js';
3
3
  import { type FormModel } from '~/src/server/plugins/engine/models/index.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
- export declare function format(context: FormContext, items: DetailItem[], model: FormModel, _submitResponse: SubmitResponsePayload, _formStatus: ReturnType<typeof checkFormStatus>): string;
6
+ export declare function format(context: FormContext, items: DetailItem[], model: FormModel, _submitResponse: SubmitResponsePayload, _formStatus: ReturnType<typeof checkFormStatus>, _formMetadata?: FormMetadata): string;
@@ -2,7 +2,7 @@ import { config } from "../../../../../config/index.js";
2
2
  import { getAnswer } from "../../components/helpers.js";
3
3
  import { FileUploadField } from "../../components/index.js";
4
4
  const designerUrl = config.get('designerUrl');
5
- export function format(context, items, model, _submitResponse, _formStatus) {
5
+ export function format(context, items, model, _submitResponse, _formStatus, _formMetadata) {
6
6
  const now = new Date();
7
7
  const categorisedData = categoriseData(items);
8
8
  const data = {
@@ -1 +1 @@
1
- {"version":3,"file":"v1.js","names":["config","getAnswer","FileUploadField","designerUrl","get","format","context","items","model","_submitResponse","_formStatus","now","Date","categorisedData","categoriseData","data","meta","schemaVersion","timestamp","toISOString","referenceNumber","definition","def","body","JSON","stringify","output","main","repeaters","files","forEach","item","name","extractRepeaters","isFileUploadFieldItem","extractFileUploads","field","state","subItems","inputRepeaterItem","outputRepeaterItem","repeaterComponent","push","fileUploadState","getContextValueFromState","map","fileId","userDownloadLink"],"sources":["../../../../../../src/server/plugins/engine/outputFormatters/machine/v1.ts"],"sourcesContent":["import { type SubmitResponsePayload } from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { getAnswer } from '~/src/server/plugins/engine/components/helpers.js'\nimport { FileUploadField } from '~/src/server/plugins/engine/components/index.js'\nimport { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport {\n type DetailItem,\n type DetailItemField,\n type DetailItemRepeat\n} from '~/src/server/plugins/engine/models/types.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\n\nconst designerUrl = config.get('designerUrl')\n\nexport function format(\n context: FormContext,\n items: DetailItem[],\n model: FormModel,\n _submitResponse: SubmitResponsePayload,\n _formStatus: ReturnType<typeof checkFormStatus>\n) {\n const now = new Date()\n\n const categorisedData = categoriseData(items)\n\n const data = {\n meta: {\n schemaVersion: '1',\n timestamp: now.toISOString(),\n referenceNumber: context.referenceNumber,\n definition: model.def\n },\n data: categorisedData\n }\n\n const body = JSON.stringify(data)\n\n return body\n}\n\n/**\n * Categories the form submission data into the \"main\" body and \"repeaters\".\n *\n * {\n * main: {\n * componentName: 'componentValue',\n * },\n * repeaters: {\n * repeaterName: [\n * {\n * componentName: 'componentValue'\n * }\n * ]\n * },\n * files: {\n * fileComponentName: [\n * {\n * fileId: '123-456-789',\n * link: 'https://forms-designer/file-download/123-456-789'\n * }\n * ]\n * }\n * }\n */\nfunction categoriseData(items: DetailItem[]) {\n const output: {\n main: Record<string, string>\n repeaters: Record<string, Record<string, string>[]>\n files: Record<string, Record<string, string>[]>\n } = { main: {}, repeaters: {}, files: {} }\n\n items.forEach((item) => {\n if ('subItems' in item) {\n output.repeaters[item.name] = extractRepeaters(item)\n } else if (isFileUploadFieldItem(item)) {\n output.files[item.name] = extractFileUploads(item)\n } else {\n output.main[item.name] = getAnswer(item.field, item.state, {\n format: 'data'\n })\n }\n })\n\n return output\n}\n\n/**\n * Returns the \"repeaters\" section of the response body\n * @param item - the repeater item\n * @returns the repeater item\n */\nfunction extractRepeaters(item: DetailItemRepeat) {\n const repeaters: Record<string, string>[] = []\n\n item.subItems.forEach((inputRepeaterItem) => {\n const outputRepeaterItem: Record<string, string> = {}\n\n inputRepeaterItem.forEach((repeaterComponent) => {\n outputRepeaterItem[repeaterComponent.name] = getAnswer(\n repeaterComponent.field,\n repeaterComponent.state,\n {\n format: 'data'\n }\n )\n })\n\n repeaters.push(outputRepeaterItem)\n })\n\n return repeaters\n}\n\n/**\n * Returns the \"files\" section of the response body\n * @param item - the file upload item in the form\n * @returns the file upload data\n */\nfunction extractFileUploads(item: FileUploadFieldDetailitem) {\n const fileUploadState = item.field.getContextValueFromState(item.state) ?? []\n\n return fileUploadState.map((fileId) => {\n return {\n fileId,\n userDownloadLink: `${designerUrl}/file-download/${fileId}`\n }\n })\n}\n\nfunction isFileUploadFieldItem(\n item: DetailItemField\n): item is FileUploadFieldDetailitem {\n return item.field instanceof FileUploadField\n}\n\n/**\n * A detail item specifically for files\n */\ntype FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"v1.js","names":["config","getAnswer","FileUploadField","designerUrl","get","format","context","items","model","_submitResponse","_formStatus","_formMetadata","now","Date","categorisedData","categoriseData","data","meta","schemaVersion","timestamp","toISOString","referenceNumber","definition","def","body","JSON","stringify","output","main","repeaters","files","forEach","item","name","extractRepeaters","isFileUploadFieldItem","extractFileUploads","field","state","subItems","inputRepeaterItem","outputRepeaterItem","repeaterComponent","push","fileUploadState","getContextValueFromState","map","fileId","userDownloadLink"],"sources":["../../../../../../src/server/plugins/engine/outputFormatters/machine/v1.ts"],"sourcesContent":["import {\n type FormMetadata,\n type SubmitResponsePayload\n} from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { getAnswer } from '~/src/server/plugins/engine/components/helpers.js'\nimport { FileUploadField } from '~/src/server/plugins/engine/components/index.js'\nimport { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport {\n type DetailItem,\n type DetailItemField,\n type DetailItemRepeat\n} from '~/src/server/plugins/engine/models/types.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\n\nconst designerUrl = config.get('designerUrl')\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) {\n const now = new Date()\n\n const categorisedData = categoriseData(items)\n\n const data = {\n meta: {\n schemaVersion: '1',\n timestamp: now.toISOString(),\n referenceNumber: context.referenceNumber,\n definition: model.def\n },\n data: categorisedData\n }\n\n const body = JSON.stringify(data)\n\n return body\n}\n\n/**\n * Categories the form submission data into the \"main\" body and \"repeaters\".\n *\n * {\n * main: {\n * componentName: 'componentValue',\n * },\n * repeaters: {\n * repeaterName: [\n * {\n * componentName: 'componentValue'\n * }\n * ]\n * },\n * files: {\n * fileComponentName: [\n * {\n * fileId: '123-456-789',\n * link: 'https://forms-designer/file-download/123-456-789'\n * }\n * ]\n * }\n * }\n */\nfunction categoriseData(items: DetailItem[]) {\n const output: {\n main: Record<string, string>\n repeaters: Record<string, Record<string, string>[]>\n files: Record<string, Record<string, string>[]>\n } = { main: {}, repeaters: {}, files: {} }\n\n items.forEach((item) => {\n if ('subItems' in item) {\n output.repeaters[item.name] = extractRepeaters(item)\n } else if (isFileUploadFieldItem(item)) {\n output.files[item.name] = extractFileUploads(item)\n } else {\n output.main[item.name] = getAnswer(item.field, item.state, {\n format: 'data'\n })\n }\n })\n\n return output\n}\n\n/**\n * Returns the \"repeaters\" section of the response body\n * @param item - the repeater item\n * @returns the repeater item\n */\nfunction extractRepeaters(item: DetailItemRepeat) {\n const repeaters: Record<string, string>[] = []\n\n item.subItems.forEach((inputRepeaterItem) => {\n const outputRepeaterItem: Record<string, string> = {}\n\n inputRepeaterItem.forEach((repeaterComponent) => {\n outputRepeaterItem[repeaterComponent.name] = getAnswer(\n repeaterComponent.field,\n repeaterComponent.state,\n {\n format: 'data'\n }\n )\n })\n\n repeaters.push(outputRepeaterItem)\n })\n\n return repeaters\n}\n\n/**\n * Returns the \"files\" section of the response body\n * @param item - the file upload item in the form\n * @returns the file upload data\n */\nfunction extractFileUploads(item: FileUploadFieldDetailitem) {\n const fileUploadState = item.field.getContextValueFromState(item.state) ?? []\n\n return fileUploadState.map((fileId) => {\n return {\n fileId,\n userDownloadLink: `${designerUrl}/file-download/${fileId}`\n }\n })\n}\n\nfunction isFileUploadFieldItem(\n item: DetailItemField\n): item is FileUploadFieldDetailitem {\n return item.field instanceof FileUploadField\n}\n\n/**\n * A detail item specifically for files\n */\ntype FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,6 +1,9 @@
1
1
  import { type SubmitResponsePayload } from '@defra/forms-model';
2
+ import { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js';
3
+ import { type DatePartsState, type MonthYearState } from '~/src/server/plugins/engine/components/types.js';
2
4
  import { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js';
3
5
  import { type FormModel } from '~/src/server/plugins/engine/models/index.js';
4
6
  import { type DetailItem } from '~/src/server/plugins/engine/models/types.js';
5
- import { type FormContext } from '~/src/server/plugins/engine/types.js';
7
+ import { type FormContext, type FormPayload, type FormValue } from '~/src/server/plugins/engine/types.js';
6
8
  export declare function format(context: FormContext, items: DetailItem[], model: FormModel, _submitResponse: SubmitResponsePayload, _formStatus: ReturnType<typeof checkFormStatus>): string;
9
+ export type RichFormValue = FormValue | FormPayload | DatePartsState | MonthYearState | UkAddressState;
@@ -1 +1 @@
1
- {"version":3,"file":"v2.js","names":["config","FileUploadField","designerUrl","get","format","context","items","model","_submitResponse","_formStatus","now","Date","categorisedData","categoriseData","data","meta","schemaVersion","timestamp","toISOString","definition","def","referenceNumber","body","JSON","stringify","output","main","repeaters","files","forEach","item","name","state","extractRepeaters","isFileUploadFieldItem","extractFileUploads","field","getFormValueFromState","subItems","inputRepeaterItem","outputRepeaterItem","repeaterComponent","push","fileUploadState","getContextValueFromState","map","fileId","userDownloadLink"],"sources":["../../../../../../src/server/plugins/engine/outputFormatters/machine/v2.ts"],"sourcesContent":["import { type SubmitResponsePayload } from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { FileUploadField } from '~/src/server/plugins/engine/components/index.js'\nimport {\n type DatePartsState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport {\n type DetailItem,\n type DetailItemField,\n type DetailItemRepeat\n} from '~/src/server/plugins/engine/models/types.js'\nimport {\n type FormContext,\n type FormPayload,\n type FormValue\n} from '~/src/server/plugins/engine/types.js'\n\nconst designerUrl = config.get('designerUrl')\n\nexport function format(\n context: FormContext,\n items: DetailItem[],\n model: FormModel,\n _submitResponse: SubmitResponsePayload,\n _formStatus: ReturnType<typeof checkFormStatus>\n) {\n const now = new Date()\n\n const categorisedData = categoriseData(items)\n\n const data = {\n meta: {\n schemaVersion: '2',\n timestamp: now.toISOString(),\n definition: model.def,\n referenceNumber: context.referenceNumber\n },\n data: categorisedData\n }\n\n const body = JSON.stringify(data)\n\n return body\n}\n\n/**\n * Categories the form submission data into the \"main\" body and \"repeaters\".\n *\n * {\n * main: {\n * componentName: 'componentValue',\n * },\n * repeaters: {\n * repeaterName: [\n * {\n * textComponentName: 'componentValue'\n * },\n * {\n * richComponentName: { foo: 'bar', 'baz': true }\n * }\n * ]\n * },\n * files: {\n * fileComponentName: [\n * {\n * fileId: '123-456-789',\n * link: 'https://forms-designer/file-download/123-456-789'\n * }\n * ]\n * }\n * }\n */\nfunction categoriseData(items: DetailItem[]) {\n const output: {\n main: Record<string, RichFormValue>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, Record<string, string>[]>\n } = { main: {}, repeaters: {}, files: {} }\n\n items.forEach((item) => {\n const { name, state } = item\n\n if ('subItems' in item) {\n output.repeaters[name] = extractRepeaters(item)\n } else if (isFileUploadFieldItem(item)) {\n output.files[name] = extractFileUploads(item)\n } else {\n output.main[name] = item.field.getFormValueFromState(state)\n }\n })\n\n return output\n}\n\n/**\n * Returns the \"repeaters\" section of the response body\n * @param item - the repeater item\n * @returns the repeater item\n */\nfunction extractRepeaters(item: DetailItemRepeat) {\n const repeaters: Record<string, RichFormValue>[] = []\n\n item.subItems.forEach((inputRepeaterItem) => {\n const outputRepeaterItem: Record<string, RichFormValue> = {}\n\n inputRepeaterItem.forEach((repeaterComponent) => {\n const { field, state } = repeaterComponent\n\n outputRepeaterItem[repeaterComponent.name] =\n field.getFormValueFromState(state)\n })\n\n repeaters.push(outputRepeaterItem)\n })\n\n return repeaters\n}\n\n/**\n * Returns the \"files\" section of the response body\n * @param item - the file upload item in the form\n * @returns the file upload data\n */\nfunction extractFileUploads(item: FileUploadFieldDetailitem) {\n const fileUploadState = item.field.getContextValueFromState(item.state) ?? []\n\n return fileUploadState.map((fileId) => {\n return {\n fileId,\n userDownloadLink: `${designerUrl}/file-download/${fileId}`\n }\n })\n}\n\nfunction isFileUploadFieldItem(\n item: DetailItemField\n): item is FileUploadFieldDetailitem {\n return item.field instanceof FileUploadField\n}\n\n/**\n * A detail item specifically for files\n */\ntype FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\n\ntype RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"v2.js","names":["config","FileUploadField","designerUrl","get","format","context","items","model","_submitResponse","_formStatus","now","Date","categorisedData","categoriseData","data","meta","schemaVersion","timestamp","toISOString","definition","def","referenceNumber","body","JSON","stringify","output","main","repeaters","files","forEach","item","name","state","extractRepeaters","isFileUploadFieldItem","extractFileUploads","field","getFormValueFromState","subItems","inputRepeaterItem","outputRepeaterItem","repeaterComponent","push","fileUploadState","getContextValueFromState","map","fileId","userDownloadLink"],"sources":["../../../../../../src/server/plugins/engine/outputFormatters/machine/v2.ts"],"sourcesContent":["import { type SubmitResponsePayload } from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { FileUploadField } from '~/src/server/plugins/engine/components/index.js'\nimport {\n type DatePartsState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport {\n type DetailItem,\n type DetailItemField,\n type DetailItemRepeat\n} from '~/src/server/plugins/engine/models/types.js'\nimport {\n type FormContext,\n type FormPayload,\n type FormValue\n} from '~/src/server/plugins/engine/types.js'\n\nconst designerUrl = config.get('designerUrl')\n\nexport function format(\n context: FormContext,\n items: DetailItem[],\n model: FormModel,\n _submitResponse: SubmitResponsePayload,\n _formStatus: ReturnType<typeof checkFormStatus>\n) {\n const now = new Date()\n\n const categorisedData = categoriseData(items)\n\n const data = {\n meta: {\n schemaVersion: '2',\n timestamp: now.toISOString(),\n definition: model.def,\n referenceNumber: context.referenceNumber\n },\n data: categorisedData\n }\n\n const body = JSON.stringify(data)\n\n return body\n}\n\n/**\n * Categories the form submission data into the \"main\" body and \"repeaters\".\n *\n * {\n * main: {\n * componentName: 'componentValue',\n * },\n * repeaters: {\n * repeaterName: [\n * {\n * textComponentName: 'componentValue'\n * },\n * {\n * richComponentName: { foo: 'bar', 'baz': true }\n * }\n * ]\n * },\n * files: {\n * fileComponentName: [\n * {\n * fileId: '123-456-789',\n * link: 'https://forms-designer/file-download/123-456-789'\n * }\n * ]\n * }\n * }\n */\nfunction categoriseData(items: DetailItem[]) {\n const output: {\n main: Record<string, RichFormValue>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, Record<string, string>[]>\n } = { main: {}, repeaters: {}, files: {} }\n\n items.forEach((item) => {\n const { name, state } = item\n\n if ('subItems' in item) {\n output.repeaters[name] = extractRepeaters(item)\n } else if (isFileUploadFieldItem(item)) {\n output.files[name] = extractFileUploads(item)\n } else {\n output.main[name] = item.field.getFormValueFromState(state)\n }\n })\n\n return output\n}\n\n/**\n * Returns the \"repeaters\" section of the response body\n * @param item - the repeater item\n * @returns the repeater item\n */\nfunction extractRepeaters(item: DetailItemRepeat) {\n const repeaters: Record<string, RichFormValue>[] = []\n\n item.subItems.forEach((inputRepeaterItem) => {\n const outputRepeaterItem: Record<string, RichFormValue> = {}\n\n inputRepeaterItem.forEach((repeaterComponent) => {\n const { field, state } = repeaterComponent\n\n outputRepeaterItem[repeaterComponent.name] =\n field.getFormValueFromState(state)\n })\n\n repeaters.push(outputRepeaterItem)\n })\n\n return repeaters\n}\n\n/**\n * Returns the \"files\" section of the response body\n * @param item - the file upload item in the form\n * @returns the file upload data\n */\nfunction extractFileUploads(item: FileUploadFieldDetailitem) {\n const fileUploadState = item.field.getContextValueFromState(item.state) ?? []\n\n return fileUploadState.map((fileId) => {\n return {\n fileId,\n userDownloadLink: `${designerUrl}/file-download/${fileId}`\n }\n })\n}\n\nfunction isFileUploadFieldItem(\n item: DetailItemField\n): item is FileUploadFieldDetailitem {\n return item.field instanceof FileUploadField\n}\n\n/**\n * A detail item specifically for files\n */\ntype FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\n\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n"],"mappings":"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","ignoreList":[]}
@@ -77,9 +77,10 @@ export class SummaryPageController extends QuestionPageController {
77
77
  } = formsService;
78
78
 
79
79
  // Get the form metadata using the `slug` param
80
+ const formMetadata = await getFormMetadata(params.slug);
80
81
  const {
81
82
  notificationEmail
82
- } = await getFormMetadata(params.slug);
83
+ } = formMetadata;
83
84
  const {
84
85
  isPreview
85
86
  } = checkFormStatus(request.params);
@@ -89,7 +90,7 @@ export class SummaryPageController extends QuestionPageController {
89
90
  // Send submission email
90
91
  if (emailAddress) {
91
92
  const viewModel = this.getSummaryViewModel(request, context);
92
- await submitForm(context, request, viewModel, model, emailAddress);
93
+ await submitForm(context, request, viewModel, model, emailAddress, formMetadata);
93
94
  }
94
95
  await cacheService.setConfirmationState(request, {
95
96
  confirmed: true
@@ -112,7 +113,7 @@ export class SummaryPageController extends QuestionPageController {
112
113
  };
113
114
  }
114
115
  }
115
- async function submitForm(context, request, summaryViewModel, model, emailAddress) {
116
+ async function submitForm(context, request, summaryViewModel, model, emailAddress, formMetadata) {
116
117
  await extendFileRetention(model, context.state, emailAddress);
117
118
  const formStatus = checkFormStatus(request.params);
118
119
  const logTags = ['submit', 'submissionApi'];
@@ -127,7 +128,7 @@ async function submitForm(context, request, summaryViewModel, model, emailAddres
127
128
  if (submitResponse === undefined) {
128
129
  throw Boom.badRequest('Unexpected empty response from submit api');
129
130
  }
130
- return model.services.outputService.submit(context, request, model, emailAddress, items, submitResponse);
131
+ return model.services.outputService.submit(context, request, model, emailAddress, items, submitResponse, formMetadata);
131
132
  }
132
133
  async function extendFileRetention(model, state, updatedRetrievalKey) {
133
134
  const {
@@ -1 +1 @@
1
- {"version":3,"file":"SummaryPageController.js","names":["hasComponentsEvenIfNoNext","Boom","ComponentCollection","FileUploadField","getAnswer","checkEmailAddressForLiveFormSubmission","checkFormStatus","getCacheService","SummaryViewModel","QuestionPageController","SummaryPageController","constructor","model","pageDef","viewName","collection","components","page","getSummaryViewModel","request","context","viewModel","query","payload","errors","getViewModel","backLink","getBackLink","feedbackLink","phaseTag","allowSaveAndReturn","shouldShowSaveAndReturn","server","makeGetRouteHandler","h","hasMissingNotificationEmail","view","makePostRouteHandler","params","cacheService","formsService","services","getFormMetadata","notificationEmail","slug","isPreview","emailAddress","def","outputEmail","submitForm","setConfirmationState","confirmed","clearState","proceed","getStatusPath","postRouteOptions","ext","onPreHandler","method","continue","summaryViewModel","extendFileRetention","state","formStatus","logTags","logger","info","items","getFormSubmissionData","details","submitResponse","submitData","yar","id","undefined","badRequest","outputService","submit","updatedRetrievalKey","formSubmissionService","persistFiles","files","pages","forEach","fileUploadComponents","fields","filter","component","values","getFormValueFromState","length","push","map","status","fileId","form","file","initiatedRetrievalKey","metadata","retrievalKey","sessionId","main","item","name","title","label","value","field","format","repeaters","subItems","detailItems","subItem","relevantPages","href","flatMap","flat"],"sources":["../../../../../src/server/plugins/engine/pageControllers/SummaryPageController.ts"],"sourcesContent":["import {\n hasComponentsEvenIfNoNext,\n type Page,\n type SubmitPayload\n} from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport { type ResponseToolkit, type RouteOptions } from '@hapi/hapi'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport { FileUploadField } from '~/src/server/plugins/engine/components/FileUploadField.js'\nimport { getAnswer } from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n checkFormStatus,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n SummaryViewModel,\n type FormModel\n} from '~/src/server/plugins/engine/models/index.js'\nimport {\n type Detail,\n type DetailItem\n} from '~/src/server/plugins/engine/models/types.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs\n} from '~/src/server/routes/types.js'\n\nexport class SummaryPageController extends QuestionPageController {\n declare pageDef: Page\n\n /**\n * The controller which is used when Page[\"controller\"] is defined as \"./pages/summary.js\"\n */\n\n constructor(model: FormModel, pageDef: Page) {\n super(model, pageDef)\n this.viewName = 'summary'\n\n // Components collection\n this.collection = new ComponentCollection(\n hasComponentsEvenIfNoNext(pageDef) ? pageDef.components : [],\n { model, page: this }\n )\n }\n\n getSummaryViewModel(\n request: FormContextRequest,\n context: FormContext\n ): SummaryViewModel {\n const viewModel = new SummaryViewModel(request, this, context)\n\n const { query } = request\n const { payload, errors } = context\n const components = this.collection.getViewModel(payload, errors, query)\n\n // We already figure these out in the base page controller. Take them and apply them to our page-specific model.\n // This is a stop-gap until we can add proper inheritance in place.\n viewModel.backLink = this.getBackLink(request, context)\n viewModel.feedbackLink = this.feedbackLink\n viewModel.phaseTag = this.phaseTag\n viewModel.components = components\n viewModel.allowSaveAndReturn = this.shouldShowSaveAndReturn(request.server)\n\n return viewModel\n }\n\n /**\n * Returns an async function. This is called in plugin.ts when there is a GET request at `/{id}/{path*}`,\n */\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { viewName } = this\n\n const viewModel = this.getSummaryViewModel(request, context)\n\n viewModel.hasMissingNotificationEmail =\n await this.hasMissingNotificationEmail(request, context)\n\n return h.view(viewName, viewModel)\n }\n }\n\n /**\n * Returns an async function. This is called in plugin.ts when there is a POST request at `/{id}/{path*}`.\n * If a form is incomplete, a user will be redirected to the start page.\n */\n makePostRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { model } = this\n const { params } = request\n const cacheService = getCacheService(request.server)\n\n const { formsService } = this.model.services\n const { getFormMetadata } = formsService\n\n // Get the form metadata using the `slug` param\n const { notificationEmail } = await getFormMetadata(params.slug)\n const { isPreview } = checkFormStatus(request.params)\n const emailAddress = notificationEmail ?? this.model.def.outputEmail\n\n checkEmailAddressForLiveFormSubmission(emailAddress, isPreview)\n\n // Send submission email\n if (emailAddress) {\n const viewModel = this.getSummaryViewModel(request, context)\n await submitForm(context, request, viewModel, model, emailAddress)\n }\n\n await cacheService.setConfirmationState(request, { confirmed: true })\n\n // Clear all form data\n await cacheService.clearState(request)\n\n return this.proceed(request, h, this.getStatusPath())\n }\n }\n\n get postRouteOptions(): RouteOptions<FormRequestPayloadRefs> {\n return {\n ext: {\n onPreHandler: {\n method(request, h) {\n return h.continue\n }\n }\n }\n }\n }\n}\n\nasync function submitForm(\n context: FormContext,\n request: FormRequestPayload,\n summaryViewModel: SummaryViewModel,\n model: FormModel,\n emailAddress: string\n) {\n await extendFileRetention(model, context.state, emailAddress)\n\n const formStatus = checkFormStatus(request.params)\n const logTags = ['submit', 'submissionApi']\n\n request.logger.info(logTags, 'Preparing email', formStatus)\n\n // Get detail items\n const items = getFormSubmissionData(\n summaryViewModel.context,\n summaryViewModel.details\n )\n\n // Submit data\n request.logger.info(logTags, 'Submitting data')\n const submitResponse = await submitData(\n model,\n items,\n emailAddress,\n request.yar.id\n )\n\n if (submitResponse === undefined) {\n throw Boom.badRequest('Unexpected empty response from submit api')\n }\n\n return model.services.outputService.submit(\n context,\n request,\n model,\n emailAddress,\n items,\n submitResponse\n )\n}\n\nasync function extendFileRetention(\n model: FormModel,\n state: FormSubmissionState,\n updatedRetrievalKey: string\n) {\n const { formSubmissionService } = model.services\n const { persistFiles } = formSubmissionService\n const files: { fileId: string; initiatedRetrievalKey: string }[] = []\n\n // For each file upload component with files in\n // state, add the files to the batch getting persisted\n model.pages.forEach((page) => {\n const fileUploadComponents = page.collection.fields.filter(\n (component) => component instanceof FileUploadField\n )\n\n fileUploadComponents.forEach((component) => {\n const values = component.getFormValueFromState(state)\n if (!values?.length) {\n return\n }\n\n files.push(\n ...values.map(({ status }) => ({\n fileId: status.form.file.fileId,\n initiatedRetrievalKey: status.metadata.retrievalKey\n }))\n )\n })\n })\n\n if (files.length) {\n return persistFiles(files, updatedRetrievalKey)\n }\n}\n\nfunction submitData(\n model: FormModel,\n items: DetailItem[],\n retrievalKey: string,\n sessionId: string\n) {\n const { formSubmissionService } = model.services\n const { submit } = formSubmissionService\n\n const payload: SubmitPayload = {\n sessionId,\n retrievalKey,\n\n // Main form answers\n main: items\n .filter((item) => 'field' in item)\n .map((item) => ({\n name: item.name,\n title: item.label,\n value: getAnswer(item.field, item.state, { format: 'data' })\n })),\n\n // Repeater form answers\n repeaters: items\n .filter((item) => 'subItems' in item)\n .map((item) => ({\n name: item.name,\n title: item.label,\n\n // Repeater item values\n value: item.subItems.map((detailItems) =>\n detailItems.map((subItem) => ({\n name: subItem.name,\n title: subItem.label,\n value: getAnswer(subItem.field, subItem.state, { format: 'data' })\n }))\n )\n }))\n }\n\n return submit(payload)\n}\n\nexport function getFormSubmissionData(context: FormContext, details: Detail[]) {\n return context.relevantPages\n .map(({ href }) =>\n details.flatMap(({ items }) =>\n items.filter(({ page }) => page.href === href)\n )\n )\n .flat()\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"SummaryPageController.js","names":["hasComponentsEvenIfNoNext","Boom","ComponentCollection","FileUploadField","getAnswer","checkEmailAddressForLiveFormSubmission","checkFormStatus","getCacheService","SummaryViewModel","QuestionPageController","SummaryPageController","constructor","model","pageDef","viewName","collection","components","page","getSummaryViewModel","request","context","viewModel","query","payload","errors","getViewModel","backLink","getBackLink","feedbackLink","phaseTag","allowSaveAndReturn","shouldShowSaveAndReturn","server","makeGetRouteHandler","h","hasMissingNotificationEmail","view","makePostRouteHandler","params","cacheService","formsService","services","getFormMetadata","formMetadata","slug","notificationEmail","isPreview","emailAddress","def","outputEmail","submitForm","setConfirmationState","confirmed","clearState","proceed","getStatusPath","postRouteOptions","ext","onPreHandler","method","continue","summaryViewModel","extendFileRetention","state","formStatus","logTags","logger","info","items","getFormSubmissionData","details","submitResponse","submitData","yar","id","undefined","badRequest","outputService","submit","updatedRetrievalKey","formSubmissionService","persistFiles","files","pages","forEach","fileUploadComponents","fields","filter","component","values","getFormValueFromState","length","push","map","status","fileId","form","file","initiatedRetrievalKey","metadata","retrievalKey","sessionId","main","item","name","title","label","value","field","format","repeaters","subItems","detailItems","subItem","relevantPages","href","flatMap","flat"],"sources":["../../../../../src/server/plugins/engine/pageControllers/SummaryPageController.ts"],"sourcesContent":["import {\n hasComponentsEvenIfNoNext,\n type FormMetadata,\n type Page,\n type SubmitPayload\n} from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport { type ResponseToolkit, type RouteOptions } from '@hapi/hapi'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport { FileUploadField } from '~/src/server/plugins/engine/components/FileUploadField.js'\nimport { getAnswer } from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n checkFormStatus,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n SummaryViewModel,\n type FormModel\n} from '~/src/server/plugins/engine/models/index.js'\nimport {\n type Detail,\n type DetailItem\n} from '~/src/server/plugins/engine/models/types.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs\n} from '~/src/server/routes/types.js'\n\nexport class SummaryPageController extends QuestionPageController {\n declare pageDef: Page\n\n /**\n * The controller which is used when Page[\"controller\"] is defined as \"./pages/summary.js\"\n */\n\n constructor(model: FormModel, pageDef: Page) {\n super(model, pageDef)\n this.viewName = 'summary'\n\n // Components collection\n this.collection = new ComponentCollection(\n hasComponentsEvenIfNoNext(pageDef) ? pageDef.components : [],\n { model, page: this }\n )\n }\n\n getSummaryViewModel(\n request: FormContextRequest,\n context: FormContext\n ): SummaryViewModel {\n const viewModel = new SummaryViewModel(request, this, context)\n\n const { query } = request\n const { payload, errors } = context\n const components = this.collection.getViewModel(payload, errors, query)\n\n // We already figure these out in the base page controller. Take them and apply them to our page-specific model.\n // This is a stop-gap until we can add proper inheritance in place.\n viewModel.backLink = this.getBackLink(request, context)\n viewModel.feedbackLink = this.feedbackLink\n viewModel.phaseTag = this.phaseTag\n viewModel.components = components\n viewModel.allowSaveAndReturn = this.shouldShowSaveAndReturn(request.server)\n\n return viewModel\n }\n\n /**\n * Returns an async function. This is called in plugin.ts when there is a GET request at `/{id}/{path*}`,\n */\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { viewName } = this\n\n const viewModel = this.getSummaryViewModel(request, context)\n\n viewModel.hasMissingNotificationEmail =\n await this.hasMissingNotificationEmail(request, context)\n\n return h.view(viewName, viewModel)\n }\n }\n\n /**\n * Returns an async function. This is called in plugin.ts when there is a POST request at `/{id}/{path*}`.\n * If a form is incomplete, a user will be redirected to the start page.\n */\n makePostRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) => {\n const { model } = this\n const { params } = request\n const cacheService = getCacheService(request.server)\n\n const { formsService } = this.model.services\n const { getFormMetadata } = formsService\n\n // Get the form metadata using the `slug` param\n const formMetadata = await getFormMetadata(params.slug)\n const { notificationEmail } = formMetadata\n const { isPreview } = checkFormStatus(request.params)\n const emailAddress = notificationEmail ?? this.model.def.outputEmail\n\n checkEmailAddressForLiveFormSubmission(emailAddress, isPreview)\n\n // Send submission email\n if (emailAddress) {\n const viewModel = this.getSummaryViewModel(request, context)\n await submitForm(\n context,\n request,\n viewModel,\n model,\n emailAddress,\n formMetadata\n )\n }\n\n await cacheService.setConfirmationState(request, { confirmed: true })\n\n // Clear all form data\n await cacheService.clearState(request)\n\n return this.proceed(request, h, this.getStatusPath())\n }\n }\n\n get postRouteOptions(): RouteOptions<FormRequestPayloadRefs> {\n return {\n ext: {\n onPreHandler: {\n method(request, h) {\n return h.continue\n }\n }\n }\n }\n }\n}\n\nasync function submitForm(\n context: FormContext,\n request: FormRequestPayload,\n summaryViewModel: SummaryViewModel,\n model: FormModel,\n emailAddress: string,\n formMetadata: FormMetadata\n) {\n await extendFileRetention(model, context.state, emailAddress)\n\n const formStatus = checkFormStatus(request.params)\n const logTags = ['submit', 'submissionApi']\n\n request.logger.info(logTags, 'Preparing email', formStatus)\n\n // Get detail items\n const items = getFormSubmissionData(\n summaryViewModel.context,\n summaryViewModel.details\n )\n\n // Submit data\n request.logger.info(logTags, 'Submitting data')\n const submitResponse = await submitData(\n model,\n items,\n emailAddress,\n request.yar.id\n )\n\n if (submitResponse === undefined) {\n throw Boom.badRequest('Unexpected empty response from submit api')\n }\n\n return model.services.outputService.submit(\n context,\n request,\n model,\n emailAddress,\n items,\n submitResponse,\n formMetadata\n )\n}\n\nasync function extendFileRetention(\n model: FormModel,\n state: FormSubmissionState,\n updatedRetrievalKey: string\n) {\n const { formSubmissionService } = model.services\n const { persistFiles } = formSubmissionService\n const files: { fileId: string; initiatedRetrievalKey: string }[] = []\n\n // For each file upload component with files in\n // state, add the files to the batch getting persisted\n model.pages.forEach((page) => {\n const fileUploadComponents = page.collection.fields.filter(\n (component) => component instanceof FileUploadField\n )\n\n fileUploadComponents.forEach((component) => {\n const values = component.getFormValueFromState(state)\n if (!values?.length) {\n return\n }\n\n files.push(\n ...values.map(({ status }) => ({\n fileId: status.form.file.fileId,\n initiatedRetrievalKey: status.metadata.retrievalKey\n }))\n )\n })\n })\n\n if (files.length) {\n return persistFiles(files, updatedRetrievalKey)\n }\n}\n\nfunction submitData(\n model: FormModel,\n items: DetailItem[],\n retrievalKey: string,\n sessionId: string\n) {\n const { formSubmissionService } = model.services\n const { submit } = formSubmissionService\n\n const payload: SubmitPayload = {\n sessionId,\n retrievalKey,\n\n // Main form answers\n main: items\n .filter((item) => 'field' in item)\n .map((item) => ({\n name: item.name,\n title: item.label,\n value: getAnswer(item.field, item.state, { format: 'data' })\n })),\n\n // Repeater form answers\n repeaters: items\n .filter((item) => 'subItems' in item)\n .map((item) => ({\n name: item.name,\n title: item.label,\n\n // Repeater item values\n value: item.subItems.map((detailItems) =>\n detailItems.map((subItem) => ({\n name: subItem.name,\n title: subItem.label,\n value: getAnswer(subItem.field, subItem.state, { format: 'data' })\n }))\n )\n }))\n }\n\n return submit(payload)\n}\n\nexport function getFormSubmissionData(context: FormContext, details: Detail[]) {\n return context.relevantPages\n .map(({ href }) =>\n details.flatMap(({ items }) =>\n items.filter(({ page }) => page.href === href)\n )\n )\n .flat()\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- export function formsService(): Promise<import("../../../types.js").FormsService>;
1
+ export function formsService(): Promise<import("../types/index.js").FormsService>;
@@ -1,6 +1,11 @@
1
- import { type SubmitResponsePayload } from '@defra/forms-model';
1
+ import { type FormMetadata, type SubmitResponsePayload } from '@defra/forms-model';
2
2
  import { type FormModel } from '~/src/server/plugins/engine/models/index.js';
3
3
  import { type DetailItem } from '~/src/server/plugins/engine/models/types.js';
4
4
  import { type FormContext } from '~/src/server/plugins/engine/types.js';
5
5
  import { type FormRequestPayload } from '~/src/server/routes/types.js';
6
- export declare function submit(context: FormContext, request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload): Promise<void>;
6
+ /**
7
+ * Optional GOV.UK Notify service for consumers who want email notifications
8
+ * Can be disabled by not providing notifyTemplateId in config
9
+ * Can be overridden by providing a custom outputService in the services config
10
+ */
11
+ export declare function submit(context: FormContext, request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload, formMetadata?: FormMetadata): Promise<void>;