@defra/forms-engine-plugin 4.0.41 → 4.0.43

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 (110) hide show
  1. package/.public/javascripts/application.min.js +1 -1
  2. package/.public/javascripts/application.min.js.map +1 -1
  3. package/.public/javascripts/shared.min.js +1 -1
  4. package/.public/javascripts/shared.min.js.map +1 -1
  5. package/.public/stylesheets/application.min.css +2 -2
  6. package/.public/stylesheets/application.min.css.map +1 -1
  7. package/.server/client/javascripts/location-map.d.ts +93 -0
  8. package/.server/client/javascripts/location-map.js +749 -0
  9. package/.server/client/javascripts/location-map.js.map +1 -0
  10. package/.server/client/javascripts/shared.d.ts +4 -0
  11. package/.server/client/javascripts/shared.js +5 -0
  12. package/.server/client/javascripts/shared.js.map +1 -1
  13. package/.server/client/stylesheets/_location-fields.scss +11 -0
  14. package/.server/client/stylesheets/application.scss +0 -1
  15. package/.server/client/stylesheets/shared.scss +1 -0
  16. package/.server/config/index.js +1 -1
  17. package/.server/config/index.js.map +1 -1
  18. package/.server/server/plugins/engine/configureEnginePlugin.d.ts +1 -1
  19. package/.server/server/plugins/engine/configureEnginePlugin.js +4 -2
  20. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
  21. package/.server/server/plugins/engine/options.js +2 -1
  22. package/.server/server/plugins/engine/options.js.map +1 -1
  23. package/.server/server/plugins/engine/plugin.js +14 -1
  24. package/.server/server/plugins/engine/plugin.js.map +1 -1
  25. package/.server/server/plugins/engine/types.d.ts +1 -0
  26. package/.server/server/plugins/engine/types.js.map +1 -1
  27. package/.server/server/plugins/engine/views/components/_location-field-base.html +1 -1
  28. package/.server/server/plugins/engine/views/components/osgridreffield.html +4 -2
  29. package/.server/server/plugins/map/index.d.ts +7 -0
  30. package/.server/server/plugins/map/index.js +20 -0
  31. package/.server/server/plugins/map/index.js.map +1 -0
  32. package/.server/server/plugins/map/routes/get-os-token.d.ts +6 -0
  33. package/.server/server/plugins/map/routes/get-os-token.js +41 -0
  34. package/.server/server/plugins/map/routes/get-os-token.js.map +1 -0
  35. package/.server/server/plugins/map/routes/get-os-token.test.js +49 -0
  36. package/.server/server/plugins/map/routes/get-os-token.test.js.map +1 -0
  37. package/.server/server/plugins/map/routes/index.d.ts +10 -0
  38. package/.server/server/plugins/map/routes/index.js +172 -0
  39. package/.server/server/plugins/map/routes/index.js.map +1 -0
  40. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark.json +690 -0
  41. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark.png +0 -0
  42. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark@2x.json +690 -0
  43. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark@2x.png +0 -0
  44. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale.json +690 -0
  45. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale.png +0 -0
  46. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale@2x.json +690 -0
  47. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale@2x.png +0 -0
  48. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite.json +690 -0
  49. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite.png +0 -0
  50. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite@2x.json +690 -0
  51. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite@2x.png +0 -0
  52. package/.server/server/plugins/map/routes/vts/OS_VTS_3857_Black_and_White.json +7858 -0
  53. package/.server/server/plugins/map/routes/vts/OS_VTS_3857_Dark.json +7669 -0
  54. package/.server/server/plugins/map/routes/vts/OS_VTS_3857_Outdoor.json +7653 -0
  55. package/.server/server/plugins/map/routes/vts/README.md +5 -0
  56. package/.server/server/plugins/map/service.d.ts +14 -0
  57. package/.server/server/plugins/map/service.js +76 -0
  58. package/.server/server/plugins/map/service.js.map +1 -0
  59. package/.server/server/plugins/map/service.test.js +120 -0
  60. package/.server/server/plugins/map/service.test.js.map +1 -0
  61. package/.server/server/plugins/map/test/__stubs__/find.d.ts +3 -0
  62. package/.server/server/plugins/map/test/__stubs__/find.js +216 -0
  63. package/.server/server/plugins/map/test/__stubs__/find.js.map +1 -0
  64. package/.server/server/plugins/map/test/__stubs__/nearest.d.ts +37 -0
  65. package/.server/server/plugins/map/test/__stubs__/nearest.js +38 -0
  66. package/.server/server/plugins/map/test/__stubs__/nearest.js.map +1 -0
  67. package/.server/server/plugins/map/types.d.ts +233 -0
  68. package/.server/server/plugins/map/types.js +121 -0
  69. package/.server/server/plugins/map/types.js.map +1 -0
  70. package/.server/server/types.d.ts +1 -0
  71. package/.server/server/types.js.map +1 -1
  72. package/package.json +3 -1
  73. package/src/client/javascripts/location-map.js +774 -0
  74. package/src/client/javascripts/shared.js +4 -0
  75. package/src/client/stylesheets/_location-fields.scss +11 -0
  76. package/src/client/stylesheets/application.scss +0 -1
  77. package/src/client/stylesheets/shared.scss +1 -0
  78. package/src/config/index.ts +1 -1
  79. package/src/server/plugins/engine/configureEnginePlugin.ts +4 -2
  80. package/src/server/plugins/engine/options.js +2 -1
  81. package/src/server/plugins/engine/plugin.ts +14 -1
  82. package/src/server/plugins/engine/types.ts +1 -0
  83. package/src/server/plugins/engine/views/components/_location-field-base.html +1 -1
  84. package/src/server/plugins/engine/views/components/osgridreffield.html +4 -2
  85. package/src/server/plugins/map/index.js +19 -0
  86. package/src/server/plugins/map/routes/get-os-token.js +41 -0
  87. package/src/server/plugins/map/routes/get-os-token.test.js +55 -0
  88. package/src/server/plugins/map/routes/index.js +192 -0
  89. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark.json +690 -0
  90. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark.png +0 -0
  91. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark@2x.json +690 -0
  92. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark@2x.png +0 -0
  93. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale.json +690 -0
  94. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale.png +0 -0
  95. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale@2x.json +690 -0
  96. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale@2x.png +0 -0
  97. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite.json +690 -0
  98. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite.png +0 -0
  99. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite@2x.json +690 -0
  100. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite@2x.png +0 -0
  101. package/src/server/plugins/map/routes/vts/OS_VTS_3857_Black_and_White.json +7858 -0
  102. package/src/server/plugins/map/routes/vts/OS_VTS_3857_Dark.json +7669 -0
  103. package/src/server/plugins/map/routes/vts/OS_VTS_3857_Outdoor.json +7653 -0
  104. package/src/server/plugins/map/routes/vts/README.md +5 -0
  105. package/src/server/plugins/map/service.js +84 -0
  106. package/src/server/plugins/map/service.test.js +144 -0
  107. package/src/server/plugins/map/test/__stubs__/find.js +271 -0
  108. package/src/server/plugins/map/test/__stubs__/nearest.js +46 -0
  109. package/src/server/plugins/map/types.js +120 -0
  110. package/src/server/types.ts +1 -0
