@defra/forms-engine-plugin 4.0.42 → 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 (37) hide show
  1. package/.public/javascripts/shared.min.js +1 -1
  2. package/.public/javascripts/shared.min.js.map +1 -1
  3. package/.server/client/javascripts/location-map.js +8 -4
  4. package/.server/client/javascripts/location-map.js.map +1 -1
  5. package/.server/server/plugins/engine/configureEnginePlugin.d.ts +1 -1
  6. package/.server/server/plugins/engine/configureEnginePlugin.js +4 -2
  7. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
  8. package/.server/server/plugins/engine/options.js +2 -1
  9. package/.server/server/plugins/engine/options.js.map +1 -1
  10. package/.server/server/plugins/engine/plugin.js +6 -4
  11. package/.server/server/plugins/engine/plugin.js.map +1 -1
  12. package/.server/server/plugins/engine/types.d.ts +1 -0
  13. package/.server/server/plugins/engine/types.js.map +1 -1
  14. package/.server/server/plugins/map/routes/get-os-token.d.ts +6 -0
  15. package/.server/server/plugins/map/routes/get-os-token.js +41 -0
  16. package/.server/server/plugins/map/routes/get-os-token.js.map +1 -0
  17. package/.server/server/plugins/map/routes/get-os-token.test.js +49 -0
  18. package/.server/server/plugins/map/routes/get-os-token.test.js.map +1 -0
  19. package/.server/server/plugins/map/routes/index.d.ts +1 -11
  20. package/.server/server/plugins/map/routes/index.js +60 -16
  21. package/.server/server/plugins/map/routes/index.js.map +1 -1
  22. package/.server/server/plugins/map/types.d.ts +1 -0
  23. package/.server/server/plugins/map/types.js +1 -0
  24. package/.server/server/plugins/map/types.js.map +1 -1
  25. package/.server/server/types.d.ts +1 -0
  26. package/.server/server/types.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/client/javascripts/location-map.js +12 -4
  29. package/src/server/plugins/engine/configureEnginePlugin.ts +4 -2
  30. package/src/server/plugins/engine/options.js +2 -1
  31. package/src/server/plugins/engine/plugin.ts +6 -4
  32. package/src/server/plugins/engine/types.ts +1 -0
  33. package/src/server/plugins/map/routes/get-os-token.js +41 -0
  34. package/src/server/plugins/map/routes/get-os-token.test.js +55 -0
  35. package/src/server/plugins/map/routes/index.js +70 -24
  36. package/src/server/plugins/map/types.js +1 -0
  37. package/src/server/types.ts +1 -0
@@ -2,17 +2,7 @@
2
2
  * Gets the map support routes
3
3
  * @param {MapConfiguration} options - ordnance survey names api key
4
4
  */
5
- export function getRoutes(options: MapConfiguration): (ServerRoute<MapProxyGetRequestRefs> | ServerRoute<MapGeocodeGetRequestRefs> | ServerRoute<MapReverseGeocodeGetRequestRefs> | {
6
- method: string;
7
- path: string;
8
- options: {
9
- handler: {
10
- directory: {
11
- path: string;
12
- };
13
- };
14
- };
15
- })[];
5
+ export function getRoutes(options: MapConfiguration): (ServerRoute<MapProxyGetRequestRefs> | ServerRoute<MapGeocodeGetRequestRefs> | ServerRoute<MapReverseGeocodeGetRequestRefs>)[];
16
6
  import type { MapConfiguration } from '~/src/server/plugins/map/types.js';
17
7
  import type { MapProxyGetRequestRefs } from '~/src/server/plugins/map/types.js';
18
8
  import type { ServerRoute } from '@hapi/hapi';
@@ -1,20 +1,22 @@
1
1
  import { resolve } from 'node:path';
2
+ import { StatusCodes } from 'http-status-codes';
2
3
  import Joi from 'joi';
4
+ import { getAccessToken } from "./get-os-token.js";
3
5
  import { find, nearest } from "../service.js";
4
- import { request as httpRequest } from "../../../services/httpService.js";
6
+ import { get, request as httpRequest } from "../../../services/httpService.js";
5
7
 
6
8
  /**
7
9
  * Gets the map support routes
8
10
  * @param {MapConfiguration} options - ordnance survey names api key
9
11
  */
