@defra/forms-engine-plugin 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/.public/stylesheets/application.min.css +3 -3
  2. package/.public/stylesheets/application.min.css.map +1 -1
  3. package/.server/client/stylesheets/application.scss +14 -0
  4. package/.server/config/index.d.ts +1 -0
  5. package/.server/config/index.js +7 -0
  6. package/.server/config/index.js.map +1 -1
  7. package/.server/index.js +6 -2
  8. package/.server/index.js.map +1 -1
  9. package/.server/server/constants.d.ts +2 -0
  10. package/.server/server/constants.js +2 -0
  11. package/.server/server/constants.js.map +1 -1
  12. package/.server/server/forms/components.json +7 -0
  13. package/.server/server/forms/register-as-a-unicorn-breeder.yaml +18 -2
  14. package/.server/server/plugins/engine/components/UkAddressField.d.ts +15 -9
  15. package/.server/server/plugins/engine/components/UkAddressField.js +67 -6
  16. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  17. package/.server/server/plugins/engine/configureEnginePlugin.d.ts +1 -1
  18. package/.server/server/plugins/engine/configureEnginePlugin.js +6 -3
  19. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
  20. package/.server/server/plugins/engine/models/FormModel.d.ts +2 -0
  21. package/.server/server/plugins/engine/models/FormModel.js +3 -1
  22. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  23. package/.server/server/plugins/engine/options.js +2 -1
  24. package/.server/server/plugins/engine/options.js.map +1 -1
  25. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -0
  26. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +46 -3
  27. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  28. package/.server/server/plugins/engine/plugin.js +13 -1
  29. package/.server/server/plugins/engine/plugin.js.map +1 -1
  30. package/.server/server/plugins/engine/routes/index.js +41 -3
  31. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  32. package/.server/server/plugins/engine/types.d.ts +19 -1
  33. package/.server/server/plugins/engine/types.js.map +1 -1
  34. package/.server/server/plugins/engine/validationHelpers.d.ts +15 -0
  35. package/.server/server/plugins/engine/validationHelpers.js +29 -0
  36. package/.server/server/plugins/engine/validationHelpers.js.map +1 -0
  37. package/.server/server/plugins/engine/views/components/ukaddressfield.html +50 -6
  38. package/.server/server/plugins/engine/vision.js +3 -1
  39. package/.server/server/plugins/engine/vision.js.map +1 -1
  40. package/.server/server/plugins/postcode-lookup/index.d.ts +8 -0
  41. package/.server/server/plugins/postcode-lookup/index.js +21 -0
  42. package/.server/server/plugins/postcode-lookup/index.js.map +1 -0
  43. package/.server/server/plugins/postcode-lookup/models/index.d.ts +255 -0
  44. package/.server/server/plugins/postcode-lookup/models/index.js +517 -0
  45. package/.server/server/plugins/postcode-lookup/models/index.js.map +1 -0
  46. package/.server/server/plugins/postcode-lookup/routes/index.d.ts +19 -0
  47. package/.server/server/plugins/postcode-lookup/routes/index.js +267 -0
  48. package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -0
  49. package/.server/server/plugins/postcode-lookup/service.d.ts +26 -0
  50. package/.server/server/plugins/postcode-lookup/service.js +148 -0
  51. package/.server/server/plugins/postcode-lookup/service.js.map +1 -0
  52. package/.server/server/plugins/postcode-lookup/service.test.js +144 -0
  53. package/.server/server/plugins/postcode-lookup/service.test.js.map +1 -0
  54. package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.d.ts +282 -0
  55. package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.js +370 -0
  56. package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.js.map +1 -0
  57. package/.server/server/plugins/postcode-lookup/test/__stubs__/query.d.ts +131 -0
  58. package/.server/server/plugins/postcode-lookup/test/__stubs__/query.js +195 -0
  59. package/.server/server/plugins/postcode-lookup/test/__stubs__/query.js.map +1 -0
  60. package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.d.ts +51 -0
  61. package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.js +52 -0
  62. package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.js.map +1 -0
  63. package/.server/server/plugins/postcode-lookup/types.d.ts +204 -0
  64. package/.server/server/plugins/postcode-lookup/types.js +144 -0
  65. package/.server/server/plugins/postcode-lookup/types.js.map +1 -0
  66. package/.server/server/plugins/postcode-lookup/views/postcode-lookup-details.html +83 -0
  67. package/.server/server/routes/types.d.ts +6 -1
  68. package/.server/server/routes/types.js +6 -0
  69. package/.server/server/routes/types.js.map +1 -1
  70. package/.server/server/schemas/index.js +1 -1
  71. package/.server/server/schemas/index.js.map +1 -1
  72. package/.server/server/types.d.ts +1 -0
  73. package/.server/server/types.js.map +1 -1
  74. package/package.json +2 -2
  75. package/src/client/stylesheets/application.scss +14 -0
  76. package/src/config/index.ts +9 -1
  77. package/src/index.ts +5 -4
  78. package/src/server/constants.js +2 -0
  79. package/src/server/forms/components.json +7 -0
  80. package/src/server/forms/register-as-a-unicorn-breeder.yaml +18 -2
  81. package/src/server/plugins/engine/components/UkAddressField.test.ts +50 -27
  82. package/src/server/plugins/engine/components/UkAddressField.ts +91 -8
  83. package/src/server/plugins/engine/configureEnginePlugin.ts +5 -3
  84. package/src/server/plugins/engine/models/FormModel.ts +10 -2
  85. package/src/server/plugins/engine/options.js +2 -1
  86. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +1 -0
  87. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +69 -1
  88. package/src/server/plugins/engine/plugin.ts +13 -1
  89. package/src/server/plugins/engine/routes/index.test.ts +1 -0
  90. package/src/server/plugins/engine/routes/index.ts +71 -3
  91. package/src/server/plugins/engine/types.ts +21 -1
  92. package/src/server/plugins/engine/validationHelpers.ts +48 -0
  93. package/src/server/plugins/engine/views/components/ukaddressfield.html +50 -6
  94. package/src/server/plugins/engine/vision.ts +6 -0
  95. package/src/server/plugins/postcode-lookup/index.js +21 -0
  96. package/src/server/plugins/postcode-lookup/models/index.js +549 -0
  97. package/src/server/plugins/postcode-lookup/routes/index.js +258 -0
  98. package/src/server/plugins/postcode-lookup/service.js +188 -0
  99. package/src/server/plugins/postcode-lookup/service.test.js +177 -0
  100. package/src/server/plugins/postcode-lookup/test/__stubs__/postcode.js +382 -0
  101. package/src/server/plugins/postcode-lookup/test/__stubs__/query.js +200 -0
  102. package/src/server/plugins/postcode-lookup/test/__stubs__/uprn.js +53 -0
  103. package/src/server/plugins/postcode-lookup/types.js +143 -0
  104. package/src/server/plugins/postcode-lookup/views/postcode-lookup-details.html +83 -0
  105. package/src/server/postcode-lookup.test.ts +64 -0
  106. package/src/server/routes/types.ts +7 -1
  107. package/src/server/schemas/index.ts +5 -7
  108. package/src/server/types.ts +1 -0
