From 2edb362b1ffefc9f2837953232b94231a1f475fb Mon Sep 17 00:00:00 2001 From: Lucia Sarni Date: Fri, 7 Jul 2023 15:16:46 +0200 Subject: [PATCH] LIWO-875: retry requests for getFeature (#420) * Retry getFeature request if the response type isn't json * Show notifications if the retries fail * Avoid using ? * Return valid geojson to prevent map error * Add some comments * Remove unnecessary change * Fail if any of the layers isn't available * Fix outputFormat --- src/lib/leaflet-utils/layer-factory.js | 5 +- src/lib/load-geojson.js | 69 ++++++++++++++++++++++---- src/lib/load-layersets.js | 2 +- src/store.js | 10 ++-- 4 files changed, 69 insertions(+), 17 deletions(-) diff --git a/src/lib/leaflet-utils/layer-factory.js b/src/lib/leaflet-utils/layer-factory.js index 2f9ab5e8..d1dc564f 100644 --- a/src/lib/leaflet-utils/layer-factory.js +++ b/src/lib/leaflet-utils/layer-factory.js @@ -140,7 +140,10 @@ export function createClusterGeoJson (layer, onClick) { const unselectedGeoJson = { ...layer.geojson } - unselectedGeoJson.features = unselectedGeoJson.features.filter(feature => !feature.properties.selected) + if (unselectedGeoJson.features) { + unselectedGeoJson.features = unselectedGeoJson.features.filter(feature => !feature.properties.selected) + } + return L.geoJson(unselectedGeoJson, options) } diff --git a/src/lib/load-geojson.js b/src/lib/load-geojson.js index ba022a0f..a6f1804b 100644 --- a/src/lib/load-geojson.js +++ b/src/lib/load-geojson.js @@ -1,4 +1,8 @@ import mapConfig from '../map.config.js' +import store from '@/store' + +const MAX_RETRIES = 5 +const RETRY_DELAY = 1000 // Delay in milliseconds between retries const requestOptions = ({ namespace, layer }) => ({ isActive: true, @@ -13,7 +17,57 @@ const requestOptions = ({ namespace, layer }) => ({ maxFeatures: 3000 }) -export async function loadGeojson (jsonLayer, { filteredIds = [] } = {}) { +const getFeatures = (url, jsonLayer, layerSetId, retries = 0) => { + return new Promise((resolve, reject) => { + fetch(url, { mode: 'cors' }) + .then(resp => { + // Workaround for geoserver issue: + // When the request fails it returns 200 but the response is an XML with an error message + // We can detect this by checking the content-type header + if ( + resp.headers.get('content-type') && + !resp.headers.get('content-type').includes('application/json') + ) { + caches.delete(url) + throw new Error('Response is not JSON') + } + return resp.json() + }) + .then(geojson => { + geojson.features = geojson.features.map(feature => { + feature.properties.isControllable = !!jsonLayer.iscontrollayer + feature.properties.icon = 'default' + return feature + }) + + resolve(geojson) + }) + .catch(error => { + if (retries < MAX_RETRIES) { + console.warn(`Retry ${retries + 1} - ${error}`) + setTimeout( + () => + getFeatures(url, jsonLayer, layerSetId, retries + 1) + .then(resolve) + .catch(reject), + RETRY_DELAY + ) + } else { + console.error(`Max retries exceeded - ${error}`) + // Show an error notification if the layer is not available + const notification = { + message: 'Probeer het over 5 tot 10 minuten opnieuw.', + type: 'error', + show: true + } + store.commit('addNotificationById', { id: layerSetId, notification }) + reject(error) + } + }) + }) +} + +export async function loadGeojson (jsonLayer, layerSetId, { filteredIds = [] } = {}) { // fetch the geojson and add it to the layer // no json, nothing to do @@ -33,17 +87,12 @@ export async function loadGeojson (jsonLayer, { filteredIds = [] } = {}) { const services = await mapConfig.getServices() const url = `${services.STATIC_GEOSERVER_URL}?${params}${filterString}` - const result = fetch(url, { mode: 'cors' }) - .then(resp => resp.json()) + const result = getFeatures(url, jsonLayer, layerSetId) .then(geojson => { - geojson.features = geojson.features.map(feature => { - feature.properties.isControllable = !!jsonLayer.iscontrollayer - feature.properties.icon = 'default' - return feature - }) - return geojson }) - .catch(error => console.warn(error, jsonLayer)) + .catch(error => { + console.warn(error, jsonLayer) + }) return result } diff --git a/src/lib/load-layersets.js b/src/lib/load-layersets.js index e7243192..b9bffbab 100644 --- a/src/lib/load-layersets.js +++ b/src/lib/load-layersets.js @@ -73,7 +73,7 @@ export async function loadLayerSetById (id, options) { const variants = await Promise.all( layer.variants.map(async (variant) => { if (_.includes(['json', 'cluster'], variant.map.type)) { - const geojson = await loadGeojson(variant.map) + const geojson = await loadGeojson(variant.map, layerSet.id) variant.map.geojson = geojson } return variant diff --git a/src/store.js b/src/store.js index 18b02f89..fcd6cdc7 100644 --- a/src/store.js +++ b/src/store.js @@ -91,7 +91,7 @@ export default new Vuex.Store({ commit('setSelectedVariantIndexByBreachBandId', { selectedIndex, breachBandId }) } }, - async loadLayerSetById (state, { id }) { + async loadLayerSetById ({ commit, state }, { id }) { // Skip if we already loaded this layerSet if (_.has(state.layerSetsById, id)) { return @@ -112,15 +112,15 @@ export default new Vuex.Store({ // The layers are in a deep structure. Flatten it before building the notifications const layers = flattenLayerSet(layerSet) - const notifications = buildLayerSetNotifications(layers) + const currentNotifications = state.notificationsById[id] || [] + const notifications = [...currentNotifications, ...buildLayerSetNotifications(layers)] // TODO: the function is called setLayerSet[s] // but it only loads the layers of 1 layerSet, make this consistent - - state.commit('setLayerSetById', { id, layerSet: layerSet }) + commit('setLayerSetById', { id, layerSet: layerSet }) // TODO: why not in the view... - state.commit('setNotificationsById', { id, notifications }) + commit('setNotificationsById', { id, notifications }) } },