10
12
  export function getRoutes(options) {
11
- return [mapProxyRoute(options), geocodeProxyRoute(options), reverseGeocodeProxyRoute(options), ...tileRoutes()];
13
+ return [mapStyleResourceRoutes(), mapProxyRoute(options), tileProxyRoute(options), geocodeProxyRoute(options), reverseGeocodeProxyRoute(options)];
12
14
  }
13
15
 
14
16
  /**
15
17
  * Proxies ordnance survey requests from the front end to api.os.com
16
- * Used for VTS map tiles, sprites and fonts by forwarding on the request
17
- * and adding the apikey and optionally an SRS (spatial reference system)
18
+ * Used for the VTS map source by forwarding on the request
19
+ * and adding the auth token and SRS (spatial reference system)
18
20
  * @param {MapConfiguration} options - the map options
19
21
  * @returns {ServerRoute<MapProxyGetRequestRefs>}
20
22
  */
@@ -27,13 +29,13 @@ function mapProxyRoute(options) {
27
29
  query
28
30
  } = request;
29
31
  const targetUrl = new URL(decodeURIComponent(query.url));
30
-
31
- // Add API key server-side and set SRS
32
- targetUrl.searchParams.set('key', options.ordnanceSurveyApiKey);
33
- if (!targetUrl.searchParams.has('srs')) {
34
- targetUrl.searchParams.set('srs', '3857');
35
- }
36
- const proxyResponse = await httpRequest('get', targetUrl.toString());
32
+ const token = await getAccessToken(options);
33
+ targetUrl.searchParams.set('srs', '3857');
34
+ const proxyResponse = await httpRequest('get', targetUrl.toString(), {
35
+ headers: {
36
+ Authorization: `Bearer ${token}`
37
+ }
38
+ });
37
39
  const buffer = proxyResponse.payload;
38
40
  const contentType = proxyResponse.res.headers['content-type'];
39
41
  const response = h.response(buffer);
@@ -53,7 +55,44 @@ function mapProxyRoute(options) {
53
55
  }
54
56
 
55
57
  /**
56
- * Proxies ordnance survey geocode requests from the front end to api.os.com
58
+ * Proxies ordnance survey requests from the front end to api.os.uk
59
+ * Used for VTS map tiles forwarding on the request and adding the auth token
60
+ * @param {MapConfiguration} options - the map options
61
+ * @returns {ServerRoute<MapProxyGetRequestRefs>}
62
+ */
63
+ function tileProxyRoute(options) {
64
+ return {
65
+ method: 'GET',
66
+ path: '/api/tile/{z}/{y}/{x}.pbf',
67
+ handler: async (request, h) => {
68
+ const {
69
+ z,
70
+ y,
71
+ x
72
+ } = request.params;
73
+ const token = await getAccessToken(options);
74
+ const url = `https://api.os.uk/maps/vector/v1/vts/tile/${z}/${y}/${x}.pbf?srs=3857`;
75
+ const {
76
+ payload,
77
+ res
78
+ } = await get(url, {
79
+ headers: {
80
+ Authorization: `Bearer ${token}`,
81
+ Accept: 'application/x-protobuf'
82
+ },
83
+ json: false,
84
+ gunzip: true
85
+ });
86
+ if (res.statusCode && res.statusCode !== StatusCodes.OK.valueOf()) {
87
+ return h.response('Tile fetch failed').code(res.statusCode);
88
+ }
89
+ return h.response(payload).type('application/x-protobuf').header('Cache-Control', 'public, max-age=86400');
90
+ }
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Proxies ordnance survey geocode requests from the front end to api.os.uk
57
96
  * Used for the gazzeteer address lookup to find name from query strings like postcode and place names
58
97
  * @param {MapConfiguration} options - the map options
59
98
  * @returns {ServerRoute<MapGeocodeGetRequestRefs>}
@@ -80,7 +119,7 @@ function geocodeProxyRoute(options) {
80
119
  }
81
120
 
82
121
  /**
83
- * Proxies ordnance survey reverse geocode requests from the front end to api.os.com
122
+ * Proxies ordnance survey reverse geocode requests from the front end to api.os.uk
84
123
  * Used to find name from easting and northing points.
85
124
  * N.B this endpoint is currently not used by the front end but will be soon in "maps V2"
86
125
  * @param {MapConfiguration} options - the map options
@@ -107,8 +146,13 @@ function reverseGeocodeProxyRoute(options) {
107
146
  }
108
147
  };
109
148
  }
110
- function tileRoutes() {
111
- return [{
149
+
150
+ /**
151
+ * Resource routes to return sprites and glyphs
152
+ * @returns {ServerRoute<MapProxyGetRequestRefs>}
153
+ */
154
+ function mapStyleResourceRoutes() {
155
+ return {
112
156
  method: 'GET',
113
157
  path: '/api/maps/vts/{path*}',
114
158
  options: {
@@ -118,7 +162,7 @@ function tileRoutes() {
118
162
  }
119
163
  }
120
164
  }
121
- }];
165
+ };
122
166
  }
