From 986526fd1a0840e90d332f76f0ebe6acc18e42ea Mon Sep 17 00:00:00 2001 From: pmshaw15 Date: Tue, 21 Jan 2025 17:13:03 +0000 Subject: [PATCH 1/9] Initial commit to include additional data for results page --- config/.env-example | 6 ++++ config/__tests__/config.spec.js | 7 +++- config/index.js | 7 +++- config/schema.js | 7 +++- .../__mocks__/floodZonesByPolygonMock.js | 5 +++ .../__tests__/flood-zones-by-polygon.spec.js | 7 +--- .../services/agol/getRiversAndSeaDefended.js | 34 +++++++++++++++++++ .../getRiversAndSeaDefendedClimateChange.js | 34 +++++++++++++++++++ .../agol/getRiversAndSeaUndefended.js | 33 ++++++++++++++++++ .../getRiversAndSeaUndefendedClimateChange.js | 33 ++++++++++++++++++ server/services/agol/getSurfaceWater.js | 32 +++++++++++++++++ server/services/agol/index.js | 22 +++++++++++- server/services/flood-zones-by-polygon.js | 14 ++++++-- server/views/results.html | 17 ++++++++++ 14 files changed, 245 insertions(+), 13 deletions(-) create mode 100644 server/services/agol/getRiversAndSeaDefended.js create mode 100644 server/services/agol/getRiversAndSeaDefendedClimateChange.js create mode 100644 server/services/agol/getRiversAndSeaUndefended.js create mode 100644 server/services/agol/getRiversAndSeaUndefendedClimateChange.js create mode 100644 server/services/agol/getSurfaceWater.js diff --git a/config/.env-example b/config/.env-example index 36db0a8e..592f0110 100644 --- a/config/.env-example +++ b/config/.env-example @@ -30,6 +30,12 @@ agolCustomerTeamEndPoint=/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/Fe agolLocalAuthorityEndPoint=/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/1 agolIsEnglandEndPoint=/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/2 agolFloodZonesRiversAndSeaEndPoint=/Flood_Zones_2_and_3_Rivers_and_Sea_NON_PRODUCTION/FeatureServer/0 +agolRiversAndSeaDefendedEndPoint=/Rivers_and_Sea_Defended_Depth_NON_PRODUCTION/FeatureServer +agolRiversAndSeaUndefendedEndPoint=/Rivers_and_Sea_Undefended_Depth_NON_PRODUCTION/FeatureServer +agolRiversAndSeaDefendedCCP1EndPoint=/Rivers_and_Sea_Defended_Depth_CCP1_NON_PRODUCTION/FeatureServer +agolRiversAndSeaUndefendedCCP1EndPoint=/Rivers_and_Sea_Undefended_Depth_CCP1_NON_PRODUCTION/FeatureServer +agolSurfaceWaterEndPoint=/Risk_of_Flooding_from_Surface_Water_Depth_0mm_NON_PRODUCTION/FeatureServer/0 + #EA Maps eamapsServiceUrl=http://dummyEAMapslUrl eamapsProduct1User=PRODUCT1_USER diff --git a/config/__tests__/config.spec.js b/config/__tests__/config.spec.js index 90df3cb1..64054fad 100644 --- a/config/__tests__/config.spec.js +++ b/config/__tests__/config.spec.js @@ -40,7 +40,12 @@ describe('Ensure config is correct', () => { customerTeamEndPoint: '/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/0', localAuthorityEndPoint: '/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/1', isEnglandEndPoint: '/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/2', - floodZonesRiversAndSeaEndPoint: '/Flood_Zones_2_and_3_Rivers_and_Sea_NON_PRODUCTION/FeatureServer/0' + floodZonesRiversAndSeaEndPoint: '/Flood_Zones_2_and_3_Rivers_and_Sea_NON_PRODUCTION/FeatureServer/0', + riversAndSeaDefendedEndPoint: '/Rivers_and_Sea_Defended_Depth_NON_PRODUCTION/FeatureServer', + riversAndSeaUndefendedEndPoint: '/Rivers_and_Sea_Undefended_Depth_NON_PRODUCTION/FeatureServer', + riversAndSeaDefendedCCP1EndPoint: '/Rivers_and_Sea_Defended_Depth_CCP1_NON_PRODUCTION/FeatureServer', + riversAndSeaUndefendedCCP1EndPoint: '/Rivers_and_Sea_Undefended_Depth_CCP1_NON_PRODUCTION/FeatureServer', + surfaceWaterEndPoint: '/Risk_of_Flooding_from_Surface_Water_Depth_0mm_NON_PRODUCTION/FeatureServer/0' }, eamaps: { serviceUrl: 'http://dummyEAMapslUrl', diff --git a/config/index.js b/config/index.js index a92f591e..621f98ef 100644 --- a/config/index.js +++ b/config/index.js @@ -39,7 +39,12 @@ const config = { customerTeamEndPoint: process.env.agolCustomerTeamEndPoint, localAuthorityEndPoint: process.env.agolLocalAuthorityEndPoint, isEnglandEndPoint: process.env.agolIsEnglandEndPoint, - floodZonesRiversAndSeaEndPoint: process.env.agolFloodZonesRiversAndSeaEndPoint + floodZonesRiversAndSeaEndPoint: process.env.agolFloodZonesRiversAndSeaEndPoint, + riversAndSeaDefendedEndPoint: process.env.agolRiversAndSeaDefendedEndPoint, + riversAndSeaUndefendedEndPoint: process.env.agolRiversAndSeaUndefendedEndPoint, + riversAndSeaDefendedCCP1EndPoint: process.env.agolRiversAndSeaDefendedCCP1EndPoint, + riversAndSeaUndefendedCCP1EndPoint: process.env.agolRiversAndSeaUndefendedCCP1EndPoint, + surfaceWaterEndPoint: process.env.agolSurfaceWaterEndPoint }, eamaps: { serviceUrl: process.env.eamapsServiceUrl, diff --git a/config/schema.js b/config/schema.js index 860686e5..f07c088c 100644 --- a/config/schema.js +++ b/config/schema.js @@ -44,7 +44,12 @@ const schema = Joi.object({ customerTeamEndPoint: Joi.string().required(), localAuthorityEndPoint: Joi.string().required(), isEnglandEndPoint: Joi.string().required(), - floodZonesRiversAndSeaEndPoint: Joi.string().required() + floodZonesRiversAndSeaEndPoint: Joi.string().required(), + riversAndSeaDefendedEndPoint: Joi.string().required(), + riversAndSeaUndefendedEndPoint: Joi.string().required(), + riversAndSeaDefendedCCP1EndPoint: Joi.string().required(), + riversAndSeaUndefendedCCP1EndPoint: Joi.string().required(), + surfaceWaterEndPoint: Joi.string().required() }, eamaps: { serviceUrl: Joi.string().uri().required(), diff --git a/server/services/__tests__/__mocks__/floodZonesByPolygonMock.js b/server/services/__tests__/__mocks__/floodZonesByPolygonMock.js index 81b8a92a..800f20dc 100644 --- a/server/services/__tests__/__mocks__/floodZonesByPolygonMock.js +++ b/server/services/__tests__/__mocks__/floodZonesByPolygonMock.js @@ -2,5 +2,10 @@ const mockPolygons = require('../../__data__/mockPolygons.json') jest.mock('../../agol/getFloodZones') jest.mock('../../riskAdmin/isRiskAdminArea') +jest.mock('../../agol/getRiversAndSeaDefended') +jest.mock('../../agol/getRiversAndSeaUndefended') +jest.mock('../../agol/getRiversAndSeaDefendedClimateChange') +jest.mock('../../agol/getRiversAndSeaUndefendedClimateChange') +jest.mock('../../agol/getSurfaceWater') module.exports = { mockPolygons } diff --git a/server/services/__tests__/flood-zones-by-polygon.spec.js b/server/services/__tests__/flood-zones-by-polygon.spec.js index 0fad9518..6db7744d 100644 --- a/server/services/__tests__/flood-zones-by-polygon.spec.js +++ b/server/services/__tests__/flood-zones-by-polygon.spec.js @@ -28,11 +28,11 @@ describe('getFloodZonesByPolygon - Error Handling Scenarios', () => { describe('getFloodZonesByPolygon - Flood Zone Only Scenarios', () => { it('getFloodZonesByPolygon should return data as expected for FZ3 only', async () => { const response = await getFloodZonesByPolygon(mockPolygons.fz3_only) + console.log(response) expect(response).toEqual({ floodZone: '3', floodzone_2: false, floodzone_3: true, - surface_water: false, isRiskAdminArea: false }) }) @@ -43,7 +43,6 @@ describe('getFloodZonesByPolygon - Flood Zone Only Scenarios', () => { floodZone: '2', floodzone_2: true, floodzone_3: false, - surface_water: false, isRiskAdminArea: false }) }) @@ -54,7 +53,6 @@ describe('getFloodZonesByPolygon - Flood Zone Only Scenarios', () => { floodZone: '3', floodzone_2: true, floodzone_3: true, - surface_water: false, isRiskAdminArea: false }) }) @@ -67,7 +65,6 @@ describe('getFloodZonesByPolygon - Flood Zone with RiskAdmin Scenarios', () => { floodZone: '3', floodzone_2: false, floodzone_3: true, - surface_water: false, isRiskAdminArea: true }) }) @@ -78,7 +75,6 @@ describe('getFloodZonesByPolygon - Flood Zone with RiskAdmin Scenarios', () => { floodZone: '2', floodzone_2: true, floodzone_3: false, - surface_water: false, isRiskAdminArea: true }) }) @@ -89,7 +85,6 @@ describe('getFloodZonesByPolygon - Flood Zone with RiskAdmin Scenarios', () => { floodZone: '3', floodzone_2: true, floodzone_3: true, - surface_water: false, isRiskAdminArea: true }) }) diff --git a/server/services/agol/getRiversAndSeaDefended.js b/server/services/agol/getRiversAndSeaDefended.js new file mode 100644 index 00000000..335e4856 --- /dev/null +++ b/server/services/agol/getRiversAndSeaDefended.js @@ -0,0 +1,34 @@ +const { config } = require('../../../config') +const { esriRestRequest, makePolygonGeometry } = require('.') + +const layerDefs = { 0: '', 1: '', 2: '' } + +const LayerRiskBand = { + 0: 'more than a 3.3%', + 1: 'a 1%', + 2: 'less than a 0.1%', + 3: false +} + +const assignResponse = (response, results) => { + let lowestLayerId = 3 + response.layers.forEach(layer => { + if (layer.count > 0) { + if (layer.id < lowestLayerId) { + lowestLayerId = layer.id + } + } + }) + results.rivers_and_sea_defended_risk_band = LayerRiskBand[lowestLayerId] + return results +} + +const getRiversAndSeaDefended = async (options) => { + const results = { + rivers_and_sea_defended_risk_band: false + } + return esriRestRequest(config.agol.riversAndSeaDefendedEndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon', layerDefs) + .then((esriResponse) => assignResponse(esriResponse, results)) +} + +module.exports = { getRiversAndSeaDefended } diff --git a/server/services/agol/getRiversAndSeaDefendedClimateChange.js b/server/services/agol/getRiversAndSeaDefendedClimateChange.js new file mode 100644 index 00000000..a791fa3f --- /dev/null +++ b/server/services/agol/getRiversAndSeaDefendedClimateChange.js @@ -0,0 +1,34 @@ +const { config } = require('../../../config') +const { esriRestRequest, makePolygonGeometry } = require('.') + +const layerDefs = { 0: '', 1: '', 2: '' } + +const LayerRiskBand = { + 0: 'more than a 3.3%', + 1: 'a 1%', + 2: 'less than a 0.1%', + 3: false +} + +const assignResponse = (response, results) => { + let lowestLayerId = 3 + response.layers.forEach(layer => { + if (layer.count > 0) { + if (layer.id < lowestLayerId) { + lowestLayerId = layer.id + } + } + }) + results.rivers_and_sea_defended_climate_change_1_risk_band = LayerRiskBand[lowestLayerId] + return results +} + +const getRiversAndSeaDefendedClimateChange = async (options) => { + const results = { + rivers_and_sea_defended_climate_change_1_risk_band: false + } + return esriRestRequest(config.agol.riversAndSeaDefendedCCP1EndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon', layerDefs) + .then((esriResponse) => assignResponse(esriResponse, results)) +} + +module.exports = { getRiversAndSeaDefendedClimateChange } diff --git a/server/services/agol/getRiversAndSeaUndefended.js b/server/services/agol/getRiversAndSeaUndefended.js new file mode 100644 index 00000000..b798b722 --- /dev/null +++ b/server/services/agol/getRiversAndSeaUndefended.js @@ -0,0 +1,33 @@ +const { config } = require('../../../config') +const { esriRestRequest, makePolygonGeometry } = require('.') + +const layerDefs = { 0: '', 1: '' } + +const LayerRiskBand = { + 0: 'a 1%', + 1: 'less than a 0.1%', + 2: false +} + +const assignResponse = (response, results) => { + let lowestLayerId = 2 + response.layers.forEach(layer => { + if (layer.count > 0) { + if (layer.id < lowestLayerId) { + lowestLayerId = layer.id + } + } + }) + results.rivers_and_sea_undefended_risk_band = LayerRiskBand[lowestLayerId] + return results +} + +const getRiversAndSeaUndefended = async (options) => { + const results = { + rivers_and_sea_undefended_risk_band: false + } + return esriRestRequest(config.agol.riversAndSeaUndefendedEndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon', layerDefs) + .then((esriResponse) => assignResponse(esriResponse, results)) +} + +module.exports = { getRiversAndSeaUndefended } diff --git a/server/services/agol/getRiversAndSeaUndefendedClimateChange.js b/server/services/agol/getRiversAndSeaUndefendedClimateChange.js new file mode 100644 index 00000000..3929c14d --- /dev/null +++ b/server/services/agol/getRiversAndSeaUndefendedClimateChange.js @@ -0,0 +1,33 @@ +const { config } = require('../../../config') +const { esriRestRequest, makePolygonGeometry } = require('.') + +const layerDefs = { 0: '', 1: '' } + +const LayerRiskBand = { + 0: 'a 1%', + 1: 'less than a 0.1%', + 2: false +} + +const assignResponse = (response, results) => { + let lowestLayerId = 2 + response.layers.forEach(layer => { + if (layer.count > 0) { + if (layer.id < lowestLayerId) { + lowestLayerId = layer.id + } + } + }) + results.rivers_and_sea_undefended_climate_change_1_risk_band = LayerRiskBand[lowestLayerId] + return results +} + +const getRiversAndSeaUndefendedClimateChange = async (options) => { + const results = { + rivers_and_sea_undefended_climate_change_1_risk_band: false + } + return esriRestRequest(config.agol.riversAndSeaUndefendedCCP1EndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon', layerDefs) + .then((esriResponse) => assignResponse(esriResponse, results)) +} + +module.exports = { getRiversAndSeaUndefendedClimateChange } diff --git a/server/services/agol/getSurfaceWater.js b/server/services/agol/getSurfaceWater.js new file mode 100644 index 00000000..e35a6d79 --- /dev/null +++ b/server/services/agol/getSurfaceWater.js @@ -0,0 +1,32 @@ +const { config } = require('../../../config') +const { esriRequest, makePolygonGeometry } = require('.') + +const riskBandPriority = { + false: -1, + Low: 1, + Medium: 2, + High: 3 +} + +const assignResponse = (response, results) => { + let highestRiskBand = false + response.forEach(item => { + const riskBand = item.attributes.Risk_band + if (riskBandPriority[riskBand] > riskBandPriority[highestRiskBand]) { + highestRiskBand = riskBand + } + }) + results.surface_water_risk_band = highestRiskBand + return results +} + +const getSurfaceWater = async (options) => { + const results = { + surface_water_risk_band: false + } + + return esriRequest(config.agol.surfaceWaterEndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon') + .then((esriResponse) => assignResponse(esriResponse, results)) +} + +module.exports = { getSurfaceWater } diff --git a/server/services/agol/index.js b/server/services/agol/index.js index 6ada0d02..813e3d8c 100644 --- a/server/services/agol/index.js +++ b/server/services/agol/index.js @@ -1,5 +1,6 @@ const { config } = require('../../../config') const { queryFeatures } = require('@esri/arcgis-rest-feature-service') +const { request } = require('@esri/arcgis-rest-request') const { getEsriToken } = require('./getEsriToken') const makePointGeometry = (x, y) => ({ x, y, spatialReference: { wkid: 27700 } }) @@ -28,4 +29,23 @@ const esriRequest = async (endPoint, geometry, geometryType) => { return result.features } -module.exports = { esriRequest, makePointGeometry, makePolygonGeometry } +const esriRestRequest = async (endPoint, geometry, geometryType, layerDefs) => { + const esriToken = await getEsriToken() + const url = `${config.agol.serviceUrl}${endPoint}/query` + const requestObject = { + httpMethod: 'GET', + authentication: esriToken, + params: { + layerDefs, + geometry, + geometryType, + spatialRel: 'esriSpatialRelIntersects', + returnGeometry: 'false', + returnCountOnly: 'true' + } + } + const result = await request(url, requestObject) + return result +} + +module.exports = { esriRequest, esriRestRequest, makePointGeometry, makePolygonGeometry } diff --git a/server/services/flood-zones-by-polygon.js b/server/services/flood-zones-by-polygon.js index 12a3c68d..910f898e 100644 --- a/server/services/flood-zones-by-polygon.js +++ b/server/services/flood-zones-by-polygon.js @@ -1,17 +1,25 @@ const { getFloodZones } = require('./agol/getFloodZones') const { isRiskAdminArea } = require('./riskAdmin/isRiskAdminArea') +const { getRiversAndSeaDefended } = require('./agol/getRiversAndSeaDefended') +const { getRiversAndSeaUndefended } = require('./agol/getRiversAndSeaUndefended') +const { getRiversAndSeaDefendedClimateChange } = require('./agol/getRiversAndSeaDefendedClimateChange') +const { getRiversAndSeaUndefendedClimateChange } = require('./agol/getRiversAndSeaUndefendedClimateChange') +const { getSurfaceWater } = require('./agol/getSurfaceWater') const getFloodZonesByPolygon = async (polygon) => { if (!polygon) { throw new Error('getFloodZonesByPolygon - No Polygon provided') } try { - const results = { - surface_water: false - } + const results = {} await Promise.all([ getFloodZones({ geometryType: 'esriGeometryPolygon', polygon }), + getRiversAndSeaDefended({ geometryType: 'esriGeometryPolygon', polygon }), + getRiversAndSeaUndefended({ geometryType: 'esriGeometryPolygon', polygon }), + getRiversAndSeaDefendedClimateChange({ geometryType: 'esriGeometryPolygon', polygon }), + getRiversAndSeaUndefendedClimateChange({ geometryType: 'esriGeometryPolygon', polygon }), + getSurfaceWater({ geometryType: 'esriGeometryPolygon', polygon }), isRiskAdminArea(polygon) ]).then((responseArray) => { return Object.assign(results, ...responseArray) diff --git a/server/views/results.html b/server/views/results.html index 6ecc42dd..5e334e27 100644 --- a/server/views/results.html +++ b/server/views/results.html @@ -10,6 +10,21 @@