@@ -0,0 +1,267 @@
1
+ import Boom from '@hapi/boom';
2
+ import { StatusCodes } from 'http-status-codes';
3
+ import Joi from 'joi';
4
+ import { EXTERNAL_STATE_APPENDAGE } from "../../../constants.js";
5
+ import { JOURNEY_BASE_URL, detailsPayloadSchema, detailsViewModel, manualPayloadSchema, manualViewModel, selectPayloadSchema, selectViewModel, stepSchema, steps } from "../models/index.js";
6
+ import * as service from "../service.js";
7
+ const viewName = 'postcode-lookup-details';
8
+
9
+ /**
10
+ * Get the session state associated with this journey
11
+ * @param {PostcodeLookupRequest} request
12
+ */
13
+ function getSessionState(request) {
14
+ /**
15
+ * @type {PostcodeLookupSessionData | undefined}
16
+ */
17
+ const state = request.yar.get(JOURNEY_BASE_URL);
18
+ if (!state) {
19
+ throw Boom.internal(`No postcode lookup data found for ${JOURNEY_BASE_URL}`);
20
+ }
21
+ return state;
22
+ }
23
+
24
+ /**
25
+ * Flash form component state
26
+ * @param {PostcodeLookupRequest} request - the request
27
+ * @param {string} componentName - the component name
28
+ * @param {Address | PostcodeLookupManualPayload} address - the address from ordnance survey or manually entered
29
+ */
30
+ function flashComponentState(request, componentName, address) {
31
+ const addressState = {
32
+ addressLine1: address.addressLine1,
33
+ addressLine2: address.addressLine2,
34
+ town: address.town,
35
+ county: address.county,
36
+ postcode: address.postcode,
37
+ uprn: 'uprn' in address && address.uprn ? address.uprn : undefined
38
+ };
39
+
40
+ /**
41
+ * @type {ExternalStateAppendage}
42
+ */
43
+ const appendage = {
44
+ component: componentName,
45
+ data: addressState
46
+ };
47
+ request.yar.flash(EXTERNAL_STATE_APPENDAGE, appendage, true);
48
+ }
49
+
50
+ /**
51
+ * Initialises and dispatches the request to the postcode lookup journey
52
+ * @param {FormRequestPayload} request - the source page
53
+ * @param {FormResponseToolkit} h - the source page
54
+ * @param {PostcodeLookupDispatchData} initial - the source data
55
+ */
56
+ export function dispatch(request, h, initial) {
57
+ /**
58
+ * @type {PostcodeLookupSessionData}
59
+ */
60
+ const data = {
61
+ initial,
62
+ details: {
63
+ postcodeQuery: '',
64
+ buildingNameQuery: ''
65
+ }
66
+ };
67
+ request.yar.set(JOURNEY_BASE_URL, data);
68
+ const query = initial.step ? `?step=${initial.step}` : '';
69
+ return h.redirect(`${JOURNEY_BASE_URL}${query}`).code(StatusCodes.SEE_OTHER);
70
+ }
71
+
72
+ /**
73
+ * Gets the postcode lookup routes
74
+ * @param {PostcodeLookupConfiguration} options - ordnance survey api key
75
+ */
76
+ export function getRoutes(options) {
77
+ return [getRoute(), postRoute(options)];
78
+ }
79
+
80
+ /**
81
+ * @returns {ServerRoute<PostcodeLookupGetRequestRefs>}
82
+ */
83
+ function getRoute() {
84
+ return {
85
+ method: 'GET',
86
+ path: JOURNEY_BASE_URL,
87
+ handler(request, h) {
88
+ const {
89
+ query
90
+ } = request;
91
+ const {
92
+ step
93
+ } = query;
94
+ const session = getSessionState(request);
95
+ const model = step === steps.manual ? manualViewModel(session) : detailsViewModel(session);
96
+ return h.view(viewName, model);
97
+ },
98
+ options: {
99
+ validate: {
100
+ query: Joi.object().keys({
101
+ step: Joi.string().allow(steps.details, steps.manual).optional()
102
+ }).optional()
103
+ }
104
+ }
105
+ };
106
+ }
107
+
108
+ /**
109
+ * @param {PostcodeLookupConfiguration} options
110
+ * @returns {ServerRoute<PostcodeLookupPostRequestRefs>}
111
+ */
112
+ function postRoute(options) {
113
+ return {
114
+ method: 'POST',
115
+ path: JOURNEY_BASE_URL,
116
+ async handler(request, h) {
117
+ const {
118
+ payload
119
+ } = request;
120
+ const {
121
+ step
122
+ } = payload;
123
+ switch (step) {
124
+ case steps.details:
125
+ {
126
+ return detailsPostHandler(request, h, options);
127
+ }
128
+ case steps.select:
129
+ {
130
+ return selectPostHandler(request, h, options);
131
+ }
132
+ case steps.manual:
133
+ {
134
+ return manualPostHandler(request, h);
135
+ }
136
+ default:
137
+ throw Boom.badRequest(`Invalid step ${step}`);
138
+ }
139
+ },
140
+ options: {
141
+ validate: {
142
+ payload: Joi.object().keys({
143
+ step: stepSchema
144
+ }).unknown(true)
145
+ }
146
+ }
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Post handler for the details step
152
+ * @param {PostcodeLookupPostRequest} request
153
+ * @param {ResponseToolkit<PostcodeLookupPostRequestRefs>} h
154
+ * @param {PostcodeLookupConfiguration} options
155
+ */
156
+ async function detailsPostHandler(request, h, options) {
157
+ const {
158
+ payload
159
+ } = request;
160
+ const session = getSessionState(request);
161
+ const {
162
+ ordnanceSurveyApiKey: apiKey
163
+ } = options;
164
+ const {
165
+ value: details,
166
+ error
167
+ } = detailsPayloadSchema.validate(payload);
168
+ let model;
169
+ if (error) {
170
+ model = detailsViewModel(session, details, error);
171
+ return h.view(viewName, model);
172
+ }
173
+ const {
174
+ postcodeQuery,
175
+ buildingNameQuery
176
+ } = details;
177
+ session.details = {
178
+ postcodeQuery,
179
+ buildingNameQuery
180
+ };
181
+
182
+ // Store the updated session
183
+ request.yar.set(JOURNEY_BASE_URL, session);
184
+ model = await selectViewModel({
185
+ session,
186
+ apiKey
187
+ });
188
+ return h.view(viewName, model);
189
+ }
190
+
191
+ /**
192
+ * Post handler for the select step
193
+ * @param {PostcodeLookupPostRequest} request
194
+ * @param {ResponseToolkit<PostcodeLookupPostRequestRefs>} h
195
+ * @param {PostcodeLookupConfiguration} options
196
+ */
197
+ async function selectPostHandler(request, h, options) {
198
+ const {
199
+ payload
200
+ } = request;
201
+ const session = getSessionState(request);
202
+ const {
203
+ ordnanceSurveyApiKey: apiKey
204
+ } = options;
205
+ const {
206
+ value: select,
207
+ error
208
+ } = selectPayloadSchema.validate(payload);
209
+ if (error) {
210
+ const model = await selectViewModel({
211
+ session,
212
+ apiKey
213
+ }, select, error);
214
+ return h.view(viewName, model);
215
+ }
216
+ const addresses = await service.searchByUPRN(select.uprn, apiKey);
217
+ const property = addresses.at(0);
218
+ if (!property) {
219
+ throw Boom.internal(`UPRN ${property} not found`);
220
+ }
221
+ const {
222
+ componentName,
223
+ sourceUrl
224
+ } = session.initial;
225
+ flashComponentState(request, componentName, property);
226
+
227
+ // Redirect back to the source form page
228
+ return h.redirect(sourceUrl).code(StatusCodes.SEE_OTHER);
229
+ }
230
+
231
+ /**
232
+ * Post handler for the manual step
233
+ * @param {PostcodeLookupPostRequest} request
234
+ * @param {ResponseToolkit<PostcodeLookupPostRequestRefs>} h
235
+ */
236
+ function manualPostHandler(request, h) {
237
+ const {
238
+ payload
239
+ } = request;
240
+ const session = getSessionState(request);
241
+ const {
242
+ value: manual,
243
+ error
244
+ } = manualPayloadSchema.validate(payload, {
245
+ abortEarly: false
246
+ });
247
+ if (error) {
248
+ const model = manualViewModel(session, manual, error);
249
+ return h.view(viewName, model);
250
+ }
251
+ const {
252
+ componentName,
253
+ sourceUrl
254
+ } = session.initial;
255
+ flashComponentState(request, componentName, manual);
256
+
257
+ // Redirect back to the source form page
258
+ return h.redirect(sourceUrl).code(StatusCodes.SEE_OTHER);
259
+ }
260
+
261
+ /**
262
+ * @import { ResponseToolkit, ServerRoute } from '@hapi/hapi'
263
+ * @import { PostcodeLookupManualPayload, Address, PostcodeLookupGetRequestRefs, PostcodeLookupPostRequestRefs, PostcodeLookupRequest, PostcodeLookupPostRequest, PostcodeLookupConfiguration, PostcodeLookupDispatchData, PostcodeLookupSessionData } from '~/src/server/plugins/postcode-lookup/types.js'
264
+ * @import { FormRequestPayload, FormResponseToolkit } from '~/src/server/routes/types.js'
265
+ * @import { ExternalStateAppendage } from '~/src/server/plugins/engine/types.js'
266
+ */
267
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["Boom","StatusCodes","Joi","EXTERNAL_STATE_APPENDAGE","JOURNEY_BASE_URL","detailsPayloadSchema","detailsViewModel","manualPayloadSchema","manualViewModel","selectPayloadSchema","selectViewModel","stepSchema","steps","service","viewName","getSessionState","request","state","yar","get","internal","flashComponentState","componentName","address","addressState","addressLine1","addressLine2","town","county","postcode","uprn","undefined","appendage","component","data","flash","dispatch","h","initial","details","postcodeQuery","buildingNameQuery","set","query","step","redirect","code","SEE_OTHER","getRoutes","options","getRoute","postRoute","method","path","handler","session","model","manual","view","validate","object","keys","string","allow","optional","payload","detailsPostHandler","select","selectPostHandler","manualPostHandler","badRequest","unknown","ordnanceSurveyApiKey","apiKey","value","error","addresses","searchByUPRN","property","at","sourceUrl","abortEarly"],"sources":["../../../../../src/server/plugins/postcode-lookup/routes/index.js"],"sourcesContent":["import Boom from '@hapi/boom'\nimport { StatusCodes } from 'http-status-codes'\nimport Joi from 'joi'\n\nimport { EXTERNAL_STATE_APPENDAGE } from '~/src/server/constants.js'\nimport {\n JOURNEY_BASE_URL,\n detailsPayloadSchema,\n detailsViewModel,\n manualPayloadSchema,\n manualViewModel,\n selectPayloadSchema,\n selectViewModel,\n stepSchema,\n steps\n} from '~/src/server/plugins/postcode-lookup/models/index.js'\nimport * as service from '~/src/server/plugins/postcode-lookup/service.js'\n\nconst viewName = 'postcode-lookup-details'\n\n/**\n * Get the session state associated with this journey\n * @param {PostcodeLookupRequest} request\n */\nfunction getSessionState(request) {\n /**\n * @type {PostcodeLookupSessionData | undefined}\n */\n const state = request.yar.get(JOURNEY_BASE_URL)\n\n if (!state) {\n throw Boom.internal(`No postcode lookup data found for ${JOURNEY_BASE_URL}`)\n }\n\n return state\n}\n\n/**\n * Flash form component state\n * @param {PostcodeLookupRequest} request - the request\n * @param {string} componentName - the component name\n * @param {Address | PostcodeLookupManualPayload} address - the address from ordnance survey or manually entered\n */\nfunction flashComponentState(request, componentName, address) {\n const addressState = {\n addressLine1: address.addressLine1,\n addressLine2: address.addressLine2,\n town: address.town,\n county: address.county,\n postcode: address.postcode,\n uprn: 'uprn' in address && address.uprn ? address.uprn : undefined\n }\n\n /**\n * @type {ExternalStateAppendage}\n */\n const appendage = {\n component: componentName,\n data: addressState\n }\n\n request.yar.flash(EXTERNAL_STATE_APPENDAGE, appendage, true)\n}\n\n/**\n * Initialises and dispatches the request to the postcode lookup journey\n * @param {FormRequestPayload} request - the source page\n * @param {FormResponseToolkit} h - the source page\n * @param {PostcodeLookupDispatchData} initial - the source data\n */\nexport function dispatch(request, h, initial) {\n /**\n * @type {PostcodeLookupSessionData}\n */\n const data = {\n initial,\n details: { postcodeQuery: '', buildingNameQuery: '' }\n }\n\n request.yar.set(JOURNEY_BASE_URL, data)\n\n const query = initial.step ? `?step=${initial.step}` : ''\n\n return h.redirect(`${JOURNEY_BASE_URL}${query}`).code(StatusCodes.SEE_OTHER)\n}\n\n/**\n * Gets the postcode lookup routes\n * @param {PostcodeLookupConfiguration} options - ordnance survey api key\n */\nexport function getRoutes(options) {\n return [getRoute(), postRoute(options)]\n}\n\n/**\n * @returns {ServerRoute<PostcodeLookupGetRequestRefs>}\n */\nfunction getRoute() {\n return {\n method: 'GET',\n path: JOURNEY_BASE_URL,\n handler(request, h) {\n const { query } = request\n const { step } = query\n const session = getSessionState(request)\n\n const model =\n step === steps.manual\n ? manualViewModel(session)\n : detailsViewModel(session)\n\n return h.view(viewName, model)\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n step: Joi.string().allow(steps.details, steps.manual).optional()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * @param {PostcodeLookupConfiguration} options\n * @returns {ServerRoute<PostcodeLookupPostRequestRefs>}\n */\nfunction postRoute(options) {\n return {\n method: 'POST',\n path: JOURNEY_BASE_URL,\n async handler(request, h) {\n const { payload } = request\n const { step } = payload\n\n switch (step) {\n case steps.details: {\n return detailsPostHandler(request, h, options)\n }\n case steps.select: {\n return selectPostHandler(request, h, options)\n }\n case steps.manual: {\n return manualPostHandler(request, h)\n }\n default:\n throw Boom.badRequest(`Invalid step ${step}`)\n }\n },\n options: {\n validate: {\n payload: Joi.object()\n .keys({\n step: stepSchema\n })\n .unknown(true)\n }\n }\n }\n}\n\n/**\n * Post handler for the details step\n * @param {PostcodeLookupPostRequest} request\n * @param {ResponseToolkit<PostcodeLookupPostRequestRefs>} h\n * @param {PostcodeLookupConfiguration} options\n */\nasync function detailsPostHandler(request, h, options) {\n const { payload } = request\n const session = getSessionState(request)\n const { ordnanceSurveyApiKey: apiKey } = options\n const { value: details, error } = detailsPayloadSchema.validate(payload)\n\n let model\n\n if (error) {\n model = detailsViewModel(session, details, error)\n\n return h.view(viewName, model)\n }\n\n const { postcodeQuery, buildingNameQuery } = details\n session.details = { postcodeQuery, buildingNameQuery }\n\n // Store the updated session\n request.yar.set(JOURNEY_BASE_URL, session)\n\n model = await selectViewModel({ session, apiKey })\n\n return h.view(viewName, model)\n}\n\n/**\n * Post handler for the select step\n * @param {PostcodeLookupPostRequest} request\n * @param {ResponseToolkit<PostcodeLookupPostRequestRefs>} h\n * @param {PostcodeLookupConfiguration} options\n */\nasync function selectPostHandler(request, h, options) {\n const { payload } = request\n const session = getSessionState(request)\n const { ordnanceSurveyApiKey: apiKey } = options\n const { value: select, error } = selectPayloadSchema.validate(payload)\n\n if (error) {\n const model = await selectViewModel({ session, apiKey }, select, error)\n\n return h.view(viewName, model)\n }\n\n const addresses = await service.searchByUPRN(select.uprn, apiKey)\n const property = addresses.at(0)\n\n if (!property) {\n throw Boom.internal(`UPRN ${property} not found`)\n }\n\n const { componentName, sourceUrl } = session.initial\n flashComponentState(request, componentName, property)\n\n // Redirect back to the source form page\n return h.redirect(sourceUrl).code(StatusCodes.SEE_OTHER)\n}\n\n/**\n * Post handler for the manual step\n * @param {PostcodeLookupPostRequest} request\n * @param {ResponseToolkit<PostcodeLookupPostRequestRefs>} h\n */\nfunction manualPostHandler(request, h) {\n const { payload } = request\n const session = getSessionState(request)\n\n const { value: manual, error } = manualPayloadSchema.validate(payload, {\n abortEarly: false\n })\n\n if (error) {\n const model = manualViewModel(session, manual, error)\n\n return h.view(viewName, model)\n }\n\n const { componentName, sourceUrl } = session.initial\n flashComponentState(request, componentName, manual)\n\n // Redirect back to the source form page\n return h.redirect(sourceUrl).code(StatusCodes.SEE_OTHER)\n}\n\n/**\n * @import { ResponseToolkit, ServerRoute } from '@hapi/hapi'\n * @import { PostcodeLookupManualPayload, Address, PostcodeLookupGetRequestRefs, PostcodeLookupPostRequestRefs, PostcodeLookupRequest, PostcodeLookupPostRequest, PostcodeLookupConfiguration, PostcodeLookupDispatchData, PostcodeLookupSessionData } from '~/src/server/plugins/postcode-lookup/types.js'\n * @import { FormRequestPayload, FormResponseToolkit } from '~/src/server/routes/types.js'\n * @import { ExternalStateAppendage } from '~/src/server/plugins/engine/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * OS places search
3
+ * @param {string} query - the search term
4
+ * @param {string} apiKey - the OS api key
5
+ */
6
+ export function searchByQuery(query: string, apiKey: string): Promise<Address[]>;
7
+ /**
8
+ * OS postcode search
9
+ * @param {string} postcode - the postcode
10
+ * @param {string} apiKey - the OS api key
11
+ */
12
+ export function searchByPostcode(postcode: string, apiKey: string): Promise<Address[]>;
13
+ /**
14
+ * OS UPRN search
15
+ * @param {string} uprn - the unique property reference number
16
+ * @param {string} apiKey - the OS api key
17
+ */
18
+ export function searchByUPRN(uprn: string, apiKey: string): Promise<Address[]>;
19
+ /**
20
+ * OS postcode and building name search
21
+ * @param {string} postcodeQuery - the postcode query
22
+ * @param {string} buildingNameQuery - the building name query
23
+ * @param {string} apiKey - the OS api key
24
+ */
25
+ export function search(postcodeQuery: string, buildingNameQuery: string, apiKey: string): Promise<Address[]>;
26
+ import type { Address } from '~/src/server/plugins/postcode-lookup/types.js';
@@ -0,0 +1,148 @@
1
+ import { getErrorMessage } from '@defra/forms-model';
2
+ import Boom from '@hapi/boom';
3
+ import { createLogger } from "../../common/helpers/logging/logger.js";
4
+ import { getJson } from "../../services/httpService.js";
5
+ const logger = createLogger();
6
+
7
+ /**
8
+ * Returns an empty result set
9
+ */
10
+ function empty() {
11
+ return [];
12
+ }
13
+
14
+ /**
15
+ * Logs OS places errors
16
+ * @param {unknown} err - the error
17
+ * @param {string} endpoint - the OS api endpoint
18
+ */
19
+ function logErrorAndReturnEmpty(err, endpoint) {
20
+ const msg = `${getErrorMessage(err)} ${(Boom.isBoom(err) && err.data?.payload?.error?.message) ?? ''}`;
21
+ logger.error(err, `Exception occured calling OS places ${endpoint} - ${msg}}`);
22
+ return empty();
23
+ }
24
+
25
+ /**
26
+ * Fetch data from OS API
27
+ * @param {string} url - the url to get address json data from
28
+ * @param {string} endpoint - the url endpoint description for logging
29
+ */
30
+ async function getAddressData(url, endpoint) {
31
+ const getJsonByType = /** @type {typeof getJson<DeliveryPointAddressResult>} */getJson;
32
+ try {
33
+ const response = await getJsonByType(url);
34
+ if (response.error) {
35
+ return logErrorAndReturnEmpty(response.error, endpoint);
36
+ }
37
+ const results = response.payload.results;
38
+ if (!Array.isArray(results)) {
39
+ return empty();
40
+ }
41
+ return results.map(result => formatAddress(result.DPA));
42
+ } catch (err) {
43
+ return logErrorAndReturnEmpty(err, endpoint);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * OS places search
49
+ * @param {string} query - the search term
50
+ * @param {string} apiKey - the OS api key
51
+ */
52
+ export async function searchByQuery(query, apiKey) {
53
+ const endpoint = 'find';
54
+ const url = `https://api.os.uk/search/places/v1/${endpoint}?query=${encodeURIComponent(query)}&key=${apiKey}`;
55
+ return getAddressData(url, endpoint);
56
+ }
57
+
58
+ /**
59
+ * OS postcode search
60
+ * @param {string} postcode - the postcode
61
+ * @param {string} apiKey - the OS api key
62
+ */
63
+ export async function searchByPostcode(postcode, apiKey) {
64
+ const endpoint = 'postcode';
65
+ const url = `https://api.os.uk/search/places/v1/${endpoint}?postcode=${encodeURIComponent(postcode.replaceAll(/\s/g, ''))}&key=${apiKey}`;
66
+ return getAddressData(url, endpoint);
67
+ }
68
+
69
+ /**
70
+ * OS UPRN search
71
+ * @param {string} uprn - the unique property reference number
72
+ * @param {string} apiKey - the OS api key
73
+ */
74
+ export async function searchByUPRN(uprn, apiKey) {
75
+ const endpoint = 'uprn';
76
+ const url = `https://api.os.uk/search/places/v1/${endpoint}?uprn=${uprn}&key=${apiKey}`;
77
+ return getAddressData(url, endpoint);
78
+ }
79
+
80
+ /**
81
+ * OS postcode and building name search
82
+ * @param {string} postcodeQuery - the postcode query
83
+ * @param {string} buildingNameQuery - the building name query
84
+ * @param {string} apiKey - the OS api key
85
+ */
86
+ export async function search(postcodeQuery, buildingNameQuery, apiKey) {
87
+ let addresses = await searchByPostcode(postcodeQuery, apiKey);
88
+ if (buildingNameQuery) {
89
+ addresses = addresses.filter(item => item.address.includes(buildingNameQuery.toUpperCase()));
90
+ }
91
+ return addresses;
92
+ }
93
+
94
+ /**
95
+ * Converts a delivery point address to an address
96
+ * Taken from http://github.com/dwp/find-an-address-plugin/blob/main/utils/getData.js
97
+ * @param {DeliveryPointAddress} dpa
98
+ */
99
+ function formatAddress(dpa) {
100
+ const addressLine1 = formatAddressLine1(dpa);
101
+ const addressLine2 = formatAddressLine2(dpa);
102
+ const town = titleCase(dpa.POST_TOWN || '');
103
+ const postcode = dpa.POSTCODE || '';
104
+ const lines = [addressLine1, addressLine2, town];
105
+ const formatted = `${lines.filter(i => !!i).join(', ')}, ${postcode}`;
106
+
107
+ /**
108
+ * @type {Address}
109
+ */
110
+ const address = {
111
+ uprn: dpa.UPRN,
112
+ address: dpa.ADDRESS,
113
+ addressLine1,
114
+ addressLine2,
115
+ town,
116
+ county: '',
117
+ postcode,
118
+ formatted
119
+ };
120
+ return address;
121
+ }
122
+
123
+ /**
124
+ * @param {DeliveryPointAddress} dpa
125
+ */
126
+ function formatAddressLine1(dpa) {
127
+ return titleCase(dpa.ORGANISATION_NAME || dpa.SUB_BUILDING_NAME || dpa.BUILDING_NAME || dpa.BUILDING_NUMBER ? [dpa.ORGANISATION_NAME || '', dpa.SUB_BUILDING_NAME || '', dpa.BUILDING_NAME || '', dpa.BUILDING_NUMBER || ''].filter(item => !!item).join(' ') : '');
128
+ }
129
+
130
+ /**
131
+ * @param {DeliveryPointAddress} dpa
132
+ */
133
+ function formatAddressLine2(dpa) {
134
+ return titleCase(dpa.THOROUGHFARE_NAME || dpa.DEPENDENT_LOCALITY ? [dpa.THOROUGHFARE_NAME || '', dpa.DEPENDENT_LOCALITY || ''].filter(item => !!item).join(', ') : '');
135
+ }
136
+
137
+ /**
138
+ * Title case address
139
+ * @param {string} address
140
+ */
141
+ function titleCase(address) {
142
+ return address.split(' ').map(item => item.charAt(0).toUpperCase() + item.slice(1).toLowerCase()).join(' ');
143
+ }
144
+
145
+ /**
146
+ * @import { Address, DeliveryPointAddress, DeliveryPointAddressResult } from '~/src/server/plugins/postcode-lookup/types.js'
147
+ */
148
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","names":["getErrorMessage","Boom","createLogger","getJson","logger","empty","logErrorAndReturnEmpty","err","endpoint","msg","isBoom","data","payload","error","message","getAddressData","url","getJsonByType","response","results","Array","isArray","map","result","formatAddress","DPA","searchByQuery","query","apiKey","encodeURIComponent","searchByPostcode","postcode","replaceAll","searchByUPRN","uprn","search","postcodeQuery","buildingNameQuery","addresses","filter","item","address","includes","toUpperCase","dpa","addressLine1","formatAddressLine1","addressLine2","formatAddressLine2","town","titleCase","POST_TOWN","POSTCODE","lines","formatted","i","join","UPRN","ADDRESS","county","ORGANISATION_NAME","SUB_BUILDING_NAME","BUILDING_NAME","BUILDING_NUMBER","THOROUGHFARE_NAME","DEPENDENT_LOCALITY","split","charAt","slice","toLowerCase"],"sources":["../../../../src/server/plugins/postcode-lookup/service.js"],"sourcesContent":["import { getErrorMessage } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\n\nimport { createLogger } from '~/src/server/common/helpers/logging/logger.js'\nimport { getJson } from '~/src/server/services/httpService.js'\n\nconst logger = createLogger()\n\n/**\n * Returns an empty result set\n */\nfunction empty() {\n return []\n}\n\n/**\n * Logs OS places errors\n * @param {unknown} err - the error\n * @param {string} endpoint - the OS api endpoint\n */\nfunction logErrorAndReturnEmpty(err, endpoint) {\n const msg = `${getErrorMessage(err)} ${(Boom.isBoom(err) && err.data?.payload?.error?.message) ?? ''}`\n\n logger.error(err, `Exception occured calling OS places ${endpoint} - ${msg}}`)\n\n return empty()\n}\n\n/**\n * Fetch data from OS API\n * @param {string} url - the url to get address json data from\n * @param {string} endpoint - the url endpoint description for logging\n */\nasync function getAddressData(url, endpoint) {\n const getJsonByType =\n /** @type {typeof getJson<DeliveryPointAddressResult>} */ (getJson)\n\n try {\n const response = await getJsonByType(url)\n\n if (response.error) {\n return logErrorAndReturnEmpty(response.error, endpoint)\n }\n\n const results = response.payload.results\n\n if (!Array.isArray(results)) {\n return empty()\n }\n\n return results.map((result) => formatAddress(result.DPA))\n } catch (err) {\n return logErrorAndReturnEmpty(err, endpoint)\n }\n}\n\n/**\n * OS places search\n * @param {string} query - the search term\n * @param {string} apiKey - the OS api key\n */\nexport async function searchByQuery(query, apiKey) {\n const endpoint = 'find'\n const url = `https://api.os.uk/search/places/v1/${endpoint}?query=${encodeURIComponent(query)}&key=${apiKey}`\n\n return getAddressData(url, endpoint)\n}\n\n/**\n * OS postcode search\n * @param {string} postcode - the postcode\n * @param {string} apiKey - the OS api key\n */\nexport async function searchByPostcode(postcode, apiKey) {\n const endpoint = 'postcode'\n const url = `https://api.os.uk/search/places/v1/${endpoint}?postcode=${encodeURIComponent(postcode.replaceAll(/\\s/g, ''))}&key=${apiKey}`\n\n return getAddressData(url, endpoint)\n}\n\n/**\n * OS UPRN search\n * @param {string} uprn - the unique property reference number\n * @param {string} apiKey - the OS api key\n */\nexport async function searchByUPRN(uprn, apiKey) {\n const endpoint = 'uprn'\n const url = `https://api.os.uk/search/places/v1/${endpoint}?uprn=${uprn}&key=${apiKey}`\n\n return getAddressData(url, endpoint)\n}\n\n/**\n * OS postcode and building name search\n * @param {string} postcodeQuery - the postcode query\n * @param {string} buildingNameQuery - the building name query\n * @param {string} apiKey - the OS api key\n */\nexport async function search(postcodeQuery, buildingNameQuery, apiKey) {\n let addresses = await searchByPostcode(postcodeQuery, apiKey)\n\n if (buildingNameQuery) {\n addresses = addresses.filter((item) =>\n item.address.includes(buildingNameQuery.toUpperCase())\n )\n }\n\n return addresses\n}\n\n/**\n * Converts a delivery point address to an address\n * Taken from http://github.com/dwp/find-an-address-plugin/blob/main/utils/getData.js\n * @param {DeliveryPointAddress} dpa\n */\nfunction formatAddress(dpa) {\n const addressLine1 = formatAddressLine1(dpa)\n const addressLine2 = formatAddressLine2(dpa)\n const town = titleCase(dpa.POST_TOWN || '')\n const postcode = dpa.POSTCODE || ''\n const lines = [addressLine1, addressLine2, town]\n const formatted = `${lines.filter((i) => !!i).join(', ')}, ${postcode}`\n\n /**\n * @type {Address}\n */\n const address = {\n uprn: dpa.UPRN,\n address: dpa.ADDRESS,\n addressLine1,\n addressLine2,\n town,\n county: '',\n postcode,\n formatted\n }\n\n return address\n}\n\n/**\n * @param {DeliveryPointAddress} dpa\n */\nfunction formatAddressLine1(dpa) {\n return titleCase(\n dpa.ORGANISATION_NAME ||\n dpa.SUB_BUILDING_NAME ||\n dpa.BUILDING_NAME ||\n dpa.BUILDING_NUMBER\n ? [\n dpa.ORGANISATION_NAME || '',\n dpa.SUB_BUILDING_NAME || '',\n dpa.BUILDING_NAME || '',\n dpa.BUILDING_NUMBER || ''\n ]\n .filter((item) => !!item)\n .join(' ')\n : ''\n )\n}\n\n/**\n * @param {DeliveryPointAddress} dpa\n */\nfunction formatAddressLine2(dpa) {\n return titleCase(\n dpa.THOROUGHFARE_NAME || dpa.DEPENDENT_LOCALITY\n ? [dpa.THOROUGHFARE_NAME || '', dpa.DEPENDENT_LOCALITY || '']\n .filter((item) => !!item)\n .join(', ')\n : ''\n )\n}\n\n/**\n * Title case address\n * @param {string} address\n */\nfunction titleCase(address) {\n return address\n .split(' ')\n .map((item) => item.charAt(0).toUpperCase() + item.slice(1).toLowerCase())\n .join(' ')\n}\n\n/**\n * @import { Address, DeliveryPointAddress, DeliveryPointAddressResult } from '~/src/server/plugins/postcode-lookup/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,144 @@
1
+ import Boom from '@hapi/boom';
2
+ import * as service from "./service.js";
3
+ import { result as postcodeResult } from "./test/__stubs__/postcode.js";
4
+ import { result as queryResult } from "./test/__stubs__/query.js";
5
+ import { result as uprnResult } from "./test/__stubs__/uprn.js";
6
+ import { getJson } from "../../services/httpService.js";
7
+ jest.mock("../../services/httpService.ts");
8
+ describe('Postcode lookup service', () => {
9
+ describe('searchByPostcode', () => {
10
+ it('should return formatted addresses', async () => {
11
+ jest.mocked(getJson).mockResolvedValueOnce({
12
+ res: (/** @type {IncomingMessage} */{
13
+ statusCode: 200,
14
+ headers: {}
15
+ }),
16
+ payload: postcodeResult,
17
+ error: undefined
18
+ });
19
+ const results = await service.searchByPostcode('NW1 6XE', 'apikey');
20
+ expect(results).toHaveLength(10);
21
+ expect(results.at(0)).toEqual({
22
+ address: "EMILIA'S CRAFTED PASTA, 215, BAKER STREET, LONDON, NW1 6XE",
23
+ addressLine1: "Emilia's Crafted Pasta 215",
24
+ addressLine2: 'Baker Street',
25
+ county: '',
26
+ formatted: "Emilia's Crafted Pasta 215, Baker Street, London, NW1 6XE",
27
+ postcode: 'NW1 6XE',
28
+ town: 'London',
29
+ uprn: '10033619968'
30
+ });
31
+ });
32
+ it('should return an empty response when an error is encountered', async () => {
33
+ jest.mocked(getJson).mockResolvedValueOnce({
34
+ res: (/** @type {IncomingMessage} */{
35
+ statusCode: 300,
36
+ headers: {}
37
+ }),
38
+ payload: undefined,
39
+ error: new Error('Unknown error')
40
+ });
41
+ const results = await service.searchByPostcode('NW1 6XE', 'apikey');
42
+ expect(results).toHaveLength(0);
43
+ expect(results).toEqual([]);
44
+ });
45
+ it('should return an empty response when a non 200 response is encountered', async () => {
46
+ jest.mocked(getJson).mockRejectedValueOnce(Boom.badRequest('OS API error', new Error('Invalid postcode segments')));
47
+ const results = await service.searchByPostcode('invalid postcode', 'apikey');
48
+ expect(results).toHaveLength(0);
49
+ expect(results).toEqual([]);
50
+ });
51
+ it('should return an empty response when no results are returned', async () => {
52
+ jest.mocked(getJson).mockResolvedValueOnce({
53
+ res: (/** @type {IncomingMessage} */{
54
+ statusCode: 200,
55
+ headers: {}
56
+ }),
57
+ payload: {
58
+ results: undefined
59
+ },
60
+ error: undefined
61
+ });
62
+ const results = await service.searchByPostcode('NW1 6XE', 'apikey');
63
+ expect(results).toHaveLength(0);
64
+ expect(results).toEqual([]);
65
+ });
66
+ });
67
+ describe('searchByUPRN', () => {
68
+ it('should return formatted addresses', async () => {
69
+ jest.mocked(getJson).mockResolvedValueOnce({
70
+ res: (/** @type {IncomingMessage} */{
71
+ statusCode: 200,
72
+ headers: {}
73
+ }),
74
+ payload: uprnResult,
75
+ error: undefined
76
+ });
77
+ const results = await service.searchByUPRN('100023071949', 'apikey');
78
+ expect(results).toHaveLength(1);
79
+ expect(results.at(0)).toEqual({
80
+ address: 'SHERLOCK HOLMES MUSEUM, 221B, BAKER STREET, LONDON, NW1 6XE',
81
+ addressLine1: 'Sherlock Holmes Museum 221b',
82
+ addressLine2: 'Baker Street',
83
+ county: '',
84
+ formatted: 'Sherlock Holmes Museum 221b, Baker Street, London, NW1 6XE',
85
+ postcode: 'NW1 6XE',
86
+ town: 'London',
87
+ uprn: '100023071949'
88
+ });
89
+ });
90
+ });
91
+ describe('searchByQuery', () => {
92
+ it('should return formatted addresses', async () => {
93
+ jest.mocked(getJson).mockResolvedValueOnce({
94
+ res: (/** @type {IncomingMessage} */{
95
+ statusCode: 200,
96
+ headers: {}
97
+ }),
98
+ payload: queryResult,
99
+ error: undefined
100
+ });
101
+ const results = await service.searchByQuery('Prime minister downing', 'apikey');
102
+ expect(results).toHaveLength(5);
103
+ expect(results.at(0)).toEqual({
104
+ address: 'BAKER STREET COTTAGE, BAKER STREET, FROME, BA11 3BL',
105
+ addressLine1: 'Baker Street Cottage',
106
+ addressLine2: 'Baker Street',
107
+ town: 'Frome',
108
+ county: '',
109
+ formatted: 'Baker Street Cottage, Baker Street, Frome, BA11 3BL',
110
+ postcode: 'BA11 3BL',
111
+ uprn: '250034655'
112
+ });
113
+ });
114
+ });
115
+ describe('search', () => {
116
+ it('should return formatted addresses', async () => {
117
+ jest.mocked(getJson).mockResolvedValueOnce({
118
+ res: (/** @type {IncomingMessage} */{
119
+ statusCode: 200,
120
+ headers: {}
121
+ }),
122
+ payload: postcodeResult,
123
+ error: undefined
124
+ });
125
+ const results = await service.search('NW1 6XE', 'Emilia', 'apikey');
126
+ expect(results).toHaveLength(1);
127
+ expect(results.at(0)).toEqual({
128
+ address: "EMILIA'S CRAFTED PASTA, 215, BAKER STREET, LONDON, NW1 6XE",
129
+ addressLine1: "Emilia's Crafted Pasta 215",
130
+ addressLine2: 'Baker Street',
131
+ county: '',
132
+ formatted: "Emilia's Crafted Pasta 215, Baker Street, London, NW1 6XE",
133
+ postcode: 'NW1 6XE',
134
+ town: 'London',
135
+ uprn: '10033619968'
136
+ });
137
+ });
138
+ });
139
+ });
140
+
141
+ /**
142
+ * @import { IncomingMessage } from 'node:http'
143
+ */
144
+ //# sourceMappingURL=service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.test.js","names":["Boom","service","result","postcodeResult","queryResult","uprnResult","getJson","jest","mock","describe","it","mocked","mockResolvedValueOnce","res","statusCode","headers","payload","error","undefined","results","searchByPostcode","expect","toHaveLength","at","toEqual","address","addressLine1","addressLine2","county","formatted","postcode","town","uprn","Error","mockRejectedValueOnce","badRequest","searchByUPRN","searchByQuery","search"],"sources":["../../../../src/server/plugins/postcode-lookup/service.test.js"],"sourcesContent":["import Boom from '@hapi/boom'\n\nimport * as service from '~/src/server/plugins/postcode-lookup/service.js'\nimport { result as postcodeResult } from '~/src/server/plugins/postcode-lookup/test/__stubs__/postcode.js'\nimport { result as queryResult } from '~/src/server/plugins/postcode-lookup/test/__stubs__/query.js'\nimport { result as uprnResult } from '~/src/server/plugins/postcode-lookup/test/__stubs__/uprn.js'\nimport { getJson } from '~/src/server/services/httpService.js'\n\njest.mock('~/src/server/services/httpService.ts')\n\ndescribe('Postcode lookup service', () => {\n describe('searchByPostcode', () => {\n it('should return formatted addresses', async () => {\n jest.mocked(getJson).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: postcodeResult,\n error: undefined\n })\n\n const results = await service.searchByPostcode('NW1 6XE', 'apikey')\n\n expect(results).toHaveLength(10)\n expect(results.at(0)).toEqual({\n address: \"EMILIA'S CRAFTED PASTA, 215, BAKER STREET, LONDON, NW1 6XE\",\n addressLine1: \"Emilia's Crafted Pasta 215\",\n addressLine2: 'Baker Street',\n county: '',\n formatted: \"Emilia's Crafted Pasta 215, Baker Street, London, NW1 6XE\",\n postcode: 'NW1 6XE',\n town: 'London',\n uprn: '10033619968'\n })\n })\n\n it('should return an empty response when an error is encountered', async () => {\n jest.mocked(getJson).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 300,\n headers: {}\n }),\n payload: undefined,\n error: new Error('Unknown error')\n })\n\n const results = await service.searchByPostcode('NW1 6XE', 'apikey')\n\n expect(results).toHaveLength(0)\n expect(results).toEqual([])\n })\n\n it('should return an empty response when a non 200 response is encountered', async () => {\n jest\n .mocked(getJson)\n .mockRejectedValueOnce(\n Boom.badRequest(\n 'OS API error',\n new Error('Invalid postcode segments')\n )\n )\n\n const results = await service.searchByPostcode(\n 'invalid postcode',\n 'apikey'\n )\n\n expect(results).toHaveLength(0)\n expect(results).toEqual([])\n })\n\n it('should return an empty response when no results are returned', async () => {\n jest.mocked(getJson).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: { results: undefined },\n error: undefined\n })\n\n const results = await service.searchByPostcode('NW1 6XE', 'apikey')\n\n expect(results).toHaveLength(0)\n expect(results).toEqual([])\n })\n })\n\n describe('searchByUPRN', () => {\n it('should return formatted addresses', async () => {\n jest.mocked(getJson).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: uprnResult,\n error: undefined\n })\n\n const results = await service.searchByUPRN('100023071949', 'apikey')\n\n expect(results).toHaveLength(1)\n expect(results.at(0)).toEqual({\n address: 'SHERLOCK HOLMES MUSEUM, 221B, BAKER STREET, LONDON, NW1 6XE',\n addressLine1: 'Sherlock Holmes Museum 221b',\n addressLine2: 'Baker Street',\n county: '',\n formatted: 'Sherlock Holmes Museum 221b, Baker Street, London, NW1 6XE',\n postcode: 'NW1 6XE',\n town: 'London',\n uprn: '100023071949'\n })\n })\n })\n\n describe('searchByQuery', () => {\n it('should return formatted addresses', async () => {\n jest.mocked(getJson).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: queryResult,\n error: undefined\n })\n\n const results = await service.searchByQuery(\n 'Prime minister downing',\n 'apikey'\n )\n\n expect(results).toHaveLength(5)\n expect(results.at(0)).toEqual({\n address: 'BAKER STREET COTTAGE, BAKER STREET, FROME, BA11 3BL',\n addressLine1: 'Baker Street Cottage',\n addressLine2: 'Baker Street',\n town: 'Frome',\n county: '',\n formatted: 'Baker Street Cottage, Baker Street, Frome, BA11 3BL',\n postcode: 'BA11 3BL',\n uprn: '250034655'\n })\n })\n })\n\n describe('search', () => {\n it('should return formatted addresses', async () => {\n jest.mocked(getJson).mockResolvedValueOnce({\n res: /** @type {IncomingMessage} */ ({\n statusCode: 200,\n headers: {}\n }),\n payload: postcodeResult,\n error: undefined\n })\n\n const results = await service.search('NW1 6XE', 'Emilia', 'apikey')\n\n expect(results).toHaveLength(1)\n expect(results.at(0)).toEqual({\n address: \"EMILIA'S CRAFTED PASTA, 215, BAKER STREET, LONDON, NW1 6XE\",\n addressLine1: \"Emilia's Crafted Pasta 215\",\n addressLine2: 'Baker Street',\n county: '',\n formatted: \"Emilia's Crafted Pasta 215, Baker Street, London, NW1 6XE\",\n postcode: 'NW1 6XE',\n town: 'London',\n uprn: '10033619968'\n })\n })\n })\n})\n\n/**\n * @import { IncomingMessage } from 'node:http'\n */\n"],"mappings":"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","ignoreList":[]}