123
167
 
124
168
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["resolve","Joi","find","nearest","request","httpRequest","getRoutes","options","mapProxyRoute","geocodeProxyRoute","reverseGeocodeProxyRoute","tileRoutes","method","path","handler","h","query","targetUrl","URL","decodeURIComponent","url","searchParams","set","ordnanceSurveyApiKey","has","proxyResponse","toString","buffer","payload","contentType","res","headers","response","type","validate","object","keys","string","required","optional","_h","data","easting","northing","number","directory","import","meta","dirname"],"sources":["../../../../../src/server/plugins/map/routes/index.js"],"sourcesContent":["import { resolve } from 'node:path'\n\nimport Joi from 'joi'\n\nimport { find, nearest } from '~/src/server/plugins/map/service.js'\nimport { request as httpRequest } from '~/src/server/services/httpService.js'\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 mapProxyRoute(options),\n geocodeProxyRoute(options),\n reverseGeocodeProxyRoute(options),\n ...tileRoutes()\n ]\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.com\n * Used for VTS map tiles, sprites and fonts by forwarding on the request\n * and adding the apikey and optionally an 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\n // Add API key server-side and set SRS\n targetUrl.searchParams.set('key', options.ordnanceSurveyApiKey)\n if (!targetUrl.searchParams.has('srs')) {\n targetUrl.searchParams.set('srs', '3857')\n }\n\n const proxyResponse = await httpRequest('get', targetUrl.toString())\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 geocode requests from the front end to api.os.com\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.com\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\nfunction tileRoutes() {\n return [\n {\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/**\n * @import { ServerRoute } from '@hapi/hapi'\n * @import { MapConfiguration, MapProxyGetRequestRefs, MapGeocodeGetRequestRefs, MapReverseGeocodeGetRequestRefs } from '~/src/server/plugins/map/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["resolve","StatusCodes","Joi","getAccessToken","find","nearest","get","request","httpRequest","getRoutes","options","mapStyleResourceRoutes","mapProxyRoute","tileProxyRoute","geocodeProxyRoute","reverseGeocodeProxyRoute","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","string","required","optional","z","y","x","params","Accept","json","gunzip","statusCode","OK","valueOf","code","header","_h","data","ordnanceSurveyApiKey","easting","northing","number","directory","import","meta","dirname"],"sources":["../../../../../src/server/plugins/map/routes/index.js"],"sourcesContent":["import { resolve } from 'node:path'\n\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\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 ]\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 { payload, res } = await get(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 * @import { ServerRoute } from '@hapi/hapi'\n * @import { MapConfiguration, MapProxyGetRequestRefs, MapGeocodeGetRequestRefs, MapReverseGeocodeGetRequestRefs } from '~/src/server/plugins/map/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -1,5 +1,6 @@
1
1
  export type MapConfiguration = {
2
2
  ordnanceSurveyApiKey: string;
3
+ ordnanceSurveyApiSecret: string;
3
4
  };
4
5
  /**
5
6
  * Map proxy query params
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @typedef {{
3
3
  * ordnanceSurveyApiKey: string
4
+ * ordnanceSurveyApiSecret: string
4
5
  * }} MapConfiguration
5
6
  */
6
7
 
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../../../src/server/plugins/map/types.js"],"sourcesContent":["/**\n * @typedef {{\n * ordnanceSurveyApiKey: string\n * }} MapConfiguration\n */\n\n//\n// Route types\n//\n\n/**\n * Map proxy query params\n * @typedef {object} MapProxyQuery\n * @property {string} url - the proxied url\n */\n\n/**\n * Map geocode query params\n * @typedef {object} MapGeocodeQuery\n * @property {string} query - name query\n */\n\n/**\n * Map reverse geocode query params\n * @typedef {object} MapReverseGeocodeQuery\n * @property {number} easting - the Easting point\n * @property {number} northing - the Northing point\n */\n\n/**\n * Map geocode get request\n * @typedef {object} MapProxyGetRequestRefs\n * @property {MapProxyQuery} Query - Request query\n */\n\n/**\n * Map geocode get request\n * @typedef {object} MapGeocodeGetRequestRefs\n * @property {MapGeocodeQuery} Query - Request query\n */\n\n/**\n * Map reverst geocode get request\n * @typedef {object} MapReverseGeocodeGetRequestRefs\n * @property {MapReverseGeocodeQuery} Query - Request query\n */\n\n/**\n * @typedef {MapProxyGetRequestRefs} MapProxyRequestRefs\n * @typedef {MapGeocodeGetRequestRefs} MapGeocodeRequestRefs\n * @typedef {MapReverseGeocodeGetRequestRefs} MapReverseGeocodeRequestRefs\n * @typedef {Request<MapGeocodeGetRequestRefs>} MapProxyGetRequest\n * @typedef {Request<MapGeocodeGetRequestRefs>} MapGeocodeGetRequest\n * @typedef {Request<MapReverseGeocodeGetRequestRefs>} MapReverseGeocodeGetRequest\n * @typedef {MapProxyGetRequest | MapGeocodeGetRequest | MapReverseGeocodeGetRequest} MapRequest\n */\n\n//\n// Service types\n//\n\n/**\n * @typedef {object} OsNamesFindResponse\n * @property {OsNamesFindHeader} header - Metadata about the search request and results.\n * @property {OsNamesFindResult[]} results - An array of matched place records from the search.\n */\n\n/**\n * @typedef {object} OsNamesFindHeader\n * @property {string} uri - The query URI (usually same as search text).\n * @property {string} query - The original text query string passed to the API.\n * @property {string} format - The response format returned (e.g., \"JSON\").\n * @property {number} maxresults - The maximum number of results requested.\n * @property {number} offset - The offset used in the search results.\n * @property {number} totalresults - The total number of results that matched the query.\n * @property {string} filter - The original filter string passed to the API.\n */\n\n/**\n * @typedef {object} OsNamesFindGazetteerEntry\n * @property {string} ID - Unique identifier for the place/feature.\n * @property {string} NAMES_URI - A URI (identifier) for this named feature.\n * @property {string} NAME1 - Primary name of the feature.\n * @property {string} TYPE - General type classification of the feature.\n * @property {string} LOCAL_TYPE - Local or more specific type classification.\n * @property {number} GEOMETRY_X - Easting coordinate (British National Grid).\n * @property {number} GEOMETRY_Y - Northing coordinate (British National Grid).\n * @property {number} MOST_DETAIL_VIEW_RES - Most detailed resolution available.\n * @property {number} LEAST_DETAIL_VIEW_RES - Least detailed resolution available.\n * @property {number} [MBR_XMIN] - Minimum bounding box X (easting).\n * @property {number} [MBR_YMIN] - Minimum bounding box Y (northing).\n * @property {number} [MBR_XMAX] - Maximum bounding box X (easting).\n * @property {number} [MBR_YMAX] - Maximum bounding box Y (northing).\n * @property {string} [DISTRICT_BOROUGH] - (Optional) District borough.\n * @property {string} [DISTRICT_BOROUGH_URI] - (Optional) URI for the district borough.\n * @property {string} [DISTRICT_BOROUGH_TYPE] - (Optional) Type of the district borough.\n * @property {string} [POSTCODE_DISTRICT] - (Optional) Postcode district.\n * @property {string} [POSTCODE_DISTRICT_URI] - (Optional) URI for the postcode district.\n * @property {string} [POPULATED_PLACE] - (Optional) Name of associated populated place.\n * @property {string} [POPULATED_PLACE_URI] - (Optional) URI of populated place.\n * @property {string} [POPULATED_PLACE_TYPE] - (Optional) Type of populated place.\n * @property {string} [COUNTY_UNITARY] - (Optional) County or unitary authority name.\n * @property {string} [COUNTY_UNITARY_URI] - (Optional) URI for county/unitary authority.\n * @property {string} [COUNTY_UNITARY_TYPE] - (Optional) Classification of county/unitary authority.\n * @property {string} [REGION] - (Optional) Region name.\n * @property {string} [REGION_URI] - (Optional) URI for region.\n * @property {string} [COUNTRY] - (Optional) Country name.\n * @property {string} [COUNTRY_URI] - (Optional) URI for country.\n */\n\n/**\n * OS names GAZETTEER_ENTRY response\n * @typedef {object} OsNamesFindResult\n * @property {OsNamesFindGazetteerEntry} GAZETTEER_ENTRY - Gazetteer entry\n */\n\n/**\n * @import { Request } from '@hapi/hapi'\n */\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../../../src/server/plugins/map/types.js"],"sourcesContent":["/**\n * @typedef {{\n * ordnanceSurveyApiKey: string\n * ordnanceSurveyApiSecret: string\n * }} MapConfiguration\n */\n\n//\n// Route types\n//\n\n/**\n * Map proxy query params\n * @typedef {object} MapProxyQuery\n * @property {string} url - the proxied url\n */\n\n/**\n * Map geocode query params\n * @typedef {object} MapGeocodeQuery\n * @property {string} query - name query\n */\n\n/**\n * Map reverse geocode query params\n * @typedef {object} MapReverseGeocodeQuery\n * @property {number} easting - the Easting point\n * @property {number} northing - the Northing point\n */\n\n/**\n * Map geocode get request\n * @typedef {object} MapProxyGetRequestRefs\n * @property {MapProxyQuery} Query - Request query\n */\n\n/**\n * Map geocode get request\n * @typedef {object} MapGeocodeGetRequestRefs\n * @property {MapGeocodeQuery} Query - Request query\n */\n\n/**\n * Map reverst geocode get request\n * @typedef {object} MapReverseGeocodeGetRequestRefs\n * @property {MapReverseGeocodeQuery} Query - Request query\n */\n\n/**\n * @typedef {MapProxyGetRequestRefs} MapProxyRequestRefs\n * @typedef {MapGeocodeGetRequestRefs} MapGeocodeRequestRefs\n * @typedef {MapReverseGeocodeGetRequestRefs} MapReverseGeocodeRequestRefs\n * @typedef {Request<MapGeocodeGetRequestRefs>} MapProxyGetRequest\n * @typedef {Request<MapGeocodeGetRequestRefs>} MapGeocodeGetRequest\n * @typedef {Request<MapReverseGeocodeGetRequestRefs>} MapReverseGeocodeGetRequest\n * @typedef {MapProxyGetRequest | MapGeocodeGetRequest | MapReverseGeocodeGetRequest} MapRequest\n */\n\n//\n// Service types\n//\n\n/**\n * @typedef {object} OsNamesFindResponse\n * @property {OsNamesFindHeader} header - Metadata about the search request and results.\n * @property {OsNamesFindResult[]} results - An array of matched place records from the search.\n */\n\n/**\n * @typedef {object} OsNamesFindHeader\n * @property {string} uri - The query URI (usually same as search text).\n * @property {string} query - The original text query string passed to the API.\n * @property {string} format - The response format returned (e.g., \"JSON\").\n * @property {number} maxresults - The maximum number of results requested.\n * @property {number} offset - The offset used in the search results.\n * @property {number} totalresults - The total number of results that matched the query.\n * @property {string} filter - The original filter string passed to the API.\n */\n\n/**\n * @typedef {object} OsNamesFindGazetteerEntry\n * @property {string} ID - Unique identifier for the place/feature.\n * @property {string} NAMES_URI - A URI (identifier) for this named feature.\n * @property {string} NAME1 - Primary name of the feature.\n * @property {string} TYPE - General type classification of the feature.\n * @property {string} LOCAL_TYPE - Local or more specific type classification.\n * @property {number} GEOMETRY_X - Easting coordinate (British National Grid).\n * @property {number} GEOMETRY_Y - Northing coordinate (British National Grid).\n * @property {number} MOST_DETAIL_VIEW_RES - Most detailed resolution available.\n * @property {number} LEAST_DETAIL_VIEW_RES - Least detailed resolution available.\n * @property {number} [MBR_XMIN] - Minimum bounding box X (easting).\n * @property {number} [MBR_YMIN] - Minimum bounding box Y (northing).\n * @property {number} [MBR_XMAX] - Maximum bounding box X (easting).\n * @property {number} [MBR_YMAX] - Maximum bounding box Y (northing).\n * @property {string} [DISTRICT_BOROUGH] - (Optional) District borough.\n * @property {string} [DISTRICT_BOROUGH_URI] - (Optional) URI for the district borough.\n * @property {string} [DISTRICT_BOROUGH_TYPE] - (Optional) Type of the district borough.\n * @property {string} [POSTCODE_DISTRICT] - (Optional) Postcode district.\n * @property {string} [POSTCODE_DISTRICT_URI] - (Optional) URI for the postcode district.\n * @property {string} [POPULATED_PLACE] - (Optional) Name of associated populated place.\n * @property {string} [POPULATED_PLACE_URI] - (Optional) URI of populated place.\n * @property {string} [POPULATED_PLACE_TYPE] - (Optional) Type of populated place.\n * @property {string} [COUNTY_UNITARY] - (Optional) County or unitary authority name.\n * @property {string} [COUNTY_UNITARY_URI] - (Optional) URI for county/unitary authority.\n * @property {string} [COUNTY_UNITARY_TYPE] - (Optional) Classification of county/unitary authority.\n * @property {string} [REGION] - (Optional) Region name.\n * @property {string} [REGION_URI] - (Optional) URI for region.\n * @property {string} [COUNTRY] - (Optional) Country name.\n * @property {string} [COUNTRY_URI] - (Optional) URI for country.\n */\n\n/**\n * OS names GAZETTEER_ENTRY response\n * @typedef {object} OsNamesFindResult\n * @property {OsNamesFindGazetteerEntry} GAZETTEER_ENTRY - Gazetteer entry\n */\n\n/**\n * @import { Request } from '@hapi/hapi'\n */\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA","ignoreList":[]}
@@ -34,6 +34,7 @@ export interface RouteConfig {
34
34
  saveAndExit?: PluginOptions['saveAndExit'];
35
35
  cacheServiceCreator?: (server: Server) => CacheService;
36
36
  ordnanceSurveyApiKey?: string;
37
+ ordnanceSurveyApiSecret?: string;
37
38
  }
38
39
  export interface OutputService {
39
40
  submit: (context: FormContext, request: FormRequestPayload, model: FormModel, emailAddress: string, items: DetailItem[], submitResponse: SubmitResponsePayload, formMetadata?: FormMetadata) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../../src/server/types.ts"],"sourcesContent":["import {\n type FormDefinition,\n type FormMetadata,\n type SubmitPayload,\n type SubmitResponsePayload\n} from '@defra/forms-model'\nimport { type Server } from '@hapi/hapi'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n type FormContext,\n type OnRequestCallback,\n type PluginOptions,\n type PreparePageEventRequestOptions\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequestPayload,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\n\nexport interface FormsService {\n getFormMetadata: (slug: string) => Promise<FormMetadata>\n getFormMetadataById: (id: string) => Promise<FormMetadata>\n getFormDefinition: (\n id: string,\n state: FormStatus\n ) => Promise<FormDefinition | undefined>\n}\n\nexport interface FormSubmissionService {\n persistFiles: (\n files: { fileId: string; initiatedRetrievalKey: string }[],\n persistedRetrievalKey: string\n ) => Promise<object>\n submit: (data: SubmitPayload) => Promise<SubmitResponsePayload | undefined>\n}\n\nexport interface Services {\n formsService: FormsService\n formSubmissionService: FormSubmissionService\n outputService: OutputService\n}\n\nexport interface RouteConfig {\n formFileName?: string\n formFilePath?: string\n enforceCsrf?: boolean\n services?: Services\n controllers?: Record<string, typeof PageController>\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n saveAndExit?: PluginOptions['saveAndExit']\n cacheServiceCreator?: (server: Server) => CacheService\n ordnanceSurveyApiKey?: string\n}\n\nexport interface OutputService {\n submit: (\n context: FormContext,\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload,\n formMetadata?: FormMetadata\n ) => Promise<void>\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../src/server/types.ts"],"sourcesContent":["import {\n type FormDefinition,\n type FormMetadata,\n type SubmitPayload,\n type SubmitResponsePayload\n} from '@defra/forms-model'\nimport { type Server } from '@hapi/hapi'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItem } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport {\n type FormContext,\n type OnRequestCallback,\n type PluginOptions,\n type PreparePageEventRequestOptions\n} from '~/src/server/plugins/engine/types.js'\nimport {\n type FormRequestPayload,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\n\nexport interface FormsService {\n getFormMetadata: (slug: string) => Promise<FormMetadata>\n getFormMetadataById: (id: string) => Promise<FormMetadata>\n getFormDefinition: (\n id: string,\n state: FormStatus\n ) => Promise<FormDefinition | undefined>\n}\n\nexport interface FormSubmissionService {\n persistFiles: (\n files: { fileId: string; initiatedRetrievalKey: string }[],\n persistedRetrievalKey: string\n ) => Promise<object>\n submit: (data: SubmitPayload) => Promise<SubmitResponsePayload | undefined>\n}\n\nexport interface Services {\n formsService: FormsService\n formSubmissionService: FormSubmissionService\n outputService: OutputService\n}\n\nexport interface RouteConfig {\n formFileName?: string\n formFilePath?: string\n enforceCsrf?: boolean\n services?: Services\n controllers?: Record<string, typeof PageController>\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n saveAndExit?: PluginOptions['saveAndExit']\n cacheServiceCreator?: (server: Server) => CacheService\n ordnanceSurveyApiKey?: string\n ordnanceSurveyApiSecret?: string\n}\n\nexport interface OutputService {\n submit: (\n context: FormContext,\n request: FormRequestPayload,\n model: FormModel,\n emailAddress: string,\n items: DetailItem[],\n submitResponse: SubmitResponsePayload,\n formMetadata?: FormMetadata\n ) => Promise<void>\n}\n"],"mappings":"","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "4.0.42",
3
+ "version": "4.0.43",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -138,10 +138,18 @@ export function makeTileRequestTransformer(apiPath) {
138
138
  * @param {string} resourceType - the resource type
139
139
  */
140
140
  return function transformTileRequest(url, resourceType) {
141
- // Only proxy OS API requests that don't already have a key
142
- if (resourceType !== 'Style' && url.startsWith('https://api.os.uk')) {
143
- const urlObj = new URL(url)
144
- if (!urlObj.searchParams.has('key')) {
141
+ if (url.startsWith('https://api.os.uk')) {
142
+ if (resourceType === 'Tile') {
143
+ return {
144
+ url: url.replace(
145
+ 'https://api.os.uk/maps/vector/v1/vts',
146
+ `${window.location.origin}${apiPath}`
147
+ ),
148
+ headers: {}
149
+ }
150
+ }
151
+
152
+ if (resourceType !== 'Style') {
145
153
  return {
146
154
  url: `${apiPath}/map-proxy?url=${encodeURIComponent(url)}`,
147
155
  headers: {}
@@ -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
  /**
@@ -38,7 +38,8 @@ export const plugin = {
38
38
  viewContext,
39
39
  preparePageEventRequestOptions,
40
40
  onRequest,
41
- ordnanceSurveyApiKey
41
+ ordnanceSurveyApiKey,
42
+ ordnanceSurveyApiSecret
42
43
  } = options
43
44
 
44
45
  const cacheService =
@@ -58,12 +59,13 @@ export const plugin = {
58
59
  })
59
60
  }
60
61
 
61
- // Register the maps plugin only if we have an OS api key
62
- if (ordnanceSurveyApiKey) {
62
+ // Register the maps plugin only if we have an OS api key & secret
63
+ if (ordnanceSurveyApiKey && ordnanceSurveyApiSecret) {
63
64
  await server.register({
64
65
  plugin: mapPlugin,
65
66
  options: {
66
- ordnanceSurveyApiKey
67
+ ordnanceSurveyApiKey,
68
+ ordnanceSurveyApiSecret
67
69
  }
68
70
  })
69
71
  }
@@ -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 {
@@ -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
+ */
@@ -1,9 +1,14 @@
1
1
  import { resolve } from 'node:path'
2
2
 
3
+ import { StatusCodes } from 'http-status-codes'
3
4
  import Joi from 'joi'
4
5
 
6
+ import { getAccessToken } from '~/src/server/plugins/map/routes/get-os-token.js'
5
7
  import { find, nearest } from '~/src/server/plugins/map/service.js'
6
- import { request as httpRequest } from '~/src/server/services/httpService.js'
8
+ import {
9
+ get,
10
+ request as httpRequest
11
+ } from '~/src/server/services/httpService.js'
7
12
 
8
13
  /**
9
14
  * Gets the map support routes
@@ -11,17 +16,18 @@ import { request as httpRequest } from '~/src/server/services/httpService.js'
11
16
  */
12
17
  export function getRoutes(options) {
13
18
  return [
19
+ mapStyleResourceRoutes(),
14
20
  mapProxyRoute(options),
21
+ tileProxyRoute(options),
15
22
  geocodeProxyRoute(options),
16
- reverseGeocodeProxyRoute(options),
17
- ...tileRoutes()
23
+ reverseGeocodeProxyRoute(options)
18
24
  ]
19
25
  }
20
26
 
21
27
  /**
22
28
  * Proxies ordnance survey requests from the front end to api.os.com
23
- * Used for VTS map tiles, sprites and fonts by forwarding on the request
24
- * and adding the apikey and optionally an SRS (spatial reference system)
29
+ * Used for the VTS map source by forwarding on the request
30
+ * and adding the auth token and SRS (spatial reference system)
25
31
  * @param {MapConfiguration} options - the map options
26
32
  * @returns {ServerRoute<MapProxyGetRequestRefs>}
27
33
  */
@@ -32,14 +38,15 @@ function mapProxyRoute(options) {
32
38
  handler: async (request, h) => {
33
39
  const { query } = request
34
40
  const targetUrl = new URL(decodeURIComponent(query.url))
41
+ const token = await getAccessToken(options)
35
42
 
36
- // Add API key server-side and set SRS
37
- targetUrl.searchParams.set('key', options.ordnanceSurveyApiKey)
38
- if (!targetUrl.searchParams.has('srs')) {
39
- targetUrl.searchParams.set('srs', '3857')
40
- }
43
+ targetUrl.searchParams.set('srs', '3857')
41
44
 
42
- const proxyResponse = await httpRequest('get', targetUrl.toString())
45
+ const proxyResponse = await httpRequest('get', targetUrl.toString(), {
46
+ headers: {
47
+ Authorization: `Bearer ${token}`
48
+ }
49
+ })
43
50
  const buffer = proxyResponse.payload
44
51
  const contentType = proxyResponse.res.headers['content-type']
45
52
  const response = h.response(buffer)
@@ -63,7 +70,44 @@ function mapProxyRoute(options) {
63
70
  }
64
71
 
65
72
  /**
66
- * Proxies ordnance survey geocode requests from the front end to api.os.com
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
67
111
  * Used for the gazzeteer address lookup to find name from query strings like postcode and place names
68
112
  * @param {MapConfiguration} options - the map options
69
113
  * @returns {ServerRoute<MapGeocodeGetRequestRefs>}
@@ -91,7 +135,7 @@ function geocodeProxyRoute(options) {
91
135
  }
92
136
 
93
137
  /**
94
- * Proxies ordnance survey reverse geocode requests from the front end to api.os.com
138
+ * Proxies ordnance survey reverse geocode requests from the front end to api.os.uk
95
139
  * Used to find name from easting and northing points.
96
140
  * N.B this endpoint is currently not used by the front end but will be soon in "maps V2"
97
141
  * @param {MapConfiguration} options - the map options
@@ -124,20 +168,22 @@ function reverseGeocodeProxyRoute(options) {
124
168
  }
125
169
  }
126
170
 
127
- function tileRoutes() {
128
- return [
129
- {
130
- method: 'GET',
131
- path: '/api/maps/vts/{path*}',
132
- options: {
133
- handler: {
134
- directory: {
135
- path: resolve(import.meta.dirname, './vts')
136
- }
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')
137
183
  }
138
184
  }
139
185
  }
140
- ]
186
+ }
141
187
  }
142
188
 
143
189
  /**
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @typedef {{
3
3
  * ordnanceSurveyApiKey: string
4
+ * ordnanceSurveyApiSecret: string
4
5
  * }} MapConfiguration
5
6
  */
6
7
 
@@ -55,6 +55,7 @@ export interface RouteConfig {
55
55
  saveAndExit?: PluginOptions['saveAndExit']
56
56
  cacheServiceCreator?: (server: Server) => CacheService
57
57
  ordnanceSurveyApiKey?: string
58
+ ordnanceSurveyApiSecret?: string
58
59
  }
59
60
 
60
61
  export interface OutputService {