This location is in flood zone {{floodData.floodZone}}

+ {% if floodData.rivers_and_sea_defended_climate_change_1_risk_band %} +

Between 2070 and 2125, taking flood defences into account, there is {{floodData.rivers_and_sea_defended_climate_change_1_risk_band}} chance of a flood at this location in any year. 

+ {% endif %} + {% if floodData.rivers_and_sea_undefended_climate_change_1_risk_band %} +

Between 2070 and 2125, without flood defences, there is {{floodData.rivers_and_sea_undefended_climate_change_1_risk_band}} chance of a flood at this location in any year.

+ {% endif %} + {% if floodData.rivers_and_sea_defended_risk_band %} +

Taking flood defences into account, there is {{floodData.rivers_and_sea_defended_risk_band}} chance of a flood at this location in any year. 

+ {% endif %} + {% if floodData.rivers_and_sea_undefended_risk_band %} +

Without flood defences, there is {{floodData.rivers_and_sea_undefended_risk_band}} chance of a flood at this location in any year.

+ {% endif %} + {% if floodData.surface_water_risk_band %} +

{{floodData.surface_water_risk_band}} risk of surface water (or flash) flooding.

+ {% endif %}
{% if floodData.isRiskAdminArea %} @@ -29,6 +44,8 @@

Order flood risk data From ca0c40a3e0b452a9ba04c11e9a27e0bcba1c7b38 Mon Sep 17 00:00:00 2001 From: Tedd Mason Date: Thu, 23 Jan 2025 13:32:57 +0000 Subject: [PATCH 2/9] FCRM-5441 - WIP refactoring and unit tests for new agol services --- server/services/__tests__/__mocks__/agol.js | 20 +++- .../__mocks__/floodZonesByPolygonMock.js | 4 +- .../__tests__/flood-zones-by-polygon.spec.js | 2 +- .../__tests__/getRiversAndSeaDefended.spec.js | 86 +++++++++++++++++ .../getRiversAndSeaDefendedCC.spec.js | 86 +++++++++++++++++ .../getRiversAndSeaUndefended.spec.js | 73 ++++++++++++++ .../getRiversAndSeaUndefendedCC.spec.js | 73 ++++++++++++++ .../agol/__tests__/getSurfaceWater.spec.js | 95 +++++++++++++++++++ .../services/agol/getRiversAndSeaDefended.js | 43 +++++---- .../agol/getRiversAndSeaDefendedCC.js | 38 ++++++++ .../getRiversAndSeaDefendedClimateChange.js | 34 ------- .../agol/getRiversAndSeaUndefended.js | 38 ++++---- .../agol/getRiversAndSeaUndefendedCC.js | 35 +++++++ .../getRiversAndSeaUndefendedClimateChange.js | 33 ------- server/services/agol/getSurfaceWater.js | 42 ++++---- server/services/flood-zones-by-polygon.js | 8 +- server/views/results.html | 11 +-- 17 files changed, 583 insertions(+), 138 deletions(-) create mode 100644 server/services/agol/__tests__/getRiversAndSeaDefended.spec.js create mode 100644 server/services/agol/__tests__/getRiversAndSeaDefendedCC.spec.js create mode 100644 server/services/agol/__tests__/getRiversAndSeaUndefended.spec.js create mode 100644 server/services/agol/__tests__/getRiversAndSeaUndefendedCC.spec.js create mode 100644 server/services/agol/__tests__/getSurfaceWater.spec.js create mode 100644 server/services/agol/getRiversAndSeaDefendedCC.js delete mode 100644 server/services/agol/getRiversAndSeaDefendedClimateChange.js create mode 100644 server/services/agol/getRiversAndSeaUndefendedCC.js delete mode 100644 server/services/agol/getRiversAndSeaUndefendedClimateChange.js diff --git a/server/services/__tests__/__mocks__/agol.js b/server/services/__tests__/__mocks__/agol.js index 2fad85e7..f6a089ec 100644 --- a/server/services/__tests__/__mocks__/agol.js +++ b/server/services/__tests__/__mocks__/agol.js @@ -17,4 +17,22 @@ const stopMockingEsriRequests = () => { jest.resetAllMocks() } -module.exports = { mockEsriRequest, mockEsriRequestWithThrow, stopMockingEsriRequests } +const mockEsriRestRequest = (result = [{}]) => { + agol.esriRestRequest.mockImplementation(async () => { + return result + }) +} + +const mockEsriRestRequestWithThrow = () => { + agol.esriRestRequest.mockImplementation(async () => { + throw new Error('mocked error') + }) +} + +module.exports = { + mockEsriRequest, + mockEsriRequestWithThrow, + stopMockingEsriRequests, + mockEsriRestRequest, + mockEsriRestRequestWithThrow +} diff --git a/server/services/__tests__/__mocks__/floodZonesByPolygonMock.js b/server/services/__tests__/__mocks__/floodZonesByPolygonMock.js index 800f20dc..1a85b1a4 100644 --- a/server/services/__tests__/__mocks__/floodZonesByPolygonMock.js +++ b/server/services/__tests__/__mocks__/floodZonesByPolygonMock.js @@ -4,8 +4,8 @@ jest.mock('../../agol/getFloodZones') jest.mock('../../riskAdmin/isRiskAdminArea') jest.mock('../../agol/getRiversAndSeaDefended') jest.mock('../../agol/getRiversAndSeaUndefended') -jest.mock('../../agol/getRiversAndSeaDefendedClimateChange') -jest.mock('../../agol/getRiversAndSeaUndefendedClimateChange') +jest.mock('../../agol/getRiversAndSeaDefendedCC') +jest.mock('../../agol/getRiversAndSeaUndefendedCC') jest.mock('../../agol/getSurfaceWater') module.exports = { mockPolygons } diff --git a/server/services/__tests__/flood-zones-by-polygon.spec.js b/server/services/__tests__/flood-zones-by-polygon.spec.js index 6db7744d..5b3e23eb 100644 --- a/server/services/__tests__/flood-zones-by-polygon.spec.js +++ b/server/services/__tests__/flood-zones-by-polygon.spec.js @@ -26,7 +26,7 @@ describe('getFloodZonesByPolygon - Error Handling Scenarios', () => { }) describe('getFloodZonesByPolygon - Flood Zone Only Scenarios', () => { - it('getFloodZonesByPolygon should return data as expected for FZ3 only', async () => { + it.only('getFloodZonesByPolygon should return data as expected for FZ3 only', async () => { const response = await getFloodZonesByPolygon(mockPolygons.fz3_only) console.log(response) expect(response).toEqual({ diff --git a/server/services/agol/__tests__/getRiversAndSeaDefended.spec.js b/server/services/agol/__tests__/getRiversAndSeaDefended.spec.js new file mode 100644 index 00000000..1d0e4ce8 --- /dev/null +++ b/server/services/agol/__tests__/getRiversAndSeaDefended.spec.js @@ -0,0 +1,86 @@ +const { mockEsriRestRequest, mockEsriRestRequestWithThrow } = require('../../../services/__tests__/__mocks__/agol') +const { getRiversAndSeaDefended } = require('../getRiversAndSeaDefended') + +const layers = [{ + id: 0, + count: 0 +}, { + id: 1, + count: 0 +}, { + id: 2, + count: 0 +}] + +// dummy polygon +const polygon = '[[123,456],[125,457],[125,456],[123,456]]' + +describe('getRiversAndSeaDefended', () => { + it('getRiversAndSeaDefended should return data as expected for no data', async () => { + mockEsriRestRequest({ layers }) + const { riversAndSeaDefended } = await getRiversAndSeaDefended({ polygon }) + expect(riversAndSeaDefended).toEqual({ + riskBandId: 3, + riskBandPercent: false + }) + }) + + it('getRiversAndSeaDefended should return data as expected for low data', async () => { + layers[2].count = 1 + mockEsriRestRequest({ layers }) + const { riversAndSeaDefended } = await getRiversAndSeaDefended({ polygon }) + expect(riversAndSeaDefended).toEqual({ + riskBandId: 2, + riskBandPercent: '0.1' + }) + }) + + it('getRiversAndSeaDefended should return data as expected for medium data', async () => { + layers[1].count = 1 + mockEsriRestRequest({ layers }) + const { riversAndSeaDefended } = await getRiversAndSeaDefended({ polygon }) + expect(riversAndSeaDefended).toEqual({ + riskBandId: 1, + riskBandPercent: '1' + }) + }) + + it('getRiversAndSeaDefended should return data as expected for high data', async () => { + layers[0].count = 1 + mockEsriRestRequest({ layers }) + const { riversAndSeaDefended } = await getRiversAndSeaDefended({ polygon }) + expect(riversAndSeaDefended).toEqual({ + riskBandId: 0, + riskBandPercent: '3.3' + }) + }) + + it('getRiversAndSeaDefended should return data if empty layers', async () => { + mockEsriRestRequest({ layers: [] }) + const { riversAndSeaDefended } = await getRiversAndSeaDefended({ polygon }) + expect(riversAndSeaDefended).toEqual({ + riskBandId: 3, + riskBandPercent: false + }) + }) + + it('getRiversAndSeaDefended should throw if duff data', async () => { + try { + mockEsriRestRequest(undefined) + await getRiversAndSeaDefended({ polygon }) + expect('').toEqual('this line should not be reached') + } catch (err) { + expect(err.message).toEqual('Cannot read properties of undefined (reading \'reduce\')') + } + }) + + it('getRiversAndSeaDefended should throw if esriRequest throws', async () => { + try { + mockEsriRestRequestWithThrow() + await getRiversAndSeaDefended({ polygon }) + expect('').toEqual('this line should not be reached') + } catch (err) { + expect(err.message).toEqual('mocked error') + } + }) +}) diff --git a/server/services/agol/__tests__/getRiversAndSeaDefendedCC.spec.js b/server/services/agol/__tests__/getRiversAndSeaDefendedCC.spec.js new file mode 100644 index 00000000..37438c92 --- /dev/null +++ b/server/services/agol/__tests__/getRiversAndSeaDefendedCC.spec.js @@ -0,0 +1,86 @@ +const { mockEsriRestRequest, mockEsriRestRequestWithThrow } = require('../../../services/__tests__/__mocks__/agol') +const { getRiversAndSeaDefendedCC } = require('../getRiversAndSeaDefendedCC') + +const layers = [{ + id: 0, + count: 0 +}, { + id: 1, + count: 0 +}, { + id: 2, + count: 0 +}] + +// dummy polygon +const polygon = '[[123,456],[125,457],[125,456],[123,456]]' + +describe('getRiversAndSeaDefendedCC', () => { + it('getRiversAndSeaDefendedCC should return data as expected for no data', async () => { + mockEsriRestRequest({ layers }) + const { riversAndSeaDefendedCC } = await getRiversAndSeaDefendedCC({ polygon }) + expect(riversAndSeaDefendedCC).toEqual({ + riskBandId: 3, + riskBandPercent: false + }) + }) + + it('getRiversAndSeaDefendedCC should return data as expected for low data', async () => { + layers[2].count = 1 + mockEsriRestRequest({ layers }) + const { riversAndSeaDefendedCC } = await getRiversAndSeaDefendedCC({ polygon }) + expect(riversAndSeaDefendedCC).toEqual({ + riskBandId: 2, + riskBandPercent: '0.1' + }) + }) + + it('getRiversAndSeaDefendedCC should return data as expected for medium data', async () => { + layers[1].count = 1 + mockEsriRestRequest({ layers }) + const { riversAndSeaDefendedCC } = await getRiversAndSeaDefendedCC({ polygon }) + expect(riversAndSeaDefendedCC).toEqual({ + riskBandId: 1, + riskBandPercent: '1' + }) + }) + + it('getRiversAndSeaDefendedCC should return data as expected for high data', async () => { + layers[0].count = 1 + mockEsriRestRequest({ layers }) + const { riversAndSeaDefendedCC } = await getRiversAndSeaDefendedCC({ polygon }) + expect(riversAndSeaDefendedCC).toEqual({ + riskBandId: 0, + riskBandPercent: '3.3' + }) + }) + + it('getRiversAndSeaDefendedCC should return data if empty layers', async () => { + mockEsriRestRequest({ layers: [] }) + const { riversAndSeaDefendedCC } = await getRiversAndSeaDefendedCC({ polygon }) + expect(riversAndSeaDefendedCC).toEqual({ + riskBandId: 3, + riskBandPercent: false + }) + }) + + it('getRiversAndSeaDefendedCC should throw if duff data', async () => { + try { + mockEsriRestRequest(undefined) + await getRiversAndSeaDefendedCC({ polygon }) + expect('').toEqual('this line should not be reached') + } catch (err) { + expect(err.message).toEqual('Cannot read properties of undefined (reading \'reduce\')') + } + }) + + it('getRiversAndSeaDefendedCC should throw if esriRequest throws', async () => { + try { + mockEsriRestRequestWithThrow() + await getRiversAndSeaDefendedCC({ polygon }) + expect('').toEqual('this line should not be reached') + } catch (err) { + expect(err.message).toEqual('mocked error') + } + }) +}) diff --git a/server/services/agol/__tests__/getRiversAndSeaUndefended.spec.js b/server/services/agol/__tests__/getRiversAndSeaUndefended.spec.js new file mode 100644 index 00000000..9317262b --- /dev/null +++ b/server/services/agol/__tests__/getRiversAndSeaUndefended.spec.js @@ -0,0 +1,73 @@ +const { mockEsriRestRequest, mockEsriRestRequestWithThrow } = require('../../../services/__tests__/__mocks__/agol') +const { getRiversAndSeaUndefended } = require('../getRiversAndSeaUndefended') + +const layers = [{ + id: 0, + count: 0 +}, { + id: 1, + count: 0 +}] + +// dummy polygon +const polygon = '[[123,456],[125,457],[125,456],[123,456]]' + +describe('getRiversAndSeaUndefended', () => { + it('getRiversAndSeaUndefended should return data as expected for no data', async () => { + mockEsriRestRequest({ layers }) + const { riversAndSeaUndefended } = await getRiversAndSeaUndefended({ polygon }) + expect(riversAndSeaUndefended).toEqual({ + riskBandId: 2, + riskBandPercent: false + }) + }) + + it('getRiversAndSeaUndefended should return data as expected for low data', async () => { + layers[1].count = 1 + mockEsriRestRequest({ layers }) + const { riversAndSeaUndefended } = await getRiversAndSeaUndefended({ polygon }) + expect(riversAndSeaUndefended).toEqual({ + riskBandId: 1, + riskBandPercent: '0.1' + }) + }) + + it('getRiversAndSeaUndefended should return data as expected for medium data', async () => { + layers[0].count = 1 + mockEsriRestRequest({ layers }) + const { riversAndSeaUndefended } = await getRiversAndSeaUndefended({ polygon }) + expect(riversAndSeaUndefended).toEqual({ + riskBandId: 0, + riskBandPercent: '1' + }) + }) + + it('getRiversAndSeaUndefended should return data if empty layers', async () => { + mockEsriRestRequest({ layers: [] }) + const { riversAndSeaUndefended } = await getRiversAndSeaUndefended({ polygon }) + expect(riversAndSeaUndefended).toEqual({ + riskBandId: 2, + riskBandPercent: false + }) + }) + + it('getRiversAndSeaUndefended should throw if duff data', async () => { + try { + mockEsriRestRequest(undefined) + await getRiversAndSeaUndefended({ polygon }) + expect('').toEqual('this line should not be reached') + } catch (err) { + expect(err.message).toEqual('Cannot read properties of undefined (reading \'reduce\')') + } + }) + + it('getRiversAndSeaUndefended should throw if esriRequest throws', async () => { + try { + mockEsriRestRequestWithThrow() + await getRiversAndSeaUndefended({ polygon }) + expect('').toEqual('this line should not be reached') + } catch (err) { + expect(err.message).toEqual('mocked error') + } + }) +}) diff --git a/server/services/agol/__tests__/getRiversAndSeaUndefendedCC.spec.js b/server/services/agol/__tests__/getRiversAndSeaUndefendedCC.spec.js new file mode 100644 index 00000000..7281e350 --- /dev/null +++ b/server/services/agol/__tests__/getRiversAndSeaUndefendedCC.spec.js @@ -0,0 +1,73 @@ +const { mockEsriRestRequest, mockEsriRestRequestWithThrow } = require('../../../services/__tests__/__mocks__/agol') +const { getRiversAndSeaUndefendedCC } = require('../getRiversAndSeaUndefendedCC') + +const layers = [{ + id: 0, + count: 0 +}, { + id: 1, + count: 0 +}] + +// dummy polygon +const polygon = '[[123,456],[125,457],[125,456],[123,456]]' + +describe('getRiversAndSeaUndefendedCC', () => { + it('getRiversAndSeaUndefendedCC should return data as expected for no data', async () => { + mockEsriRestRequest({ layers }) + const { riversAndSeaUndefendedCC } = await getRiversAndSeaUndefendedCC({ polygon }) + expect(riversAndSeaUndefendedCC).toEqual({ + riskBandId: 2, + riskBandPercent: false + }) + }) + + it('getRiversAndSeaUndefendedCC should return data as expected for low data', async () => { + layers[1].count = 1 + mockEsriRestRequest({ layers }) + const { riversAndSeaUndefendedCC } = await getRiversAndSeaUndefendedCC({ polygon }) + expect(riversAndSeaUndefendedCC).toEqual({ + riskBandId: 1, + riskBandPercent: '0.1' + }) + }) + + it('getRiversAndSeaUndefendedCC should return data as expected for medium data', async () => { + layers[0].count = 1 + mockEsriRestRequest({ layers }) + const { riversAndSeaUndefendedCC } = await getRiversAndSeaUndefendedCC({ polygon }) + expect(riversAndSeaUndefendedCC).toEqual({ + riskBandId: 0, + riskBandPercent: '1' + }) + }) + + it('getRiversAndSeaUndefendedCC should return data if empty layers', async () => { + mockEsriRestRequest({ layers: [] }) + const { riversAndSeaUndefendedCC } = await getRiversAndSeaUndefendedCC({ polygon }) + expect(riversAndSeaUndefendedCC).toEqual({ + riskBandId: 2, + riskBandPercent: false + }) + }) + + it('getRiversAndSeaUndefendedCC should throw if duff data', async () => { + try { + mockEsriRestRequest(undefined) + await getRiversAndSeaUndefendedCC({ polygon }) + expect('').toEqual('this line should not be reached') + } catch (err) { + expect(err.message).toEqual('Cannot read properties of undefined (reading \'reduce\')') + } + }) + + it('getRiversAndSeaUndefendedCC should throw if esriRequest throws', async () => { + try { + mockEsriRestRequestWithThrow() + await getRiversAndSeaUndefendedCC({ polygon }) + expect('').toEqual('this line should not be reached') + } catch (err) { + expect(err.message).toEqual('mocked error') + } + }) +}) diff --git a/server/services/agol/__tests__/getSurfaceWater.spec.js b/server/services/agol/__tests__/getSurfaceWater.spec.js new file mode 100644 index 00000000..456fbb02 --- /dev/null +++ b/server/services/agol/__tests__/getSurfaceWater.spec.js @@ -0,0 +1,95 @@ +const { mockEsriRequest, mockEsriRequestWithThrow, stopMockingEsriRequests } = require('../../../services/__tests__/__mocks__/agol') +const { getSurfaceWater } = require('../getSurfaceWater') + +const surfaceWaterLowArea = { + attributes: { + Confidence: 1, + OBJECTID: 12345, + Risk_band: 'Low', + Shape__Area: 8000, + Shape__Length: 8000 + } +} +const surfaceWaterMediumArea = { + attributes: { + Confidence: 1, + OBJECTID: 12345, + Risk_band: 'Medium', + Shape__Area: 8000, + Shape__Length: 8000 + } +} +const surfaceWaterHighArea = { + attributes: { + Confidence: 1, + OBJECTID: 12345, + Risk_band: 'High', + Shape__Area: 8000, + Shape__Length: 8000 + } +} + +// dummy polygon +const polygon = '[[123,456],[125,457],[125,456],[123,456]]' + +describe('getSurfaceWater', () => { + afterAll(async () => { + stopMockingEsriRequests() + }) + + it('getSurfaceWater should return data as expected for High surface water area', async () => { + mockEsriRequest([surfaceWaterHighArea, surfaceWaterMediumArea, surfaceWaterLowArea]) + const { surfaceWater } = await getSurfaceWater({ polygon }) + expect(surfaceWater).toEqual({ + riskBandId: 3, + riskBand: 'High' + }) + }) + + it('getSurfaceWater should return data as expected for Medium surface water area', async () => { + mockEsriRequest([surfaceWaterMediumArea, surfaceWaterLowArea]) + const { surfaceWater } = await getSurfaceWater({ polygon }) + expect(surfaceWater).toEqual({ + riskBandId: 2, + riskBand: 'Medium' + }) + }) + + it('getSurfaceWater should return data as expected for Low surface water area', async () => { + mockEsriRequest([surfaceWaterLowArea, surfaceWaterLowArea]) + const { surfaceWater } = await getSurfaceWater({ polygon }) + expect(surfaceWater).toEqual({ + riskBandId: 1, + riskBand: 'Low' + }) + }) + + it('getSurfaceWater should return data as expected for no surface water hits', async () => { + mockEsriRequest([surfaceWaterLowArea, surfaceWaterLowArea]) + const { surfaceWater } = await getSurfaceWater({ polygon }) + expect(surfaceWater).toEqual({ + riskBandId: 1, + riskBand: 'Low' + }) + }) + + it('getSurfaceWater should return data as expected for no surface water hits', async () => { + mockEsriRequest([]) + const { surfaceWater } = await getSurfaceWater({ polygon }) + expect(surfaceWater).toEqual({ + riskBandId: -1, + riskBand: false + }) + }) + + it('getSurfaceWater should throw if esriRequest throws', async () => { + try { + mockEsriRequestWithThrow() + await getSurfaceWater({ geometryType: 'esriGeometryPolygon', polygon }) + expect('').toEqual('this line should not be reached') + } catch (err) { + console.log(err) + expect(err.message).toEqual('mocked error') + } + }) +}) diff --git a/server/services/agol/getRiversAndSeaDefended.js b/server/services/agol/getRiversAndSeaDefended.js index 335e4856..85eeec0c 100644 --- a/server/services/agol/getRiversAndSeaDefended.js +++ b/server/services/agol/getRiversAndSeaDefended.js @@ -4,31 +4,36 @@ const { esriRestRequest, makePolygonGeometry } = require('.') const layerDefs = { 0: '', 1: '', 2: '' } const LayerRiskBand = { - 0: 'more than a 3.3%', - 1: 'a 1%', - 2: 'less than a 0.1%', - 3: false + 0: { + riskBandId: 0, + riskBandPercent: '3.3' + }, + 1: { + riskBandId: 1, + riskBandPercent: '1' + }, + 2: { + riskBandId: 2, + riskBandPercent: '0.1' + }, + 3: { + riskBandId: 3, + riskBandPercent: false + } } -const assignResponse = (response, results) => { - let lowestLayerId = 3 - response.layers.forEach(layer => { - if (layer.count > 0) { - if (layer.id < lowestLayerId) { - lowestLayerId = layer.id - } - } - }) - results.rivers_and_sea_defended_risk_band = LayerRiskBand[lowestLayerId] - return results +const assignResponse = (response) => { + const lowestLayerId = response.layers.reduce((lowest, layer) => { + return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest + }, 3) + return { + riversAndSeaDefended: LayerRiskBand[lowestLayerId] + } } const getRiversAndSeaDefended = async (options) => { - const results = { - rivers_and_sea_defended_risk_band: false - } return esriRestRequest(config.agol.riversAndSeaDefendedEndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon', layerDefs) - .then((esriResponse) => assignResponse(esriResponse, results)) + .then((esriResponse) => assignResponse(esriResponse)) } module.exports = { getRiversAndSeaDefended } diff --git a/server/services/agol/getRiversAndSeaDefendedCC.js b/server/services/agol/getRiversAndSeaDefendedCC.js new file mode 100644 index 00000000..e8f231dd --- /dev/null +++ b/server/services/agol/getRiversAndSeaDefendedCC.js @@ -0,0 +1,38 @@ +const { config } = require('../../../config') +const { esriRestRequest, makePolygonGeometry } = require('.') + +const layerDefs = { 0: '', 1: '', 2: '' } + +const LayerRiskBand = { + 0: { + riskBandId: 0, + riskBandPercent: '3.3' + }, + 1: { + riskBandId: 1, + riskBandPercent: '1' + }, + 2: { + riskBandId: 2, + riskBandPercent: '0.1' + }, + 3: { + riskBandId: 3, + riskBandPercent: false + } +} +const assignResponse = (response) => { + const lowestLayerId = response.layers.reduce((lowest, layer) => { + return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest + }, 3) + return { + riversAndSeaDefendedCC: LayerRiskBand[lowestLayerId] + } +} + +const getRiversAndSeaDefendedCC = async (options) => { + return esriRestRequest(config.agol.riversAndSeaDefendedCCP1EndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon', layerDefs) + .then((esriResponse) => assignResponse(esriResponse)) +} + +module.exports = { getRiversAndSeaDefendedCC } diff --git a/server/services/agol/getRiversAndSeaDefendedClimateChange.js b/server/services/agol/getRiversAndSeaDefendedClimateChange.js deleted file mode 100644 index a791fa3f..00000000 --- a/server/services/agol/getRiversAndSeaDefendedClimateChange.js +++ /dev/null @@ -1,34 +0,0 @@ -const { config } = require('../../../config') -const { esriRestRequest, makePolygonGeometry } = require('.') - -const layerDefs = { 0: '', 1: '', 2: '' } - -const LayerRiskBand = { - 0: 'more than a 3.3%', - 1: 'a 1%', - 2: 'less than a 0.1%', - 3: false -} - -const assignResponse = (response, results) => { - let lowestLayerId = 3 - response.layers.forEach(layer => { - if (layer.count > 0) { - if (layer.id < lowestLayerId) { - lowestLayerId = layer.id - } - } - }) - results.rivers_and_sea_defended_climate_change_1_risk_band = LayerRiskBand[lowestLayerId] - return results -} - -const getRiversAndSeaDefendedClimateChange = async (options) => { - const results = { - rivers_and_sea_defended_climate_change_1_risk_band: false - } - return esriRestRequest(config.agol.riversAndSeaDefendedCCP1EndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon', layerDefs) - .then((esriResponse) => assignResponse(esriResponse, results)) -} - -module.exports = { getRiversAndSeaDefendedClimateChange } diff --git a/server/services/agol/getRiversAndSeaUndefended.js b/server/services/agol/getRiversAndSeaUndefended.js index b798b722..247e290f 100644 --- a/server/services/agol/getRiversAndSeaUndefended.js +++ b/server/services/agol/getRiversAndSeaUndefended.js @@ -4,30 +4,32 @@ const { esriRestRequest, makePolygonGeometry } = require('.') const layerDefs = { 0: '', 1: '' } const LayerRiskBand = { - 0: 'a 1%', - 1: 'less than a 0.1%', - 2: false + 0: { + riskBandId: 0, + riskBandPercent: '1' + }, + 1: { + riskBandId: 1, + riskBandPercent: '0.1' + }, + 2: { + riskBandId: 2, + riskBandPercent: false + } } -const assignResponse = (response, results) => { - let lowestLayerId = 2 - response.layers.forEach(layer => { - if (layer.count > 0) { - if (layer.id < lowestLayerId) { - lowestLayerId = layer.id - } - } - }) - results.rivers_and_sea_undefended_risk_band = LayerRiskBand[lowestLayerId] - return results +const assignResponse = (response) => { + const lowestLayerId = response.layers.reduce((lowest, layer) => { + return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest + }, 2) + return { + riversAndSeaUndefended: LayerRiskBand[lowestLayerId] + } } const getRiversAndSeaUndefended = async (options) => { - const results = { - rivers_and_sea_undefended_risk_band: false - } return esriRestRequest(config.agol.riversAndSeaUndefendedEndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon', layerDefs) - .then((esriResponse) => assignResponse(esriResponse, results)) + .then((esriResponse) => assignResponse(esriResponse)) } module.exports = { getRiversAndSeaUndefended } diff --git a/server/services/agol/getRiversAndSeaUndefendedCC.js b/server/services/agol/getRiversAndSeaUndefendedCC.js new file mode 100644 index 00000000..72c13734 --- /dev/null +++ b/server/services/agol/getRiversAndSeaUndefendedCC.js @@ -0,0 +1,35 @@ +const { config } = require('../../../config') +const { esriRestRequest, makePolygonGeometry } = require('.') + +const layerDefs = { 0: '', 1: '' } + +const LayerRiskBand = { + 0: { + riskBandId: 0, + riskBandPercent: '1' + }, + 1: { + riskBandId: 1, + riskBandPercent: '0.1' + }, + 2: { + riskBandId: 2, + riskBandPercent: false + } +} + +const assignResponse = (response) => { + const lowestLayerId = response.layers.reduce((lowest, layer) => { + return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest + }, 2) + return { + riversAndSeaUndefendedCC: LayerRiskBand[lowestLayerId] + } +} + +const getRiversAndSeaUndefendedCC = async (options) => { + return esriRestRequest(config.agol.riversAndSeaUndefendedCCP1EndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon', layerDefs) + .then((esriResponse) => assignResponse(esriResponse)) +} + +module.exports = { getRiversAndSeaUndefendedCC } diff --git a/server/services/agol/getRiversAndSeaUndefendedClimateChange.js b/server/services/agol/getRiversAndSeaUndefendedClimateChange.js deleted file mode 100644 index 3929c14d..00000000 --- a/server/services/agol/getRiversAndSeaUndefendedClimateChange.js +++ /dev/null @@ -1,33 +0,0 @@ -const { config } = require('../../../config') -const { esriRestRequest, makePolygonGeometry } = require('.') - -const layerDefs = { 0: '', 1: '' } - -const LayerRiskBand = { - 0: 'a 1%', - 1: 'less than a 0.1%', - 2: false -} - -const assignResponse = (response, results) => { - let lowestLayerId = 2 - response.layers.forEach(layer => { - if (layer.count > 0) { - if (layer.id < lowestLayerId) { - lowestLayerId = layer.id - } - } - }) - results.rivers_and_sea_undefended_climate_change_1_risk_band = LayerRiskBand[lowestLayerId] - return results -} - -const getRiversAndSeaUndefendedClimateChange = async (options) => { - const results = { - rivers_and_sea_undefended_climate_change_1_risk_band: false - } - return esriRestRequest(config.agol.riversAndSeaUndefendedCCP1EndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon', layerDefs) - .then((esriResponse) => assignResponse(esriResponse, results)) -} - -module.exports = { getRiversAndSeaUndefendedClimateChange } diff --git a/server/services/agol/getSurfaceWater.js b/server/services/agol/getSurfaceWater.js index e35a6d79..f57845d7 100644 --- a/server/services/agol/getSurfaceWater.js +++ b/server/services/agol/getSurfaceWater.js @@ -2,31 +2,37 @@ const { config } = require('../../../config') const { esriRequest, makePolygonGeometry } = require('.') const riskBandPriority = { - false: -1, - Low: 1, - Medium: 2, - High: 3 + false: { + riskBandId: -1, + riskBand: false + }, + Low: { + riskBandId: 1, + riskBand: 'Low' + }, + Medium: { + riskBandId: 2, + riskBand: 'Medium' + }, + High: { + riskBandId: 3, + riskBand: 'High' + } } -const assignResponse = (response, results) => { - let highestRiskBand = false - response.forEach(item => { +const assignResponse = (response) => { + const highestRiskBand = response.reduce((highest, item) => { const riskBand = item.attributes.Risk_band - if (riskBandPriority[riskBand] > riskBandPriority[highestRiskBand]) { - highestRiskBand = riskBand - } - }) - results.surface_water_risk_band = highestRiskBand - return results + return riskBandPriority[riskBand].riskBandId > riskBandPriority[highest].riskBandId ? riskBand : highest + }, false) + return { + surfaceWater: riskBandPriority[highestRiskBand] + } } const getSurfaceWater = async (options) => { - const results = { - surface_water_risk_band: false - } - return esriRequest(config.agol.surfaceWaterEndPoint, makePolygonGeometry(options.polygon), 'esriGeometryPolygon') - .then((esriResponse) => assignResponse(esriResponse, results)) + .then((esriResponse) => assignResponse(esriResponse)) } module.exports = { getSurfaceWater } diff --git a/server/services/flood-zones-by-polygon.js b/server/services/flood-zones-by-polygon.js index 910f898e..d7aaae74 100644 --- a/server/services/flood-zones-by-polygon.js +++ b/server/services/flood-zones-by-polygon.js @@ -2,8 +2,8 @@ const { getFloodZones } = require('./agol/getFloodZones') const { isRiskAdminArea } = require('./riskAdmin/isRiskAdminArea') const { getRiversAndSeaDefended } = require('./agol/getRiversAndSeaDefended') const { getRiversAndSeaUndefended } = require('./agol/getRiversAndSeaUndefended') -const { getRiversAndSeaDefendedClimateChange } = require('./agol/getRiversAndSeaDefendedClimateChange') -const { getRiversAndSeaUndefendedClimateChange } = require('./agol/getRiversAndSeaUndefendedClimateChange') +const { getRiversAndSeaDefendedCC } = require('./agol/getRiversAndSeaDefendedCC') +const { getRiversAndSeaUndefendedCC } = require('./agol/getRiversAndSeaUndefendedCC') const { getSurfaceWater } = require('./agol/getSurfaceWater') const getFloodZonesByPolygon = async (polygon) => { @@ -17,8 +17,8 @@ const getFloodZonesByPolygon = async (polygon) => { getFloodZones({ geometryType: 'esriGeometryPolygon', polygon }), getRiversAndSeaDefended({ geometryType: 'esriGeometryPolygon', polygon }), getRiversAndSeaUndefended({ geometryType: 'esriGeometryPolygon', polygon }), - getRiversAndSeaDefendedClimateChange({ geometryType: 'esriGeometryPolygon', polygon }), - getRiversAndSeaUndefendedClimateChange({ geometryType: 'esriGeometryPolygon', polygon }), + getRiversAndSeaDefendedCC({ geometryType: 'esriGeometryPolygon', polygon }), + getRiversAndSeaUndefendedCC({ geometryType: 'esriGeometryPolygon', polygon }), getSurfaceWater({ geometryType: 'esriGeometryPolygon', polygon }), isRiskAdminArea(polygon) ]).then((responseArray) => { diff --git a/server/views/results.html b/server/views/results.html index 5e334e27..e95d6f8f 100644 --- a/server/views/results.html +++ b/server/views/results.html @@ -10,7 +10,7 @@

This location is in flood zone {{floodData.floodZone}}

- {% if floodData.rivers_and_sea_defended_climate_change_1_risk_band %} + + Flood data results: {{ floodData | dump | safe }}
{% if floodData.isRiskAdminArea %} @@ -44,8 +45,6 @@

Order flood risk data @@ -53,7 +52,3 @@

--> -{% endblock %} \ No newline at end of file From fb2fdc90c850cd8a644c8833d8dcddad83b39879 Mon Sep 17 00:00:00 2001 From: Tedd Mason Date: Thu, 23 Jan 2025 14:23:36 +0000 Subject: [PATCH 3/9] unit test for esrirestrequest --- __mocks__/@esri/arcgis-rest-request.js | 35 +++++++++++++- .../agol/__tests__/esriRestRequest.spec.js | 47 +++++++++++++++++++ server/services/agol/esriRequest.js | 3 +- server/services/agol/esriRestRequest.js | 23 +++++++++ server/services/agol/index.js | 23 +-------- 5 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 server/services/agol/__tests__/esriRestRequest.spec.js create mode 100644 server/services/agol/esriRestRequest.js diff --git a/__mocks__/@esri/arcgis-rest-request.js b/__mocks__/@esri/arcgis-rest-request.js index 20fad093..1b8741d6 100644 --- a/__mocks__/@esri/arcgis-rest-request.js +++ b/__mocks__/@esri/arcgis-rest-request.js @@ -1,4 +1,4 @@ - +let expectedParameters const response = { token: 'TEST_TOKEN', refreshToken: () => { @@ -14,4 +14,35 @@ const ApplicationCredentialsManager = { fromCredentials: () => (response) } -module.exports = { ApplicationCredentialsManager, _invalidateToken, _resetToken } +const requestSpy = { + expectParameters: (params) => { expectedParameters = params } +} + +const request = async (url, requestObject) => { + if (expectedParameters) { + expect(url).toEqual(expectedParameters.url) + expect(requestObject).toEqual(expectedParameters.requestObject) + } + return { + layers: [ + { + id: 0, + count: 0 + }, { + id: 1, + count: 0 + }, { + id: 2, + count: 0 + } + ] + } +} + +module.exports = { + ApplicationCredentialsManager, + _invalidateToken, + _resetToken, + request, + requestSpy +} diff --git a/server/services/agol/__tests__/esriRestRequest.spec.js b/server/services/agol/__tests__/esriRestRequest.spec.js new file mode 100644 index 00000000..dbefb76a --- /dev/null +++ b/server/services/agol/__tests__/esriRestRequest.spec.js @@ -0,0 +1,47 @@ +const { esriRestRequest } = require('../esriRestRequest') +const { requestSpy } = require('@esri/arcgis-rest-request') +const { config } = require('../../../../config') + +const geometry = { + rings: [[[1, 1], [1, 2], [2, 2], [2, 1], [1, 1]]], + spatialReference: { wkid: 27700 } +} + +const layerDefs = { 0: '', 1: '', 2: '' } + +const expectedResponse = { + layers: [ + { + id: 0, + count: 0 + }, { + id: 1, + count: 0 + }, { + id: 2, + count: 0 + } + ] +} + +describe('esriRestRequest', () => { + it('should call esriRestRequest with the expected object and return mocked object', async () => { + requestSpy.expectParameters({ + url: `${config.agol.serviceUrl}/endpoint/query`, + requestObject: { + httpMethod: 'GET', + authentication: 'TEST_TOKEN', + params: { + layerDefs, + geometry, + geometryType: 'esriGeometryPolygon', + spatialRel: 'esriSpatialRelIntersects', + returnGeometry: 'false', + returnCountOnly: 'true' + } + } + }) + const response = await esriRestRequest('/endpoint', geometry, 'esriGeometryPolygon', layerDefs) + expect(response).toEqual(expectedResponse) + }) +}) diff --git a/server/services/agol/esriRequest.js b/server/services/agol/esriRequest.js index 8590ee11..6d0a63d8 100644 --- a/server/services/agol/esriRequest.js +++ b/server/services/agol/esriRequest.js @@ -13,8 +13,7 @@ const esriRequest = async (endPoint, geometry, geometryType) => { authentication: esriToken, outFields: '*' } - const result = await queryFeatures(requestObject) - return result.features + return (await queryFeatures(requestObject)).features } module.exports = { esriRequest } diff --git a/server/services/agol/esriRestRequest.js b/server/services/agol/esriRestRequest.js new file mode 100644 index 00000000..85a7bb03 --- /dev/null +++ b/server/services/agol/esriRestRequest.js @@ -0,0 +1,23 @@ +const { config } = require('../../../config') +const { request } = require('@esri/arcgis-rest-request') +const { getEsriToken } = require('./getEsriToken') + +const esriRestRequest = async (endPoint, geometry, geometryType, layerDefs) => { + const esriToken = await getEsriToken() + const url = `${config.agol.serviceUrl}${endPoint}/query` + const requestObject = { + httpMethod: 'GET', + authentication: esriToken, + params: { + layerDefs, + geometry, + geometryType, + spatialRel: 'esriSpatialRelIntersects', + returnGeometry: 'false', + returnCountOnly: 'true' + } + } + return await request(url, requestObject) +} + +module.exports = { esriRestRequest } diff --git a/server/services/agol/index.js b/server/services/agol/index.js index bf7b4727..e5dc0b7b 100644 --- a/server/services/agol/index.js +++ b/server/services/agol/index.js @@ -1,7 +1,5 @@ -const { config } = require('../../../config') -const { request } = require('@esri/arcgis-rest-request') -const { getEsriToken } = require('./getEsriToken') const { esriRequest } = require('./esriRequest') +const { esriRestRequest } = require('./esriRestRequest') const makePointGeometry = (x, y) => ({ x, y, spatialReference: { wkid: 27700 } }) @@ -13,23 +11,4 @@ const makePolygonGeometry = (polygon) => { } } -const esriRestRequest = async (endPoint, geometry, geometryType, layerDefs) => { - const esriToken = await getEsriToken() - const url = `${config.agol.serviceUrl}${endPoint}/query` - const requestObject = { - httpMethod: 'GET', - authentication: esriToken, - params: { - layerDefs, - geometry, - geometryType, - spatialRel: 'esriSpatialRelIntersects', - returnGeometry: 'false', - returnCountOnly: 'true' - } - } - const result = await request(url, requestObject) - return result -} - module.exports = { esriRequest, esriRestRequest, makePointGeometry, makePolygonGeometry } From 7c96950df936faf9591f2a56a5959ae312d71c29 Mon Sep 17 00:00:00 2001 From: Tedd Mason Date: Thu, 23 Jan 2025 14:31:26 +0000 Subject: [PATCH 4/9] renaming getFloodZonesByPolygon to getFloodDataByPolygon --- server/index.js | 2 +- server/routes/check-your-details.js | 4 +- server/routes/flood-zone-results.js | 2 +- server/routes/results.js | 2 +- .../__tests__/flood-zones-by-polygon.spec.js | 96 ------------------- .../__tests__/floodDataByPolygon.spec.js | 96 +++++++++++++++++++ ...es-by-polygon.js => floodDataByPolygon.js} | 12 +-- test/routes/flood-zone-results.js | 24 ++--- 8 files changed, 119 insertions(+), 119 deletions(-) delete mode 100644 server/services/__tests__/flood-zones-by-polygon.spec.js create mode 100644 server/services/__tests__/floodDataByPolygon.spec.js rename server/services/{flood-zones-by-polygon.js => floodDataByPolygon.js} (83%) diff --git a/server/index.js b/server/index.js index 902fe14b..ebae9e66 100644 --- a/server/index.js +++ b/server/index.js @@ -30,7 +30,7 @@ async function createServer () { await server.method(require('./services/pso-contact')) await server.method(require('./services/pso-contact-by-polygon')) - await server.method(require('./services/flood-zones-by-polygon')) + await server.method(require('./services/floodDataByPolygon')) await server.method(require('./services/use-automated')) // Register the plugins diff --git a/server/routes/check-your-details.js b/server/routes/check-your-details.js index d18384d4..87e82dd4 100644 --- a/server/routes/check-your-details.js +++ b/server/routes/check-your-details.js @@ -26,7 +26,7 @@ module.exports = [ description: 'Application Review Summary', handler: async (request, h) => { const { polygon, fullName, recipientemail } = request.query - const floodZoneResults = await request.server.methods.getFloodZonesByPolygon(polygon) + const floodZoneResults = await request.server.methods.getFloodDataByPolygon(polygon) const floodZone = floodZoneResultsToFloodZone(floodZoneResults) const contactUrl = `/contact?polygon=${polygon}&fullName=${fullName}&recipientemail=${recipientemail}` const confirmLocationUrl = `confirm-location?fullName=${fullName}&recipientemail=${recipientemail}` @@ -43,7 +43,7 @@ module.exports = [ const payload = request.payload || {} const { recipientemail, fullName, polygon } = payload const coordinates = getCentreOfPolygon(polygon) - const floodZoneResults = await request.server.methods.getFloodZonesByPolygon(polygon) + const floodZoneResults = await request.server.methods.getFloodDataByPolygon(polygon) const zoneNumber = floodZoneResultsToFloodZone(floodZoneResults) let applicationReferenceNumber diff --git a/server/routes/flood-zone-results.js b/server/routes/flood-zone-results.js index f6c2c3a3..1e2a19f7 100644 --- a/server/routes/flood-zone-results.js +++ b/server/routes/flood-zone-results.js @@ -72,7 +72,7 @@ module.exports = [ if (!request.server.methods.ignoreUseAutomatedService()) { useAutomatedService = contactDetails.useAutomatedService } - const floodZoneResults = await request.server.methods.getFloodZonesByPolygon(polygon) + const floodZoneResults = await request.server.methods.getFloodDataByPolygon(polygon) const plotSize = getAreaInHectares(polygon) const floodZoneResultsData = new FloodRiskView.Model({ diff --git a/server/routes/results.js b/server/routes/results.js index 29d43f0c..f2fda6c0 100644 --- a/server/routes/results.js +++ b/server/routes/results.js @@ -10,7 +10,7 @@ module.exports = [ const { polygon } = request.query const contactData = await request.server.methods.getPsoContactsByPolygon(polygon) const showOrderProduct4Button = config.appType === 'internal' || contactData.useAutomatedService === true - const floodData = await request.server.methods.getFloodZonesByPolygon(polygon) + const floodData = await request.server.methods.getFloodDataByPolygon(polygon) return h.view('results', { polygon, floodData, contactData, showOrderProduct4Button }) } } diff --git a/server/services/__tests__/flood-zones-by-polygon.spec.js b/server/services/__tests__/flood-zones-by-polygon.spec.js deleted file mode 100644 index 328fa267..00000000 --- a/server/services/__tests__/flood-zones-by-polygon.spec.js +++ /dev/null @@ -1,96 +0,0 @@ -const { mockPolygons } = require('./__mocks__/floodZonesByPolygonMock') -const { - method: getFloodZonesByPolygon, - options: { generateKey } -} = require('../../../server/services/flood-zones-by-polygon') - -describe('getFloodZonesByPolygon - Error Handling Scenarios', () => { - it('getFloodZonesByPolygon without polygon should throw "No Polygon provided"', async () => { - try { - const response = await getFloodZonesByPolygon('') - expect(response).toEqual('this line should not be reached') - } catch (err) { - expect(err.message).toEqual('getFloodZonesByPolygon - No Polygon provided') - } - }) - - it('getFloodZonesByPolygon should throw if ezriRequest throws"', async () => { - try { - await getFloodZonesByPolygon(mockPolygons.throws) - expect('').toEqual('this line should not be reached') - } catch (err) { - console.log(err) - expect(err.message).toEqual('Fetching getFloodZonesByPolygon failed: ') - } - }) -}) - -describe('getFloodZonesByPolygon - Flood Zone Only Scenarios', () => { - it('getFloodZonesByPolygon should return data as expected for FZ3 only', async () => { - const response = await getFloodZonesByPolygon(mockPolygons.fz3_only) - expect(response).toEqual({ - floodZone: '3', - floodzone_2: false, - floodzone_3: true, - isRiskAdminArea: false - }) - }) - - it('getFloodZonesByPolygon should return data as expected for FZ2 only', async () => { - const response = await getFloodZonesByPolygon(mockPolygons.fz2_only) - expect(response).toEqual({ - floodZone: '2', - floodzone_2: true, - floodzone_3: false, - isRiskAdminArea: false - }) - }) - - it('getFloodZonesByPolygon should return data as expected for FZ2 and FZ3', async () => { - const response = await getFloodZonesByPolygon(mockPolygons.fz2_and_3) - expect(response).toEqual({ - floodZone: '3', - floodzone_2: true, - floodzone_3: true, - isRiskAdminArea: false - }) - }) -}) - -describe('getFloodZonesByPolygon - Flood Zone with RiskAdmin Scenarios', () => { - it('getFloodZonesByPolygon should return data as expected for FZ3 only in riskAdminArea', async () => { - const response = await getFloodZonesByPolygon(mockPolygons.inRiskAdmin.fz3_only) - expect(response).toEqual({ - floodZone: '3', - floodzone_2: false, - floodzone_3: true, - isRiskAdminArea: true - }) - }) - - it('getFloodZonesByPolygon should return data as expected for FZ2 only in riskAdminArea', async () => { - const response = await getFloodZonesByPolygon(mockPolygons.inRiskAdmin.fz2_only) - expect(response).toEqual({ - floodZone: '2', - floodzone_2: true, - floodzone_3: false, - isRiskAdminArea: true - }) - }) - - it('getFloodZonesByPolygon should return data as expected for FZ2 and FZ3 in riskAdminArea', async () => { - const response = await getFloodZonesByPolygon(mockPolygons.inRiskAdmin.fz2_and_3) - expect(response).toEqual({ - floodZone: '3', - floodzone_2: true, - floodzone_3: true, - isRiskAdminArea: true - }) - }) -}) - -describe('generateKey', () => { - it('generateKey should stringify a polygon', async () => { - expect(generateKey([123, 456])).toEqual('[123,456]') - }) -}) diff --git a/server/services/__tests__/floodDataByPolygon.spec.js b/server/services/__tests__/floodDataByPolygon.spec.js new file mode 100644 index 00000000..a26d5058 --- /dev/null +++ b/server/services/__tests__/floodDataByPolygon.spec.js @@ -0,0 +1,96 @@ +const { mockPolygons } = require('./__mocks__/floodZonesByPolygonMock') +const { + method: getFloodDataByPolygon, + options: { generateKey } +} = require('../../../server/services/floodDataByPolygon') + +describe('getFloodDataByPolygon - Error Handling Scenarios', () => { + it('getFloodDataByPolygon without polygon should throw "No Polygon provided"', async () => { + try { + const response = await getFloodDataByPolygon('') + expect(response).toEqual('this line should not be reached') + } catch (err) { + expect(err.message).toEqual('getFloodDataByPolygon - No Polygon provided') + } + }) + + it('getFloodDataByPolygon should throw if ezriRequest throws"', async () => { + try { + await getFloodDataByPolygon(mockPolygons.throws) + expect('').toEqual('this line should not be reached') + } catch (err) { + console.log(err) + expect(err.message).toEqual('Fetching getFloodDataByPolygon failed: ') + } + }) +}) + +describe('getFloodDataByPolygon - Flood Zone Only Scenarios', () => { + it('getFloodDataByPolygon should return data as expected for FZ3 only', async () => { + const response = await getFloodDataByPolygon(mockPolygons.fz3_only) + expect(response).toEqual({ + floodZone: '3', + floodzone_2: false, + floodzone_3: true, + isRiskAdminArea: false + }) + }) + + it('getFloodDataByPolygon should return data as expected for FZ2 only', async () => { + const response = await getFloodDataByPolygon(mockPolygons.fz2_only) + expect(response).toEqual({ + floodZone: '2', + floodzone_2: true, + floodzone_3: false, + isRiskAdminArea: false + }) + }) + + it('getFloodDataByPolygon should return data as expected for FZ2 and FZ3', async () => { + const response = await getFloodDataByPolygon(mockPolygons.fz2_and_3) + expect(response).toEqual({ + floodZone: '3', + floodzone_2: true, + floodzone_3: true, + isRiskAdminArea: false + }) + }) +}) + +describe('getFloodDataByPolygon - Flood Zone with RiskAdmin Scenarios', () => { + it('getFloodDataByPolygon should return data as expected for FZ3 only in riskAdminArea', async () => { + const response = await getFloodDataByPolygon(mockPolygons.inRiskAdmin.fz3_only) + expect(response).toEqual({ + floodZone: '3', + floodzone_2: false, + floodzone_3: true, + isRiskAdminArea: true + }) + }) + + it('getFloodDataByPolygon should return data as expected for FZ2 only in riskAdminArea', async () => { + const response = await getFloodDataByPolygon(mockPolygons.inRiskAdmin.fz2_only) + expect(response).toEqual({ + floodZone: '2', + floodzone_2: true, + floodzone_3: false, + isRiskAdminArea: true + }) + }) + + it('getFloodDataByPolygon should return data as expected for FZ2 and FZ3 in riskAdminArea', async () => { + const response = await getFloodDataByPolygon(mockPolygons.inRiskAdmin.fz2_and_3) + expect(response).toEqual({ + floodZone: '3', + floodzone_2: true, + floodzone_3: true, + isRiskAdminArea: true + }) + }) +}) + +describe('generateKey', () => { + it('generateKey should stringify a polygon', async () => { + expect(generateKey([123, 456])).toEqual('[123,456]') + }) +}) diff --git a/server/services/flood-zones-by-polygon.js b/server/services/floodDataByPolygon.js similarity index 83% rename from server/services/flood-zones-by-polygon.js rename to server/services/floodDataByPolygon.js index d7aaae74..71cffe0b 100644 --- a/server/services/flood-zones-by-polygon.js +++ b/server/services/floodDataByPolygon.js @@ -6,9 +6,9 @@ const { getRiversAndSeaDefendedCC } = require('./agol/getRiversAndSeaDefendedCC' const { getRiversAndSeaUndefendedCC } = require('./agol/getRiversAndSeaUndefendedCC') const { getSurfaceWater } = require('./agol/getSurfaceWater') -const getFloodZonesByPolygon = async (polygon) => { +const getFloodDataByPolygon = async (polygon) => { if (!polygon) { - throw new Error('getFloodZonesByPolygon - No Polygon provided') + throw new Error('getFloodDataByPolygon - No Polygon provided') } try { const results = {} @@ -26,8 +26,8 @@ const getFloodZonesByPolygon = async (polygon) => { }) return results } catch (error) { - console.log('caught getFloodZonesByPolygon ERROR', error) - throw new Error('Fetching getFloodZonesByPolygon failed: ', error) + console.log('caught getFloodDataByPolygon ERROR', error) + throw new Error('Fetching getFloodDataByPolygon failed: ', error) } } @@ -37,8 +37,8 @@ const generateTimeout = 10000 // 10 seconds const staleTimeout = 59000 // 59 seconds module.exports = { - name: 'getFloodZonesByPolygon', - method: getFloodZonesByPolygon, + name: 'getFloodDataByPolygon', + method: getFloodDataByPolygon, options: { cache: { cache: 'FMFP', diff --git a/test/routes/flood-zone-results.js b/test/routes/flood-zone-results.js index 958cf085..5f7c5e9d 100644 --- a/test/routes/flood-zone-results.js +++ b/test/routes/flood-zone-results.js @@ -12,7 +12,7 @@ const zeroAreaPolygons = require('../services/zeroAreaPolygons') lab.experiment('flood-zone-results', () => { let server let restoreGetPsoContactsByPolygon - let restoreGetFloodZonesByPolygon + let restoregetFloodDataByPolygon let restoreIgnoreUseAutomatedService const LocalAuthorities = 'Ryedale' @@ -79,20 +79,20 @@ lab.experiment('flood-zone-results', () => { await server.initialize() restoreIgnoreUseAutomatedService = server.methods.ignoreUseAutomatedService restoreGetPsoContactsByPolygon = server.methods.getPsoContactsByPolygon - restoreGetFloodZonesByPolygon = server.methods.getFloodZonesByPolygon + restoregetFloodDataByPolygon = server.methods.getFloodDataByPolygon server.methods.getPsoContactsByPolygon = async () => optInPSOContactResponse }) lab.after(async () => { server.methods.getPsoContactsByPolygon = restoreGetPsoContactsByPolygon - server.methods.getFloodZonesByPolygon = restoreGetFloodZonesByPolygon + server.methods.getFloodDataByPolygon = restoregetFloodDataByPolygon server.methods.ignoreUseAutomatedService = restoreIgnoreUseAutomatedService await server.stop() }) lab.test('get flood-zone-results with a valid polygon should succeed', async () => { server.methods.getPsoContactsByPolygon = async () => optInPSOContactResponse - server.methods.getFloodZonesByPolygon = async () => zone1GetByPolygonResponse + server.methods.getFloodDataByPolygon = async () => zone1GetByPolygonResponse const response = await server.inject({ method: 'GET', url: fzrUrl }) const { payload } = response Code.expect(response.statusCode).to.equal(200) @@ -106,7 +106,7 @@ lab.experiment('flood-zone-results', () => { lab.test('get flood-zone-results with a empty contact details should succeed', async () => { server.methods.getPsoContactsByPolygon = async () => emptyPSOContactResponse - server.methods.getFloodZonesByPolygon = async () => zone1GetByPolygonResponse + server.methods.getFloodDataByPolygon = async () => zone1GetByPolygonResponse const response = await server.inject({ method: 'GET', url: fzrUrl }) const { payload } = response Code.expect(response.statusCode).to.equal(200) @@ -129,7 +129,7 @@ lab.experiment('flood-zone-results', () => { server.methods.getPsoContactsByPolygon = async () => optInPSOContactResponse - server.methods.getFloodZonesByPolygon = () => zone1GetByPolygonResponse + server.methods.getFloodDataByPolygon = () => zone1GetByPolygonResponse await server.inject(options) Code.expect(FloodRiskViewModelSpy.callCount).to.equal(1) @@ -269,7 +269,7 @@ lab.experiment('flood-zone-results', () => { EmailAddress: 'psoContact@example.com', AreaName: 'Yorkshire' }) - server.methods.getFloodZonesByPolygon = () => ({ in_england: false }) + server.methods.getFloodDataByPolygon = () => ({ in_england: false }) const response = await server.inject(options) Code.expect(response.statusCode).to.equal(302) @@ -290,7 +290,7 @@ lab.experiment('flood-zone-results', () => { AreaName: 'Yorkshire', LocalAuthorities }) - server.methods.getFloodZonesByPolygon = () => { + server.methods.getFloodDataByPolygon = () => { throw new Error('Deliberate Testing Error ') } @@ -306,7 +306,7 @@ lab.experiment('flood-zone-results', () => { '/flood-zone-results?center=[341638,352001]&location=Caldecott%2520Green&polygon=[[479472,484194],[479467,484032],[479678,484015],[479691,484176],[479472,484194]]' const options = { method: 'GET', url } server.methods.getPsoContactsByPolygon = async () => Object.assign({}, optInPSOContactResponse, { isEngland: false }) - server.methods.getFloodZonesByPolygon = () => ({ in_england: false }) + server.methods.getFloodDataByPolygon = () => ({ in_england: false }) const response = await server.inject(options) Code.expect(response.statusCode).to.equal(302) const { headers } = response @@ -410,7 +410,7 @@ lab.experiment('flood-zone-results', () => { async ([getByPolygonResponse, expectedFloodZone]) => { const getDocument = async () => { const options = { method: 'GET', url: fzrUrl } - server.methods.getFloodZonesByPolygon = () => getByPolygonResponse + server.methods.getFloodDataByPolygon = () => getByPolygonResponse server.methods.getPsoContactsByPolygon = async () => optInPSOContactResponse const response = await server.inject(options) const { payload } = response @@ -463,7 +463,7 @@ lab.experiment('flood-zone-results', () => { lab.test( `a flood zone request for ${expectedPolygonString} should not redirect`, async () => { - server.methods.getFloodZonesByPolygon = () => zone1GetByPolygonResponse + server.methods.getFloodDataByPolygon = () => zone1GetByPolygonResponse server.methods.getPsoContactsByPolygon = async () => optInPSOContactResponse const options = { method: 'GET', url: fzrBuffedZeroAreaPolygonUrl } const response = await server.inject(options) @@ -483,7 +483,7 @@ lab.experiment('flood-zone-results', () => { lab.test( `a flood zone request for ${zeroAreaPolygonString} should redirect: , ${fzrBuffedZeroAreaPolygonUrl}`, async () => { - server.methods.getFloodZonesByPolygon = () => zone1GetByPolygonResponse + server.methods.getFloodDataByPolygon = () => zone1GetByPolygonResponse server.methods.getPsoContactsByPolygon = async () => optInPSOContactResponse const options = { method: 'GET', url: fzrBuffedZeroAreaPolygonUrl } const response = await server.inject(options) From c94d85be445ac9aa349981a76b10cf9e0796ff4b Mon Sep 17 00:00:00 2001 From: Tedd Mason Date: Mon, 27 Jan 2025 09:41:14 +0000 Subject: [PATCH 5/9] Adding agol endpoints as configuration constants --- config/.env-example | 9 --------- config/index.js | 42 +++++++++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/config/.env-example b/config/.env-example index 6aae00c8..e2bd89e4 100644 --- a/config/.env-example +++ b/config/.env-example @@ -27,15 +27,6 @@ placeApiUrl=http://dummyuri agolClientId=TEST_AGOL_CLIENT_ID agolClientSecret=TEST_AGOL_CLIENT_SECRET agolServiceId=DUMMY_SERVICE_ID -agolCustomerTeamEndPoint=/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/0 -agolLocalAuthorityEndPoint=/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/1 -agolIsEnglandEndPoint=/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/2 -agolFloodZonesRiversAndSeaEndPoint=/Flood_Zones_2_and_3_Rivers_and_Sea_NON_PRODUCTION/FeatureServer/0 -agolRiversAndSeaDefendedEndPoint=/Rivers_and_Sea_Defended_Depth_NON_PRODUCTION/FeatureServer -agolRiversAndSeaUndefendedEndPoint=/Rivers_and_Sea_Undefended_Depth_NON_PRODUCTION/FeatureServer -agolRiversAndSeaDefendedCCP1EndPoint=/Rivers_and_Sea_Defended_Depth_CCP1_NON_PRODUCTION/FeatureServer -agolRiversAndSeaUndefendedCCP1EndPoint=/Rivers_and_Sea_Undefended_Depth_CCP1_NON_PRODUCTION/FeatureServer -agolSurfaceWaterEndPoint=/Risk_of_Flooding_from_Surface_Water_Depth_0mm_NON_PRODUCTION/FeatureServer/0 #EA Maps eamapsServiceUrl=http://dummyEAMapslUrl diff --git a/config/index.js b/config/index.js index 851fac8c..06b5debd 100644 --- a/config/index.js +++ b/config/index.js @@ -2,6 +2,30 @@ const { validateSchema } = require('./schema') const { toBool } = require('./toBool') require('./environment') +const agolEndpoints = { + customerTeamEndPoint: '/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/0', + localAuthorityEndPoint: '/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/1', + isEnglandEndPoint: '/Flood_Map_for_Planning_Query_Service_NON_PRODUCTION/FeatureServer/2', + floodZonesRiversAndSeaEndPoint: '/Flood_Zones_2_and_3_Rivers_and_Sea_NON_PRODUCTION/FeatureServer/0', + riversAndSeaDefendedEndPoint: '/Rivers_and_Sea_Defended_Depth_NON_PRODUCTION/FeatureServer', + riversAndSeaUndefendedEndPoint: '/Rivers_and_Sea_Undefended_Depth_NON_PRODUCTION/FeatureServer', + riversAndSeaDefendedCCP1EndPoint: '/Rivers_and_Sea_Defended_Depth_CCP1_NON_PRODUCTION/FeatureServer', + riversAndSeaUndefendedCCP1EndPoint: '/Rivers_and_Sea_Undefended_Depth_CCP1_NON_PRODUCTION/FeatureServer', + surfaceWaterEndPoint: '/Risk_of_Flooding_from_Surface_Water_Depth_0mm_NON_PRODUCTION/FeatureServer/0' +} + +const isProduction = () => { + return process.env.ENV === 'prd' || + process.env.ENV === 'production' || + process.env.ENV === 'prod' || + process.env.ENV === 'prod-green' || + process.env.ENV === 'prod-blue' +} + +const productioniseEndpoint = (endpoint) => { + return isProduction() ? endpoint.replace('_NON_PRODUCTION', '') : endpoint +} + const config = { env: process.env.ENV, appType: process.env.fmpAppType, @@ -37,15 +61,15 @@ const config = { serviceId: process.env.agolServiceId, serviceUrl: `https://services1.arcgis.com/${process.env.agolServiceId}/arcgis/rest/services`, vectorTileUrl: `https://tiles.arcgis.com/tiles/${process.env.agolServiceId}/arcgis/rest/services`, - customerTeamEndPoint: process.env.agolCustomerTeamEndPoint, - localAuthorityEndPoint: process.env.agolLocalAuthorityEndPoint, - isEnglandEndPoint: process.env.agolIsEnglandEndPoint, - floodZonesRiversAndSeaEndPoint: process.env.agolFloodZonesRiversAndSeaEndPoint, - riversAndSeaDefendedEndPoint: process.env.agolRiversAndSeaDefendedEndPoint, - riversAndSeaUndefendedEndPoint: process.env.agolRiversAndSeaUndefendedEndPoint, - riversAndSeaDefendedCCP1EndPoint: process.env.agolRiversAndSeaDefendedCCP1EndPoint, - riversAndSeaUndefendedCCP1EndPoint: process.env.agolRiversAndSeaUndefendedCCP1EndPoint, - surfaceWaterEndPoint: process.env.agolSurfaceWaterEndPoint + customerTeamEndPoint: productioniseEndpoint(agolEndpoints.customerTeamEndPoint), + localAuthorityEndPoint: productioniseEndpoint(agolEndpoints.localAuthorityEndPoint), + isEnglandEndPoint: productioniseEndpoint(agolEndpoints.isEnglandEndPoint), + floodZonesRiversAndSeaEndPoint: productioniseEndpoint(agolEndpoints.floodZonesRiversAndSeaEndPoint), + riversAndSeaDefendedEndPoint: productioniseEndpoint(agolEndpoints.riversAndSeaDefendedEndPoint), + riversAndSeaUndefendedEndPoint: productioniseEndpoint(agolEndpoints.riversAndSeaUndefendedEndPoint), + riversAndSeaDefendedCCP1EndPoint: productioniseEndpoint(agolEndpoints.riversAndSeaDefendedCCP1EndPoint), + riversAndSeaUndefendedCCP1EndPoint: productioniseEndpoint(agolEndpoints.riversAndSeaUndefendedCCP1EndPoint), + surfaceWaterEndPoint: productioniseEndpoint(agolEndpoints.surfaceWaterEndPoint) }, eamaps: { serviceUrl: process.env.eamapsServiceUrl, From e207478a1f6a91de35bd36c7768971cf38f254c9 Mon Sep 17 00:00:00 2001 From: Tedd Mason Date: Mon, 27 Jan 2025 12:26:57 +0000 Subject: [PATCH 6/9] Sorting some unit tests, moving server setup to helpers file --- .jest/setup.js | 18 ----- config/__tests__/config.spec.js | 65 ++++++++++++++++++- server/__test-helpers__/server.js | 20 +++++- .../__tests__/check-your-details.spec.js | 20 +++--- 4 files changed, 91 insertions(+), 32 deletions(-) diff --git a/.jest/setup.js b/.jest/setup.js index cc4b8574..1a2ef64b 100644 --- a/.jest/setup.js +++ b/.jest/setup.js @@ -1,16 +1,10 @@ jest.mock('../config/environment') -const createServer = require('../server') -const ORIGINAL_ENV = process.env const CONSOLE_ERROR = console.error const CONSOLE_LOG = console.log const DEV_NULL_LOG = () => {} -let server beforeEach(async () => { jest.resetAllMocks() - // add any common mockage here, eg json POSTs - server = await createServer() - await server.initialize() if (process.env.NOLOG) { console.log = DEV_NULL_LOG console.error = DEV_NULL_LOG @@ -18,18 +12,6 @@ beforeEach(async () => { }) afterEach(async () => { - try { - if (server) { - await server.stop() - } - } finally { - // reset environment variables after test - process.env = { ...ORIGINAL_ENV } - } console.log = CONSOLE_LOG console.error = CONSOLE_ERROR }) - -const getServer = () => server - -export { getServer } \ No newline at end of file diff --git a/config/__tests__/config.spec.js b/config/__tests__/config.spec.js index 994abad8..5b96c345 100644 --- a/config/__tests__/config.spec.js +++ b/config/__tests__/config.spec.js @@ -1,6 +1,9 @@ const { toBool } = require('../toBool') describe('Ensure config is correct', () => { + beforeEach(() => { + jest.resetModules() + }) it('test config', () => { expect(() => { require('../index') @@ -56,8 +59,66 @@ describe('Ensure config is correct', () => { tokenEndPoint: '/tokens/generateToken' }, defraMap: { - agolServiceUrl: 'https://services1.arcgis.com/DUMMY_SERVICE_ID/arcgis/rest/services', - agolVectorTileUrl: 'https://tiles.arcgis.com/tiles/DUMMY_SERVICE_ID/arcgis/rest/services', + layerNameSuffix: '_NON_PRODUCTION' + }, + riskAdminApi: { + url: 'http://riskadmin-api-url' + } + } + expect(config).toStrictEqual(expectedConfig) + }) + + it('test config values in production', () => { + jest.resetModules() + process.env.ENV = 'prod-green' + const { config } = require('../index') + const expectedConfig = { + env: 'prod-green', + appType: 'internal', + server: { port: '8050' }, + geoserver: 'http://dummyuri', + views: { isCached: false }, + analyticsAccount: 'replace_this', + googleVerification: 'replace_this', + fbAppId: 'replace_this', + httpTimeoutMs: '3000', + ordnanceSurvey: { + osGetCapabilitiesUrl: 'http://dummyuri', + osMapsUrl: 'http://dummyuri', + osNamesUrl: 'http://dummyuri', + osSearchKey: 'replace_this', + osMapsKey: 'replace_this', + osClientId: 'replace_this', + osClientSecret: 'replace_this' + }, + siteUrl: 'http://dummyuri', + functionAppUrl: 'http://dummyuri', + ignoreUseAutomatedService: true, + placeApi: { url: 'http://dummyuri' }, + agol: { + clientId: 'TEST_AGOL_CLIENT_ID', + clientSecret: 'TEST_AGOL_CLIENT_SECRET', + serviceId: 'DUMMY_SERVICE_ID', + serviceUrl: 'https://services1.arcgis.com/DUMMY_SERVICE_ID/arcgis/rest/services', + vectorTileUrl: 'https://tiles.arcgis.com/tiles/DUMMY_SERVICE_ID/arcgis/rest/services', + customerTeamEndPoint: '/Flood_Map_for_Planning_Query_Service/FeatureServer/0', + localAuthorityEndPoint: '/Flood_Map_for_Planning_Query_Service/FeatureServer/1', + isEnglandEndPoint: '/Flood_Map_for_Planning_Query_Service/FeatureServer/2', + floodZonesRiversAndSeaEndPoint: '/Flood_Zones_2_and_3_Rivers_and_Sea/FeatureServer/0', + riversAndSeaDefendedEndPoint: '/Rivers_and_Sea_Defended_Depth/FeatureServer', + riversAndSeaUndefendedEndPoint: '/Rivers_and_Sea_Undefended_Depth/FeatureServer', + riversAndSeaDefendedCCP1EndPoint: '/Rivers_and_Sea_Defended_Depth_CCP1/FeatureServer', + riversAndSeaUndefendedCCP1EndPoint: '/Rivers_and_Sea_Undefended_Depth_CCP1/FeatureServer', + surfaceWaterEndPoint: '/Risk_of_Flooding_from_Surface_Water_Depth_0mm/FeatureServer/0' + }, + eamaps: { + serviceUrl: 'http://dummyEAMapslUrl', + product1User: 'PRODUCT1_USER', + product1Password: 'PRODUCT1_PASSWORD', + product1EndPoint: '/rest/services/FMfP/FMFPGetProduct1/GPServer/fmfp_get_product1/execute', + tokenEndPoint: '/tokens/generateToken' + }, + defraMap: { layerNameSuffix: '_NON_PRODUCTION' }, riskAdminApi: { diff --git a/server/__test-helpers__/server.js b/server/__test-helpers__/server.js index d41ec8dd..0ff16094 100644 --- a/server/__test-helpers__/server.js +++ b/server/__test-helpers__/server.js @@ -1,5 +1,17 @@ -const { getServer } = require('../../.jest/setup') const constants = require('../constants') +const createServer = require('../../server') +let server + +beforeEach(async () => { + server = await createServer() + await server.initialize() +}) + +afterEach(async () => { + await server.stop() +}) + +const getServer = () => server const submitGetRequest = async (options, header, expectedResponseCode = constants.statusCodes.OK) => { options.method = 'GET' @@ -13,7 +25,8 @@ const submitGetRequest = async (options, header, expectedResponseCode = constant const submitPostRequest = async (options, expectedResponseCode = constants.statusCodes.REDIRECT) => { options.method = 'POST' - return submitRequest(options, expectedResponseCode) + const response = await submitRequest(options, expectedResponseCode) + return response } const submitPostRequestExpectHandledError = async (options, errorMessage) => { @@ -45,5 +58,6 @@ module.exports = { submitGetRequest, submitPostRequest, submitPostRequestExpectHandledError, - submitPostRequestExpectServiceError + submitPostRequestExpectServiceError, + getServer } diff --git a/server/routes/__tests__/check-your-details.spec.js b/server/routes/__tests__/check-your-details.spec.js index d5c81c7c..0ef637e1 100644 --- a/server/routes/__tests__/check-your-details.spec.js +++ b/server/routes/__tests__/check-your-details.spec.js @@ -1,28 +1,30 @@ -const { getServer } = require('../../../.jest/setup') const { assertCopy } = require('../../__test-helpers__/copy') const { submitGetRequest, - submitPostRequest + submitPostRequest, + getServer } = require('../../__test-helpers__/server') const { mockPolygons } = require('../../services/__tests__/__mocks__/floodZonesByPolygonMock') const { getCentreOfPolygon } = require('../../services/shape-utils') jest.mock('../../services/agol/getContacts') jest.mock('../../services/address') -jest.mock('@hapi/wreck') const wreck = require('@hapi/wreck') const user = { fullName: 'John Smith', email: 'john.smith@email.com' } - const url = '/check-your-details' +let postSpy describe('Check your details page', () => { beforeEach(() => { - wreck.post.mockResolvedValue({ - payload: { - applicationReferenceNumber: '12345', - nextTask: 'SEND_CONFIRMATION_EMAIL' + postSpy = jest.spyOn(wreck, 'post').mockImplementation(() => { + console.log('in mock') + return { + payload: { + applicationReferenceNumber: '12345', + nextTask: 'SEND_CONFIRMATION_EMAIL' + } } }) }) @@ -123,7 +125,7 @@ describe('Check your details page', () => { postcode: 'M1 1AA' }) - expect(wreck.post).toHaveBeenCalledWith('http://dummyuri/order-product-four', { json: true, payload: expectedPayload }) + expect(postSpy).toHaveBeenCalledWith('http://dummyuri/order-product-four', { json: true, payload: expectedPayload }) } }) }) From 3e5f52738cf71c2e3918cd0a4fa066aaa2bd3dc7 Mon Sep 17 00:00:00 2001 From: Tedd Mason Date: Mon, 27 Jan 2025 13:14:14 +0000 Subject: [PATCH 7/9] Adding getFloodZoneByPolygon to server methods, and switching check-your-details page to use zone get only --- server/index.js | 2 + .../__tests__/check-your-details.spec.js | 3 +- server/routes/__tests__/results.spec.js | 2 +- server/routes/check-your-details.js | 9 +-- ...lygonMock.js => floodDataByPolygonMock.js} | 0 .../__mocks__/floodZoneByPolygonMock.js | 5 ++ .../__tests__/floodDataByPolygon.spec.js | 2 +- .../__tests__/floodZoneByPolygon.spec.js | 61 +++++++++++++++++++ server/services/floodZoneByPolygon.js | 33 ++++++++++ 9 files changed, 106 insertions(+), 11 deletions(-) rename server/services/__tests__/__mocks__/{floodZonesByPolygonMock.js => floodDataByPolygonMock.js} (100%) create mode 100644 server/services/__tests__/__mocks__/floodZoneByPolygonMock.js create mode 100644 server/services/__tests__/floodZoneByPolygon.spec.js create mode 100644 server/services/floodZoneByPolygon.js diff --git a/server/index.js b/server/index.js index ebae9e66..9bb94dd5 100644 --- a/server/index.js +++ b/server/index.js @@ -28,9 +28,11 @@ async function createServer () { ] }) + // Cached server methods await server.method(require('./services/pso-contact')) await server.method(require('./services/pso-contact-by-polygon')) await server.method(require('./services/floodDataByPolygon')) + await server.method(require('./services/floodZoneByPolygon')) await server.method(require('./services/use-automated')) // Register the plugins diff --git a/server/routes/__tests__/check-your-details.spec.js b/server/routes/__tests__/check-your-details.spec.js index 0ef637e1..25049562 100644 --- a/server/routes/__tests__/check-your-details.spec.js +++ b/server/routes/__tests__/check-your-details.spec.js @@ -4,7 +4,7 @@ const { submitPostRequest, getServer } = require('../../__test-helpers__/server') -const { mockPolygons } = require('../../services/__tests__/__mocks__/floodZonesByPolygonMock') +const { mockPolygons } = require('../../services/__tests__/__mocks__/floodZoneByPolygonMock') const { getCentreOfPolygon } = require('../../services/shape-utils') jest.mock('../../services/agol/getContacts') jest.mock('../../services/address') @@ -19,7 +19,6 @@ let postSpy describe('Check your details page', () => { beforeEach(() => { postSpy = jest.spyOn(wreck, 'post').mockImplementation(() => { - console.log('in mock') return { payload: { applicationReferenceNumber: '12345', diff --git a/server/routes/__tests__/results.spec.js b/server/routes/__tests__/results.spec.js index 66c9a8d4..0bf05b50 100644 --- a/server/routes/__tests__/results.spec.js +++ b/server/routes/__tests__/results.spec.js @@ -1,6 +1,6 @@ const { submitGetRequest } = require('../../__test-helpers__/server') const { assertCopy } = require('../../__test-helpers__/copy') -const { mockPolygons } = require('../../services/__tests__/__mocks__/floodZonesByPolygonMock') +const { mockPolygons } = require('../../services/__tests__/__mocks__/floodDataByPolygonMock') const { config } = require('../../../config') jest.mock('../../services/agol/getContacts') diff --git a/server/routes/check-your-details.js b/server/routes/check-your-details.js index 87e82dd4..cb54d9ef 100644 --- a/server/routes/check-your-details.js +++ b/server/routes/check-your-details.js @@ -15,9 +15,6 @@ const getFunctionAppResponse = async (data) => { return wreck.post(publishToQueueURL, { json: true, payload: JSON.stringify(payload) }) } -const floodZoneResultsToFloodZone = (floodZoneResults) => - floodZoneResults.floodzone_3 ? '3' : floodZoneResults.floodzone_2 ? '2' : '1' - module.exports = [ { method: 'GET', @@ -26,8 +23,7 @@ module.exports = [ description: 'Application Review Summary', handler: async (request, h) => { const { polygon, fullName, recipientemail } = request.query - const floodZoneResults = await request.server.methods.getFloodDataByPolygon(polygon) - const floodZone = floodZoneResultsToFloodZone(floodZoneResults) + const { floodZone } = await request.server.methods.getFloodZoneByPolygon(polygon) const contactUrl = `/contact?polygon=${polygon}&fullName=${fullName}&recipientemail=${recipientemail}` const confirmLocationUrl = `confirm-location?fullName=${fullName}&recipientemail=${recipientemail}` return h.view('check-your-details', { polygon, fullName, recipientemail, contactUrl, confirmLocationUrl, floodZone }) @@ -43,8 +39,7 @@ module.exports = [ const payload = request.payload || {} const { recipientemail, fullName, polygon } = payload const coordinates = getCentreOfPolygon(polygon) - const floodZoneResults = await request.server.methods.getFloodDataByPolygon(polygon) - const zoneNumber = floodZoneResultsToFloodZone(floodZoneResults) + const { floodZone: zoneNumber } = await request.server.methods.getFloodZoneByPolygon(polygon) let applicationReferenceNumber // Check if p4Request is duplicate diff --git a/server/services/__tests__/__mocks__/floodZonesByPolygonMock.js b/server/services/__tests__/__mocks__/floodDataByPolygonMock.js similarity index 100% rename from server/services/__tests__/__mocks__/floodZonesByPolygonMock.js rename to server/services/__tests__/__mocks__/floodDataByPolygonMock.js diff --git a/server/services/__tests__/__mocks__/floodZoneByPolygonMock.js b/server/services/__tests__/__mocks__/floodZoneByPolygonMock.js new file mode 100644 index 00000000..31ef9fbf --- /dev/null +++ b/server/services/__tests__/__mocks__/floodZoneByPolygonMock.js @@ -0,0 +1,5 @@ +const mockPolygons = require('../../__data__/mockPolygons.json') + +jest.mock('../../agol/getFloodZones') + +module.exports = { mockPolygons } diff --git a/server/services/__tests__/floodDataByPolygon.spec.js b/server/services/__tests__/floodDataByPolygon.spec.js index a26d5058..b194a742 100644 --- a/server/services/__tests__/floodDataByPolygon.spec.js +++ b/server/services/__tests__/floodDataByPolygon.spec.js @@ -1,4 +1,4 @@ -const { mockPolygons } = require('./__mocks__/floodZonesByPolygonMock') +const { mockPolygons } = require('./__mocks__/floodDataByPolygonMock') const { method: getFloodDataByPolygon, options: { generateKey } diff --git a/server/services/__tests__/floodZoneByPolygon.spec.js b/server/services/__tests__/floodZoneByPolygon.spec.js new file mode 100644 index 00000000..e449a92f --- /dev/null +++ b/server/services/__tests__/floodZoneByPolygon.spec.js @@ -0,0 +1,61 @@ +const { mockPolygons } = require('./__mocks__/floodZoneByPolygonMock') +const { + method: getFloodZoneByPolygon, + options: { generateKey } +} = require('../../../server/services/floodZoneByPolygon') + +describe('getFloodZoneByPolygon - Error Handling Scenarios', () => { + it('getFloodZoneByPolygon without polygon should throw "No Polygon provided"', async () => { + try { + const response = await getFloodZoneByPolygon('') + expect(response).toEqual('this line should not be reached') + } catch (err) { + expect(err.message).toEqual('getFloodZoneByPolygon - No Polygon provided') + } + }) + + it('getFloodZoneByPolygon should throw if esriRequest throws"', async () => { + try { + await getFloodZoneByPolygon(mockPolygons.throws) + expect('').toEqual('this line should not be reached') + } catch (err) { + console.log(err) + expect(err.message).toEqual('Fetching getFloodZoneByPolygon failed: ') + } + }) +}) + +describe('getFloodZoneByPolygon - Flood Zone Only Scenarios', () => { + it('getFloodZoneByPolygon should return data as expected for FZ3 only', async () => { + const response = await getFloodZoneByPolygon(mockPolygons.fz3_only) + expect(response).toEqual({ + floodZone: '3', + floodzone_2: false, + floodzone_3: true + }) + }) + + it('getFloodZoneByPolygon should return data as expected for FZ2 only', async () => { + const response = await getFloodZoneByPolygon(mockPolygons.fz2_only) + expect(response).toEqual({ + floodZone: '2', + floodzone_2: true, + floodzone_3: false + }) + }) + + it('getFloodZoneByPolygon should return data as expected for FZ2 and FZ3', async () => { + const response = await getFloodZoneByPolygon(mockPolygons.fz2_and_3) + expect(response).toEqual({ + floodZone: '3', + floodzone_2: true, + floodzone_3: true + }) + }) +}) + +describe('generateKey', () => { + it('generateKey should stringify a polygon', async () => { + expect(generateKey([123, 456])).toEqual('[123,456]') + }) +}) diff --git a/server/services/floodZoneByPolygon.js b/server/services/floodZoneByPolygon.js new file mode 100644 index 00000000..70d40e2a --- /dev/null +++ b/server/services/floodZoneByPolygon.js @@ -0,0 +1,33 @@ +const { getFloodZones } = require('./agol/getFloodZones') + +const getFloodZoneByPolygon = async (polygon) => { + if (!polygon) { + throw new Error('getFloodZoneByPolygon - No Polygon provided') + } + try { + return await getFloodZones({ geometryType: 'esriGeometryPolygon', polygon }) + } catch (error) { + console.log('caught getFloodZoneByPolygon ERROR', error) + throw new Error('Fetching getFloodZoneByPolygon failed: ', error) + } +} + +const expiresIn = 600000 // 10 minutes +const staleIn = 540000 // 9 minutes +const generateTimeout = 10000 // 10 seconds +const staleTimeout = 59000 // 59 seconds + +module.exports = { + name: 'getFloodZoneByPolygon', + method: getFloodZoneByPolygon, + options: { + cache: { + cache: 'FMFP', + expiresIn, + staleIn, + generateTimeout, + staleTimeout + }, + generateKey: (polygon) => JSON.stringify(polygon) + } +} From f8d429e739561d45d3eacd0bfa56a93e24af8d9d Mon Sep 17 00:00:00 2001 From: Tedd Mason Date: Mon, 27 Jan 2025 13:41:34 +0000 Subject: [PATCH 8/9] Sonarcloud fixes --- .../services/agol/getRiversAndSeaDefended.js | 25 ++------------ .../agol/getRiversAndSeaDefendedCC.js | 24 ++----------- .../agol/getRiversAndSeaUndefended.js | 21 ++---------- .../agol/getRiversAndSeaUndefendedCC.js | 21 ++---------- server/services/agol/layerDefs.js | 4 +++ server/services/agol/layerRiskBands.js | 34 +++++++++++++++++++ 6 files changed, 50 insertions(+), 79 deletions(-) create mode 100644 server/services/agol/layerDefs.js create mode 100644 server/services/agol/layerRiskBands.js diff --git a/server/services/agol/getRiversAndSeaDefended.js b/server/services/agol/getRiversAndSeaDefended.js index 85eeec0c..4c864794 100644 --- a/server/services/agol/getRiversAndSeaDefended.js +++ b/server/services/agol/getRiversAndSeaDefended.js @@ -1,33 +1,14 @@ const { config } = require('../../../config') const { esriRestRequest, makePolygonGeometry } = require('.') - -const layerDefs = { 0: '', 1: '', 2: '' } - -const LayerRiskBand = { - 0: { - riskBandId: 0, - riskBandPercent: '3.3' - }, - 1: { - riskBandId: 1, - riskBandPercent: '1' - }, - 2: { - riskBandId: 2, - riskBandPercent: '0.1' - }, - 3: { - riskBandId: 3, - riskBandPercent: false - } -} +const { riversAndSeaDefended: layerRiskBand } = require('./layerRiskBands') +const { riversAndSeaDefended: layerDefs } = require('./layerDefs') const assignResponse = (response) => { const lowestLayerId = response.layers.reduce((lowest, layer) => { return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest }, 3) return { - riversAndSeaDefended: LayerRiskBand[lowestLayerId] + riversAndSeaDefended: layerRiskBand[lowestLayerId] } } diff --git a/server/services/agol/getRiversAndSeaDefendedCC.js b/server/services/agol/getRiversAndSeaDefendedCC.js index e8f231dd..6c991dc7 100644 --- a/server/services/agol/getRiversAndSeaDefendedCC.js +++ b/server/services/agol/getRiversAndSeaDefendedCC.js @@ -1,32 +1,14 @@ const { config } = require('../../../config') const { esriRestRequest, makePolygonGeometry } = require('.') +const { riversAndSeaDefended: layerRiskBand } = require('./layerRiskBands') +const { riversAndSeaDefended: layerDefs } = require('./layerDefs') -const layerDefs = { 0: '', 1: '', 2: '' } - -const LayerRiskBand = { - 0: { - riskBandId: 0, - riskBandPercent: '3.3' - }, - 1: { - riskBandId: 1, - riskBandPercent: '1' - }, - 2: { - riskBandId: 2, - riskBandPercent: '0.1' - }, - 3: { - riskBandId: 3, - riskBandPercent: false - } -} const assignResponse = (response) => { const lowestLayerId = response.layers.reduce((lowest, layer) => { return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest }, 3) return { - riversAndSeaDefendedCC: LayerRiskBand[lowestLayerId] + riversAndSeaDefendedCC: layerRiskBand[lowestLayerId] } } diff --git a/server/services/agol/getRiversAndSeaUndefended.js b/server/services/agol/getRiversAndSeaUndefended.js index 247e290f..11e94da6 100644 --- a/server/services/agol/getRiversAndSeaUndefended.js +++ b/server/services/agol/getRiversAndSeaUndefended.js @@ -1,29 +1,14 @@ const { config } = require('../../../config') const { esriRestRequest, makePolygonGeometry } = require('.') - -const layerDefs = { 0: '', 1: '' } - -const LayerRiskBand = { - 0: { - riskBandId: 0, - riskBandPercent: '1' - }, - 1: { - riskBandId: 1, - riskBandPercent: '0.1' - }, - 2: { - riskBandId: 2, - riskBandPercent: false - } -} +const { riversAndSeaUndefended: layerRiskBand } = require('./layerRiskBands') +const { riversAndSeaUndefended: layerDefs } = require('./layerDefs') const assignResponse = (response) => { const lowestLayerId = response.layers.reduce((lowest, layer) => { return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest }, 2) return { - riversAndSeaUndefended: LayerRiskBand[lowestLayerId] + riversAndSeaUndefended: layerRiskBand[lowestLayerId] } } diff --git a/server/services/agol/getRiversAndSeaUndefendedCC.js b/server/services/agol/getRiversAndSeaUndefendedCC.js index 72c13734..30919b97 100644 --- a/server/services/agol/getRiversAndSeaUndefendedCC.js +++ b/server/services/agol/getRiversAndSeaUndefendedCC.js @@ -1,29 +1,14 @@ const { config } = require('../../../config') const { esriRestRequest, makePolygonGeometry } = require('.') - -const layerDefs = { 0: '', 1: '' } - -const LayerRiskBand = { - 0: { - riskBandId: 0, - riskBandPercent: '1' - }, - 1: { - riskBandId: 1, - riskBandPercent: '0.1' - }, - 2: { - riskBandId: 2, - riskBandPercent: false - } -} +const { riversAndSeaUndefended: layerRiskBand } = require('./layerRiskBands') +const { riversAndSeaUndefended: layerDefs } = require('./layerDefs') const assignResponse = (response) => { const lowestLayerId = response.layers.reduce((lowest, layer) => { return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest }, 2) return { - riversAndSeaUndefendedCC: LayerRiskBand[lowestLayerId] + riversAndSeaUndefendedCC: layerRiskBand[lowestLayerId] } } diff --git a/server/services/agol/layerDefs.js b/server/services/agol/layerDefs.js new file mode 100644 index 00000000..b2f27413 --- /dev/null +++ b/server/services/agol/layerDefs.js @@ -0,0 +1,4 @@ +module.exports = { + riversAndSeaDefended: { 0: '', 1: '', 2: '' }, + riversAndSeaUndefended: { 0: '', 1: '' } +} diff --git a/server/services/agol/layerRiskBands.js b/server/services/agol/layerRiskBands.js new file mode 100644 index 00000000..9dc99d2d --- /dev/null +++ b/server/services/agol/layerRiskBands.js @@ -0,0 +1,34 @@ +module.exports = { + riversAndSeaDefended: { + 0: { + riskBandId: 0, + riskBandPercent: '3.3' + }, + 1: { + riskBandId: 1, + riskBandPercent: '1' + }, + 2: { + riskBandId: 2, + riskBandPercent: '0.1' + }, + 3: { + riskBandId: 3, + riskBandPercent: false + } + }, + riversAndSeaUndefended: { + 0: { + riskBandId: 0, + riskBandPercent: '1' + }, + 1: { + riskBandId: 1, + riskBandPercent: '0.1' + }, + 2: { + riskBandId: 2, + riskBandPercent: false + } + } +} From e7302153d4aa1ca1c931aea72f360da5192f9b1d Mon Sep 17 00:00:00 2001 From: Tedd Mason Date: Mon, 27 Jan 2025 13:58:50 +0000 Subject: [PATCH 9/9] sonarcloud fixes --- server/services/agol/esriRestRequest.js | 2 +- server/services/agol/getRiversAndSeaDefended.js | 2 +- server/services/agol/getRiversAndSeaDefendedCC.js | 2 +- server/services/agol/getRiversAndSeaUndefended.js | 2 +- .../services/agol/getRiversAndSeaUndefendedCC.js | 2 +- server/views/results.html | 15 --------------- 6 files changed, 5 insertions(+), 20 deletions(-) diff --git a/server/services/agol/esriRestRequest.js b/server/services/agol/esriRestRequest.js index 85a7bb03..06a63c83 100644 --- a/server/services/agol/esriRestRequest.js +++ b/server/services/agol/esriRestRequest.js @@ -17,7 +17,7 @@ const esriRestRequest = async (endPoint, geometry, geometryType, layerDefs) => { returnCountOnly: 'true' } } - return await request(url, requestObject) + return request(url, requestObject) } module.exports = { esriRestRequest } diff --git a/server/services/agol/getRiversAndSeaDefended.js b/server/services/agol/getRiversAndSeaDefended.js index 4c864794..51397ee2 100644 --- a/server/services/agol/getRiversAndSeaDefended.js +++ b/server/services/agol/getRiversAndSeaDefended.js @@ -6,7 +6,7 @@ const { riversAndSeaDefended: layerDefs } = require('./layerDefs') const assignResponse = (response) => { const lowestLayerId = response.layers.reduce((lowest, layer) => { return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest - }, 3) + }, Number(Object.keys(layerRiskBand)[Object.keys(layerRiskBand).length - 1])) // Max layer ID for comparison return { riversAndSeaDefended: layerRiskBand[lowestLayerId] } diff --git a/server/services/agol/getRiversAndSeaDefendedCC.js b/server/services/agol/getRiversAndSeaDefendedCC.js index 6c991dc7..6beeaece 100644 --- a/server/services/agol/getRiversAndSeaDefendedCC.js +++ b/server/services/agol/getRiversAndSeaDefendedCC.js @@ -6,7 +6,7 @@ const { riversAndSeaDefended: layerDefs } = require('./layerDefs') const assignResponse = (response) => { const lowestLayerId = response.layers.reduce((lowest, layer) => { return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest - }, 3) + }, Number(Object.keys(layerRiskBand)[Object.keys(layerRiskBand).length - 1])) // Max layer ID for comparison return { riversAndSeaDefendedCC: layerRiskBand[lowestLayerId] } diff --git a/server/services/agol/getRiversAndSeaUndefended.js b/server/services/agol/getRiversAndSeaUndefended.js index 11e94da6..e43625db 100644 --- a/server/services/agol/getRiversAndSeaUndefended.js +++ b/server/services/agol/getRiversAndSeaUndefended.js @@ -6,7 +6,7 @@ const { riversAndSeaUndefended: layerDefs } = require('./layerDefs') const assignResponse = (response) => { const lowestLayerId = response.layers.reduce((lowest, layer) => { return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest - }, 2) + }, Number(Object.keys(layerRiskBand)[Object.keys(layerRiskBand).length - 1])) // Max layer ID for comparison return { riversAndSeaUndefended: layerRiskBand[lowestLayerId] } diff --git a/server/services/agol/getRiversAndSeaUndefendedCC.js b/server/services/agol/getRiversAndSeaUndefendedCC.js index 30919b97..5963014c 100644 --- a/server/services/agol/getRiversAndSeaUndefendedCC.js +++ b/server/services/agol/getRiversAndSeaUndefendedCC.js @@ -6,7 +6,7 @@ const { riversAndSeaUndefended: layerDefs } = require('./layerDefs') const assignResponse = (response) => { const lowestLayerId = response.layers.reduce((lowest, layer) => { return (layer.count > 0 && layer.id < lowest) ? layer.id : lowest - }, 2) + }, Number(Object.keys(layerRiskBand)[Object.keys(layerRiskBand).length - 1])) // Max layer ID for comparison return { riversAndSeaUndefendedCC: layerRiskBand[lowestLayerId] } diff --git a/server/views/results.html b/server/views/results.html index 50963966..4f09c2d3 100644 --- a/server/views/results.html +++ b/server/views/results.html @@ -10,21 +10,6 @@

This location is in flood zone {{floodData.floodZone}}

- Flood data results: {{ floodData | dump | safe }}