@defra/forms-engine-plugin 2.0.1 → 2.0.3

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 (49) hide show
  1. package/.server/server/plugins/engine/helpers.d.ts +1 -1
  2. package/.server/server/plugins/engine/outputFormatters/human/v1.d.ts +2 -1
  3. package/.server/server/plugins/engine/outputFormatters/human/v1.js +1 -1
  4. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  5. package/.server/server/plugins/engine/outputFormatters/index.d.ts +2 -1
  6. package/.server/server/plugins/engine/outputFormatters/index.js.map +1 -1
  7. package/.server/server/plugins/engine/outputFormatters/machine/v1.d.ts +2 -1
  8. package/.server/server/plugins/engine/outputFormatters/machine/v1.js +2 -1
  9. package/.server/server/plugins/engine/outputFormatters/machine/v1.js.map +1 -1
  10. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +2 -1
  11. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +3 -2
  12. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  13. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +9 -2
  14. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  15. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +4 -7
  16. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  17. package/.server/server/plugins/engine/plugin.js +1 -2
  18. package/.server/server/plugins/engine/plugin.js.map +1 -1
  19. package/.server/server/plugins/engine/routes/questions.js +1 -1
  20. package/.server/server/plugins/engine/routes/questions.js.map +1 -1
  21. package/.server/server/plugins/engine/services/notifyService.d.ts +2 -1
  22. package/.server/server/plugins/engine/services/notifyService.js +2 -2
  23. package/.server/server/plugins/engine/services/notifyService.js.map +1 -1
  24. package/.server/server/plugins/engine/types.d.ts +1 -1
  25. package/.server/server/plugins/engine/types.js.map +1 -1
  26. package/.server/server/services/cacheService.d.ts +0 -2
  27. package/.server/server/services/cacheService.js +1 -4
  28. package/.server/server/services/cacheService.js.map +1 -1
  29. package/.server/server/types.d.ts +2 -2
  30. package/.server/server/types.js.map +1 -1
  31. package/package.json +2 -2
  32. package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +3 -3
  33. package/src/server/plugins/engine/outputFormatters/human/v1.ts +2 -0
  34. package/src/server/plugins/engine/outputFormatters/index.ts +2 -0
  35. package/src/server/plugins/engine/outputFormatters/machine/v1.test.ts +40 -1
  36. package/src/server/plugins/engine/outputFormatters/machine/v1.ts +3 -0
  37. package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +40 -1
  38. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +4 -1
  39. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +81 -16
  40. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +13 -1
  41. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +4 -4
  42. package/src/server/plugins/engine/plugin.ts +1 -2
  43. package/src/server/plugins/engine/routes/questions.ts +1 -1
  44. package/src/server/plugins/engine/services/notifyService.test.ts +26 -3
  45. package/src/server/plugins/engine/services/notifyService.ts +3 -1
  46. package/src/server/plugins/engine/types.ts +0 -1
  47. package/src/server/services/cacheService.test.ts +8 -2
  48. package/src/server/services/cacheService.ts +1 -13
  49. package/src/server/types.ts +2 -0
