From fd1d49e465122ecee42eb8c76f4039963a0e6c55 Mon Sep 17 00:00:00 2001 From: David Stone Date: Wed, 26 Aug 2026 13:12:07 +0100 Subject: [PATCH 1/4] Add generateReferenceNumber option to plugin and update related types --- src/server/plugins/engine/options.js | 3 ++- src/server/plugins/engine/plugin.ts | 8 ++++++++ src/server/plugins/engine/routes/index.ts | 5 +++-- src/server/plugins/engine/types.ts | 5 +++++ src/typings/hapi/index.d.ts | 2 ++ 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/server/plugins/engine/options.js b/src/server/plugins/engine/options.js index 41d8bce44..768d73a22 100644 --- a/src/server/plugins/engine/options.js +++ b/src/server/plugins/engine/options.js @@ -26,7 +26,8 @@ const pluginRegistrationOptionsSchema = Joi.object({ saveAndExit: Joi.function().optional(), ordnanceSurveyApiKey: Joi.string().optional(), ordnanceSurveyApiSecret: Joi.string().optional(), - getLanguage: Joi.function().optional() + getLanguage: Joi.function().optional(), + generateReferenceNumber: Joi.function().optional() }) /** diff --git a/src/server/plugins/engine/plugin.ts b/src/server/plugins/engine/plugin.ts index 941f63bd6..a015815c4 100644 --- a/src/server/plugins/engine/plugin.ts +++ b/src/server/plugins/engine/plugin.ts @@ -1,3 +1,5 @@ +import { promisify } from 'util' + import { type Lifecycle, type Plugin, @@ -8,6 +10,7 @@ import { import { type FormModel } from '~/src/server/plugins/engine/models/index.js' import { validatePluginOptions } from '~/src/server/plugins/engine/options.js' +import { generateUniqueReference } from '~/src/server/plugins/engine/referenceNumbers.js' import { getRoutes as getFileUploadStatusRoutes } from '~/src/server/plugins/engine/routes/file-upload.js' import { makeLoadFormPreHandler } from '~/src/server/plugins/engine/routes/index.js' import { getRoutes as getPaymentRoutes } from '~/src/server/plugins/engine/routes/payment.js' @@ -37,6 +40,7 @@ export const plugin = { cache, saveAndExit, getLanguage, + generateReferenceNumber, nunjucks: nunjucksOptions, viewContext, preparePageEventRequestOptions, @@ -80,6 +84,10 @@ export const plugin = { server.expose('cacheService', cacheService) server.expose('saveAndExit', saveAndExit) server.expose('getLanguage', getLanguage) + server.expose( + 'generateReferenceNumber', + generateReferenceNumber ?? promisify(generateUniqueReference) + ) server.expose('baseUrl', baseUrl) server.expose('services', services) diff --git a/src/server/plugins/engine/routes/index.ts b/src/server/plugins/engine/routes/index.ts index e39d5e555..dddfe37ba 100644 --- a/src/server/plugins/engine/routes/index.ts +++ b/src/server/plugins/engine/routes/index.ts @@ -20,6 +20,7 @@ import { findPage, getCacheService, getPage, + getPluginOptions, getStartPath, proceed } from '~/src/server/plugins/engine/helpers.js' @@ -28,7 +29,6 @@ import { checkSaveAndExitRepeater, copyNotYetValidatedState } from '~/src/server/plugins/engine/pageControllers/helpers/state.js' -import { generateUniqueReference } from '~/src/server/plugins/engine/referenceNumbers.js' import * as defaultServices from '~/src/server/plugins/engine/services/index.js' import { type AnyFormRequest, @@ -74,7 +74,8 @@ export async function redirectOrMakeHandler( ) } - const referenceNumber = generateUniqueReference(prefix) + const { generateReferenceNumber } = getPluginOptions(request.server) + const referenceNumber = await generateReferenceNumber(prefix) state = await page.mergeState(request, state, { $$__referenceNumber: referenceNumber }) diff --git a/src/server/plugins/engine/types.ts b/src/server/plugins/engine/types.ts index 76a96349c..b4eea4b95 100644 --- a/src/server/plugins/engine/types.ts +++ b/src/server/plugins/engine/types.ts @@ -483,6 +483,10 @@ export type SaveAndExitHandler = ( export type GetLanguageHandler = (query?: RequestQuery, yar?: Yar) => string +export type GenerateReferenceNumberHandler = ( + prefix?: string +) => Promise + export interface ExternalArgs { component: ComponentDef controller: QuestionPageController @@ -523,6 +527,7 @@ export interface PluginOptions { preparePageEventRequestOptions?: PreparePageEventRequestOptions onRequest?: OnRequestCallback getLanguage?: GetLanguageHandler + generateReferenceNumber?: GenerateReferenceNumberHandler baseUrl: string // base URL of the application, protocol and hostname e.g. "https://myapp.com" ordnanceSurveyApiKey?: string ordnanceSurveyApiSecret?: string diff --git a/src/typings/hapi/index.d.ts b/src/typings/hapi/index.d.ts index e61d5c32f..2cecea6f3 100644 --- a/src/typings/hapi/index.d.ts +++ b/src/typings/hapi/index.d.ts @@ -11,6 +11,7 @@ import { type FormModel } from '~/src/server/plugins/engine/models/index.js' import { type AnyFormRequest, type FormSubmissionError, + type GenerateReferenceNumberHandler, type PluginOptions } from '~/src/server/plugins/engine/types.ts' import { type CacheService } from '~/src/server/services/index.js' @@ -41,6 +42,7 @@ declare module '@hapi/hapi' { getLanguage?: PluginOptions['getLanguage'] baseUrl: string services: PluginOptions['services'] + generateReferenceNumber: GenerateReferenceNumberHandler } } From 5bac316b6def9483252e374e3dc6b443c69ad2e1 Mon Sep 17 00:00:00 2001 From: David Stone Date: Thu, 27 Aug 2026 14:06:00 +0100 Subject: [PATCH 2/4] Refactor generateReferenceNumber exposure to remove promisify --- src/server/plugins/engine/plugin.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/server/plugins/engine/plugin.ts b/src/server/plugins/engine/plugin.ts index a015815c4..ca56fc050 100644 --- a/src/server/plugins/engine/plugin.ts +++ b/src/server/plugins/engine/plugin.ts @@ -1,5 +1,3 @@ -import { promisify } from 'util' - import { type Lifecycle, type Plugin, @@ -86,7 +84,7 @@ export const plugin = { server.expose('getLanguage', getLanguage) server.expose( 'generateReferenceNumber', - generateReferenceNumber ?? promisify(generateUniqueReference) + generateReferenceNumber ?? generateUniqueReference ) server.expose('baseUrl', baseUrl) server.expose('services', services) From 6b8d76ce0081a80ee53aacf31e544e923fc75a00 Mon Sep 17 00:00:00 2001 From: David Stone Date: Thu, 27 Aug 2026 14:06:17 +0100 Subject: [PATCH 3/4] Add generateReferenceNumber mock to postcode lookup tests --- test/form/postcode-lookup.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/form/postcode-lookup.test.js b/test/form/postcode-lookup.test.js index c92de22e2..d05f24373 100644 --- a/test/form/postcode-lookup.test.js +++ b/test/form/postcode-lookup.test.js @@ -72,7 +72,8 @@ describe('Postcode lookup form pages', () => { getFlash: jest.fn().mockReturnValue(undefined), setFlash: jest.fn() }, - saveAndExit: undefined + saveAndExit: undefined, + generateReferenceNumber: jest.fn().mockReturnValue('XXX-XXX-XXX') }) }) From 2732d3e2b4b893e487e529d91595defdd9b9bc71 Mon Sep 17 00:00:00 2001 From: David Stone Date: Fri, 28 Aug 2026 10:58:57 +0100 Subject: [PATCH 4/4] Add DEFAULT_REFERENCE_NUMBER constant and update reference number logic in redirectOrMakeHandler --- src/server/plugins/engine/referenceNumbers.ts | 2 ++ src/server/plugins/engine/routes/index.ts | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/server/plugins/engine/referenceNumbers.ts b/src/server/plugins/engine/referenceNumbers.ts index 2b4fde674..b7653916c 100644 --- a/src/server/plugins/engine/referenceNumbers.ts +++ b/src/server/plugins/engine/referenceNumbers.ts @@ -6,6 +6,8 @@ import { englishRecommendedTransformers } from 'obscenity' +export const DEFAULT_REFERENCE_NUMBER = 'XXX-XXX-XXX' + /** * To prevent confusion to users reading the reference number, ambiguous letters and numbers are removed. * @param strCodes - array of binary input values diff --git a/src/server/plugins/engine/routes/index.ts b/src/server/plugins/engine/routes/index.ts index dddfe37ba..a17d34cf8 100644 --- a/src/server/plugins/engine/routes/index.ts +++ b/src/server/plugins/engine/routes/index.ts @@ -29,6 +29,7 @@ import { checkSaveAndExitRepeater, copyNotYetValidatedState } from '~/src/server/plugins/engine/pageControllers/helpers/state.js' +import { DEFAULT_REFERENCE_NUMBER } from '~/src/server/plugins/engine/referenceNumbers.js' import * as defaultServices from '~/src/server/plugins/engine/services/index.js' import { type AnyFormRequest, @@ -54,7 +55,7 @@ export async function redirectOrMakeHandler( context: FormContext ) => ResponseObject | Promise ) { - const { app, params } = request + const { app, params, query } = request const { model } = app if (!model) { @@ -74,8 +75,12 @@ export async function redirectOrMakeHandler( ) } + const isForce = 'force' in query const { generateReferenceNumber } = getPluginOptions(request.server) - const referenceNumber = await generateReferenceNumber(prefix) + const referenceNumber = isForce + ? DEFAULT_REFERENCE_NUMBER + : await generateReferenceNumber(prefix) + state = await page.mergeState(request, state, { $$__referenceNumber: referenceNumber })