@defra/forms-engine-plugin 4.10.0 → 4.11.0
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.
- package/.public/javascripts/shared.min.js +1 -1
- package/.public/javascripts/shared.min.js.map +1 -1
- package/.server/client/javascripts/geospatial-map.js +34 -3
- package/.server/client/javascripts/geospatial-map.js.map +1 -1
- package/.server/client/javascripts/map.d.ts +0 -6
- package/.server/client/javascripts/map.js +1 -14
- package/.server/client/javascripts/map.js.map +1 -1
- package/.server/server/plugins/engine/components/GeospatialField.d.ts +1 -0
- package/.server/server/plugins/engine/components/GeospatialField.js +6 -2
- package/.server/server/plugins/engine/components/GeospatialField.js.map +1 -1
- package/.server/server/plugins/engine/components/helpers/geospatial.d.ts +2 -1
- package/.server/server/plugins/engine/components/helpers/geospatial.js +32 -1
- package/.server/server/plugins/engine/components/helpers/geospatial.js.map +1 -1
- package/.server/server/plugins/engine/components/helpers/geospatial.test.js +21 -1
- package/.server/server/plugins/engine/components/helpers/geospatial.test.js.map +1 -1
- package/.server/server/plugins/engine/views/components/geospatialfield.html +1 -1
- package/.server/server/plugins/map/routes/index.d.ts +9 -1
- package/.server/server/plugins/map/routes/index.js +51 -2
- package/.server/server/plugins/map/routes/index.js.map +1 -1
- package/.server/server/plugins/map/routes/vts/countries.geojson +39285 -0
- package/.server/server/plugins/map/types.d.ts +23 -1
- package/.server/server/plugins/map/types.js +14 -1
- package/.server/server/plugins/map/types.js.map +1 -1
- package/package.json +10 -8
- package/src/client/javascripts/geospatial-map.js +39 -3
- package/src/client/javascripts/map.js +1 -14
- package/src/server/plugins/engine/components/GeospatialField.ts +8 -2
- package/src/server/plugins/engine/components/helpers/geospatial.ts +48 -2
- package/src/server/plugins/engine/views/components/geospatialfield.html +1 -1
- package/src/server/plugins/map/routes/index.js +58 -2
- package/src/server/plugins/map/routes/vts/countries.geojson +39285 -0
- package/src/server/plugins/map/types.js +14 -1
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
import { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model';
|
|
1
2
|
import Bourne from '@hapi/bourne';
|
|
3
|
+
import { booleanWithin } from '@turf/boolean-within';
|
|
2
4
|
import JoiBase from 'joi';
|
|
5
|
+
import { countries } from "../../../map/routes/index.js";
|
|
6
|
+
const countriesDesc = {
|
|
7
|
+
[GeospatialFieldOptionsCountryEnum.England]: 'England',
|
|
8
|
+
[GeospatialFieldOptionsCountryEnum.NorthernIreland]: 'Northern Ireland',
|
|
9
|
+
[GeospatialFieldOptionsCountryEnum.Scotland]: 'Scotland',
|
|
10
|
+
[GeospatialFieldOptionsCountryEnum.Wales]: 'Wales'
|
|
11
|
+
};
|
|
3
12
|
const Joi = JoiBase.extend({
|
|
4
13
|
type: 'array',
|
|
5
14
|
base: JoiBase.array(),
|
|
@@ -63,7 +72,29 @@ const featureSchema = Joi.object().keys({
|
|
|
63
72
|
properties: featurePropertiesSchema,
|
|
64
73
|
geometry: featureGeometrySchema
|
|
65
74
|
});
|
|
66
|
-
|
|
75
|
+
const geospatialSchema = Joi.array().items(featureSchema).unique('id').required();
|
|
76
|
+
export function getGeospatialSchema(country) {
|
|
77
|
+
if (!country) {
|
|
78
|
+
return geospatialSchema;
|
|
79
|
+
}
|
|
80
|
+
const validateCountryBounds = (value, helpers) => {
|
|
81
|
+
const countryFeature = countries.features.find(feature => feature.id === country);
|
|
82
|
+
if (!countryFeature) {
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
const result = booleanWithin(value, countryFeature);
|
|
87
|
+
if (!result) {
|
|
88
|
+
return helpers.error('any.custom', {
|
|
89
|
+
country: countriesDesc[country]
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
94
|
+
return value;
|
|
95
|
+
};
|
|
96
|
+
return Joi.array().items(featureSchema.custom(validateCountryBounds)).unique('id').required();
|
|
97
|
+
}
|
|
67
98
|
|
|
68
99
|
/**
|
|
69
100
|
* @import { CustomHelpers } from 'joi'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geospatial.js","names":["Bourne","JoiBase","Joi","extend","type","base","array","messages","coerce","from","method","value","helpers","trim","undefined","parse","result","errors","error","coordinatesSchema","items","number","required","featurePropertiesSchema","object","keys","description","string","coordinateGridReference","centroidGridReference","featureGeometrySchema","valid","coordinates","when","switch","is","then","min","featureSchema","id","properties","geometry","geospatialSchema","unique"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.ts"],"sourcesContent":["import Bourne from '@hapi/bourne'\nimport JoiBase from 'joi'\n\nimport {\n type Coordinates,\n type Feature,\n type FeatureProperties,\n type Geometry\n} from '~/src/server/plugins/engine/types.js'\n\nconst Joi = JoiBase.extend({\n type: 'array',\n base: JoiBase.array(),\n messages: {\n 'object.invalidjson': '{{#label}} must be a valid json array string'\n },\n coerce: {\n from: 'string',\n method(value, helpers) {\n if (typeof value === 'string') {\n if (value.trim() === '') {\n return {\n value: undefined\n }\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n return { value: Bourne.parse(value) }\n } catch {\n const result = {\n value,\n errors: [helpers.error('object.invalidjson')]\n }\n\n return result\n }\n } else {\n return {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value\n }\n }\n }\n }\n}) as JoiBase.Root\n\nconst coordinatesSchema = Joi.array<Coordinates[]>()\n .items(Joi.number().required(), Joi.number().required())\n .required()\n\nconst featurePropertiesSchema = Joi.object<FeatureProperties>()\n .keys({\n description: Joi.string().required(),\n coordinateGridReference: Joi.string().required(),\n centroidGridReference: Joi.string().required()\n })\n .required()\n\nconst featureGeometrySchema = Joi.object<Geometry>().keys({\n type: Joi.string().valid('Point', 'LineString', 'Polygon').required(),\n coordinates: Joi.array()\n .when('type', {\n switch: [\n { is: 'Point', then: coordinatesSchema },\n {\n is: 'LineString',\n then: Joi.array().items(coordinatesSchema).min(2)\n },\n {\n is: 'Polygon',\n then: Joi.array().items(Joi.array().items(coordinatesSchema).min(3))\n }\n ]\n })\n .required()\n})\n\nconst featureSchema = Joi.object<Feature>().keys({\n id: Joi.string().required(),\n type: Joi.string().valid('Feature').required(),\n properties: featurePropertiesSchema,\n geometry: featureGeometrySchema\n})\n\
|
|
1
|
+
{"version":3,"file":"geospatial.js","names":["GeospatialFieldOptionsCountryEnum","Bourne","booleanWithin","JoiBase","countries","countriesDesc","England","NorthernIreland","Scotland","Wales","Joi","extend","type","base","array","messages","coerce","from","method","value","helpers","trim","undefined","parse","result","errors","error","coordinatesSchema","items","number","required","featurePropertiesSchema","object","keys","description","string","coordinateGridReference","centroidGridReference","featureGeometrySchema","valid","coordinates","when","switch","is","then","min","featureSchema","id","properties","geometry","geospatialSchema","unique","getGeospatialSchema","country","validateCountryBounds","countryFeature","features","find","feature","custom"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.ts"],"sourcesContent":["import {\n GeospatialFieldOptionsCountryEnum,\n type GeospatialFieldOptionsCountry\n} from '@defra/forms-model'\nimport Bourne from '@hapi/bourne'\nimport { booleanWithin } from '@turf/boolean-within'\nimport JoiBase, { type CustomValidator } from 'joi'\n\nimport {\n type Coordinates,\n type Feature,\n type FeatureProperties,\n type Geometry\n} from '~/src/server/plugins/engine/types.js'\nimport { countries } from '~/src/server/plugins/map/routes/index.js'\n\nconst countriesDesc: Record<GeospatialFieldOptionsCountryEnum, string> = {\n [GeospatialFieldOptionsCountryEnum.England]: 'England',\n [GeospatialFieldOptionsCountryEnum.NorthernIreland]: 'Northern Ireland',\n [GeospatialFieldOptionsCountryEnum.Scotland]: 'Scotland',\n [GeospatialFieldOptionsCountryEnum.Wales]: 'Wales'\n}\n\nconst Joi = JoiBase.extend({\n type: 'array',\n base: JoiBase.array(),\n messages: {\n 'object.invalidjson': '{{#label}} must be a valid json array string'\n },\n coerce: {\n from: 'string',\n method(value, helpers) {\n if (typeof value === 'string') {\n if (value.trim() === '') {\n return {\n value: undefined\n }\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n return { value: Bourne.parse(value) }\n } catch {\n const result = {\n value,\n errors: [helpers.error('object.invalidjson')]\n }\n\n return result\n }\n } else {\n return {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value\n }\n }\n }\n }\n}) as JoiBase.Root\n\nconst coordinatesSchema = Joi.array<Coordinates[]>()\n .items(Joi.number().required(), Joi.number().required())\n .required()\n\nconst featurePropertiesSchema = Joi.object<FeatureProperties>()\n .keys({\n description: Joi.string().required(),\n coordinateGridReference: Joi.string().required(),\n centroidGridReference: Joi.string().required()\n })\n .required()\n\nconst featureGeometrySchema = Joi.object<Geometry>().keys({\n type: Joi.string().valid('Point', 'LineString', 'Polygon').required(),\n coordinates: Joi.array()\n .when('type', {\n switch: [\n { is: 'Point', then: coordinatesSchema },\n {\n is: 'LineString',\n then: Joi.array().items(coordinatesSchema).min(2)\n },\n {\n is: 'Polygon',\n then: Joi.array().items(Joi.array().items(coordinatesSchema).min(3))\n }\n ]\n })\n .required()\n})\n\nconst featureSchema = Joi.object<Feature>().keys({\n id: Joi.string().required(),\n type: Joi.string().valid('Feature').required(),\n properties: featurePropertiesSchema,\n geometry: featureGeometrySchema\n})\n\nconst geospatialSchema = Joi.array<Feature[]>()\n .items(featureSchema)\n .unique('id')\n .required()\n\nexport function getGeospatialSchema(country?: GeospatialFieldOptionsCountry) {\n if (!country) {\n return geospatialSchema\n }\n\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countries.features.find(\n (feature) => feature.id === country\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const result = booleanWithin(value, countryFeature)\n\n if (!result) {\n return helpers.error('any.custom', {\n country: countriesDesc[country as GeospatialFieldOptionsCountryEnum]\n })\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return Joi.array<Feature[]>()\n .items(featureSchema.custom(validateCountryBounds))\n .unique('id')\n .required()\n}\n\n/**\n * @import { CustomHelpers } from 'joi'\n */\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model';
|
|
1
2
|
import { validState } from "./__stubs__/geospatial.js";
|
|
2
|
-
import {
|
|
3
|
+
import { getGeospatialSchema } from "./geospatial.js";
|
|
4
|
+
const geospatialSchema = getGeospatialSchema();
|
|
3
5
|
describe('Geospatial validation helpers', () => {
|
|
4
6
|
test('it should not have errors for valid geojson object', () => {
|
|
5
7
|
const result = geospatialSchema.validate(validState);
|
|
@@ -38,5 +40,23 @@ describe('Geospatial validation helpers', () => {
|
|
|
38
40
|
expect(result.error).toBeDefined();
|
|
39
41
|
expect(result.value).toBeUndefined();
|
|
40
42
|
});
|
|
43
|
+
test('it should be valid inside country bounds', () => {
|
|
44
|
+
const schema = getGeospatialSchema(GeospatialFieldOptionsCountryEnum.England);
|
|
45
|
+
expect(schema.validate(validState).error).toBeUndefined();
|
|
46
|
+
expect(schema.validate(validState.slice(1)).error).toBeUndefined();
|
|
47
|
+
expect(schema.validate(validState.slice(2)).error).toBeUndefined();
|
|
48
|
+
expect(schema.validate(validState.slice(3)).error).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
test('it should be invalid outside country bounds', () => {
|
|
51
|
+
const schema = getGeospatialSchema(GeospatialFieldOptionsCountryEnum.Scotland);
|
|
52
|
+
expect(schema.validate(validState).error).toBeDefined();
|
|
53
|
+
expect(schema.validate(validState.slice(1)).error).toBeDefined();
|
|
54
|
+
expect(schema.validate(validState.slice(2)).error).toBeDefined();
|
|
55
|
+
expect(schema.validate(validState.slice(3)).error).toBeDefined();
|
|
56
|
+
});
|
|
57
|
+
test('it should be valid with no country bounds', () => {
|
|
58
|
+
const schema = getGeospatialSchema();
|
|
59
|
+
expect(schema.validate(validState).error).toBeUndefined();
|
|
60
|
+
});
|
|
41
61
|
});
|
|
42
62
|
//# sourceMappingURL=geospatial.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geospatial.test.js","names":["validState","geospatialSchema","describe","test","result","validate","expect","error","toBeUndefined","value","toBeDefined","toHaveLength","JSON","stringify","toBe","toEqual"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.test.js"],"sourcesContent":["import { validState } from '~/src/server/plugins/engine/components/helpers/__stubs__/geospatial.js'\nimport {
|
|
1
|
+
{"version":3,"file":"geospatial.test.js","names":["GeospatialFieldOptionsCountryEnum","validState","getGeospatialSchema","geospatialSchema","describe","test","result","validate","expect","error","toBeUndefined","value","toBeDefined","toHaveLength","JSON","stringify","toBe","toEqual","schema","England","slice","Scotland"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.test.js"],"sourcesContent":["import { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model'\n\nimport { validState } from '~/src/server/plugins/engine/components/helpers/__stubs__/geospatial.js'\nimport { getGeospatialSchema } from '~/src/server/plugins/engine/components/helpers/geospatial.js'\n\nconst geospatialSchema = getGeospatialSchema()\n\ndescribe('Geospatial validation helpers', () => {\n test('it should not have errors for valid geojson object', () => {\n const result = geospatialSchema.validate(validState)\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBeDefined()\n expect(result.value).toHaveLength(4)\n })\n\n test('it should not have errors for valid geojson string', () => {\n const result = geospatialSchema.validate(JSON.stringify(validState))\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBeDefined()\n expect(result.value).toHaveLength(4)\n })\n\n test('it should have errors for invalid json string', () => {\n const result = geospatialSchema.validate('{')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBe('{')\n })\n\n test('it should have errors for invalid geojson string', () => {\n const result = geospatialSchema.validate('[')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBe('[')\n })\n\n test('it should validate an empty array', () => {\n const result = geospatialSchema.validate('[]')\n\n expect(result.error).toBeUndefined()\n expect(result.value).toEqual([])\n })\n\n test('it should not validate an empty object', () => {\n const result = geospatialSchema.validate('{}')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBeUndefined()\n })\n\n test('it should validate an empty string', () => {\n const result = geospatialSchema.validate('')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBeUndefined()\n })\n\n test('it should be valid inside country bounds', () => {\n const schema = getGeospatialSchema(\n GeospatialFieldOptionsCountryEnum.England\n )\n\n expect(schema.validate(validState).error).toBeUndefined()\n expect(schema.validate(validState.slice(1)).error).toBeUndefined()\n expect(schema.validate(validState.slice(2)).error).toBeUndefined()\n expect(schema.validate(validState.slice(3)).error).toBeUndefined()\n })\n\n test('it should be invalid outside country bounds', () => {\n const schema = getGeospatialSchema(\n GeospatialFieldOptionsCountryEnum.Scotland\n )\n\n expect(schema.validate(validState).error).toBeDefined()\n expect(schema.validate(validState.slice(1)).error).toBeDefined()\n expect(schema.validate(validState.slice(2)).error).toBeDefined()\n expect(schema.validate(validState.slice(3)).error).toBeDefined()\n })\n\n test('it should be valid with no country bounds', () => {\n const schema = getGeospatialSchema()\n\n expect(schema.validate(validState).error).toBeUndefined()\n })\n})\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{% from "govuk/components/textarea/macro.njk" import govukTextarea %}
|
|
2
2
|
|
|
3
3
|
{% macro GeospatialField(component) %}
|
|
4
|
-
<div class="app-geospatial-field">
|
|
4
|
+
<div class="app-geospatial-field" data-country="{{component.model.country}}">
|
|
5
5
|
{{ govukTextarea(component.model) }}
|
|
6
6
|
</div>
|
|
7
7
|
{% endmacro %}
|
|
@@ -2,9 +2,17 @@
|
|
|
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>)[];
|
|
5
|
+
export function getRoutes(options: MapConfiguration): (ServerRoute<MapProxyGetRequestRefs> | ServerRoute<MapGeocodeGetRequestRefs> | ServerRoute<MapReverseGeocodeGetRequestRefs> | ServerRoute<GeospatialCountriesGetRequestRefs>)[];
|
|
6
|
+
/**
|
|
7
|
+
* @type {FeatureCollection}
|
|
8
|
+
*/
|
|
9
|
+
export const countries: FeatureCollection;
|
|
10
|
+
export const countrySchema: Joi.StringSchema<string>;
|
|
6
11
|
import type { MapConfiguration } from '../../../../server/plugins/map/types.js';
|
|
7
12
|
import type { MapProxyGetRequestRefs } from '../../../../server/plugins/map/types.js';
|
|
8
13
|
import type { ServerRoute } from '@hapi/hapi';
|
|
9
14
|
import type { MapGeocodeGetRequestRefs } from '../../../../server/plugins/map/types.js';
|
|
10
15
|
import type { MapReverseGeocodeGetRequestRefs } from '../../../../server/plugins/map/types.js';
|
|
16
|
+
import type { GeospatialCountriesGetRequestRefs } from '../../../../server/plugins/map/types.js';
|
|
17
|
+
import type { FeatureCollection } from 'geojson';
|
|
18
|
+
import Joi from 'joi';
|
|
@@ -1,16 +1,26 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import { resolve } from 'node:path';
|
|
3
|
+
import { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model';
|
|
2
4
|
import { StatusCodes } from 'http-status-codes';
|
|
3
5
|
import Joi from 'joi';
|
|
4
6
|
import { getAccessToken } from "./get-os-token.js";
|
|
5
7
|
import { find, nearest } from "../service.js";
|
|
6
8
|
import { get, request as httpRequest } from "../../../services/httpService.js";
|
|
9
|
+
const filePath = resolve(import.meta.dirname, './vts/countries.geojson');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @type {FeatureCollection}
|
|
13
|
+
*/
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
15
|
+
export const countries = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
16
|
+
export const countrySchema = Joi.string().valid(...Object.values(GeospatialFieldOptionsCountryEnum));
|
|
7
17
|
|
|
8
18
|
/**
|
|
9
19
|
* Gets the map support routes
|
|
10
20
|
* @param {MapConfiguration} options - ordnance survey names api key
|
|
11
21
|
*/
|
|
12
22
|
export function getRoutes(options) {
|
|
13
|
-
return [mapStyleResourceRoutes(), mapProxyRoute(options), tileProxyRoute(options), geocodeProxyRoute(options), reverseGeocodeProxyRoute(options)];
|
|
23
|
+
return [mapStyleResourceRoutes(), mapProxyRoute(options), tileProxyRoute(options), geocodeProxyRoute(options), reverseGeocodeProxyRoute(options), getGeospatialCountries()];
|
|
14
24
|
}
|
|
15
25
|
|
|
16
26
|
/**
|
|
@@ -166,8 +176,47 @@ function mapStyleResourceRoutes() {
|
|
|
166
176
|
};
|
|
167
177
|
}
|
|
168
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Resource routes to return sprites and glyphs
|
|
181
|
+
* @returns {ServerRoute<GeospatialCountriesGetRequestRefs>}
|
|
182
|
+
*/
|
|
183
|
+
function getGeospatialCountries() {
|
|
184
|
+
return {
|
|
185
|
+
method: 'GET',
|
|
186
|
+
path: '/api/maps/countries.geojson',
|
|
187
|
+
handler: request => {
|
|
188
|
+
const {
|
|
189
|
+
omit,
|
|
190
|
+
only
|
|
191
|
+
} = request.query;
|
|
192
|
+
if (omit) {
|
|
193
|
+
return {
|
|
194
|
+
...countries,
|
|
195
|
+
features: countries.features.filter(feature => feature.id !== omit)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (only) {
|
|
199
|
+
return {
|
|
200
|
+
...countries,
|
|
201
|
+
features: countries.features.filter(feature => feature.id === only)
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
return countries;
|
|
205
|
+
},
|
|
206
|
+
options: {
|
|
207
|
+
validate: {
|
|
208
|
+
query: Joi.object().keys({
|
|
209
|
+
omit: countrySchema.optional(),
|
|
210
|
+
only: countrySchema.optional()
|
|
211
|
+
}).optional()
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
169
217
|
/**
|
|
170
218
|
* @import { ServerRoute } from '@hapi/hapi'
|
|
171
|
-
* @import {
|
|
219
|
+
* @import { FeatureCollection } from 'geojson'
|
|
220
|
+
* @import { MapConfiguration, MapProxyGetRequestRefs, MapGeocodeGetRequestRefs, MapReverseGeocodeGetRequestRefs, GeospatialCountriesGetRequestRefs } from '../../../../server/plugins/map/types.js'
|
|
172
221
|
*/
|
|
173
222
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
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","getBuffer","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 getBuffer = /** @type {typeof get<Buffer>} */ (get)\n\n const { payload, res } = await getBuffer(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/x-protobuf'\n },\n json: false,\n gunzip: true\n })\n\n if (res.statusCode && res.statusCode !== StatusCodes.OK.valueOf()) {\n return h.response('Tile fetch failed').code(res.statusCode)\n }\n\n return h\n .response(payload)\n .type('application/x-protobuf')\n .header('Cache-Control', 'public, max-age=86400')\n }\n }\n}\n\n/**\n * Proxies ordnance survey geocode requests from the front end to api.os.uk\n * Used for the gazzeteer address lookup to find name from query strings like postcode and place names\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapGeocodeGetRequestRefs>}\n */\nfunction geocodeProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/geocode-proxy',\n async handler(request, _h) {\n const { query } = request\n const data = await find(query.query, options.ordnanceSurveyApiKey)\n\n return data\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n query: Joi.string().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * Proxies ordnance survey reverse geocode requests from the front end to api.os.uk\n * Used to find name from easting and northing points.\n * N.B this endpoint is currently not used by the front end but will be soon in \"maps V2\"\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapReverseGeocodeGetRequestRefs>}\n */\nfunction reverseGeocodeProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/reverse-geocode-proxy',\n async handler(request, _h) {\n const { query } = request\n const data = await nearest(\n query.easting,\n query.northing,\n options.ordnanceSurveyApiKey\n )\n\n return data\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n easting: Joi.number().required(),\n northing: Joi.number().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapStyleResourceRoutes() {\n return {\n method: 'GET',\n path: '/api/maps/vts/{path*}',\n options: {\n handler: {\n directory: {\n path: resolve(import.meta.dirname, './vts')\n }\n }\n }\n }\n}\n\n/**\n * @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":["fs","resolve","GeospatialFieldOptionsCountryEnum","StatusCodes","Joi","getAccessToken","find","nearest","get","request","httpRequest","filePath","import","meta","dirname","countries","JSON","parse","readFileSync","countrySchema","string","valid","Object","values","getRoutes","options","mapStyleResourceRoutes","mapProxyRoute","tileProxyRoute","geocodeProxyRoute","reverseGeocodeProxyRoute","getGeospatialCountries","method","path","handler","h","query","targetUrl","URL","decodeURIComponent","url","token","searchParams","set","proxyResponse","toString","headers","Authorization","buffer","payload","contentType","res","response","type","validate","object","keys","required","optional","z","y","x","params","getBuffer","Accept","json","gunzip","statusCode","OK","valueOf","code","header","_h","data","ordnanceSurveyApiKey","easting","northing","number","directory","omit","only","features","filter","feature","id"],"sources":["../../../../../src/server/plugins/map/routes/index.js"],"sourcesContent":["import fs from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model'\nimport { StatusCodes } from 'http-status-codes'\nimport Joi from 'joi'\n\nimport { getAccessToken } from '~/src/server/plugins/map/routes/get-os-token.js'\nimport { find, nearest } from '~/src/server/plugins/map/service.js'\nimport {\n get,\n request as httpRequest\n} from '~/src/server/services/httpService.js'\n\nconst filePath = resolve(import.meta.dirname, './vts/countries.geojson')\n\n/**\n * @type {FeatureCollection}\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\nexport const countries = JSON.parse(fs.readFileSync(filePath, 'utf8'))\n\nexport const countrySchema = Joi.string().valid(\n ...Object.values(GeospatialFieldOptionsCountryEnum)\n)\n\n/**\n * Gets the map support routes\n * @param {MapConfiguration} options - ordnance survey names api key\n */\nexport function getRoutes(options) {\n return [\n mapStyleResourceRoutes(),\n mapProxyRoute(options),\n tileProxyRoute(options),\n geocodeProxyRoute(options),\n reverseGeocodeProxyRoute(options),\n getGeospatialCountries()\n ]\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.com\n * Used for the VTS map source by forwarding on the request\n * and adding the auth token and SRS (spatial reference system)\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/map-proxy',\n handler: async (request, h) => {\n const { query } = request\n const targetUrl = new URL(decodeURIComponent(query.url))\n const token = await getAccessToken(options)\n\n targetUrl.searchParams.set('srs', '3857')\n\n const proxyResponse = await httpRequest('get', targetUrl.toString(), {\n headers: {\n Authorization: `Bearer ${token}`\n }\n })\n const buffer = proxyResponse.payload\n const contentType = proxyResponse.res.headers['content-type']\n const response = h.response(buffer)\n\n if (contentType) {\n response.type(contentType)\n }\n\n return response\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n url: Joi.string().required()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.uk\n * Used for VTS map tiles forwarding on the request and adding the auth token\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction tileProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/tile/{z}/{y}/{x}.pbf',\n handler: async (request, h) => {\n const { z, y, x } = request.params\n const token = await getAccessToken(options)\n\n const url = `https://api.os.uk/maps/vector/v1/vts/tile/${z}/${y}/${x}.pbf?srs=3857`\n\n const getBuffer = /** @type {typeof get<Buffer>} */ (get)\n\n const { payload, res } = await getBuffer(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/x-protobuf'\n },\n json: false,\n gunzip: true\n })\n\n if (res.statusCode && res.statusCode !== StatusCodes.OK.valueOf()) {\n return h.response('Tile fetch failed').code(res.statusCode)\n }\n\n return h\n .response(payload)\n .type('application/x-protobuf')\n .header('Cache-Control', 'public, max-age=86400')\n }\n }\n}\n\n/**\n * Proxies ordnance survey geocode requests from the front end to api.os.uk\n * Used for the gazzeteer address lookup to find name from query strings like postcode and place names\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapGeocodeGetRequestRefs>}\n */\nfunction geocodeProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/geocode-proxy',\n async handler(request, _h) {\n const { query } = request\n const data = await find(query.query, options.ordnanceSurveyApiKey)\n\n return data\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n query: Joi.string().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * Proxies ordnance survey reverse geocode requests from the front end to api.os.uk\n * Used to find name from easting and northing points.\n * N.B this endpoint is currently not used by the front end but will be soon in \"maps V2\"\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapReverseGeocodeGetRequestRefs>}\n */\nfunction reverseGeocodeProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/reverse-geocode-proxy',\n async handler(request, _h) {\n const { query } = request\n const data = await nearest(\n query.easting,\n query.northing,\n options.ordnanceSurveyApiKey\n )\n\n return data\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n easting: Joi.number().required(),\n northing: Joi.number().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapStyleResourceRoutes() {\n return {\n method: 'GET',\n path: '/api/maps/vts/{path*}',\n options: {\n handler: {\n directory: {\n path: resolve(import.meta.dirname, './vts')\n }\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<GeospatialCountriesGetRequestRefs>}\n */\nfunction getGeospatialCountries() {\n return {\n method: 'GET',\n path: '/api/maps/countries.geojson',\n handler: (request) => {\n const { omit, only } = request.query\n\n if (omit) {\n return {\n ...countries,\n features: countries.features.filter((feature) => feature.id !== omit)\n }\n }\n\n if (only) {\n return {\n ...countries,\n features: countries.features.filter((feature) => feature.id === only)\n }\n }\n\n return countries\n },\n options: {\n validate: {\n query: Joi.object()\n .keys({\n omit: countrySchema.optional(),\n only: countrySchema.optional()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * @import { ServerRoute } from '@hapi/hapi'\n * @import { FeatureCollection } from 'geojson'\n * @import { MapConfiguration, MapProxyGetRequestRefs, MapGeocodeGetRequestRefs, MapReverseGeocodeGetRequestRefs, GeospatialCountriesGetRequestRefs } from '~/src/server/plugins/map/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
|