@@ -1 +1 @@
1
- {"version":3,"file":"questions.js","names":["hasFormComponents","slugSchema","Boom","Joi","normalisePath","proceed","redirectPath","SummaryViewModel","format","getFormSubmissionData","dispatchHandler","redirectOrMakeHandler","actionSchema","crumbSchema","itemIdSchema","pathSchema","stateSchema","httpService","makeGetHandler","preparePageEventRequestOptions","getHandler","request","h","params","path","page","context","events","model","app","notFound","onLoad","type","options","url","viewModel","items","details","payload","undefined","opts","response","postJson","Object","assign","data","makeGetRouteHandler","postHandler","query","pageDef","isForceAccess","href","makePostRouteHandler","getRoutes","getRouteOptions","postRouteOptions","method","handler","validate","object","keys","slug","state","itemId","optional","crumb","action","unknown","required"],"sources":["../../../../../src/server/plugins/engine/routes/questions.ts"],"sourcesContent":["import { hasFormComponents, slugSchema } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport {\n type ResponseToolkit,\n type RouteOptions,\n type ServerRoute\n} from '@hapi/hapi'\nimport Joi from 'joi'\n\nimport {\n normalisePath,\n proceed,\n redirectPath\n} from '~/src/server/plugins/engine/helpers.js'\nimport { SummaryViewModel } from '~/src/server/plugins/engine/models/index.js'\nimport { format } from '~/src/server/plugins/engine/outputFormatters/machine/v1.js'\nimport { getFormSubmissionData } from '~/src/server/plugins/engine/pageControllers/SummaryPageController.js'\nimport {\n dispatchHandler,\n redirectOrMakeHandler\n} from '~/src/server/plugins/engine/routes/index.js'\nimport { type PreparePageEventRequestOptions } from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport {\n actionSchema,\n crumbSchema,\n itemIdSchema,\n pathSchema,\n stateSchema\n} from '~/src/server/schemas/index.js'\nimport * as httpService from '~/src/server/services/httpService.js'\n\nfunction makeGetHandler(\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n) {\n return function getHandler(\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) {\n const { params } = request\n\n if (normalisePath(params.path) === '') {\n return dispatchHandler(request, h)\n }\n\n return redirectOrMakeHandler(request, h, async (page, context) => {\n // Check for a page onLoad HTTP event and if one exists,\n // call it and assign the response to the context data\n const { events } = page\n const { model } = request.app\n\n if (!model) {\n throw Boom.notFound(`No model found for /${params.path}`)\n }\n\n if (events?.onLoad && events.onLoad.type === 'http') {\n const { options } = events.onLoad\n const { url } = options\n\n // TODO: Update structured data POST payload with when helper\n // is updated to removing the dependency on `SummaryViewModel` etc.\n const viewModel = new SummaryViewModel(request, page, context)\n const items = getFormSubmissionData(\n viewModel.context,\n viewModel.details\n )\n\n // @ts-expect-error - function signature will be refactored in the next iteration of the formatter\n const payload = format(items, model, undefined, undefined)\n const opts = { payload }\n\n if (preparePageEventRequestOptions) {\n preparePageEventRequestOptions(opts, events.onLoad, page, context)\n }\n\n const { payload: response } = await httpService.postJson(url, opts)\n\n Object.assign(context.data, response)\n }\n\n return page.makeGetRouteHandler()(request, context, h)\n })\n }\n}\n\nfunction postHandler(\n request: FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n) {\n const { query } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n const { pageDef } = page\n const { isForceAccess } = context\n\n // Redirect to GET for preview URL direct access\n if (isForceAccess && !hasFormComponents(pageDef)) {\n return proceed(request, h, redirectPath(page.href, query))\n }\n\n return page.makePostRouteHandler()(request, context, h)\n })\n}\n\nexport function getRoutes(\n getRouteOptions: RouteOptions<FormRequestRefs>,\n postRouteOptions: RouteOptions<FormRequestPayloadRefs>,\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n): (ServerRoute<FormRequestRefs> | ServerRoute<FormRequestPayloadRefs>)[] {\n return [\n {\n method: 'get',\n path: '/{slug}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema\n })\n }\n }\n },\n {\n method: 'get',\n path: '/preview/{state}/{slug}',\n handler: dispatchHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema\n })\n }\n }\n },\n {\n method: 'get',\n path: '/{slug}/{path}/{itemId?}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n },\n {\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n },\n {\n method: 'post',\n path: '/{slug}/{path}/{itemId?}',\n handler: postHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n },\n {\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: postHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n }\n ]\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"questions.js","names":["hasFormComponents","slugSchema","Boom","Joi","normalisePath","proceed","redirectPath","SummaryViewModel","format","getFormSubmissionData","dispatchHandler","redirectOrMakeHandler","actionSchema","crumbSchema","itemIdSchema","pathSchema","stateSchema","httpService","makeGetHandler","preparePageEventRequestOptions","getHandler","request","h","params","path","page","context","events","model","app","notFound","onLoad","type","options","url","viewModel","items","details","payload","undefined","opts","response","postJson","Object","assign","data","makeGetRouteHandler","postHandler","query","pageDef","isForceAccess","href","makePostRouteHandler","getRoutes","getRouteOptions","postRouteOptions","method","handler","validate","object","keys","slug","state","itemId","optional","crumb","action","unknown","required"],"sources":["../../../../../src/server/plugins/engine/routes/questions.ts"],"sourcesContent":["import { hasFormComponents, slugSchema } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport {\n type ResponseToolkit,\n type RouteOptions,\n type ServerRoute\n} from '@hapi/hapi'\nimport Joi from 'joi'\n\nimport {\n normalisePath,\n proceed,\n redirectPath\n} from '~/src/server/plugins/engine/helpers.js'\nimport { SummaryViewModel } from '~/src/server/plugins/engine/models/index.js'\nimport { format } from '~/src/server/plugins/engine/outputFormatters/machine/v1.js'\nimport { getFormSubmissionData } from '~/src/server/plugins/engine/pageControllers/SummaryPageController.js'\nimport {\n dispatchHandler,\n redirectOrMakeHandler\n} from '~/src/server/plugins/engine/routes/index.js'\nimport { type PreparePageEventRequestOptions } from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload,\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport {\n actionSchema,\n crumbSchema,\n itemIdSchema,\n pathSchema,\n stateSchema\n} from '~/src/server/schemas/index.js'\nimport * as httpService from '~/src/server/services/httpService.js'\n\nfunction makeGetHandler(\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n) {\n return function getHandler(\n request: FormRequest,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n ) {\n const { params } = request\n\n if (normalisePath(params.path) === '') {\n return dispatchHandler(request, h)\n }\n\n return redirectOrMakeHandler(request, h, async (page, context) => {\n // Check for a page onLoad HTTP event and if one exists,\n // call it and assign the response to the context data\n const { events } = page\n const { model } = request.app\n\n if (!model) {\n throw Boom.notFound(`No model found for /${params.path}`)\n }\n\n if (events?.onLoad && events.onLoad.type === 'http') {\n const { options } = events.onLoad\n const { url } = options\n\n // TODO: Update structured data POST payload with when helper\n // is updated to removing the dependency on `SummaryViewModel` etc.\n const viewModel = new SummaryViewModel(request, page, context)\n const items = getFormSubmissionData(\n viewModel.context,\n viewModel.details\n )\n\n // @ts-expect-error - function signature will be refactored in the next iteration of the formatter\n const payload = format(context, items, model, undefined, undefined)\n const opts = { payload }\n\n if (preparePageEventRequestOptions) {\n preparePageEventRequestOptions(opts, events.onLoad, page, context)\n }\n\n const { payload: response } = await httpService.postJson(url, opts)\n\n Object.assign(context.data, response)\n }\n\n return page.makeGetRouteHandler()(request, context, h)\n })\n }\n}\n\nfunction postHandler(\n request: FormRequestPayload,\n h: Pick<ResponseToolkit, 'redirect' | 'view'>\n) {\n const { query } = request\n\n return redirectOrMakeHandler(request, h, (page, context) => {\n const { pageDef } = page\n const { isForceAccess } = context\n\n // Redirect to GET for preview URL direct access\n if (isForceAccess && !hasFormComponents(pageDef)) {\n return proceed(request, h, redirectPath(page.href, query))\n }\n\n return page.makePostRouteHandler()(request, context, h)\n })\n}\n\nexport function getRoutes(\n getRouteOptions: RouteOptions<FormRequestRefs>,\n postRouteOptions: RouteOptions<FormRequestPayloadRefs>,\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n): (ServerRoute<FormRequestRefs> | ServerRoute<FormRequestPayloadRefs>)[] {\n return [\n {\n method: 'get',\n path: '/{slug}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema\n })\n }\n }\n },\n {\n method: 'get',\n path: '/preview/{state}/{slug}',\n handler: dispatchHandler,\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema\n })\n }\n }\n },\n {\n method: 'get',\n path: '/{slug}/{path}/{itemId?}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n },\n {\n method: 'get',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: makeGetHandler(preparePageEventRequestOptions),\n options: {\n ...getRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n })\n }\n }\n },\n {\n method: 'post',\n path: '/{slug}/{path}/{itemId?}',\n handler: postHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n },\n {\n method: 'post',\n path: '/preview/{state}/{slug}/{path}/{itemId?}',\n handler: postHandler,\n options: {\n ...postRouteOptions,\n validate: {\n params: Joi.object().keys({\n state: stateSchema,\n slug: slugSchema,\n path: pathSchema,\n itemId: itemIdSchema.optional()\n }),\n payload: Joi.object()\n .keys({\n crumb: crumbSchema,\n action: actionSchema\n })\n .unknown(true)\n .required()\n }\n }\n }\n ]\n}\n"],"mappings":"AAAA,SAASA,iBAAiB,EAAEC,UAAU,QAAQ,oBAAoB;AAClE,OAAOC,IAAI,MAAM,YAAY;AAM7B,OAAOC,GAAG,MAAM,KAAK;AAErB,SACEC,aAAa,EACbC,OAAO,EACPC,YAAY;AAEd,SAASC,gBAAgB;AACzB,SAASC,MAAM;AACf,SAASC,qBAAqB;AAC9B,SACEC,eAAe,EACfC,qBAAqB;AASvB,SACEC,YAAY,EACZC,WAAW,EACXC,YAAY,EACZC,UAAU,EACVC,WAAW;AAEb,OAAO,KAAKC,WAAW;AAEvB,SAASC,cAAcA,CACrBC,8BAA+D,EAC/D;EACA,OAAO,SAASC,UAAUA,CACxBC,OAAoB,EACpBC,CAA6C,EAC7C;IACA,MAAM;MAAEC;IAAO,CAAC,GAAGF,OAAO;IAE1B,IAAIjB,aAAa,CAACmB,MAAM,CAACC,IAAI,CAAC,KAAK,EAAE,EAAE;MACrC,OAAOd,eAAe,CAACW,OAAO,EAAEC,CAAC,CAAC;IACpC;IAEA,OAAOX,qBAAqB,CAACU,OAAO,EAAEC,CAAC,EAAE,OAAOG,IAAI,EAAEC,OAAO,KAAK;MAChE;MACA;MACA,MAAM;QAAEC;MAAO,CAAC,GAAGF,IAAI;MACvB,MAAM;QAAEG;MAAM,CAAC,GAAGP,OAAO,CAACQ,GAAG;MAE7B,IAAI,CAACD,KAAK,EAAE;QACV,MAAM1B,IAAI,CAAC4B,QAAQ,CAAC,uBAAuBP,MAAM,CAACC,IAAI,EAAE,CAAC;MAC3D;MAEA,IAAIG,MAAM,EAAEI,MAAM,IAAIJ,MAAM,CAACI,MAAM,CAACC,IAAI,KAAK,MAAM,EAAE;QACnD,MAAM;UAAEC;QAAQ,CAAC,GAAGN,MAAM,CAACI,MAAM;QACjC,MAAM;UAAEG;QAAI,CAAC,GAAGD,OAAO;;QAEvB;QACA;QACA,MAAME,SAAS,GAAG,IAAI5B,gBAAgB,CAACc,OAAO,EAAEI,IAAI,EAAEC,OAAO,CAAC;QAC9D,MAAMU,KAAK,GAAG3B,qBAAqB,CACjC0B,SAAS,CAACT,OAAO,EACjBS,SAAS,CAACE,OACZ,CAAC;;QAED;QACA,MAAMC,OAAO,GAAG9B,MAAM,CAACkB,OAAO,EAAEU,KAAK,EAAER,KAAK,EAAEW,SAAS,EAAEA,SAAS,CAAC;QACnE,MAAMC,IAAI,GAAG;UAAEF;QAAQ,CAAC;QAExB,IAAInB,8BAA8B,EAAE;UAClCA,8BAA8B,CAACqB,IAAI,EAAEb,MAAM,CAACI,MAAM,EAAEN,IAAI,EAAEC,OAAO,CAAC;QACpE;QAEA,MAAM;UAAEY,OAAO,EAAEG;QAAS,CAAC,GAAG,MAAMxB,WAAW,CAACyB,QAAQ,CAACR,GAAG,EAAEM,IAAI,CAAC;QAEnEG,MAAM,CAACC,MAAM,CAAClB,OAAO,CAACmB,IAAI,EAAEJ,QAAQ,CAAC;MACvC;MAEA,OAAOhB,IAAI,CAACqB,mBAAmB,CAAC,CAAC,CAACzB,OAAO,EAAEK,OAAO,EAAEJ,CAAC,CAAC;IACxD,CAAC,CAAC;EACJ,CAAC;AACH;AAEA,SAASyB,WAAWA,CAClB1B,OAA2B,EAC3BC,CAA6C,EAC7C;EACA,MAAM;IAAE0B;EAAM,CAAC,GAAG3B,OAAO;EAEzB,OAAOV,qBAAqB,CAACU,OAAO,EAAEC,CAAC,EAAE,CAACG,IAAI,EAAEC,OAAO,KAAK;IAC1D,MAAM;MAAEuB;IAAQ,CAAC,GAAGxB,IAAI;IACxB,MAAM;MAAEyB;IAAc,CAAC,GAAGxB,OAAO;;IAEjC;IACA,IAAIwB,aAAa,IAAI,CAAClD,iBAAiB,CAACiD,OAAO,CAAC,EAAE;MAChD,OAAO5C,OAAO,CAACgB,OAAO,EAAEC,CAAC,EAAEhB,YAAY,CAACmB,IAAI,CAAC0B,IAAI,EAAEH,KAAK,CAAC,CAAC;IAC5D;IAEA,OAAOvB,IAAI,CAAC2B,oBAAoB,CAAC,CAAC,CAAC/B,OAAO,EAAEK,OAAO,EAAEJ,CAAC,CAAC;EACzD,CAAC,CAAC;AACJ;AAEA,OAAO,SAAS+B,SAASA,CACvBC,eAA8C,EAC9CC,gBAAsD,EACtDpC,8BAA+D,EACS;EACxE,OAAO,CACL;IACEqC,MAAM,EAAE,KAAK;IACbhC,IAAI,EAAE,SAAS;IACfiC,OAAO,EAAEvC,cAAc,CAACC,8BAA8B,CAAC;IACvDc,OAAO,EAAE;MACP,GAAGqB,eAAe;MAClBI,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBC,IAAI,EAAE5D;QACR,CAAC;MACH;IACF;EACF,CAAC,EACD;IACEuD,MAAM,EAAE,KAAK;IACbhC,IAAI,EAAE,yBAAyB;IAC/BiC,OAAO,EAAE/C,eAAe;IACxBuB,OAAO,EAAE;MACP,GAAGqB,eAAe;MAClBI,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBE,KAAK,EAAE9C,WAAW;UAClB6C,IAAI,EAAE5D;QACR,CAAC;MACH;IACF;EACF,CAAC,EACD;IACEuD,MAAM,EAAE,KAAK;IACbhC,IAAI,EAAE,0BAA0B;IAChCiC,OAAO,EAAEvC,cAAc,CAACC,8BAA8B,CAAC;IACvDc,OAAO,EAAE;MACP,GAAGqB,eAAe;MAClBI,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBC,IAAI,EAAE5D,UAAU;UAChBuB,IAAI,EAAET,UAAU;UAChBgD,MAAM,EAAEjD,YAAY,CAACkD,QAAQ,CAAC;QAChC,CAAC;MACH;IACF;EACF,CAAC,EACD;IACER,MAAM,EAAE,KAAK;IACbhC,IAAI,EAAE,0CAA0C;IAChDiC,OAAO,EAAEvC,cAAc,CAACC,8BAA8B,CAAC;IACvDc,OAAO,EAAE;MACP,GAAGqB,eAAe;MAClBI,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBE,KAAK,EAAE9C,WAAW;UAClB6C,IAAI,EAAE5D,UAAU;UAChBuB,IAAI,EAAET,UAAU;UAChBgD,MAAM,EAAEjD,YAAY,CAACkD,QAAQ,CAAC;QAChC,CAAC;MACH;IACF;EACF,CAAC,EACD;IACER,MAAM,EAAE,MAAM;IACdhC,IAAI,EAAE,0BAA0B;IAChCiC,OAAO,EAAEV,WAAW;IACpBd,OAAO,EAAE;MACP,GAAGsB,gBAAgB;MACnBG,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBC,IAAI,EAAE5D,UAAU;UAChBuB,IAAI,EAAET,UAAU;UAChBgD,MAAM,EAAEjD,YAAY,CAACkD,QAAQ,CAAC;QAChC,CAAC,CAAC;QACF1B,OAAO,EAAEnC,GAAG,CAACwD,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;UACJK,KAAK,EAAEpD,WAAW;UAClBqD,MAAM,EAAEtD;QACV,CAAC,CAAC,CACDuD,OAAO,CAAC,IAAI,CAAC,CACbC,QAAQ,CAAC;MACd;IACF;EACF,CAAC,EACD;IACEZ,MAAM,EAAE,MAAM;IACdhC,IAAI,EAAE,0CAA0C;IAChDiC,OAAO,EAAEV,WAAW;IACpBd,OAAO,EAAE;MACP,GAAGsB,gBAAgB;MACnBG,QAAQ,EAAE;QACRnC,MAAM,EAAEpB,GAAG,CAACwD,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC;UACxBE,KAAK,EAAE9C,WAAW;UAClB6C,IAAI,EAAE5D,UAAU;UAChBuB,IAAI,EAAET,UAAU;UAChBgD,MAAM,EAAEjD,YAAY,CAACkD,QAAQ,CAAC;QAChC,CAAC,CAAC;QACF1B,OAAO,EAAEnC,GAAG,CAACwD,MAAM,CAAC,CAAC,CAClBC,IAAI,CAAC;UACJK,KAAK,EAAEpD,WAAW;UAClBqD,MAAM,EAAEtD;QACV,CAAC,CAAC,CACDuD,OAAO,CAAC,IAAI,CAAC,CACbC,QAAQ,CAAC;MACd;IACF;EACF,CAAC,CACF;AACH","ignoreList":[]}
@@ -1,5 +1,6 @@
1
1
  import { 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
+ import { type FormContext } from '~/src/server/plugins/engine/types.js';
4
5
  import { type FormRequestPayload } from '~/src/server/routes/types.js';
5
- export declare function submit(request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload): Promise<void>;
6
+ export declare function submit(context: FormContext, request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload): Promise<void>;
@@ -5,7 +5,7 @@ import { checkFormStatus } from "../helpers.js";
5
5
  import { getFormatter } from "../outputFormatters/index.js";
6
6
  import { sendNotification } from "../../../utils/notify.js";
7
7
  const templateId = config.get('notifyTemplateId');
8
- export async function submit(request, model, emailAddress, items, submitResponse) {
8
+ export async function submit(context, request, model, emailAddress, items, submitResponse) {
9
9
  const logTags = ['submit', 'email'];
10
10
  const formStatus = checkFormStatus(request.params);
11
11
 
@@ -16,7 +16,7 @@ export async function submit(request, model, emailAddress, items, submitResponse
16
16
  const outputAudience = model.def.output?.audience ?? 'human';
17
17
  const outputVersion = model.def.output?.version ?? '1';
18
18
  const outputFormatter = getFormatter(outputAudience, outputVersion);
19
- let body = outputFormatter(items, model, submitResponse, formStatus);
19
+ let body = outputFormatter(context, items, model, submitResponse, formStatus);
20
20
 
21
21
  // GOV.UK Notify transforms quotes into curly quotes, so we can't just send the raw payload
22
22
  // This is logic specific to Notify, so we include the logic here rather than in the formatter
@@ -1 +1 @@
1
- {"version":3,"file":"notifyService.js","names":["getErrorMessage","config","escapeMarkdown","checkFormStatus","getFormatter","sendNotification","templateId","get","submit","request","model","emailAddress","items","submitResponse","logTags","formStatus","params","logger","info","formName","name","subject","isPreview","outputAudience","def","output","audience","outputVersion","version","outputFormatter","body","Buffer","from","toString","personalisation","err","errMsg","error"],"sources":["../../../../../src/server/plugins/engine/services/notifyService.ts"],"sourcesContent":["import { getErrorMessage, type SubmitResponsePayload } from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { escapeMarkdown } from '~/src/server/plugins/engine/components/helpers.js'\nimport { 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 { getFormatter } from '~/src/server/plugins/engine/outputFormatters/index.js'\nimport { type FormRequestPayload } from '~/src/server/routes/types.js'\nimport { sendNotification } from '~/src/server/utils/notify.js'\n\nconst templateId = config.get('notifyTemplateId')\n\nexport async function submit(\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload\n) {\n const logTags = ['submit', 'email']\n const formStatus = checkFormStatus(request.params)\n\n // Get submission email personalisation\n request.logger.info(logTags, 'Getting personalisation data')\n\n const formName = escapeMarkdown(model.name)\n const subject = formStatus.isPreview\n ? `TEST FORM SUBMISSION: ${formName}`\n : `Form submission: ${formName}`\n\n const outputAudience = model.def.output?.audience ?? 'human'\n const outputVersion = model.def.output?.version ?? '1'\n\n const outputFormatter = getFormatter(outputAudience, outputVersion)\n let body = outputFormatter(items, model, submitResponse, formStatus)\n\n // GOV.UK Notify transforms quotes into curly quotes, so we can't just send the raw payload\n // This is logic specific to Notify, so we include the logic here rather than in the formatter\n if (outputAudience === 'machine') {\n body = Buffer.from(body).toString('base64')\n }\n\n request.logger.info(logTags, 'Sending email')\n\n try {\n // Send submission email\n await sendNotification({\n templateId,\n emailAddress,\n personalisation: {\n subject,\n body\n }\n })\n\n request.logger.info(logTags, 'Email sent successfully')\n } catch (err) {\n const errMsg = getErrorMessage(err)\n request.logger.error(\n errMsg,\n `[emailSendFailed] Error sending notification email - templateId: ${templateId} - recipient: ${emailAddress} - ${errMsg}`\n )\n\n throw err\n }\n}\n"],"mappings":"AAAA,SAASA,eAAe,QAAoC,oBAAoB;AAEhF,SAASC,MAAM;AACf,SAASC,cAAc;AACvB,SAASC,eAAe;AAGxB,SAASC,YAAY;AAErB,SAASC,gBAAgB;AAEzB,MAAMC,UAAU,GAAGL,MAAM,CAACM,GAAG,CAAC,kBAAkB,CAAC;AAEjD,OAAO,eAAeC,MAAMA,CAC1BC,OAA2B,EAC3BC,KAAgB,EAChBC,YAAoB,EACpBC,KAAmB,EACnBC,cAAqC,EACrC;EACA,MAAMC,OAAO,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC;EACnC,MAAMC,UAAU,GAAGZ,eAAe,CAACM,OAAO,CAACO,MAAM,CAAC;;EAElD;EACAP,OAAO,CAACQ,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,8BAA8B,CAAC;EAE5D,MAAMK,QAAQ,GAAGjB,cAAc,CAACQ,KAAK,CAACU,IAAI,CAAC;EAC3C,MAAMC,OAAO,GAAGN,UAAU,CAACO,SAAS,GAChC,yBAAyBH,QAAQ,EAAE,GACnC,oBAAoBA,QAAQ,EAAE;EAElC,MAAMI,cAAc,GAAGb,KAAK,CAACc,GAAG,CAACC,MAAM,EAAEC,QAAQ,IAAI,OAAO;EAC5D,MAAMC,aAAa,GAAGjB,KAAK,CAACc,GAAG,CAACC,MAAM,EAAEG,OAAO,IAAI,GAAG;EAEtD,MAAMC,eAAe,GAAGzB,YAAY,CAACmB,cAAc,EAAEI,aAAa,CAAC;EACnE,IAAIG,IAAI,GAAGD,eAAe,CAACjB,KAAK,EAAEF,KAAK,EAAEG,cAAc,EAAEE,UAAU,CAAC;;EAEpE;EACA;EACA,IAAIQ,cAAc,KAAK,SAAS,EAAE;IAChCO,IAAI,GAAGC,MAAM,CAACC,IAAI,CAACF,IAAI,CAAC,CAACG,QAAQ,CAAC,QAAQ,CAAC;EAC7C;EAEAxB,OAAO,CAACQ,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,eAAe,CAAC;EAE7C,IAAI;IACF;IACA,MAAMT,gBAAgB,CAAC;MACrBC,UAAU;MACVK,YAAY;MACZuB,eAAe,EAAE;QACfb,OAAO;QACPS;MACF;IACF,CAAC,CAAC;IAEFrB,OAAO,CAACQ,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,yBAAyB,CAAC;EACzD,CAAC,CAAC,OAAOqB,GAAG,EAAE;IACZ,MAAMC,MAAM,GAAGpC,eAAe,CAACmC,GAAG,CAAC;IACnC1B,OAAO,CAACQ,MAAM,CAACoB,KAAK,CAClBD,MAAM,EACN,oEAAoE9B,UAAU,iBAAiBK,YAAY,MAAMyB,MAAM,EACzH,CAAC;IAED,MAAMD,GAAG;EACX;AACF","ignoreList":[]}
1
+ {"version":3,"file":"notifyService.js","names":["getErrorMessage","config","escapeMarkdown","checkFormStatus","getFormatter","sendNotification","templateId","get","submit","context","request","model","emailAddress","items","submitResponse","logTags","formStatus","params","logger","info","formName","name","subject","isPreview","outputAudience","def","output","audience","outputVersion","version","outputFormatter","body","Buffer","from","toString","personalisation","err","errMsg","error"],"sources":["../../../../../src/server/plugins/engine/services/notifyService.ts"],"sourcesContent":["import { getErrorMessage, type SubmitResponsePayload } from '@defra/forms-model'\n\nimport { config } from '~/src/config/index.js'\nimport { escapeMarkdown } from '~/src/server/plugins/engine/components/helpers.js'\nimport { 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 { getFormatter } from '~/src/server/plugins/engine/outputFormatters/index.js'\nimport { type FormContext } from '~/src/server/plugins/engine/types.js'\nimport { type FormRequestPayload } from '~/src/server/routes/types.js'\nimport { sendNotification } from '~/src/server/utils/notify.js'\n\nconst templateId = config.get('notifyTemplateId')\n\nexport async function submit(\n context: FormContext,\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload\n) {\n const logTags = ['submit', 'email']\n const formStatus = checkFormStatus(request.params)\n\n // Get submission email personalisation\n request.logger.info(logTags, 'Getting personalisation data')\n\n const formName = escapeMarkdown(model.name)\n const subject = formStatus.isPreview\n ? `TEST FORM SUBMISSION: ${formName}`\n : `Form submission: ${formName}`\n\n const outputAudience = model.def.output?.audience ?? 'human'\n const outputVersion = model.def.output?.version ?? '1'\n\n const outputFormatter = getFormatter(outputAudience, outputVersion)\n let body = outputFormatter(context, items, model, submitResponse, formStatus)\n\n // GOV.UK Notify transforms quotes into curly quotes, so we can't just send the raw payload\n // This is logic specific to Notify, so we include the logic here rather than in the formatter\n if (outputAudience === 'machine') {\n body = Buffer.from(body).toString('base64')\n }\n\n request.logger.info(logTags, 'Sending email')\n\n try {\n // Send submission email\n await sendNotification({\n templateId,\n emailAddress,\n personalisation: {\n subject,\n body\n }\n })\n\n request.logger.info(logTags, 'Email sent successfully')\n } catch (err) {\n const errMsg = getErrorMessage(err)\n request.logger.error(\n errMsg,\n `[emailSendFailed] Error sending notification email - templateId: ${templateId} - recipient: ${emailAddress} - ${errMsg}`\n )\n\n throw err\n }\n}\n"],"mappings":"AAAA,SAASA,eAAe,QAAoC,oBAAoB;AAEhF,SAASC,MAAM;AACf,SAASC,cAAc;AACvB,SAASC,eAAe;AAGxB,SAASC,YAAY;AAGrB,SAASC,gBAAgB;AAEzB,MAAMC,UAAU,GAAGL,MAAM,CAACM,GAAG,CAAC,kBAAkB,CAAC;AAEjD,OAAO,eAAeC,MAAMA,CAC1BC,OAAoB,EACpBC,OAA2B,EAC3BC,KAAgB,EAChBC,YAAoB,EACpBC,KAAmB,EACnBC,cAAqC,EACrC;EACA,MAAMC,OAAO,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC;EACnC,MAAMC,UAAU,GAAGb,eAAe,CAACO,OAAO,CAACO,MAAM,CAAC;;EAElD;EACAP,OAAO,CAACQ,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,8BAA8B,CAAC;EAE5D,MAAMK,QAAQ,GAAGlB,cAAc,CAACS,KAAK,CAACU,IAAI,CAAC;EAC3C,MAAMC,OAAO,GAAGN,UAAU,CAACO,SAAS,GAChC,yBAAyBH,QAAQ,EAAE,GACnC,oBAAoBA,QAAQ,EAAE;EAElC,MAAMI,cAAc,GAAGb,KAAK,CAACc,GAAG,CAACC,MAAM,EAAEC,QAAQ,IAAI,OAAO;EAC5D,MAAMC,aAAa,GAAGjB,KAAK,CAACc,GAAG,CAACC,MAAM,EAAEG,OAAO,IAAI,GAAG;EAEtD,MAAMC,eAAe,GAAG1B,YAAY,CAACoB,cAAc,EAAEI,aAAa,CAAC;EACnE,IAAIG,IAAI,GAAGD,eAAe,CAACrB,OAAO,EAAEI,KAAK,EAAEF,KAAK,EAAEG,cAAc,EAAEE,UAAU,CAAC;;EAE7E;EACA;EACA,IAAIQ,cAAc,KAAK,SAAS,EAAE;IAChCO,IAAI,GAAGC,MAAM,CAACC,IAAI,CAACF,IAAI,CAAC,CAACG,QAAQ,CAAC,QAAQ,CAAC;EAC7C;EAEAxB,OAAO,CAACQ,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,eAAe,CAAC;EAE7C,IAAI;IACF;IACA,MAAMV,gBAAgB,CAAC;MACrBC,UAAU;MACVM,YAAY;MACZuB,eAAe,EAAE;QACfb,OAAO;QACPS;MACF;IACF,CAAC,CAAC;IAEFrB,OAAO,CAACQ,MAAM,CAACC,IAAI,CAACJ,OAAO,EAAE,yBAAyB,CAAC;EACzD,CAAC,CAAC,OAAOqB,GAAG,EAAE;IACZ,MAAMC,MAAM,GAAGrC,eAAe,CAACoC,GAAG,CAAC;IACnC1B,OAAO,CAACQ,MAAM,CAACoB,KAAK,CAClBD,MAAM,EACN,oEAAoE/B,UAAU,iBAAiBM,YAAY,MAAMyB,MAAM,EACzH,CAAC;IAED,MAAMD,GAAG;EACX;AACF","ignoreList":[]}
@@ -273,7 +273,7 @@ export interface PluginOptions {
273
273
  saveAndReturn?: {
274
274
  keyGenerator: (request: RequestType) => string;
275
275
  sessionHydrator: (request: RequestType) => Promise<FormSubmissionState>;
276
- sessionPersister: (key: string, state: FormSubmissionState, request: RequestType) => Promise<void>;
276
+ sessionPersister: (state: FormSubmissionState, request: RequestType) => Promise<void>;
277
277
  };
278
278
  pluginPath?: string;
279
279
  nunjucks: {
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":["UploadStatus","FileStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormDefinition,\n type FormMetadata,\n type Item,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { type PluginProperties, type Request } from '@hapi/hapi'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormParams,\n type FormRequest,\n type FormRequestPayload\n} from '~/src/server/routes/types.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\ntype RequestType = Request | FormRequest | FormRequestPayload\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError\n extends Pick<ValidationErrorItem, 'context' | 'path'> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport enum UploadStatus {\n initiated = 'initiated',\n pending = 'pending',\n ready = 'ready'\n}\n\nexport enum FileStatus {\n complete = 'complete',\n rejected = 'rejected',\n pending = 'pending'\n}\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n allowSaveAndReturn: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: FormRequest | FormRequestPayload,\n params: FormParams,\n definition: FormDefinition,\n metadata: FormMetadata\n) => void\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cacheName?: string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndReturn?: {\n keyGenerator: (request: RequestType) => string\n sessionHydrator: (request: RequestType) => Promise<FormSubmissionState>\n sessionPersister: (\n key: string,\n state: FormSubmissionState,\n request: RequestType\n ) => Promise<void>\n }\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n}\n"],"mappings":"AAkCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAmBA;AACA;AACA;AACA;;AAqGA,WAAYA,YAAY,0BAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA;AAMxB,WAAYC,UAAU,0BAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAA,OAAVA,UAAU;AAAA","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":["UploadStatus","FileStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormDefinition,\n type FormMetadata,\n type Item,\n type List,\n type Page\n} from '@defra/forms-model'\nimport { type PluginProperties, type Request } from '@hapi/hapi'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormParams,\n type FormRequest,\n type FormRequestPayload\n} from '~/src/server/routes/types.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\ntype RequestType = Request | FormRequest | FormRequestPayload\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError\n extends Pick<ValidationErrorItem, 'context' | 'path'> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport enum UploadStatus {\n initiated = 'initiated',\n pending = 'pending',\n ready = 'ready'\n}\n\nexport enum FileStatus {\n complete = 'complete',\n rejected = 'rejected',\n pending = 'pending'\n}\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n allowSaveAndReturn: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: FormRequest | FormRequestPayload,\n params: FormParams,\n definition: FormDefinition,\n metadata: FormMetadata\n) => void\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cacheName?: string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndReturn?: {\n keyGenerator: (request: RequestType) => string\n sessionHydrator: (request: RequestType) => Promise<FormSubmissionState>\n sessionPersister: (\n state: FormSubmissionState,\n request: RequestType\n ) => Promise<void>\n }\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n}\n"],"mappings":"AAkCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAmBA;AACA;AACA;AACA;;AAqGA,WAAYA,YAAY,0BAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA;AAMxB,WAAYC,UAAU,0BAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAVA,UAAU;EAAA,OAAVA,UAAU;AAAA","ignoreList":[]}
@@ -14,7 +14,6 @@ export declare class CacheService {
14
14
  }>;
15
15
  generateKey?: (request: Request | FormRequest | FormRequestPayload) => string;
16
16
  customFetcher?: (request: Request | FormRequest | FormRequestPayload) => Promise<FormSubmissionState | null>;
17
- customPersister?: (key: string, state: FormSubmissionState, request: Request | FormRequest | FormRequestPayload) => Promise<void>;
18
17
  logger: Server['logger'];
19
18
  constructor({ server, cacheName, options }: {
20
19
  server: Server;
@@ -22,7 +21,6 @@ export declare class CacheService {
22
21
  options?: {
23
22
  keyGenerator?: (request: Request | FormRequest | FormRequestPayload) => string;
24
23
  sessionHydrator?: (request: Request | FormRequest | FormRequestPayload) => Promise<FormSubmissionState | null>;
25
- sessionPersister?: (key: string, state: FormSubmissionState, request: Request | FormRequest | FormRequestPayload) => Promise<void>;
26
24
  };
27
25
  });
28
26
  getState(request: Request | FormRequest | FormRequestPayload): Promise<FormSubmissionState>;
@@ -12,7 +12,6 @@ export class CacheService {
12
12
  cache;
13
13
  generateKey;
14
14
  customFetcher;
15
- customPersister;
16
15
  logger;
17
16
  constructor({
18
17
  server,
@@ -21,15 +20,13 @@ export class CacheService {
21
20
  }) {
22
21
  const {
23
22
  keyGenerator,
24
- sessionHydrator,
25
- sessionPersister
23
+ sessionHydrator
26
24
  } = options ?? {};
27
25
  if (!cacheName) {
28
26
  server.log('warn', 'You are using the default hapi cache. Please provide a cache name in plugin registration options.');
29
27
  }
30
28
  this.generateKey = keyGenerator ?? this.defaultKeyGenerator.bind(this);
31
29
  this.customFetcher = sessionHydrator ?? undefined;
32
- this.customPersister = sessionPersister ?? undefined;
33
30
  this.cache = server.cache({
34
31
  cache: cacheName,
35
32
  segment: 'formSubmission'
@@ -1 +1 @@
1
- {"version":3,"file":"cacheService.js","names":["Hoek","config","partition","ADDITIONAL_IDENTIFIER","CacheService","cache","generateKey","customFetcher","customPersister","logger","constructor","server","cacheName","options","keyGenerator","sessionHydrator","sessionPersister","log","defaultKeyGenerator","bind","undefined","segment","getState","request","key","Key","cached","get","rehydrated","set","setState","state","ttl","getConfirmationState","Confirmation","value","setConfirmationState","confirmationState","clearState","yar","id","drop","getFlash","messages","flash","Array","isArray","length","at","setFlash","message","Error","params","slug","additionalIdentifier","baseKey","merge","update","mergeArrays"],"sources":["../../../src/server/services/cacheService.ts"],"sourcesContent":["import { type Request, type Server } from '@hapi/hapi'\nimport * as Hoek from '@hapi/hoek'\n\nimport { config } from '~/src/config/index.js'\nimport { type createServer } from '~/src/server/index.js'\nimport {\n type FormPayload,\n type FormState,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload\n} from '~/src/server/routes/types.js'\n\nconst partition = 'cache'\n\nexport enum ADDITIONAL_IDENTIFIER {\n Confirmation = ':confirmation'\n}\n\nexport class CacheService {\n /**\n * This service is responsible for getting, storing or deleting a user's session data in the cache. This service has been registered by {@link createServer}\n */\n cache\n generateKey?: (request: Request | FormRequest | FormRequestPayload) => string\n customFetcher?: (\n request: Request | FormRequest | FormRequestPayload\n ) => Promise<FormSubmissionState | null>\n\n customPersister?: (\n key: string,\n state: FormSubmissionState,\n request: Request | FormRequest | FormRequestPayload\n ) => Promise<void>\n\n logger: Server['logger']\n\n constructor({\n server,\n cacheName,\n options\n }: {\n server: Server\n cacheName?: string\n options?: {\n keyGenerator?: (\n request: Request | FormRequest | FormRequestPayload\n ) => string\n sessionHydrator?: (\n request: Request | FormRequest | FormRequestPayload\n ) => Promise<FormSubmissionState | null>\n sessionPersister?: (\n key: string,\n state: FormSubmissionState,\n request: Request | FormRequest | FormRequestPayload\n ) => Promise<void>\n }\n }) {\n const { keyGenerator, sessionHydrator, sessionPersister } = options ?? {}\n if (!cacheName) {\n server.log(\n 'warn',\n 'You are using the default hapi cache. Please provide a cache name in plugin registration options.'\n )\n }\n this.generateKey = keyGenerator ?? this.defaultKeyGenerator.bind(this)\n this.customFetcher = sessionHydrator ?? undefined\n this.customPersister = sessionPersister ?? undefined\n this.cache = server.cache({ cache: cacheName, segment: 'formSubmission' })\n this.logger = server.logger\n }\n\n async getState(\n request: Request | FormRequest | FormRequestPayload\n ): Promise<FormSubmissionState> {\n const key = this.Key(request)\n\n let cached = await this.cache.get(key)\n\n // If nothing in Redis, attempt to rehydrate from backend DB\n if (!cached && this.customFetcher) {\n const rehydrated = await this.customFetcher(request)\n\n if (rehydrated != null) {\n await this.cache.set(key, rehydrated, config.get('sessionTimeout'))\n cached = await this.getState(request)\n }\n }\n\n return cached ?? {}\n }\n\n async setState(\n request: FormRequest | FormRequestPayload,\n state: FormSubmissionState\n ) {\n const key = this.Key(request)\n const ttl = config.get('sessionTimeout')\n\n await this.cache.set(key, state, ttl)\n\n return this.getState(request)\n }\n\n async getConfirmationState(\n request: FormRequest | FormRequestPayload\n ): Promise<{ confirmed?: true }> {\n const key = this.Key(request, ADDITIONAL_IDENTIFIER.Confirmation)\n const value = await this.cache.get(key)\n\n return value ?? {}\n }\n\n async setConfirmationState(\n request: FormRequest | FormRequestPayload,\n confirmationState: { confirmed?: true }\n ) {\n const key = this.Key(request, ADDITIONAL_IDENTIFIER.Confirmation)\n const ttl = config.get('confirmationSessionTimeout')\n\n return this.cache.set(key, confirmationState, ttl)\n }\n\n async clearState(request: FormRequest | FormRequestPayload) {\n if (request.yar.id) {\n await this.cache.drop(this.Key(request))\n }\n }\n\n getFlash(\n request: FormRequest | FormRequestPayload\n ): { errors: FormSubmissionError[] } | undefined {\n const key = this.Key(request)\n const messages = request.yar.flash(key.id)\n\n if (Array.isArray(messages) && messages.length) {\n return messages.at(0) as { errors: FormSubmissionError[] }\n }\n }\n\n setFlash(\n request: FormRequest | FormRequestPayload,\n message: { errors: FormSubmissionError[] }\n ) {\n const key = this.Key(request)\n\n request.yar.flash(key.id, message)\n }\n\n private defaultKeyGenerator(\n request: Request | FormRequest | FormRequestPayload\n ): string {\n if (!request.yar.id) {\n throw new Error('No session ID found')\n }\n\n const state = (request.params.state as string) || ''\n const slug = (request.params.slug as string) || ''\n return `${request.yar.id}:${state}:${slug}:`\n }\n\n /**\n * The key used to store user session data against.\n * If there are multiple forms on the same runner instance, for example `form-a` and `form-a-feedback` this will prevent CacheService from clearing data from `form-a` if a user gave feedback before they finished `form-a`\n * @param request - hapi request object\n * @param additionalIdentifier - appended to the id\n */\n Key(\n request: Request | FormRequest | FormRequestPayload,\n additionalIdentifier?: ADDITIONAL_IDENTIFIER\n ) {\n const baseKey = this.generateKey\n ? this.generateKey(request)\n : this.defaultKeyGenerator(request)\n\n return {\n segment: partition,\n id: `${baseKey}${additionalIdentifier ?? ''}`\n }\n }\n}\n\n/**\n * State merge helper\n * 1. Merges objects (form fields)\n * 2. Overwrites arrays\n */\nexport function merge<StateType extends FormState | FormPayload>(\n state: StateType,\n update: object\n): StateType {\n return Hoek.merge(state, update, {\n mergeArrays: false\n })\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"cacheService.js","names":["Hoek","config","partition","ADDITIONAL_IDENTIFIER","CacheService","cache","generateKey","customFetcher","logger","constructor","server","cacheName","options","keyGenerator","sessionHydrator","log","defaultKeyGenerator","bind","undefined","segment","getState","request","key","Key","cached","get","rehydrated","set","setState","state","ttl","getConfirmationState","Confirmation","value","setConfirmationState","confirmationState","clearState","yar","id","drop","getFlash","messages","flash","Array","isArray","length","at","setFlash","message","Error","params","slug","additionalIdentifier","baseKey","merge","update","mergeArrays"],"sources":["../../../src/server/services/cacheService.ts"],"sourcesContent":["import { type Request, type Server } from '@hapi/hapi'\nimport * as Hoek from '@hapi/hoek'\n\nimport { config } from '~/src/config/index.js'\nimport { type createServer } from '~/src/server/index.js'\nimport {\n type FormPayload,\n type FormState,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequest,\n type FormRequestPayload\n} from '~/src/server/routes/types.js'\n\nconst partition = 'cache'\n\nexport enum ADDITIONAL_IDENTIFIER {\n Confirmation = ':confirmation'\n}\n\nexport class CacheService {\n /**\n * This service is responsible for getting, storing or deleting a user's session data in the cache. This service has been registered by {@link createServer}\n */\n cache\n generateKey?: (request: Request | FormRequest | FormRequestPayload) => string\n customFetcher?: (\n request: Request | FormRequest | FormRequestPayload\n ) => Promise<FormSubmissionState | null>\n\n logger: Server['logger']\n\n constructor({\n server,\n cacheName,\n options\n }: {\n server: Server\n cacheName?: string\n options?: {\n keyGenerator?: (\n request: Request | FormRequest | FormRequestPayload\n ) => string\n sessionHydrator?: (\n request: Request | FormRequest | FormRequestPayload\n ) => Promise<FormSubmissionState | null>\n }\n }) {\n const { keyGenerator, sessionHydrator } = options ?? {}\n if (!cacheName) {\n server.log(\n 'warn',\n 'You are using the default hapi cache. Please provide a cache name in plugin registration options.'\n )\n }\n this.generateKey = keyGenerator ?? this.defaultKeyGenerator.bind(this)\n this.customFetcher = sessionHydrator ?? undefined\n this.cache = server.cache({ cache: cacheName, segment: 'formSubmission' })\n this.logger = server.logger\n }\n\n async getState(\n request: Request | FormRequest | FormRequestPayload\n ): Promise<FormSubmissionState> {\n const key = this.Key(request)\n\n let cached = await this.cache.get(key)\n\n // If nothing in Redis, attempt to rehydrate from backend DB\n if (!cached && this.customFetcher) {\n const rehydrated = await this.customFetcher(request)\n\n if (rehydrated != null) {\n await this.cache.set(key, rehydrated, config.get('sessionTimeout'))\n cached = await this.getState(request)\n }\n }\n\n return cached ?? {}\n }\n\n async setState(\n request: FormRequest | FormRequestPayload,\n state: FormSubmissionState\n ) {\n const key = this.Key(request)\n const ttl = config.get('sessionTimeout')\n\n await this.cache.set(key, state, ttl)\n\n return this.getState(request)\n }\n\n async getConfirmationState(\n request: FormRequest | FormRequestPayload\n ): Promise<{ confirmed?: true }> {\n const key = this.Key(request, ADDITIONAL_IDENTIFIER.Confirmation)\n const value = await this.cache.get(key)\n\n return value ?? {}\n }\n\n async setConfirmationState(\n request: FormRequest | FormRequestPayload,\n confirmationState: { confirmed?: true }\n ) {\n const key = this.Key(request, ADDITIONAL_IDENTIFIER.Confirmation)\n const ttl = config.get('confirmationSessionTimeout')\n\n return this.cache.set(key, confirmationState, ttl)\n }\n\n async clearState(request: FormRequest | FormRequestPayload) {\n if (request.yar.id) {\n await this.cache.drop(this.Key(request))\n }\n }\n\n getFlash(\n request: FormRequest | FormRequestPayload\n ): { errors: FormSubmissionError[] } | undefined {\n const key = this.Key(request)\n const messages = request.yar.flash(key.id)\n\n if (Array.isArray(messages) && messages.length) {\n return messages.at(0) as { errors: FormSubmissionError[] }\n }\n }\n\n setFlash(\n request: FormRequest | FormRequestPayload,\n message: { errors: FormSubmissionError[] }\n ) {\n const key = this.Key(request)\n\n request.yar.flash(key.id, message)\n }\n\n private defaultKeyGenerator(\n request: Request | FormRequest | FormRequestPayload\n ): string {\n if (!request.yar.id) {\n throw new Error('No session ID found')\n }\n\n const state = (request.params.state as string) || ''\n const slug = (request.params.slug as string) || ''\n return `${request.yar.id}:${state}:${slug}:`\n }\n\n /**\n * The key used to store user session data against.\n * If there are multiple forms on the same runner instance, for example `form-a` and `form-a-feedback` this will prevent CacheService from clearing data from `form-a` if a user gave feedback before they finished `form-a`\n * @param request - hapi request object\n * @param additionalIdentifier - appended to the id\n */\n Key(\n request: Request | FormRequest | FormRequestPayload,\n additionalIdentifier?: ADDITIONAL_IDENTIFIER\n ) {\n const baseKey = this.generateKey\n ? this.generateKey(request)\n : this.defaultKeyGenerator(request)\n\n return {\n segment: partition,\n id: `${baseKey}${additionalIdentifier ?? ''}`\n }\n }\n}\n\n/**\n * State merge helper\n * 1. Merges objects (form fields)\n * 2. Overwrites arrays\n */\nexport function merge<StateType extends FormState | FormPayload>(\n state: StateType,\n update: object\n): StateType {\n return Hoek.merge(state, update, {\n mergeArrays: false\n })\n}\n"],"mappings":"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","ignoreList":[]}
@@ -2,7 +2,7 @@ import { type FormDefinition, type FormMetadata, type SubmitPayload, type Submit
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 PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js';
5
- import { type OnRequestCallback, type PluginOptions, type PreparePageEventRequestOptions } from '~/src/server/plugins/engine/types.js';
5
+ import { type FormContext, type OnRequestCallback, type PluginOptions, type PreparePageEventRequestOptions } from '~/src/server/plugins/engine/types.js';
6
6
  import { type FormRequestPayload, type FormStatus } from '~/src/server/routes/types.js';
7
7
  export interface FormsService {
8
8
  getFormMetadata: (slug: string) => Promise<FormMetadata>;
@@ -31,5 +31,5 @@ export interface RouteConfig {
31
31
  saveAndReturn?: PluginOptions['saveAndReturn'];
32
32
  }
33
33
  export interface OutputService {
34
- submit: (request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload) => Promise<void>;
34
+ submit: (context: FormContext, request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload) => Promise<void>;
35
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../src/server/types.ts"],"sourcesContent":["import {\n type FormDefinition,\n type FormMetadata,\n type SubmitPayload,\n type SubmitResponsePayload\n} from '@defra/forms-model'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n type OnRequestCallback,\n type PluginOptions,\n type PreparePageEventRequestOptions\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequestPayload,\n type FormStatus\n} from '~/src/server/routes/types.js'\n\nexport interface FormsService {\n getFormMetadata: (slug: string) => Promise<FormMetadata>\n getFormDefinition: (\n id: string,\n state: FormStatus\n ) => Promise<FormDefinition | undefined>\n}\n\nexport interface FormSubmissionService {\n persistFiles: (\n files: { fileId: string; initiatedRetrievalKey: string }[],\n persistedRetrievalKey: string\n ) => Promise<object>\n submit: (data: SubmitPayload) => Promise<SubmitResponsePayload | undefined>\n}\n\nexport interface Services {\n formsService: FormsService\n formSubmissionService: FormSubmissionService\n outputService: OutputService\n}\n\nexport interface RouteConfig {\n formFileName?: string\n formFilePath?: string\n enforceCsrf?: boolean\n services?: Services\n controllers?: Record<string, typeof PageController>\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n saveAndReturn?: PluginOptions['saveAndReturn']\n}\n\nexport interface OutputService {\n submit: (\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload\n ) => Promise<void>\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../src/server/types.ts"],"sourcesContent":["import {\n type FormDefinition,\n type FormMetadata,\n type SubmitPayload,\n type SubmitResponsePayload\n} from '@defra/forms-model'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n type FormContext,\n type OnRequestCallback,\n type PluginOptions,\n type PreparePageEventRequestOptions\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequestPayload,\n type FormStatus\n} from '~/src/server/routes/types.js'\n\nexport interface FormsService {\n getFormMetadata: (slug: string) => Promise<FormMetadata>\n getFormDefinition: (\n id: string,\n state: FormStatus\n ) => Promise<FormDefinition | undefined>\n}\n\nexport interface FormSubmissionService {\n persistFiles: (\n files: { fileId: string; initiatedRetrievalKey: string }[],\n persistedRetrievalKey: string\n ) => Promise<object>\n submit: (data: SubmitPayload) => Promise<SubmitResponsePayload | undefined>\n}\n\nexport interface Services {\n formsService: FormsService\n formSubmissionService: FormSubmissionService\n outputService: OutputService\n}\n\nexport interface RouteConfig {\n formFileName?: string\n formFilePath?: string\n enforceCsrf?: boolean\n services?: Services\n controllers?: Record<string, typeof PageController>\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n saveAndReturn?: PluginOptions['saveAndReturn']\n}\n\nexport interface OutputService {\n submit: (\n context: FormContext,\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload\n ) => Promise<void>\n}\n"],"mappings":"","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -64,7 +64,7 @@
64
64
  "license": "SEE LICENSE IN LICENSE",
65
65
  "dependencies": {
66
66
  "@defra/forms-model": "^3.0.506",
67
- "@defra/hapi-tracing": "^1.0.0",
67
+ "@defra/hapi-tracing": "^1.26.0",
68
68
  "@elastic/ecs-pino-format": "^1.5.0",
69
69
  "@hapi/boom": "^10.0.1",
70
70
  "@hapi/catbox": "^12.1.1",
@@ -83,7 +83,7 @@ describe('getPersonalisation', () => {
83
83
  isPreview: true
84
84
  }
85
85
  ])('should personalise $state email', (formStatus) => {
86
- const body = format(items, model, submitResponse, formStatus)
86
+ const body = format(context, items, model, submitResponse, formStatus)
87
87
 
88
88
  const dateNow = new Date()
89
89
  const dateExpiry = addDays(dateNow, 90)
@@ -122,12 +122,12 @@ describe('getPersonalisation', () => {
122
122
  isPreview: true
123
123
  }
124
124
 
125
- const body1 = format(items, model, submitResponse, {
125
+ const body1 = format(context, items, model, submitResponse, {
126
126
  state: FormStatus.Live,
127
127
  isPreview: false
128
128
  })
129
129
 
130
- const body2 = format(items, model, submitResponse, {
130
+ const body2 = format(context, items, model, submitResponse, {
131
131
  state: FormStatus.Draft,
132
132
  isPreview: true
133
133
  })
@@ -9,10 +9,12 @@ import {
9
9
  import { type checkFormStatus } from '~/src/server/plugins/engine/helpers.js'
10
10
  import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
11
11
  import { type DetailItem } from '~/src/server/plugins/engine/models/types.js'
12
+ import { type FormContext } from '~/src/server/plugins/engine/types.js'
12
13
 
13
14
  const designerUrl = config.get('designerUrl')
14
15
 
15
16
  export function format(
17
+ _context: FormContext,
16
18
  items: DetailItem[],
17
19
  model: FormModel,
18
20
  submitResponse: SubmitResponsePayload,
@@ -6,8 +6,10 @@ import { type DetailItem } from '~/src/server/plugins/engine/models/types.js'
6
6
  import { format as formatHumanV1 } from '~/src/server/plugins/engine/outputFormatters/human/v1.js'
7
7
  import { format as formatMachineV1 } from '~/src/server/plugins/engine/outputFormatters/machine/v1.js'
8
8
  import { format as formatMachineV2 } from '~/src/server/plugins/engine/outputFormatters/machine/v2.js'
9
+ import { type FormContext } from '~/src/server/plugins/engine/types.js'
9
10
 
10
11
  type Formatter = (
12
+ context: FormContext,
11
13
  items: DetailItem[],
12
14
  model: FormModel,
13
15
  submitResponse: SubmitResponsePayload,
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
+
2
3
  import { FileUploadField } from '~/src/server/plugins/engine/components/FileUploadField.js'
3
4
  import { type Field } from '~/src/server/plugins/engine/components/helpers.js'
4
5
  import { FormModel } from '~/src/server/plugins/engine/models/index.js'
@@ -8,6 +9,7 @@ import {
8
9
  type DetailItemRepeat
9
10
  } from '~/src/server/plugins/engine/models/types.js'
10
11
  import { format } from '~/src/server/plugins/engine/outputFormatters/machine/v1.js'
12
+ import { buildFormContextRequest } from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js'
11
13
  import {
12
14
  FileStatus,
13
15
  UploadStatus,
@@ -41,6 +43,42 @@ const dummyField: Field = {
41
43
  getContextValueFromState: (_) => 'hello world'
42
44
  } as Field
43
45
 
46
+ const itemId1 = 'abc-123'
47
+ const itemId2 = 'xyz-987'
48
+
49
+ const state = {
50
+ $$__referenceNumber: 'foobar',
51
+ orderType: 'delivery',
52
+ pizza: [
53
+ {
54
+ toppings: 'Ham',
55
+ quantity: 2,
56
+ itemId: itemId1
57
+ },
58
+ {
59
+ toppings: 'Pepperoni',
60
+ quantity: 1,
61
+ itemId: itemId2
62
+ }
63
+ ]
64
+ }
65
+
66
+ const pageUrl = new URL('http://example.com/repeat/pizza-order/summary')
67
+
68
+ const request = buildFormContextRequest({
69
+ method: 'get',
70
+ url: pageUrl,
71
+ path: pageUrl.pathname,
72
+ params: {
73
+ path: 'pizza-order',
74
+ slug: 'repeat'
75
+ },
76
+ query: {},
77
+ app: { model }
78
+ })
79
+
80
+ const context = model.getFormContext(request, state)
81
+
44
82
  const testDetailItemField: DetailItemField = {
45
83
  name: 'exampleField',
46
84
  label: 'Example Field',
@@ -182,7 +220,7 @@ describe('getPersonalisation', () => {
182
220
  it('should return the machine output', () => {
183
221
  model.def = definition
184
222
 
185
- const body = format(items, model, submitResponse, formStatus)
223
+ const body = format(context, items, model, submitResponse, formStatus)
186
224
 
187
225
  const parsedBody = JSON.parse(body)
188
226
 
@@ -224,6 +262,7 @@ describe('getPersonalisation', () => {
224
262
  expect(parsedBody.meta.schemaVersion).toBe('1')
225
263
  expect(parsedBody.meta.timestamp).toBeDateString()
226
264
  expect(parsedBody.meta.definition).toEqual(definition)
265
+ expect(parsedBody.meta.referenceNumber).toBe('foobar')
227
266
  expect(parsedBody.data).toEqual(expectedData)
228
267
  })
229
268
  })
@@ -10,10 +10,12 @@ import {
10
10
  type DetailItemField,
11
11
  type DetailItemRepeat
12
12
  } from '~/src/server/plugins/engine/models/types.js'
13
+ import { type FormContext } from '~/src/server/plugins/engine/types.js'
13
14
 
14
15
  const designerUrl = config.get('designerUrl')
15
16
 
16
17
  export function format(
18
+ context: FormContext,
17
19
  items: DetailItem[],
18
20
  model: FormModel,
19
21
  _submitResponse: SubmitResponsePayload,
@@ -27,6 +29,7 @@ export function format(
27
29
  meta: {
28
30
  schemaVersion: '1',
29
31
  timestamp: now.toISOString(),
32
+ referenceNumber: context.referenceNumber,
30
33
  definition: model.def
31
34
  },
32
35
  data: categorisedData
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
+
2
3
  import { FileUploadField } from '~/src/server/plugins/engine/components/FileUploadField.js'
3
4
  import { type Field } from '~/src/server/plugins/engine/components/helpers.js'
4
5
  import { FormModel } from '~/src/server/plugins/engine/models/index.js'
@@ -8,6 +9,7 @@ import {
8
9
  type DetailItemRepeat
9
10
  } from '~/src/server/plugins/engine/models/types.js'
10
11
  import { format } from '~/src/server/plugins/engine/outputFormatters/machine/v2.js'
12
+ import { buildFormContextRequest } from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js'
11
13
  import {
12
14
  FileStatus,
13
15
  UploadStatus,
@@ -41,6 +43,42 @@ const dummyField: Field = {
41
43
  getFormValueFromState: (_) => 'hello world'
42
44
  } as Field
43
45
 
46
+ const itemId1 = 'abc-123'
47
+ const itemId2 = 'xyz-987'
48
+
49
+ const state = {
50
+ $$__referenceNumber: 'foobar',
51
+ orderType: 'delivery',
52
+ pizza: [
53
+ {
54
+ toppings: 'Ham',
55
+ quantity: 2,
56
+ itemId: itemId1
57
+ },
58
+ {
59
+ toppings: 'Pepperoni',
60
+ quantity: 1,
61
+ itemId: itemId2
62
+ }
63
+ ]
64
+ }
65
+
66
+ const pageUrl = new URL('http://example.com/repeat/pizza-order/summary')
67
+
68
+ const request = buildFormContextRequest({
69
+ method: 'get',
70
+ url: pageUrl,
71
+ path: pageUrl.pathname,
72
+ params: {
73
+ path: 'pizza-order',
74
+ slug: 'repeat'
75
+ },
76
+ query: {},
77
+ app: { model }
78
+ })
79
+
80
+ const context = model.getFormContext(request, state)
81
+
44
82
  const testDetailItemField: DetailItemField = {
45
83
  name: 'exampleField',
46
84
  label: 'Example Field',
@@ -182,7 +220,7 @@ describe('getPersonalisation', () => {
182
220
  it('should return the machine output', () => {
183
221
  model.def = definition
184
222
 
185
- const body = format(items, model, submitResponse, formStatus)
223
+ const body = format(context, items, model, submitResponse, formStatus)
186
224
 
187
225
  const parsedBody = JSON.parse(body)
188
226
 
@@ -224,6 +262,7 @@ describe('getPersonalisation', () => {
224
262
  expect(parsedBody.meta.schemaVersion).toBe('2')
225
263
  expect(parsedBody.meta.timestamp).toBeDateString()
226
264
  expect(parsedBody.meta.definition).toEqual(definition)
265
+ expect(parsedBody.meta.referenceNumber).toBe('foobar')
227
266
  expect(parsedBody.data).toEqual(expectedData)
228
267
  })
229
268
  })
@@ -15,6 +15,7 @@ import {
15
15
  type DetailItemRepeat
16
16
  } from '~/src/server/plugins/engine/models/types.js'
17
17
  import {
18
+ type FormContext,
18
19
  type FormPayload,
19
20
  type FormValue
20
21
  } from '~/src/server/plugins/engine/types.js'
@@ -22,6 +23,7 @@ import {
22
23
  const designerUrl = config.get('designerUrl')
23
24
 
24
25
  export function format(
26
+ context: FormContext,
25
27
  items: DetailItem[],
26
28
  model: FormModel,
27
29
  _submitResponse: SubmitResponsePayload,
@@ -35,7 +37,8 @@ export function format(
35
37
  meta: {
36
38
  schemaVersion: '2',
37
39
  timestamp: now.toISOString(),
38
- definition: model.def
40
+ definition: model.def,
41
+ referenceNumber: context.referenceNumber
39
42
  },
40
43
  data: categorisedData
41
44
  }