@defra/forms-engine-plugin 4.11.0 → 4.11.2

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.
@@ -15,9 +15,9 @@ export class CheckboxesField extends SelectionControlField {
15
15
  let formSchema = type === 'string' ? joi.array() : joi.array();
16
16
  const itemsSchema = joi[type]().valid(...this.values).label(this.label);
17
17
  formSchema = formSchema.items(itemsSchema).single().label(this.label).required().messages({
18
- 'array.min': 'Select at least {{#limit}} options from the list',
19
- 'array.max': 'Only {{#limit}} can be selected from the list',
20
- 'array.length': 'Select only {{#limit}} options from the list'
18
+ 'array.min': messageTemplate.arrayMin,
19
+ 'array.max': messageTemplate.arrayMax,
20
+ 'array.length': messageTemplate.arrayLength
21
21
  });
22
22
  if (options.required === false) {
23
23
  formSchema = formSchema.optional();
@@ -1 +1 @@
1
- {"version":3,"file":"CheckboxesField.js","names":["joi","isFormValue","SelectionControlField","messageTemplate","CheckboxesField","constructor","def","props","listType","type","options","schema","formSchema","array","itemsSchema","valid","values","label","items","single","required","messages","optional","length","min","max","default","stateSchema","allow","getFormValueFromState","state","name","getFormValue","selected","filter","item","includes","value","map","undefined","isValue","getDisplayStringFromFormValue","text","join","getContextValueFromFormValue","getDisplayStringFromState","getContextValueFromState","getAllPossibleErrors","parentErrors","advancedSettingsErrors","template","arrayMin","arrayMax","arrayLength","Array","isArray","every"],"sources":["../../../../../src/server/plugins/engine/components/CheckboxesField.ts"],"sourcesContent":["import { type CheckboxesFieldComponent, type Item } from '@defra/forms-model'\nimport joi, { type ArraySchema } from 'joi'\n\nimport { isFormValue } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormState,\n type FormStateValue,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class CheckboxesField extends SelectionControlField {\n declare options: CheckboxesFieldComponent['options']\n declare schema: CheckboxesFieldComponent['schema']\n declare formSchema: ArraySchema<string> | ArraySchema<number>\n declare stateSchema: ArraySchema<string> | ArraySchema<number>\n\n constructor(\n def: CheckboxesFieldComponent,\n props: ConstructorParameters<typeof SelectionControlField>[1]\n ) {\n super(def, props)\n\n const { listType: type } = this\n const { options, schema } = def\n\n let formSchema =\n type === 'string' ? joi.array<string>() : joi.array<number>()\n\n const itemsSchema = joi[type]()\n .valid(...this.values)\n .label(this.label)\n\n formSchema = formSchema\n .items(itemsSchema)\n .single()\n .label(this.label)\n .required()\n .messages({\n 'array.min': 'Select at least {{#limit}} options from the list',\n 'array.max': 'Only {{#limit}} can be selected from the list',\n 'array.length': 'Select only {{#limit}} options from the list'\n })\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n if (typeof schema?.length === 'number') {\n formSchema = formSchema.length(schema.length)\n } else {\n if (typeof schema?.min === 'number') {\n formSchema = formSchema.min(schema.min)\n }\n\n if (typeof schema?.max === 'number') {\n formSchema = formSchema.max(schema.max)\n }\n }\n\n this.formSchema = formSchema.default([])\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { items, name } = this\n\n // State checkbox values\n const values = this.getFormValue(state[name]) ?? []\n\n // Map (or discard) state values to item values\n const selected = items\n .filter((item) => values.includes(item.value))\n .map((item) => item.value)\n\n return selected.length ? selected : undefined\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(\n selected: (string | number | boolean)[] | undefined\n ) {\n const { items } = this\n\n if (!selected) {\n return ''\n }\n\n // Map selected values to text\n return items\n .filter((item) => selected.includes(item.value))\n .map((item) => item.text)\n .join(', ')\n }\n\n getContextValueFromFormValue(\n values: (string | number | boolean)[] | undefined\n ): (string | number | boolean)[] {\n /**\n * For evaluation context purposes, optional {@link CheckboxesField}\n * with an undefined value (i.e. nothing selected) should default to [].\n * This way conditions are not evaluated against `undefined` which throws errors.\n * Currently these errors are caught and the evaluation returns default `false`.\n * @see {@link QuestionPageController.getNextPath} for `undefined` return value\n * @see {@link FormModel.makeCondition} for try/catch block with default `false`\n * For negative conditions this is a problem because E.g.\n * The condition: 'selectedchecks' does not contain 'someval'\n * should return true IF 'selectedchecks' is undefined, not throw and return false.\n */\n return values ?? []\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n // Selected checkbox values\n const selected = this.getFormValueFromState(state) ?? []\n\n // Map selected values to text\n return this.getDisplayStringFromFormValue(selected)\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const values = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(values)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return CheckboxesField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n const parentErrors = SelectionControlField.getAllPossibleErrors()\n\n return {\n ...parentErrors,\n advancedSettingsErrors: [\n ...parentErrors.advancedSettingsErrors,\n { type: 'array.min', template: messageTemplate.arrayMin },\n { type: 'array.max', template: messageTemplate.arrayMax },\n { type: 'array.length', template: messageTemplate.arrayLength }\n ]\n }\n }\n\n isValue(value?: FormStateValue | FormState): value is Item['value'][] {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isFormValue)\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"CheckboxesField.js","names":["joi","isFormValue","SelectionControlField","messageTemplate","CheckboxesField","constructor","def","props","listType","type","options","schema","formSchema","array","itemsSchema","valid","values","label","items","single","required","messages","arrayMin","arrayMax","arrayLength","optional","length","min","max","default","stateSchema","allow","getFormValueFromState","state","name","getFormValue","selected","filter","item","includes","value","map","undefined","isValue","getDisplayStringFromFormValue","text","join","getContextValueFromFormValue","getDisplayStringFromState","getContextValueFromState","getAllPossibleErrors","parentErrors","advancedSettingsErrors","template","Array","isArray","every"],"sources":["../../../../../src/server/plugins/engine/components/CheckboxesField.ts"],"sourcesContent":["import { type CheckboxesFieldComponent, type Item } from '@defra/forms-model'\nimport joi, { type ArraySchema } from 'joi'\n\nimport { isFormValue } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormState,\n type FormStateValue,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class CheckboxesField extends SelectionControlField {\n declare options: CheckboxesFieldComponent['options']\n declare schema: CheckboxesFieldComponent['schema']\n declare formSchema: ArraySchema<string> | ArraySchema<number>\n declare stateSchema: ArraySchema<string> | ArraySchema<number>\n\n constructor(\n def: CheckboxesFieldComponent,\n props: ConstructorParameters<typeof SelectionControlField>[1]\n ) {\n super(def, props)\n\n const { listType: type } = this\n const { options, schema } = def\n\n let formSchema =\n type === 'string' ? joi.array<string>() : joi.array<number>()\n\n const itemsSchema = joi[type]()\n .valid(...this.values)\n .label(this.label)\n\n formSchema = formSchema\n .items(itemsSchema)\n .single()\n .label(this.label)\n .required()\n .messages({\n 'array.min': messageTemplate.arrayMin as string,\n 'array.max': messageTemplate.arrayMax as string,\n 'array.length': messageTemplate.arrayLength as string\n })\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n if (typeof schema?.length === 'number') {\n formSchema = formSchema.length(schema.length)\n } else {\n if (typeof schema?.min === 'number') {\n formSchema = formSchema.min(schema.min)\n }\n\n if (typeof schema?.max === 'number') {\n formSchema = formSchema.max(schema.max)\n }\n }\n\n this.formSchema = formSchema.default([])\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { items, name } = this\n\n // State checkbox values\n const values = this.getFormValue(state[name]) ?? []\n\n // Map (or discard) state values to item values\n const selected = items\n .filter((item) => values.includes(item.value))\n .map((item) => item.value)\n\n return selected.length ? selected : undefined\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(\n selected: (string | number | boolean)[] | undefined\n ) {\n const { items } = this\n\n if (!selected) {\n return ''\n }\n\n // Map selected values to text\n return items\n .filter((item) => selected.includes(item.value))\n .map((item) => item.text)\n .join(', ')\n }\n\n getContextValueFromFormValue(\n values: (string | number | boolean)[] | undefined\n ): (string | number | boolean)[] {\n /**\n * For evaluation context purposes, optional {@link CheckboxesField}\n * with an undefined value (i.e. nothing selected) should default to [].\n * This way conditions are not evaluated against `undefined` which throws errors.\n * Currently these errors are caught and the evaluation returns default `false`.\n * @see {@link QuestionPageController.getNextPath} for `undefined` return value\n * @see {@link FormModel.makeCondition} for try/catch block with default `false`\n * For negative conditions this is a problem because E.g.\n * The condition: 'selectedchecks' does not contain 'someval'\n * should return true IF 'selectedchecks' is undefined, not throw and return false.\n */\n return values ?? []\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n // Selected checkbox values\n const selected = this.getFormValueFromState(state) ?? []\n\n // Map selected values to text\n return this.getDisplayStringFromFormValue(selected)\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const values = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(values)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return CheckboxesField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n const parentErrors = SelectionControlField.getAllPossibleErrors()\n\n return {\n ...parentErrors,\n advancedSettingsErrors: [\n ...parentErrors.advancedSettingsErrors,\n { type: 'array.min', template: messageTemplate.arrayMin },\n { type: 'array.max', template: messageTemplate.arrayMax },\n { type: 'array.length', template: messageTemplate.arrayLength }\n ]\n }\n }\n\n isValue(value?: FormStateValue | FormState): value is Item['value'][] {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isFormValue)\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -9,7 +9,7 @@ export class MultilineTextField extends FormComponent {
9
9
  schema,
10
10
  options
11
11
  } = def;
12
- let formSchema = Joi.string().trim().label(this.label).required();
12
+ let formSchema = Joi.string().replace(/\r\n/g, '\n').trim().label(this.label).required();
13
13
  if (options.required === false) {
14
14
  formSchema = formSchema.allow('');
15
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MultilineTextField.js","names":["Joi","FormComponent","messageTemplate","MultilineTextField","isCharacterOrWordCount","constructor","def","props","schema","options","formSchema","string","trim","label","required","allow","length","max","min","maxWords","custom","getValidatorMaxWords","regex","pattern","RegExp","customValidationMessage","message","messages","customValidationMessages","minMaxErrorText","buildMinMaxText","ruleset","default","stateSchema","getViewModel","payload","errors","viewModel","maxlength","maxwords","rows","minMaxError","minMax","replace","toString","getAllPossibleErrors","staticErrors","advancedSettingsErrors","type","template","baseErrors","component","validator","value","helpers","limit","count","error","text","tokens","match"],"sources":["../../../../../src/server/plugins/engine/components/MultilineTextField.ts"],"sourcesContent":["import { type MultilineTextFieldComponent } from '@defra/forms-model'\nimport Joi, { type CustomValidator, type StringSchema } from 'joi'\n\nimport { type ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormSubmissionError\n} from '~/src/server/plugins/engine/types.js'\n\nexport class MultilineTextField extends FormComponent {\n declare options: MultilineTextFieldComponent['options']\n declare schema: MultilineTextFieldComponent['schema']\n declare formSchema: StringSchema\n declare stateSchema: StringSchema\n\n isCharacterOrWordCount = false\n\n constructor(\n def: MultilineTextFieldComponent,\n props: ConstructorParameters<typeof ComponentBase>[1]\n ) {\n super(def, props)\n\n const { schema, options } = def\n\n let formSchema = Joi.string().trim().label(this.label).required()\n\n if (options.required === false) {\n formSchema = formSchema.allow('')\n }\n\n if (typeof schema.length !== 'number') {\n if (typeof schema.max === 'number') {\n formSchema = formSchema.max(schema.max)\n this.isCharacterOrWordCount = true\n }\n\n if (typeof schema.min === 'number') {\n formSchema = formSchema.min(schema.min)\n }\n } else {\n formSchema = formSchema.length(schema.length)\n }\n\n if (typeof options.maxWords === 'number') {\n formSchema = formSchema.custom(\n getValidatorMaxWords(this),\n 'max words validation'\n )\n\n this.isCharacterOrWordCount = true\n }\n\n if (schema.regex) {\n const pattern = new RegExp(schema.regex)\n formSchema = formSchema.pattern(pattern)\n }\n\n if (options.customValidationMessage) {\n const message = options.customValidationMessage\n\n formSchema = formSchema.messages({\n 'any.required': message,\n 'string.empty': message,\n 'string.max': message,\n 'string.min': message,\n 'string.length': message,\n 'string.pattern.base': message,\n 'string.maxWords': message\n })\n } else if (options.customValidationMessages) {\n formSchema = formSchema.messages(options.customValidationMessages)\n } else if (\n typeof schema.max === 'number' &&\n typeof schema.min === 'number'\n ) {\n const minMaxErrorText = this.buildMinMaxText(schema.min, schema.max)\n formSchema = formSchema.ruleset\n .min(schema.min)\n .max(schema.max)\n .message(minMaxErrorText)\n }\n\n this.formSchema = formSchema.default('')\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n this.schema = schema\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const { schema, options, isCharacterOrWordCount } = this\n\n const viewModel = super.getViewModel(payload, errors)\n let { maxlength, maxwords, rows } = viewModel\n\n if (schema.max) {\n maxlength = schema.max\n }\n\n if (options.maxWords) {\n maxwords = options.maxWords\n }\n\n if (options.rows) {\n rows = options.rows\n }\n\n return {\n ...viewModel,\n isCharacterOrWordCount,\n maxlength,\n maxwords,\n rows\n }\n }\n\n buildMinMaxText(min?: number, max?: number): string {\n const minMaxError = messageTemplate.minMax as string\n return minMaxError\n .replace('{{#min}}', min ? min.toString() : '[min length]')\n .replace('{{#max}}', max ? max.toString() : '[max length]')\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n const staticErrors = MultilineTextField.getAllPossibleErrors()\n return {\n ...staticErrors,\n advancedSettingsErrors: [\n ...staticErrors.advancedSettingsErrors,\n {\n type: 'minMax',\n template: this.buildMinMaxText(this.schema.min, this.schema.max)\n }\n ]\n }\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [{ type: 'required', template: messageTemplate.required }],\n advancedSettingsErrors: [\n { type: 'min', template: messageTemplate.min },\n { type: 'max', template: messageTemplate.max }\n ]\n }\n }\n}\n\nfunction getValidatorMaxWords(component: MultilineTextField) {\n const validator: CustomValidator = (value: string, helpers) => {\n const { options } = component\n\n const {\n customValidationMessage: custom,\n maxWords: limit // See {{#limit}} variable\n } = options\n\n if (!limit || count(value) <= limit) {\n return value\n }\n\n return custom\n ? helpers.message({ custom }, { limit })\n : helpers.error('string.maxWords', { limit })\n }\n\n /**\n * Count the number of words in the given text\n * @see GOV.UK Frontend {@link https://github.com/alphagov/govuk-frontend/blob/v5.4.0/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs#L343 | Character count `maxwords` implementation}\n */\n function count(text: string) {\n const tokens = text.match(/\\S+/g) ?? []\n return tokens.length\n }\n\n return validator\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"MultilineTextField.js","names":["Joi","FormComponent","messageTemplate","MultilineTextField","isCharacterOrWordCount","constructor","def","props","schema","options","formSchema","string","replace","trim","label","required","allow","length","max","min","maxWords","custom","getValidatorMaxWords","regex","pattern","RegExp","customValidationMessage","message","messages","customValidationMessages","minMaxErrorText","buildMinMaxText","ruleset","default","stateSchema","getViewModel","payload","errors","viewModel","maxlength","maxwords","rows","minMaxError","minMax","toString","getAllPossibleErrors","staticErrors","advancedSettingsErrors","type","template","baseErrors","component","validator","value","helpers","limit","count","error","text","tokens","match"],"sources":["../../../../../src/server/plugins/engine/components/MultilineTextField.ts"],"sourcesContent":["import { type MultilineTextFieldComponent } from '@defra/forms-model'\nimport Joi, { type CustomValidator, type StringSchema } from 'joi'\n\nimport { type ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormSubmissionError\n} from '~/src/server/plugins/engine/types.js'\n\nexport class MultilineTextField extends FormComponent {\n declare options: MultilineTextFieldComponent['options']\n declare schema: MultilineTextFieldComponent['schema']\n declare formSchema: StringSchema\n declare stateSchema: StringSchema\n\n isCharacterOrWordCount = false\n\n constructor(\n def: MultilineTextFieldComponent,\n props: ConstructorParameters<typeof ComponentBase>[1]\n ) {\n super(def, props)\n\n const { schema, options } = def\n\n let formSchema = Joi.string()\n .replace(/\\r\\n/g, '\\n')\n .trim()\n .label(this.label)\n .required()\n\n if (options.required === false) {\n formSchema = formSchema.allow('')\n }\n\n if (typeof schema.length !== 'number') {\n if (typeof schema.max === 'number') {\n formSchema = formSchema.max(schema.max)\n this.isCharacterOrWordCount = true\n }\n\n if (typeof schema.min === 'number') {\n formSchema = formSchema.min(schema.min)\n }\n } else {\n formSchema = formSchema.length(schema.length)\n }\n\n if (typeof options.maxWords === 'number') {\n formSchema = formSchema.custom(\n getValidatorMaxWords(this),\n 'max words validation'\n )\n\n this.isCharacterOrWordCount = true\n }\n\n if (schema.regex) {\n const pattern = new RegExp(schema.regex)\n formSchema = formSchema.pattern(pattern)\n }\n\n if (options.customValidationMessage) {\n const message = options.customValidationMessage\n\n formSchema = formSchema.messages({\n 'any.required': message,\n 'string.empty': message,\n 'string.max': message,\n 'string.min': message,\n 'string.length': message,\n 'string.pattern.base': message,\n 'string.maxWords': message\n })\n } else if (options.customValidationMessages) {\n formSchema = formSchema.messages(options.customValidationMessages)\n } else if (\n typeof schema.max === 'number' &&\n typeof schema.min === 'number'\n ) {\n const minMaxErrorText = this.buildMinMaxText(schema.min, schema.max)\n formSchema = formSchema.ruleset\n .min(schema.min)\n .max(schema.max)\n .message(minMaxErrorText)\n }\n\n this.formSchema = formSchema.default('')\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n this.schema = schema\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const { schema, options, isCharacterOrWordCount } = this\n\n const viewModel = super.getViewModel(payload, errors)\n let { maxlength, maxwords, rows } = viewModel\n\n if (schema.max) {\n maxlength = schema.max\n }\n\n if (options.maxWords) {\n maxwords = options.maxWords\n }\n\n if (options.rows) {\n rows = options.rows\n }\n\n return {\n ...viewModel,\n isCharacterOrWordCount,\n maxlength,\n maxwords,\n rows\n }\n }\n\n buildMinMaxText(min?: number, max?: number): string {\n const minMaxError = messageTemplate.minMax as string\n return minMaxError\n .replace('{{#min}}', min ? min.toString() : '[min length]')\n .replace('{{#max}}', max ? max.toString() : '[max length]')\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n const staticErrors = MultilineTextField.getAllPossibleErrors()\n return {\n ...staticErrors,\n advancedSettingsErrors: [\n ...staticErrors.advancedSettingsErrors,\n {\n type: 'minMax',\n template: this.buildMinMaxText(this.schema.min, this.schema.max)\n }\n ]\n }\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [{ type: 'required', template: messageTemplate.required }],\n advancedSettingsErrors: [\n { type: 'min', template: messageTemplate.min },\n { type: 'max', template: messageTemplate.max }\n ]\n }\n }\n}\n\nfunction getValidatorMaxWords(component: MultilineTextField) {\n const validator: CustomValidator = (value: string, helpers) => {\n const { options } = component\n\n const {\n customValidationMessage: custom,\n maxWords: limit // See {{#limit}} variable\n } = options\n\n if (!limit || count(value) <= limit) {\n return value\n }\n\n return custom\n ? helpers.message({ custom }, { limit })\n : helpers.error('string.maxWords', { limit })\n }\n\n /**\n * Count the number of words in the given text\n * @see GOV.UK Frontend {@link https://github.com/alphagov/govuk-frontend/blob/v5.4.0/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs#L343 | Character count `maxwords` implementation}\n */\n function count(text: string) {\n const tokens = text.match(/\\S+/g) ?? []\n return tokens.length\n }\n\n return validator\n}\n"],"mappings":"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","ignoreList":[]}
@@ -34,7 +34,10 @@ export const messageTemplate = {
34
34
  objectMissing: joi.expression('{{#title}} must include a {{lowerFirst(#label)}}', opts),
35
35
  dateFormat: '{{#title}} must be a real date',
36
36
  dateMin: '{{#title}} must be the same as or after {{#limit}}',
37
- dateMax: '{{#title}} must be the same as or before {{#limit}}'
37
+ dateMax: '{{#title}} must be the same as or before {{#limit}}',
38
+ arrayMin: 'Select at least {{#limit}} options from the list',
39
+ arrayMax: 'Only {{#limit}} can be selected from the list',
40
+ arrayLength: 'Select only {{#limit}} options from the list'
38
41
  };
39
42
  export const messages = {
40
43
  'string.base': messageTemplate.required,
@@ -1 +1 @@
1
- {"version":3,"file":"validationOptions.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","messageTemplate","declarationRequired","expression","required","selectRequired","selectYesNoRequired","max","min","minMax","pattern","format","unicode","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","objectRequired","objectMissing","dateFormat","dateMin","dateMax","messages","messagesPre","validationOptions","abortEarly","errors","wrap","array","label"],"sources":["../../../../../src/server/plugins/engine/pageControllers/validationOptions.ts"],"sourcesContent":["// Declaration above is needed for: https://github.com/hapijs/joi/issues/3064\n\nimport joi, {\n type JoiExpression,\n type LanguageMessages,\n type LanguageMessagesExt,\n type ReferenceOptions,\n type ValidationOptions\n} from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nconst opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax\n */\nexport const messageTemplate: Record<string, JoiExpression> = {\n declarationRequired: joi.expression(\n 'You must confirm you understand and agree with the {{lowerFirst(#label)}} to continue',\n opts\n ) as JoiExpression,\n required: joi.expression(\n 'Enter {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectRequired: joi.expression(\n 'Select {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectYesNoRequired: '{{#label}} - select yes or no',\n max: '{{#label}} must be {{#limit}} characters or less',\n min: '{{#label}} must be {{#limit}} characters or more',\n minMax: '{{#label}} must be between {{#min}} and {{#max}} characters',\n pattern: joi.expression(\n 'Enter a valid {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n format: joi.expression(\n 'Enter {{lowerFirst(#label)}} in the correct format',\n opts\n ) as JoiExpression,\n unicode: '{{#label}} includes invalid characters, for example, long dashes',\n number: '{{#label}} must be a number',\n numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',\n numberInteger: '{{#label}} must be a whole number',\n numberMin: '{{#label}} must be {{#limit}} or higher',\n numberMax: '{{#label}} must be {{#limit}} or lower',\n maxWords: '{{#label}} must be {{#limit}} words or fewer',\n\n // Nested fields use component title\n\n objectRequired: joi.expression('Enter {{#label}}', opts) as JoiExpression,\n objectMissing: joi.expression(\n '{{#title}} must include a {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n dateFormat: '{{#title}} must be a real date',\n dateMin: '{{#title}} must be the same as or after {{#limit}}',\n dateMax: '{{#title}} must be the same as or before {{#limit}}'\n}\n\nexport const messages: LanguageMessagesExt = {\n 'string.base': messageTemplate.required,\n 'string.min': messageTemplate.min,\n 'string.empty': messageTemplate.required,\n 'string.max': messageTemplate.max,\n 'string.email': messageTemplate.format,\n 'string.unicode': messageTemplate.unicode,\n 'string.pattern.base': messageTemplate.pattern,\n 'string.maxWords': messageTemplate.maxWords,\n\n 'number.base': messageTemplate.number,\n 'number.precision': messageTemplate.numberPrecision,\n 'number.integer': messageTemplate.numberInteger,\n 'number.unsafe': messageTemplate.format,\n 'number.min': messageTemplate.numberMin,\n 'number.max': messageTemplate.numberMax,\n\n 'object.required': messageTemplate.objectRequired,\n 'object.and': messageTemplate.objectMissing,\n\n 'any.only': messageTemplate.selectRequired,\n 'any.required': messageTemplate.selectRequired,\n 'any.empty': messageTemplate.required,\n\n 'date.base': messageTemplate.dateFormat,\n 'date.format': messageTemplate.dateFormat,\n 'date.min': messageTemplate.dateMin,\n 'date.max': messageTemplate.dateMax,\n\n 'object.invalidjson': messageTemplate.format\n}\n\nexport const messagesPre: LanguageMessages =\n messages as unknown as LanguageMessages\n\nexport const validationOptions: ValidationOptions = {\n abortEarly: false,\n messages: messagesPre,\n errors: {\n wrap: {\n array: false,\n label: false\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"validationOptions.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","messageTemplate","declarationRequired","expression","required","selectRequired","selectYesNoRequired","max","min","minMax","pattern","format","unicode","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","objectRequired","objectMissing","dateFormat","dateMin","dateMax","arrayMin","arrayMax","arrayLength","messages","messagesPre","validationOptions","abortEarly","errors","wrap","array","label"],"sources":["../../../../../src/server/plugins/engine/pageControllers/validationOptions.ts"],"sourcesContent":["// Declaration above is needed for: https://github.com/hapijs/joi/issues/3064\n\nimport joi, {\n type JoiExpression,\n type LanguageMessages,\n type LanguageMessagesExt,\n type ReferenceOptions,\n type ValidationOptions\n} from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nconst opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax\n */\nexport const messageTemplate: Record<string, JoiExpression> = {\n declarationRequired: joi.expression(\n 'You must confirm you understand and agree with the {{lowerFirst(#label)}} to continue',\n opts\n ) as JoiExpression,\n required: joi.expression(\n 'Enter {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectRequired: joi.expression(\n 'Select {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectYesNoRequired: '{{#label}} - select yes or no',\n max: '{{#label}} must be {{#limit}} characters or less',\n min: '{{#label}} must be {{#limit}} characters or more',\n minMax: '{{#label}} must be between {{#min}} and {{#max}} characters',\n pattern: joi.expression(\n 'Enter a valid {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n format: joi.expression(\n 'Enter {{lowerFirst(#label)}} in the correct format',\n opts\n ) as JoiExpression,\n unicode: '{{#label}} includes invalid characters, for example, long dashes',\n number: '{{#label}} must be a number',\n numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',\n numberInteger: '{{#label}} must be a whole number',\n numberMin: '{{#label}} must be {{#limit}} or higher',\n numberMax: '{{#label}} must be {{#limit}} or lower',\n maxWords: '{{#label}} must be {{#limit}} words or fewer',\n\n // Nested fields use component title\n\n objectRequired: joi.expression('Enter {{#label}}', opts) as JoiExpression,\n objectMissing: joi.expression(\n '{{#title}} must include a {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n dateFormat: '{{#title}} must be a real date',\n dateMin: '{{#title}} must be the same as or after {{#limit}}',\n dateMax: '{{#title}} must be the same as or before {{#limit}}',\n arrayMin: 'Select at least {{#limit}} options from the list',\n arrayMax: 'Only {{#limit}} can be selected from the list',\n arrayLength: 'Select only {{#limit}} options from the list'\n}\n\nexport const messages: LanguageMessagesExt = {\n 'string.base': messageTemplate.required,\n 'string.min': messageTemplate.min,\n 'string.empty': messageTemplate.required,\n 'string.max': messageTemplate.max,\n 'string.email': messageTemplate.format,\n 'string.unicode': messageTemplate.unicode,\n 'string.pattern.base': messageTemplate.pattern,\n 'string.maxWords': messageTemplate.maxWords,\n\n 'number.base': messageTemplate.number,\n 'number.precision': messageTemplate.numberPrecision,\n 'number.integer': messageTemplate.numberInteger,\n 'number.unsafe': messageTemplate.format,\n 'number.min': messageTemplate.numberMin,\n 'number.max': messageTemplate.numberMax,\n\n 'object.required': messageTemplate.objectRequired,\n 'object.and': messageTemplate.objectMissing,\n\n 'any.only': messageTemplate.selectRequired,\n 'any.required': messageTemplate.selectRequired,\n 'any.empty': messageTemplate.required,\n\n 'date.base': messageTemplate.dateFormat,\n 'date.format': messageTemplate.dateFormat,\n 'date.min': messageTemplate.dateMin,\n 'date.max': messageTemplate.dateMax,\n\n 'object.invalidjson': messageTemplate.format\n}\n\nexport const messagesPre: LanguageMessages =\n messages as unknown as LanguageMessages\n\nexport const validationOptions: ValidationOptions = {\n abortEarly: false,\n messages: messagesPre,\n errors: {\n wrap: {\n array: false,\n label: false\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -178,6 +178,9 @@ describe('Payment routes', () => {
178
178
  expect(response.headers.location).toBe('http://host.com/return-url?paymentComplete=true');
179
179
  });
180
180
  });
181
+ afterAll(async () => {
182
+ await server.stop();
183
+ });
181
184
  });
182
185
 
183
186
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"payment.test.js","names":["StatusCodes","createServer","getPaymentContext","renderResponse","jest","mock","describe","server","beforeAll","initialize","beforeEach","resetAllMocks","uuid","options","method","url","paymentSessionData","formId","reference","paymentId","amount","description","isLivePayment","componentName","returnUrl","failureUrl","sessionKey","test","each","status","finalUrl","row","paymentStatus","_links","next_url","href","self","state","finished","mocked","mockResolvedValueOnce","session","response","expect","statusCode","toBe","SEE_OTHER","headers","location","it","BAD_REQUEST","result","message","INTERNAL_SERVER_ERROR","payment_id","email"],"sources":["../../../../../src/server/plugins/engine/routes/payment.test.js"],"sourcesContent":["import { StatusCodes } from 'http-status-codes'\n\nimport { createServer } from '~/src/server/index.js'\nimport { getPaymentContext } from '~/src/server/plugins/engine/routes/payment-helper.js'\nimport { renderResponse } from '~/test/helpers/component-helpers.js'\n\njest.mock('~/src/server/plugins/engine/routes/payment-helper.js')\n\ndescribe('Payment routes', () => {\n /** @type {Server} */\n let server\n\n beforeAll(async () => {\n server = await createServer()\n await server.initialize()\n })\n\n beforeEach(() => {\n jest.resetAllMocks()\n })\n\n describe('Return route /payment-callback', () => {\n const uuid = '06a5b11e-e3e0-48a2-8ac3-56c0fcb6c20d'\n const options = {\n method: 'get',\n url: `/payment-callback?uuid=${uuid}`\n }\n\n const paymentSessionData = {\n uuid,\n formId: 'form-id',\n reference: 'form-ref-123',\n paymentId: 'payment-id',\n amount: 123,\n description: 'Payment desc',\n isLivePayment: false,\n componentName: 'my-component',\n returnUrl: 'http://host.com/return-url',\n failureUrl: 'http://host.com/failure-url'\n }\n const sessionKey = 'session-key'\n\n test.each([\n {\n status: 'capturable',\n finalUrl: 'http://host.com/return-url?paymentComplete=true'\n },\n {\n status: 'success',\n finalUrl: 'http://host.com/return-url?paymentComplete=true'\n },\n { status: 'cancelled', finalUrl: 'http://host.com/failure-url' },\n { status: 'failed', finalUrl: 'http://host.com/failure-url' },\n { status: 'error', finalUrl: 'http://host.com/failure-url' },\n { status: 'created', finalUrl: '/next-url' },\n { status: 'started', finalUrl: '/next-url' },\n { status: 'submitted', finalUrl: '/next-url' }\n ])('should handle payment status of $row.status', async (row) => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n amount: 125,\n _links: {\n next_url: {\n href: '/next-url',\n method: 'get'\n },\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: /** @type {PaymentResponseState} */ ({\n status: row.status,\n finished: true\n })\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.SEE_OTHER)\n expect(response.headers.location).toBe(row.finalUrl)\n })\n\n it('should throw if nextUrl is missing', async () => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n _links: {\n next_url: {},\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: /** @type {PaymentResponseState} */ ({\n status: 'created',\n finished: true\n })\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n // @ts-expect-error - deliberate missing element from object\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.BAD_REQUEST)\n // @ts-expect-error - error object\n expect(response.result?.message).toBe(\n \"Payment in state 'created' but no next_url available\"\n )\n })\n\n it('should throw if invalid status', async () => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n _links: {\n next_url: {\n href: '/next-url',\n method: 'get'\n },\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: {\n status: 'invalid',\n finished: true\n }\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n // @ts-expect-error - deliberate invalid value which doesnt meet type\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.INTERNAL_SERVER_ERROR)\n // @ts-expect-error - error object\n expect(response.result?.message).toBe('Unknown payment status: invalid')\n })\n\n it('should handle payment with email from GOV.UK Pay response', async () => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n payment_id: 'new-payment-id',\n amount: 125,\n email: 'payer@example.com',\n _links: {\n next_url: {\n href: '/next-url',\n method: 'get'\n },\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: /** @type {PaymentResponseState} */ ({\n status: 'success',\n finished: true\n })\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.SEE_OTHER)\n expect(response.headers.location).toBe(\n 'http://host.com/return-url?paymentComplete=true'\n )\n })\n })\n})\n\n/**\n * @import { Server } from '@hapi/hapi'\n * @import { PaymentResponseState } from '~/src/server/plugins/payment/types.js'\n */\n"],"mappings":"AAAA,SAASA,WAAW,QAAQ,mBAAmB;AAE/C,SAASC,YAAY;AACrB,SAASC,iBAAiB;AAC1B,SAASC,cAAc;AAEvBC,IAAI,CAACC,IAAI,sBAAuD,CAAC;AAEjEC,QAAQ,CAAC,gBAAgB,EAAE,MAAM;EAC/B;EACA,IAAIC,MAAM;EAEVC,SAAS,CAAC,YAAY;IACpBD,MAAM,GAAG,MAAMN,YAAY,CAAC,CAAC;IAC7B,MAAMM,MAAM,CAACE,UAAU,CAAC,CAAC;EAC3B,CAAC,CAAC;EAEFC,UAAU,CAAC,MAAM;IACfN,IAAI,CAACO,aAAa,CAAC,CAAC;EACtB,CAAC,CAAC;EAEFL,QAAQ,CAAC,gCAAgC,EAAE,MAAM;IAC/C,MAAMM,IAAI,GAAG,sCAAsC;IACnD,MAAMC,OAAO,GAAG;MACdC,MAAM,EAAE,KAAK;MACbC,GAAG,EAAE,0BAA0BH,IAAI;IACrC,CAAC;IAED,MAAMI,kBAAkB,GAAG;MACzBJ,IAAI;MACJK,MAAM,EAAE,SAAS;MACjBC,SAAS,EAAE,cAAc;MACzBC,SAAS,EAAE,YAAY;MACvBC,MAAM,EAAE,GAAG;MACXC,WAAW,EAAE,cAAc;MAC3BC,aAAa,EAAE,KAAK;MACpBC,aAAa,EAAE,cAAc;MAC7BC,SAAS,EAAE,4BAA4B;MACvCC,UAAU,EAAE;IACd,CAAC;IACD,MAAMC,UAAU,GAAG,aAAa;IAEhCC,IAAI,CAACC,IAAI,CAAC,CACR;MACEC,MAAM,EAAE,YAAY;MACpBC,QAAQ,EAAE;IACZ,CAAC,EACD;MACED,MAAM,EAAE,SAAS;MACjBC,QAAQ,EAAE;IACZ,CAAC,EACD;MAAED,MAAM,EAAE,WAAW;MAAEC,QAAQ,EAAE;IAA8B,CAAC,EAChE;MAAED,MAAM,EAAE,QAAQ;MAAEC,QAAQ,EAAE;IAA8B,CAAC,EAC7D;MAAED,MAAM,EAAE,OAAO;MAAEC,QAAQ,EAAE;IAA8B,CAAC,EAC5D;MAAED,MAAM,EAAE,SAAS;MAAEC,QAAQ,EAAE;IAAY,CAAC,EAC5C;MAAED,MAAM,EAAE,SAAS;MAAEC,QAAQ,EAAE;IAAY,CAAC,EAC5C;MAAED,MAAM,EAAE,WAAW;MAAEC,QAAQ,EAAE;IAAY,CAAC,CAC/C,CAAC,CAAC,6CAA6C,EAAE,MAAOC,GAAG,IAAK;MAC/D,MAAMC,aAAa,GAAG;QACpBb,SAAS,EAAE,gBAAgB;QAC3BC,MAAM,EAAE,GAAG;QACXa,MAAM,EAAE;UACNC,QAAQ,EAAE;YACRC,IAAI,EAAE,WAAW;YACjBrB,MAAM,EAAE;UACV,CAAC;UACDsB,IAAI,EAAE;YACJD,IAAI,EAAE,OAAO;YACbrB,MAAM,EAAE;UACV;QACF,CAAC;QACDuB,KAAK,GAAE,mCAAqC;UAC1CR,MAAM,EAAEE,GAAG,CAACF,MAAM;UAClBS,QAAQ,EAAE;QACZ,CAAC;MACH,CAAC;MACDlC,IAAI,CAACmC,MAAM,CAACrC,iBAAiB,CAAC,CAACsC,qBAAqB,CAAC;QACnDC,OAAO,EAAEzB,kBAAkB;QAC3BU,UAAU;QACVM;MACF,CAAC,CAAC;MACF,MAAM;QAAEU;MAAS,CAAC,GAAG,MAAMvC,cAAc,CAACI,MAAM,EAAEM,OAAO,CAAC;MAE1D8B,MAAM,CAACD,QAAQ,CAACE,UAAU,CAAC,CAACC,IAAI,CAAC7C,WAAW,CAAC8C,SAAS,CAAC;MACvDH,MAAM,CAACD,QAAQ,CAACK,OAAO,CAACC,QAAQ,CAAC,CAACH,IAAI,CAACd,GAAG,CAACD,QAAQ,CAAC;IACtD,CAAC,CAAC;IAEFmB,EAAE,CAAC,oCAAoC,EAAE,YAAY;MACnD,MAAMjB,aAAa,GAAG;QACpBb,SAAS,EAAE,gBAAgB;QAC3Bc,MAAM,EAAE;UACNC,QAAQ,EAAE,CAAC,CAAC;UACZE,IAAI,EAAE;YACJD,IAAI,EAAE,OAAO;YACbrB,MAAM,EAAE;UACV;QACF,CAAC;QACDuB,KAAK,GAAE,mCAAqC;UAC1CR,MAAM,EAAE,SAAS;UACjBS,QAAQ,EAAE;QACZ,CAAC;MACH,CAAC;MACDlC,IAAI,CAACmC,MAAM,CAACrC,iBAAiB,CAAC,CAACsC,qBAAqB,CAAC;QACnDC,OAAO,EAAEzB,kBAAkB;QAC3BU,UAAU;QACV;QACAM;MACF,CAAC,CAAC;MACF,MAAM;QAAEU;MAAS,CAAC,GAAG,MAAMvC,cAAc,CAACI,MAAM,EAAEM,OAAO,CAAC;MAE1D8B,MAAM,CAACD,QAAQ,CAACE,UAAU,CAAC,CAACC,IAAI,CAAC7C,WAAW,CAACkD,WAAW,CAAC;MACzD;MACAP,MAAM,CAACD,QAAQ,CAACS,MAAM,EAAEC,OAAO,CAAC,CAACP,IAAI,CACnC,sDACF,CAAC;IACH,CAAC,CAAC;IAEFI,EAAE,CAAC,gCAAgC,EAAE,YAAY;MAC/C,MAAMjB,aAAa,GAAG;QACpBb,SAAS,EAAE,gBAAgB;QAC3Bc,MAAM,EAAE;UACNC,QAAQ,EAAE;YACRC,IAAI,EAAE,WAAW;YACjBrB,MAAM,EAAE;UACV,CAAC;UACDsB,IAAI,EAAE;YACJD,IAAI,EAAE,OAAO;YACbrB,MAAM,EAAE;UACV;QACF,CAAC;QACDuB,KAAK,EAAE;UACLR,MAAM,EAAE,SAAS;UACjBS,QAAQ,EAAE;QACZ;MACF,CAAC;MACDlC,IAAI,CAACmC,MAAM,CAACrC,iBAAiB,CAAC,CAACsC,qBAAqB,CAAC;QACnDC,OAAO,EAAEzB,kBAAkB;QAC3BU,UAAU;QACV;QACAM;MACF,CAAC,CAAC;MACF,MAAM;QAAEU;MAAS,CAAC,GAAG,MAAMvC,cAAc,CAACI,MAAM,EAAEM,OAAO,CAAC;MAE1D8B,MAAM,CAACD,QAAQ,CAACE,UAAU,CAAC,CAACC,IAAI,CAAC7C,WAAW,CAACqD,qBAAqB,CAAC;MACnE;MACAV,MAAM,CAACD,QAAQ,CAACS,MAAM,EAAEC,OAAO,CAAC,CAACP,IAAI,CAAC,iCAAiC,CAAC;IAC1E,CAAC,CAAC;IAEFI,EAAE,CAAC,2DAA2D,EAAE,YAAY;MAC1E,MAAMjB,aAAa,GAAG;QACpBb,SAAS,EAAE,gBAAgB;QAC3BmC,UAAU,EAAE,gBAAgB;QAC5BlC,MAAM,EAAE,GAAG;QACXmC,KAAK,EAAE,mBAAmB;QAC1BtB,MAAM,EAAE;UACNC,QAAQ,EAAE;YACRC,IAAI,EAAE,WAAW;YACjBrB,MAAM,EAAE;UACV,CAAC;UACDsB,IAAI,EAAE;YACJD,IAAI,EAAE,OAAO;YACbrB,MAAM,EAAE;UACV;QACF,CAAC;QACDuB,KAAK,GAAE,mCAAqC;UAC1CR,MAAM,EAAE,SAAS;UACjBS,QAAQ,EAAE;QACZ,CAAC;MACH,CAAC;MACDlC,IAAI,CAACmC,MAAM,CAACrC,iBAAiB,CAAC,CAACsC,qBAAqB,CAAC;QACnDC,OAAO,EAAEzB,kBAAkB;QAC3BU,UAAU;QACVM;MACF,CAAC,CAAC;MACF,MAAM;QAAEU;MAAS,CAAC,GAAG,MAAMvC,cAAc,CAACI,MAAM,EAAEM,OAAO,CAAC;MAE1D8B,MAAM,CAACD,QAAQ,CAACE,UAAU,CAAC,CAACC,IAAI,CAAC7C,WAAW,CAAC8C,SAAS,CAAC;MACvDH,MAAM,CAACD,QAAQ,CAACK,OAAO,CAACC,QAAQ,CAAC,CAACH,IAAI,CACpC,iDACF,CAAC;IACH,CAAC,CAAC;EACJ,CAAC,CAAC;AACJ,CAAC,CAAC;;AAEF;AACA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"payment.test.js","names":["StatusCodes","createServer","getPaymentContext","renderResponse","jest","mock","describe","server","beforeAll","initialize","beforeEach","resetAllMocks","uuid","options","method","url","paymentSessionData","formId","reference","paymentId","amount","description","isLivePayment","componentName","returnUrl","failureUrl","sessionKey","test","each","status","finalUrl","row","paymentStatus","_links","next_url","href","self","state","finished","mocked","mockResolvedValueOnce","session","response","expect","statusCode","toBe","SEE_OTHER","headers","location","it","BAD_REQUEST","result","message","INTERNAL_SERVER_ERROR","payment_id","email","afterAll","stop"],"sources":["../../../../../src/server/plugins/engine/routes/payment.test.js"],"sourcesContent":["import { StatusCodes } from 'http-status-codes'\n\nimport { createServer } from '~/src/server/index.js'\nimport { getPaymentContext } from '~/src/server/plugins/engine/routes/payment-helper.js'\nimport { renderResponse } from '~/test/helpers/component-helpers.js'\n\njest.mock('~/src/server/plugins/engine/routes/payment-helper.js')\n\ndescribe('Payment routes', () => {\n /** @type {Server} */\n let server\n\n beforeAll(async () => {\n server = await createServer()\n await server.initialize()\n })\n\n beforeEach(() => {\n jest.resetAllMocks()\n })\n\n describe('Return route /payment-callback', () => {\n const uuid = '06a5b11e-e3e0-48a2-8ac3-56c0fcb6c20d'\n const options = {\n method: 'get',\n url: `/payment-callback?uuid=${uuid}`\n }\n\n const paymentSessionData = {\n uuid,\n formId: 'form-id',\n reference: 'form-ref-123',\n paymentId: 'payment-id',\n amount: 123,\n description: 'Payment desc',\n isLivePayment: false,\n componentName: 'my-component',\n returnUrl: 'http://host.com/return-url',\n failureUrl: 'http://host.com/failure-url'\n }\n const sessionKey = 'session-key'\n\n test.each([\n {\n status: 'capturable',\n finalUrl: 'http://host.com/return-url?paymentComplete=true'\n },\n {\n status: 'success',\n finalUrl: 'http://host.com/return-url?paymentComplete=true'\n },\n { status: 'cancelled', finalUrl: 'http://host.com/failure-url' },\n { status: 'failed', finalUrl: 'http://host.com/failure-url' },\n { status: 'error', finalUrl: 'http://host.com/failure-url' },\n { status: 'created', finalUrl: '/next-url' },\n { status: 'started', finalUrl: '/next-url' },\n { status: 'submitted', finalUrl: '/next-url' }\n ])('should handle payment status of $row.status', async (row) => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n amount: 125,\n _links: {\n next_url: {\n href: '/next-url',\n method: 'get'\n },\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: /** @type {PaymentResponseState} */ ({\n status: row.status,\n finished: true\n })\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.SEE_OTHER)\n expect(response.headers.location).toBe(row.finalUrl)\n })\n\n it('should throw if nextUrl is missing', async () => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n _links: {\n next_url: {},\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: /** @type {PaymentResponseState} */ ({\n status: 'created',\n finished: true\n })\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n // @ts-expect-error - deliberate missing element from object\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.BAD_REQUEST)\n // @ts-expect-error - error object\n expect(response.result?.message).toBe(\n \"Payment in state 'created' but no next_url available\"\n )\n })\n\n it('should throw if invalid status', async () => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n _links: {\n next_url: {\n href: '/next-url',\n method: 'get'\n },\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: {\n status: 'invalid',\n finished: true\n }\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n // @ts-expect-error - deliberate invalid value which doesnt meet type\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.INTERNAL_SERVER_ERROR)\n // @ts-expect-error - error object\n expect(response.result?.message).toBe('Unknown payment status: invalid')\n })\n\n it('should handle payment with email from GOV.UK Pay response', async () => {\n const paymentStatus = {\n paymentId: 'new-payment-id',\n payment_id: 'new-payment-id',\n amount: 125,\n email: 'payer@example.com',\n _links: {\n next_url: {\n href: '/next-url',\n method: 'get'\n },\n self: {\n href: '/self',\n method: 'get'\n }\n },\n state: /** @type {PaymentResponseState} */ ({\n status: 'success',\n finished: true\n })\n }\n jest.mocked(getPaymentContext).mockResolvedValueOnce({\n session: paymentSessionData,\n sessionKey,\n paymentStatus\n })\n const { response } = await renderResponse(server, options)\n\n expect(response.statusCode).toBe(StatusCodes.SEE_OTHER)\n expect(response.headers.location).toBe(\n 'http://host.com/return-url?paymentComplete=true'\n )\n })\n })\n\n afterAll(async () => {\n await server.stop()\n })\n})\n\n/**\n * @import { Server } from '@hapi/hapi'\n * @import { PaymentResponseState } from '~/src/server/plugins/payment/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -55,6 +55,7 @@ function mapProxyRoute(options) {
55
55
  return response;
56
56
  },
57
57
  options: {
58
+ auth: false,
58
59
  validate: {
59
60
  query: Joi.object().keys({
60
61
  url: Joi.string().required()
@@ -98,6 +99,9 @@ function tileProxyRoute(options) {
98
99
  return h.response('Tile fetch failed').code(res.statusCode);
99
100
  }
100
101
  return h.response(payload).type('application/x-protobuf').header('Cache-Control', 'public, max-age=86400');
102
+ },
103
+ options: {
104
+ auth: false
101
105
  }
102
106
  };
103
107
  }
@@ -120,6 +124,7 @@ function geocodeProxyRoute(options) {
120
124
  return data;
121
125
  },
122
126
  options: {
127
+ auth: false,
123
128
  validate: {
124
129
  query: Joi.object().keys({
125
130
  query: Joi.string().required()
@@ -148,6 +153,7 @@ function reverseGeocodeProxyRoute(options) {
148
153
  return data;
149
154
  },
150
155
  options: {
156
+ auth: false,
151
157
  validate: {
152
158
  query: Joi.object().keys({
153
159
  easting: Joi.number().required(),
@@ -167,6 +173,7 @@ function mapStyleResourceRoutes() {
167
173
  method: 'GET',
168
174
  path: '/api/maps/vts/{path*}',
169
175
  options: {
176
+ auth: false,
170
177
  handler: {
171
178
  directory: {
172
179
  path: resolve(import.meta.dirname, './vts')
@@ -204,6 +211,7 @@ function getGeospatialCountries() {
204
211
  return countries;
205
212
  },
206
213
  options: {
214
+ auth: false,
207
215
  validate: {
208
216
  query: Joi.object().keys({
209
217
  omit: countrySchema.optional(),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["fs","resolve","GeospatialFieldOptionsCountryEnum","StatusCodes","Joi","getAccessToken","find","nearest","get","request","httpRequest","filePath","import","meta","dirname","countries","JSON","parse","readFileSync","countrySchema","string","valid","Object","values","getRoutes","options","mapStyleResourceRoutes","mapProxyRoute","tileProxyRoute","geocodeProxyRoute","reverseGeocodeProxyRoute","getGeospatialCountries","method","path","handler","h","query","targetUrl","URL","decodeURIComponent","url","token","searchParams","set","proxyResponse","toString","headers","Authorization","buffer","payload","contentType","res","response","type","validate","object","keys","required","optional","z","y","x","params","getBuffer","Accept","json","gunzip","statusCode","OK","valueOf","code","header","_h","data","ordnanceSurveyApiKey","easting","northing","number","directory","omit","only","features","filter","feature","id"],"sources":["../../../../../src/server/plugins/map/routes/index.js"],"sourcesContent":["import fs from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model'\nimport { StatusCodes } from 'http-status-codes'\nimport Joi from 'joi'\n\nimport { getAccessToken } from '~/src/server/plugins/map/routes/get-os-token.js'\nimport { find, nearest } from '~/src/server/plugins/map/service.js'\nimport {\n get,\n request as httpRequest\n} from '~/src/server/services/httpService.js'\n\nconst filePath = resolve(import.meta.dirname, './vts/countries.geojson')\n\n/**\n * @type {FeatureCollection}\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\nexport const countries = JSON.parse(fs.readFileSync(filePath, 'utf8'))\n\nexport const countrySchema = Joi.string().valid(\n ...Object.values(GeospatialFieldOptionsCountryEnum)\n)\n\n/**\n * Gets the map support routes\n * @param {MapConfiguration} options - ordnance survey names api key\n */\nexport function getRoutes(options) {\n return [\n mapStyleResourceRoutes(),\n mapProxyRoute(options),\n tileProxyRoute(options),\n geocodeProxyRoute(options),\n reverseGeocodeProxyRoute(options),\n getGeospatialCountries()\n ]\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.com\n * Used for the VTS map source by forwarding on the request\n * and adding the auth token and SRS (spatial reference system)\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/map-proxy',\n handler: async (request, h) => {\n const { query } = request\n const targetUrl = new URL(decodeURIComponent(query.url))\n const token = await getAccessToken(options)\n\n targetUrl.searchParams.set('srs', '3857')\n\n const proxyResponse = await httpRequest('get', targetUrl.toString(), {\n headers: {\n Authorization: `Bearer ${token}`\n }\n })\n const buffer = proxyResponse.payload\n const contentType = proxyResponse.res.headers['content-type']\n const response = h.response(buffer)\n\n if (contentType) {\n response.type(contentType)\n }\n\n return response\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n url: Joi.string().required()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.uk\n * Used for VTS map tiles forwarding on the request and adding the auth token\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction tileProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/tile/{z}/{y}/{x}.pbf',\n handler: async (request, h) => {\n const { z, y, x } = request.params\n const token = await getAccessToken(options)\n\n const url = `https://api.os.uk/maps/vector/v1/vts/tile/${z}/${y}/${x}.pbf?srs=3857`\n\n const getBuffer = /** @type {typeof get<Buffer>} */ (get)\n\n const { payload, res } = await getBuffer(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/x-protobuf'\n },\n json: false,\n gunzip: true\n })\n\n if (res.statusCode && res.statusCode !== StatusCodes.OK.valueOf()) {\n return h.response('Tile fetch failed').code(res.statusCode)\n }\n\n return h\n .response(payload)\n .type('application/x-protobuf')\n .header('Cache-Control', 'public, max-age=86400')\n }\n }\n}\n\n/**\n * Proxies ordnance survey geocode requests from the front end to api.os.uk\n * Used for the gazzeteer address lookup to find name from query strings like postcode and place names\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapGeocodeGetRequestRefs>}\n */\nfunction geocodeProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/geocode-proxy',\n async handler(request, _h) {\n const { query } = request\n const data = await find(query.query, options.ordnanceSurveyApiKey)\n\n return data\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n query: Joi.string().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * Proxies ordnance survey reverse geocode requests from the front end to api.os.uk\n * Used to find name from easting and northing points.\n * N.B this endpoint is currently not used by the front end but will be soon in \"maps V2\"\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapReverseGeocodeGetRequestRefs>}\n */\nfunction reverseGeocodeProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/reverse-geocode-proxy',\n async handler(request, _h) {\n const { query } = request\n const data = await nearest(\n query.easting,\n query.northing,\n options.ordnanceSurveyApiKey\n )\n\n return data\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n easting: Joi.number().required(),\n northing: Joi.number().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapStyleResourceRoutes() {\n return {\n method: 'GET',\n path: '/api/maps/vts/{path*}',\n options: {\n handler: {\n directory: {\n path: resolve(import.meta.dirname, './vts')\n }\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<GeospatialCountriesGetRequestRefs>}\n */\nfunction getGeospatialCountries() {\n return {\n method: 'GET',\n path: '/api/maps/countries.geojson',\n handler: (request) => {\n const { omit, only } = request.query\n\n if (omit) {\n return {\n ...countries,\n features: countries.features.filter((feature) => feature.id !== omit)\n }\n }\n\n if (only) {\n return {\n ...countries,\n features: countries.features.filter((feature) => feature.id === only)\n }\n }\n\n return countries\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n omit: countrySchema.optional(),\n only: countrySchema.optional()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * @import { ServerRoute } from '@hapi/hapi'\n * @import { FeatureCollection } from 'geojson'\n * @import { MapConfiguration, MapProxyGetRequestRefs, MapGeocodeGetRequestRefs, MapReverseGeocodeGetRequestRefs, GeospatialCountriesGetRequestRefs } from '~/src/server/plugins/map/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["fs","resolve","GeospatialFieldOptionsCountryEnum","StatusCodes","Joi","getAccessToken","find","nearest","get","request","httpRequest","filePath","import","meta","dirname","countries","JSON","parse","readFileSync","countrySchema","string","valid","Object","values","getRoutes","options","mapStyleResourceRoutes","mapProxyRoute","tileProxyRoute","geocodeProxyRoute","reverseGeocodeProxyRoute","getGeospatialCountries","method","path","handler","h","query","targetUrl","URL","decodeURIComponent","url","token","searchParams","set","proxyResponse","toString","headers","Authorization","buffer","payload","contentType","res","response","type","auth","validate","object","keys","required","optional","z","y","x","params","getBuffer","Accept","json","gunzip","statusCode","OK","valueOf","code","header","_h","data","ordnanceSurveyApiKey","easting","northing","number","directory","omit","only","features","filter","feature","id"],"sources":["../../../../../src/server/plugins/map/routes/index.js"],"sourcesContent":["import fs from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model'\nimport { StatusCodes } from 'http-status-codes'\nimport Joi from 'joi'\n\nimport { getAccessToken } from '~/src/server/plugins/map/routes/get-os-token.js'\nimport { find, nearest } from '~/src/server/plugins/map/service.js'\nimport {\n get,\n request as httpRequest\n} from '~/src/server/services/httpService.js'\n\nconst filePath = resolve(import.meta.dirname, './vts/countries.geojson')\n\n/**\n * @type {FeatureCollection}\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\nexport const countries = JSON.parse(fs.readFileSync(filePath, 'utf8'))\n\nexport const countrySchema = Joi.string().valid(\n ...Object.values(GeospatialFieldOptionsCountryEnum)\n)\n\n/**\n * Gets the map support routes\n * @param {MapConfiguration} options - ordnance survey names api key\n */\nexport function getRoutes(options) {\n return [\n mapStyleResourceRoutes(),\n mapProxyRoute(options),\n tileProxyRoute(options),\n geocodeProxyRoute(options),\n reverseGeocodeProxyRoute(options),\n getGeospatialCountries()\n ]\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.com\n * Used for the VTS map source by forwarding on the request\n * and adding the auth token and SRS (spatial reference system)\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/map-proxy',\n handler: async (request, h) => {\n const { query } = request\n const targetUrl = new URL(decodeURIComponent(query.url))\n const token = await getAccessToken(options)\n\n targetUrl.searchParams.set('srs', '3857')\n\n const proxyResponse = await httpRequest('get', targetUrl.toString(), {\n headers: {\n Authorization: `Bearer ${token}`\n }\n })\n const buffer = proxyResponse.payload\n const contentType = proxyResponse.res.headers['content-type']\n const response = h.response(buffer)\n\n if (contentType) {\n response.type(contentType)\n }\n\n return response\n },\n options: {\n auth: false,\n validate: {\n query: Joi.object()\n .keys({\n url: Joi.string().required()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.uk\n * Used for VTS map tiles forwarding on the request and adding the auth token\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction tileProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/tile/{z}/{y}/{x}.pbf',\n handler: async (request, h) => {\n const { z, y, x } = request.params\n const token = await getAccessToken(options)\n\n const url = `https://api.os.uk/maps/vector/v1/vts/tile/${z}/${y}/${x}.pbf?srs=3857`\n\n const getBuffer = /** @type {typeof get<Buffer>} */ (get)\n\n const { payload, res } = await getBuffer(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/x-protobuf'\n },\n json: false,\n gunzip: true\n })\n\n if (res.statusCode && res.statusCode !== StatusCodes.OK.valueOf()) {\n return h.response('Tile fetch failed').code(res.statusCode)\n }\n\n return h\n .response(payload)\n .type('application/x-protobuf')\n .header('Cache-Control', 'public, max-age=86400')\n },\n options: {\n auth: false\n }\n }\n}\n\n/**\n * Proxies ordnance survey geocode requests from the front end to api.os.uk\n * Used for the gazzeteer address lookup to find name from query strings like postcode and place names\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapGeocodeGetRequestRefs>}\n */\nfunction geocodeProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/geocode-proxy',\n async handler(request, _h) {\n const { query } = request\n const data = await find(query.query, options.ordnanceSurveyApiKey)\n\n return data\n },\n options: {\n auth: false,\n validate: {\n query: Joi.object()\n .keys({\n query: Joi.string().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * Proxies ordnance survey reverse geocode requests from the front end to api.os.uk\n * Used to find name from easting and northing points.\n * N.B this endpoint is currently not used by the front end but will be soon in \"maps V2\"\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapReverseGeocodeGetRequestRefs>}\n */\nfunction reverseGeocodeProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/reverse-geocode-proxy',\n async handler(request, _h) {\n const { query } = request\n const data = await nearest(\n query.easting,\n query.northing,\n options.ordnanceSurveyApiKey\n )\n\n return data\n },\n options: {\n auth: false,\n validate: {\n query: Joi.object()\n .keys({\n easting: Joi.number().required(),\n northing: Joi.number().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapStyleResourceRoutes() {\n return {\n method: 'GET',\n path: '/api/maps/vts/{path*}',\n options: {\n auth: false,\n handler: {\n directory: {\n path: resolve(import.meta.dirname, './vts')\n }\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<GeospatialCountriesGetRequestRefs>}\n */\nfunction getGeospatialCountries() {\n return {\n method: 'GET',\n path: '/api/maps/countries.geojson',\n handler: (request) => {\n const { omit, only } = request.query\n\n if (omit) {\n return {\n ...countries,\n features: countries.features.filter((feature) => feature.id !== omit)\n }\n }\n\n if (only) {\n return {\n ...countries,\n features: countries.features.filter((feature) => feature.id === only)\n }\n }\n\n return countries\n },\n options: {\n auth: false,\n validate: {\n query: Joi.object()\n .keys({\n omit: countrySchema.optional(),\n only: countrySchema.optional()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * @import { ServerRoute } from '@hapi/hapi'\n * @import { FeatureCollection } from 'geojson'\n * @import { MapConfiguration, MapProxyGetRequestRefs, MapGeocodeGetRequestRefs, MapReverseGeocodeGetRequestRefs, GeospatialCountriesGetRequestRefs } from '~/src/server/plugins/map/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "4.11.0",
3
+ "version": "4.11.2",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -41,9 +41,9 @@ export class CheckboxesField extends SelectionControlField {
41
41
  .label(this.label)
42
42
  .required()
43
43
  .messages({
44
- 'array.min': 'Select at least {{#limit}} options from the list',
45
- 'array.max': 'Only {{#limit}} can be selected from the list',
46
- 'array.length': 'Select only {{#limit}} options from the list'
44
+ 'array.min': messageTemplate.arrayMin as string,
45
+ 'array.max': messageTemplate.arrayMax as string,
46
+ 'array.length': messageTemplate.arrayLength as string
47
47
  })
48
48
 
49
49
  if (options.required === false) {
@@ -26,7 +26,11 @@ export class MultilineTextField extends FormComponent {
26
26
 
27
27
  const { schema, options } = def
28
28
 
29
- let formSchema = Joi.string().trim().label(this.label).required()
29
+ let formSchema = Joi.string()
30
+ .replace(/\r\n/g, '\n')
31
+ .trim()
32
+ .label(this.label)
33
+ .required()
30
34
 
31
35
  if (options.required === false) {
32
36
  formSchema = formSchema.allow('')
@@ -61,7 +61,10 @@ export const messageTemplate: Record<string, JoiExpression> = {
61
61
  ) as JoiExpression,
62
62
  dateFormat: '{{#title}} must be a real date',
63
63
  dateMin: '{{#title}} must be the same as or after {{#limit}}',
64
- dateMax: '{{#title}} must be the same as or before {{#limit}}'
64
+ dateMax: '{{#title}} must be the same as or before {{#limit}}',
65
+ arrayMin: 'Select at least {{#limit}} options from the list',
66
+ arrayMax: 'Only {{#limit}} can be selected from the list',
67
+ arrayLength: 'Select only {{#limit}} options from the list'
65
68
  }
66
69
 
67
70
  export const messages: LanguageMessagesExt = {
@@ -73,6 +73,7 @@ function mapProxyRoute(options) {
73
73
  return response
74
74
  },
75
75
  options: {
76
+ auth: false,
76
77
  validate: {
77
78
  query: Joi.object()
78
79
  .keys({
@@ -119,6 +120,9 @@ function tileProxyRoute(options) {
119
120
  .response(payload)
120
121
  .type('application/x-protobuf')
121
122
  .header('Cache-Control', 'public, max-age=86400')
123
+ },
124
+ options: {
125
+ auth: false
122
126
  }
123
127
  }
124
128
  }
@@ -140,6 +144,7 @@ function geocodeProxyRoute(options) {
140
144
  return data
141
145
  },
142
146
  options: {
147
+ auth: false,
143
148
  validate: {
144
149
  query: Joi.object()
145
150
  .keys({
@@ -173,6 +178,7 @@ function reverseGeocodeProxyRoute(options) {
173
178
  return data
174
179
  },
175
180
  options: {
181
+ auth: false,
176
182
  validate: {
177
183
  query: Joi.object()
178
184
  .keys({
@@ -194,6 +200,7 @@ function mapStyleResourceRoutes() {
194
200
  method: 'GET',
195
201
  path: '/api/maps/vts/{path*}',
196
202
  options: {
203
+ auth: false,
197
204
  handler: {
198
205
  directory: {
199
206
  path: resolve(import.meta.dirname, './vts')
@@ -231,6 +238,7 @@ function getGeospatialCountries() {
231
238
  return countries
232
239
  },
233
240
  options: {
241
+ auth: false,
234
242
  validate: {
235
243
  query: Joi.object()
236
244
  .keys({