@@ -2,12 +2,16 @@ import { initAllAutocomplete as initAllAutocompleteImp } from '~/src/client/java
2
2
  import { initFileUpload as initFileUploadImp } from '~/src/client/javascripts/file-upload.js'
3
3
  import { initAllGovuk as initAllGovukImp } from '~/src/client/javascripts/govuk.js'
4
4
  import { initPreviewCloseLink as initPreviewCloseLinkImp } from '~/src/client/javascripts/preview-close-link.js'
5
+ export { initMaps } from '~/src/client/javascripts/location-map.js'
5
6
 
6
7
  export const initAllGovuk = initAllGovukImp
7
8
  export const initAllAutocomplete = initAllAutocompleteImp
8
9
  export const initFileUpload = initFileUploadImp
9
10
  export const initPreviewCloseLink = initPreviewCloseLinkImp
10
11
 
12
+ /**
13
+ * Initialise all clientside components (but not maps as this will be an opt-in for now given the additional UMD assets that are required)
14
+ */
11
15
  export function initAll() {
12
16
  initAllGovuk()
13
17
  initAllAutocomplete()
@@ -3,3 +3,14 @@
3
3
  .govuk-form-group .app-location-field-inputs .govuk-form-group {
4
4
  margin-bottom: 0;
5
5
  }
6
+
7
+ .map-container {
8
+ margin-top: 10px;
9
+ }
10
+
11
+ // Hides the location instructions text when JS (maps) is available
12
+ .js-enabled .app-location-field {
13
+ details:last-child {
14
+ display: none;
15
+ }
16
+ }
@@ -2,7 +2,6 @@
2
2
  @use "shared";
3
3
  @use "code";
4
4
  @use "tag-env";
5
- @use "location-fields";
6
5
 
7
6
  // An example of some user-supplied styling
8
7
  // Not great practice but it illustrates the point
@@ -2,6 +2,7 @@
2
2
  @use "pkg:accessible-autocomplete";
3
3
  @use "prose";
4
4
  @use "summary-list";
5
+ @use "location-fields";
5
6
 
6
7
  // Use default GDS Transport font for autocomplete
7
8
  .autocomplete__hint,
@@ -255,7 +255,7 @@ export const config = convict({
255
255
  } as SchemaObj<string>,
256
256
 
257
257
  ordnanceSurveyApiKey: {
258
- doc: 'The ordnance survey api key use by the postcode lookup plugin',
258
+ doc: 'The ordnance survey api key used by the postcode lookup and maps plugin',
259
259
  format: String,
260
260
  nullable: true,
261
261
  default: undefined,
@@ -22,7 +22,8 @@ export const configureEnginePlugin = async (
22
22
  preparePageEventRequestOptions,
23
23
  onRequest,
24
24
  saveAndExit,
25
- ordnanceSurveyApiKey
25
+ ordnanceSurveyApiKey,
26
+ ordnanceSurveyApiSecret
26
27
  }: RouteConfig = {},
27
28
  cache?: CacheService
28
29
  ): Promise<{
@@ -65,7 +66,8 @@ export const configureEnginePlugin = async (
65
66
  onRequest,
66
67
  baseUrl: 'http://localhost:3009', // always runs locally
67
68
  saveAndExit,
68
- ordnanceSurveyApiKey
69
+ ordnanceSurveyApiKey,
70
+ ordnanceSurveyApiSecret
69
71
  }
70
72
  }
71
73
  }
@@ -26,7 +26,8 @@ const pluginRegistrationOptionsSchema = Joi.object({
26
26
  onRequest: Joi.function().optional(),
27
27
  baseUrl: Joi.string().uri().required(),
28
28
  saveAndExit: Joi.function().optional(),
29
- ordnanceSurveyApiKey: Joi.string().optional()
29
+ ordnanceSurveyApiKey: Joi.string().optional(),
30
+ ordnanceSurveyApiSecret: Joi.string().optional()
30
31
  })
31
32
 
32
33
  /**
@@ -15,6 +15,7 @@ import { getRoutes as getRepeaterItemDeleteRoutes } from '~/src/server/plugins/e
15
15
  import { getRoutes as getRepeaterSummaryRoutes } from '~/src/server/plugins/engine/routes/repeaters/summary.js'
16
16
  import { type PluginOptions } from '~/src/server/plugins/engine/types.js'
17
17
  import { registerVision } from '~/src/server/plugins/engine/vision.js'
18
+ import { mapPlugin } from '~/src/server/plugins/map/index.js'
18
19
  import { postcodeLookupPlugin } from '~/src/server/plugins/postcode-lookup/index.js'
19
20
  import {
20
21
  type FormRequestPayloadRefs,
@@ -37,7 +38,8 @@ export const plugin = {
37
38
  viewContext,
38
39
  preparePageEventRequestOptions,
39
40
  onRequest,
40
- ordnanceSurveyApiKey
41
+ ordnanceSurveyApiKey,
42
+ ordnanceSurveyApiSecret
41
43
  } = options
42
44
 
43
45
  const cacheService =
@@ -57,6 +59,17 @@ export const plugin = {
57
59
  })
58
60
  }
59
61
 
62
+ // Register the maps plugin only if we have an OS api key & secret
63
+ if (ordnanceSurveyApiKey && ordnanceSurveyApiSecret) {
64
+ await server.register({
65
+ plugin: mapPlugin,
66
+ options: {
67
+ ordnanceSurveyApiKey,
68
+ ordnanceSurveyApiSecret
69
+ }
70
+ })
71
+ }
72
+
60
73
  server.expose('baseLayoutPath', nunjucksOptions.baseLayoutPath)
61
74
  server.expose('viewContext', viewContext)
62
75
  server.expose('cacheService', cacheService)
@@ -423,6 +423,7 @@ export interface PluginOptions {
423
423
  onRequest?: OnRequestCallback
424
424
  baseUrl: string // base URL of the application, protocol and hostname e.g. "https://myapp.com"
425
425
  ordnanceSurveyApiKey?: string
426
+ ordnanceSurveyApiSecret?: string
426
427
  }
427
428
 
428
429
  export interface FormAdapterSubmissionMessageMeta {
@@ -70,7 +70,7 @@
70
70
 
71
71
  {% set hasErrors = showFieldsetError and component.model.errorMessage %}
72
72
 
73
- <div class="govuk-form-group {{ "govuk-form-group--error" if hasErrors }}">
73
+ <div class="govuk-form-group app-location-field {{ "govuk-form-group--error" if hasErrors }}" data-locationtype="{{component.type | lower}}">
74
74
  {{ govukFieldset({
75
75
  legend: {
76
76
  text: component.model.fieldset.legend.text,
@@ -3,8 +3,10 @@
3
3
 
4
4
  {% macro OsGridRefField(component) %}
5
5
  {% set hasErrors = component.model.errorMessage %}
6
- <div class="govuk-form-group {{ "govuk-form-group--error" if hasErrors }}">
7
- {{ TextField(component) }}
6
+ <div class="govuk-form-group app-location-field {{ "govuk-form-group--error" if hasErrors }}" data-locationtype="{{component.type | lower}}">
7
+ <div class="app-location-field-inputs">
8
+ {{ TextField(component) }}
9
+ </div>
8
10
 
9
11
  {% if component.model.instructionText %}
10
12
  {{ govukDetails({
@@ -0,0 +1,19 @@
1
+ import { getRoutes } from '~/src/server/plugins/map/routes/index.js'
2
+
3
+ /**
4
+ * @satisfies {NamedPlugin<MapConfiguration>}
5
+ */
6
+ export const mapPlugin = {
7
+ name: '@defra/forms-engine-plugin/map',
8
+ dependencies: ['@hapi/inert'],
9
+ multiple: false,
10
+ register(server, options) {
11
+ // @ts-expect-error - Request typing
12
+ server.route(getRoutes(options))
13
+ }
14
+ }
15
+
16
+ /**
17
+ * @import { NamedPlugin } from '@hapi/hapi'
18
+ * @import { MapConfiguration } from '~/src/server/plugins/map/types.js'
19
+ */
@@ -0,0 +1,41 @@
1
+ import { post } from '~/src/server/services/httpService.js'
2
+
3
+ /**
4
+ * @type {string}
5
+ */
6
+ let cachedToken
7
+ let tokenExpiry = 0
8
+
9
+ /**
10
+ * Get Ordnance Survey OAuth token
11
+ * @param {MapConfiguration} options - Ordnance survey map options
12
+ */
13
+ export async function getAccessToken(options) {
14
+ const { ordnanceSurveyApiKey: key, ordnanceSurveyApiSecret: secret } = options
15
+ const now = Date.now()
16
+
17
+ if (cachedToken && now < tokenExpiry) {
18
+ return cachedToken
19
+ }
20
+
21
+ const creds = `${key}:${secret}`
22
+ const result = await post('https://api.os.uk/oauth2/token/v1', {
23
+ headers: {
24
+ Authorization: `Basic ${btoa(creds)}`,
25
+ 'Content-Type': 'application/x-www-form-urlencoded'
26
+ },
27
+ payload: 'grant_type=client_credentials',
28
+ json: true
29
+ })
30
+
31
+ const data = result.payload
32
+
33
+ cachedToken = data.access_token
34
+ tokenExpiry = now + (data.expires_in - 60) * 1000 // refresh early
35
+
36
+ return cachedToken
37
+ }
38
+
39
+ /**
40
+ * @import { MapConfiguration } from '~/src/server/plugins/map/types.js'
41
+ */
@@ -0,0 +1,55 @@
1
+ import { getAccessToken } from '~/src/server/plugins/map/routes/get-os-token.js'
2
+ import { post } from '~/src/server/services/httpService.js'
3
+
4
+ jest.mock('~/src/server/services/httpService.ts')
5
+
6
+ describe('OS OAuth token', () => {
7
+ describe('getAccessToken', () => {
8
+ it('should get access token', async () => {
9
+ jest.mocked(post).mockResolvedValueOnce({
10
+ res: /** @type {IncomingMessage} */ ({
11
+ statusCode: 200,
12
+ headers: {}
13
+ }),
14
+ payload: {
15
+ access_token: 'access_token',
16
+ expires_in: '299',
17
+ issued_at: '1770036762387',
18
+ token_type: 'Bearer'
19
+ },
20
+ error: undefined
21
+ })
22
+
23
+ const token = await getAccessToken({
24
+ ordnanceSurveyApiKey: 'apikey',
25
+ ordnanceSurveyApiSecret: 'apisecret'
26
+ })
27
+
28
+ expect(token).toBe('access_token')
29
+
30
+ expect(post).toHaveBeenCalledWith('https://api.os.uk/oauth2/token/v1', {
31
+ headers: {
32
+ Authorization: `Basic ${btoa('apikey:apisecret')}`,
33
+ 'Content-Type': 'application/x-www-form-urlencoded'
34
+ },
35
+ payload: 'grant_type=client_credentials',
36
+ json: true
37
+ })
38
+ expect(post).toHaveBeenCalledTimes(1)
39
+ })
40
+
41
+ it('should return an cached token', async () => {
42
+ const token = await getAccessToken({
43
+ ordnanceSurveyApiKey: 'apikey',
44
+ ordnanceSurveyApiSecret: 'apisecret'
45
+ })
46
+
47
+ expect(token).toBe('access_token')
48
+ expect(post).toHaveBeenCalledTimes(0)
49
+ })
50
+ })
51
+ })
52
+
53
+ /**
54
+ * @import { IncomingMessage } from 'node:http'
55
+ */
@@ -0,0 +1,192 @@
1
+ import { resolve } from 'node:path'
2
+
3
+ import { StatusCodes } from 'http-status-codes'
4
+ import Joi from 'joi'
5
+
6
+ import { getAccessToken } from '~/src/server/plugins/map/routes/get-os-token.js'
7
+ import { find, nearest } from '~/src/server/plugins/map/service.js'
8
+ import {
9
+ get,
10
+ request as httpRequest
11
+ } from '~/src/server/services/httpService.js'
12
+
13
+ /**
14
+ * Gets the map support routes
15
+ * @param {MapConfiguration} options - ordnance survey names api key
16
+ */
17
+ export function getRoutes(options) {
18
+ return [
19
+ mapStyleResourceRoutes(),
20
+ mapProxyRoute(options),
21
+ tileProxyRoute(options),
22
+ geocodeProxyRoute(options),
23
+ reverseGeocodeProxyRoute(options)
24
+ ]
25
+ }
26
+
27
+ /**
28
+ * Proxies ordnance survey requests from the front end to api.os.com
29
+ * Used for the VTS map source by forwarding on the request
30
+ * and adding the auth token and SRS (spatial reference system)
31
+ * @param {MapConfiguration} options - the map options
32
+ * @returns {ServerRoute<MapProxyGetRequestRefs>}
33
+ */
34
+ function mapProxyRoute(options) {
35
+ return {
36
+ method: 'GET',
37
+ path: '/api/map-proxy',
38
+ handler: async (request, h) => {
39
+ const { query } = request
40
+ const targetUrl = new URL(decodeURIComponent(query.url))
41
+ const token = await getAccessToken(options)
42
+
43
+ targetUrl.searchParams.set('srs', '3857')
44
+
45
+ const proxyResponse = await httpRequest('get', targetUrl.toString(), {
46
+ headers: {
47
+ Authorization: `Bearer ${token}`
48
+ }
49
+ })
50
+ const buffer = proxyResponse.payload
51
+ const contentType = proxyResponse.res.headers['content-type']
52
+ const response = h.response(buffer)
53
+
54
+ if (contentType) {
55
+ response.type(contentType)
56
+ }
57
+
58
+ return response
59
+ },
60
+ options: {
61
+ validate: {
62
+ query: Joi.object()
63
+ .keys({
64
+ url: Joi.string().required()
65
+ })
66
+ .optional()
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Proxies ordnance survey requests from the front end to api.os.uk
74
+ * Used for VTS map tiles forwarding on the request and adding the auth token
75
+ * @param {MapConfiguration} options - the map options
76
+ * @returns {ServerRoute<MapProxyGetRequestRefs>}
77
+ */
78
+ function tileProxyRoute(options) {
79
+ return {
80
+ method: 'GET',
81
+ path: '/api/tile/{z}/{y}/{x}.pbf',
82
+ handler: async (request, h) => {
83
+ const { z, y, x } = request.params
84
+ const token = await getAccessToken(options)
85
+
86
+ const url = `https://api.os.uk/maps/vector/v1/vts/tile/${z}/${y}/${x}.pbf?srs=3857`
87
+
88
+ const { payload, res } = await get(url, {
89
+ headers: {
90
+ Authorization: `Bearer ${token}`,
91
+ Accept: 'application/x-protobuf'
92
+ },
93
+ json: false,
94
+ gunzip: true
95
+ })
96
+
97
+ if (res.statusCode && res.statusCode !== StatusCodes.OK.valueOf()) {
98
+ return h.response('Tile fetch failed').code(res.statusCode)
99
+ }
100
+
101
+ return h
102
+ .response(payload)
103
+ .type('application/x-protobuf')
104
+ .header('Cache-Control', 'public, max-age=86400')
105
+ }
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Proxies ordnance survey geocode requests from the front end to api.os.uk
111
+ * Used for the gazzeteer address lookup to find name from query strings like postcode and place names
112
+ * @param {MapConfiguration} options - the map options
113
+ * @returns {ServerRoute<MapGeocodeGetRequestRefs>}
114
+ */
115
+ function geocodeProxyRoute(options) {
116
+ return {
117
+ method: 'GET',
118
+ path: '/api/geocode-proxy',
119
+ async handler(request, _h) {
120
+ const { query } = request
121
+ const data = await find(query.query, options.ordnanceSurveyApiKey)
122
+
123
+ return data
124
+ },
125
+ options: {
126
+ validate: {
127
+ query: Joi.object()
128
+ .keys({
129
+ query: Joi.string().required()
130
+ })
131
+ .required()
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Proxies ordnance survey reverse geocode requests from the front end to api.os.uk
139
+ * Used to find name from easting and northing points.
140
+ * N.B this endpoint is currently not used by the front end but will be soon in "maps V2"
141
+ * @param {MapConfiguration} options - the map options
142
+ * @returns {ServerRoute<MapReverseGeocodeGetRequestRefs>}
143
+ */
144
+ function reverseGeocodeProxyRoute(options) {
145
+ return {
146
+ method: 'GET',
147
+ path: '/api/reverse-geocode-proxy',
148
+ async handler(request, _h) {
149
+ const { query } = request
150
+ const data = await nearest(
151
+ query.easting,
152
+ query.northing,
153
+ options.ordnanceSurveyApiKey
154
+ )
155
+
156
+ return data
157
+ },
158
+ options: {
159
+ validate: {
160
+ query: Joi.object()
161
+ .keys({
162
+ easting: Joi.number().required(),
163
+ northing: Joi.number().required()
164
+ })
165
+ .required()
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Resource routes to return sprites and glyphs
173
+ * @returns {ServerRoute<MapProxyGetRequestRefs>}
174
+ */
175
+ function mapStyleResourceRoutes() {
176
+ return {
177
+ method: 'GET',
178
+ path: '/api/maps/vts/{path*}',
179
+ options: {
180
+ handler: {
181
+ directory: {
182
+ path: resolve(import.meta.dirname, './vts')
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+
189
+ /**
190
+ * @import { ServerRoute } from '@hapi/hapi'
191
+ * @import { MapConfiguration, MapProxyGetRequestRefs, MapGeocodeGetRequestRefs, MapReverseGeocodeGetRequestRefs } from '~/src/server/plugins/map/types.js'
192
+ */