From ff10464f57c08320c0b52a535412d5257ebed046 Mon Sep 17 00:00:00 2001 From: mahmoud adel <58145645+mahmoudadel54@users.noreply.github.com> Date: Mon, 27 May 2024 17:16:14 +0300 Subject: [PATCH] [Backport 2024.01.xx]: #10281: Use Cache Options functionality extended to be applied on all the layers from a service (#10349, #10361) (#10363) * fix conflicts in merging #10281: Use Cache Options functionality extended to be applied on all the layers from a service (#10349) * #10281: Use Cache Options functionality extended to be applied on all the layers from a service (#10361) * #10281: Use Cache Options functionality extended to be applied on all the layers from a service Description: - handle a missing case of showing a warning message in case of not able to fetch the grid sets - add unit tests * fix FE unit test failure #10281 * fix FE unit test failure #10281 --- .../fragments/settings/WMSCacheOptions.jsx | 5 +- .../__tests__/WMSCacheOptions-test.jsx | 25 ++++++++ .../RasterAdvancedSettings.js | 8 +++ .../__tests__/RasterAdvancedSettings-test.js | 21 ++++++- web/client/epics/__tests__/catalog-test.js | 61 +++++++++++++++++++ web/client/epics/catalog.js | 32 +++++++++- .../plugins/widgetbuilder/MapBuilder.jsx | 23 ++++++- web/client/translations/data.de-DE.json | 4 ++ web/client/translations/data.en-US.json | 4 ++ web/client/translations/data.es-ES.json | 4 ++ web/client/translations/data.fr-FR.json | 4 ++ web/client/translations/data.it-IT.json | 4 ++ 12 files changed, 188 insertions(+), 7 deletions(-) diff --git a/web/client/components/TOC/fragments/settings/WMSCacheOptions.jsx b/web/client/components/TOC/fragments/settings/WMSCacheOptions.jsx index 04ac9fd5fc..0df967a465 100644 --- a/web/client/components/TOC/fragments/settings/WMSCacheOptions.jsx +++ b/web/client/components/TOC/fragments/settings/WMSCacheOptions.jsx @@ -198,7 +198,10 @@ function WMSCacheOptions({ }) { const [tileGridLoading, setTileGridLoading] = useState(false); - const [tileGridsResponseMsgId, setTileGridsResponseMsgId] = useState(''); + const [tileGridsResponseMsgId, setTileGridsResponseMsgId] = useState(() => { + const noTileGrids = layer?.tileGridStrategy === 'custom' && layer?.tiled && layer?.tileGrids?.length === 0; + return noTileGrids ? "layerProperties.noConfiguredGridSets" : ""; + }); const [tileGridsResponseMsgStyle, setTileGridsResponseMsgStyle] = useState(''); const [standardTileGridInfo, setStandardTileGridInfo] = useState({}); diff --git a/web/client/components/TOC/fragments/settings/__tests__/WMSCacheOptions-test.jsx b/web/client/components/TOC/fragments/settings/__tests__/WMSCacheOptions-test.jsx index fbe7f79991..882cea487a 100644 --- a/web/client/components/TOC/fragments/settings/__tests__/WMSCacheOptions-test.jsx +++ b/web/client/components/TOC/fragments/settings/__tests__/WMSCacheOptions-test.jsx @@ -1045,5 +1045,30 @@ describe('WMSCacheOptions', () => { .catch(done); }); + it('should display noConfiguredGridSets warning message if layer is configured from catalog to `Use Cache Options`, with no grid sets available', () => { + ReactDOM.render(, document.getElementById('container')); + expect(document.querySelector('.ms-wms-cache-options')).toBeTruthy(); + const inputs = document.querySelectorAll('input[type="checkbox"]'); + expect(inputs.length).toBe(1); + const buttons = document.querySelectorAll('button'); + expect(buttons.length).toBe(2); + expect([...buttons].map(button => button.querySelector('.glyphicon').getAttribute('class'))).toEqual([ + 'glyphicon glyphicon-refresh', + 'glyphicon glyphicon-grid-custom' + ]); + + const alert = document.querySelector('.alert'); + expect(alert.innerText).toBe('layerProperties.noConfiguredGridSets'); + }); }); diff --git a/web/client/components/catalog/editor/AdvancedSettings/RasterAdvancedSettings.js b/web/client/components/catalog/editor/AdvancedSettings/RasterAdvancedSettings.js index ea48a0f2ae..6c97d9f8c8 100644 --- a/web/client/components/catalog/editor/AdvancedSettings/RasterAdvancedSettings.js +++ b/web/client/components/catalog/editor/AdvancedSettings/RasterAdvancedSettings.js @@ -163,6 +163,14 @@ export default ({ onChange={event => onChangeServiceProperty("layerOptions", { ...service.layerOptions, serverType: event?.value })} /> + {![ServerTypes.NO_VENDOR].includes(service.layerOptions?.serverType) && service.type === "wms" && + onChangeServiceProperty("layerOptions", { ...service.layerOptions, remoteTileGrids: e.target.checked})} + checked={!isNil(service.layerOptions?.remoteTileGrids) ? service.layerOptions?.remoteTileGrids : false}> + +  } /> + + }
diff --git a/web/client/components/catalog/editor/AdvancedSettings/__tests__/RasterAdvancedSettings-test.js b/web/client/components/catalog/editor/AdvancedSettings/__tests__/RasterAdvancedSettings-test.js index 4512f22ed3..d04ce06250 100644 --- a/web/client/components/catalog/editor/AdvancedSettings/__tests__/RasterAdvancedSettings-test.js +++ b/web/client/components/catalog/editor/AdvancedSettings/__tests__/RasterAdvancedSettings-test.js @@ -33,7 +33,7 @@ describe('Test Raster advanced settings', () => { const advancedSettingPanel = document.getElementsByClassName("mapstore-switch-panel"); expect(advancedSettingPanel).toBeTruthy(); const fields = document.querySelectorAll(".form-group"); - expect(fields.length).toBe(13); + expect(fields.length).toBe(14); }); it('test csw advanced options', () => { ReactDOM.render(, document.getElementById("container")); @@ -209,6 +209,25 @@ describe('Test Raster advanced settings', () => { expect(spyOn).toHaveBeenCalled(); expect(spyOn.calls[1].arguments).toEqual([ 'allowUnsecureLayers', false ]); }); + it('test component onChangeServiceProperty useCacheOption for remote tile grids', () => { + const action = { + onChangeServiceProperty: () => {} + }; + const spyOn = expect.spyOn(action, 'onChangeServiceProperty'); + ReactDOM.render(, document.getElementById("container")); + const advancedSettingsPanel = document.getElementsByClassName("mapstore-switch-panel"); + expect(advancedSettingsPanel).toBeTruthy(); + const formGroup = document.querySelectorAll('.form-group')[6]; + expect(formGroup.textContent.trim()).toBe('layerProperties.useCacheOptionInfo.label'); + const useCacheOption = formGroup.querySelector('input[type="checkbox"]'); + expect(useCacheOption).toBeTruthy(); + TestUtils.Simulate.change(useCacheOption, { "target": { "checked": true }}); + expect(spyOn).toHaveBeenCalled(); + expect(spyOn.calls[0].arguments).toEqual([ 'layerOptions', { remoteTileGrids: true } ]); + }); it('test component onChangeServiceProperty singleTile', () => { const action = { onChangeServiceProperty: () => {} diff --git a/web/client/epics/__tests__/catalog-test.js b/web/client/epics/__tests__/catalog-test.js index c7b143d7a3..b3a7d1c8ff 100644 --- a/web/client/epics/__tests__/catalog-test.js +++ b/web/client/epics/__tests__/catalog-test.js @@ -680,6 +680,67 @@ describe('catalog Epics', () => { } }); }); + it('addLayerAndDescribeEpic for wms layer with remoteTileGrids = true', (done) => { + const layer = { + type: 'wms', + url: 'base/web/client/test-resources/wms/DescribeLayers.xml', + visibility: true, + dimensions: [], + name: 'workspace:vector_layer', + title: 'workspace:vector_layer', + bbox: {"crs": "EPSG:4326", "bounds": {"minx": "-103.87791475407893", "miny": "44.37246687108142", "maxx": "-103.62278893469492", "maxy": "44.50235105543566"}}, + links: [], + params: { + CQL_FILTER: 'NAME=\'Test\'' + }, + allowedSRS: { + 'EPSG:3857': true, + 'EPSG:4326': true + }, + remoteTileGrids: true + }; + const NUM_ACTIONS = 3; + testEpic(addTimeoutEpic(addLayerAndDescribeEpic, 0), NUM_ACTIONS, + addLayerAndDescribe(layer), + (actions) => { + expect(actions.length).toBe(NUM_ACTIONS); + actions.map((action) => { + switch (action.type) { + case ADD_LAYER: + expect(action.layer.name).toBe("workspace:vector_layer"); + expect(action.layer.title).toBe("workspace:vector_layer"); + expect(action.layer.type).toBe("wms"); + expect(action.layer.params).toEqual(layer.params); + break; + case CHANGE_LAYER_PROPERTIES: + expect(action.newProperties).toExist(); + expect(action.newProperties.search).toExist(); + expect(action.newProperties.search.url).toBe("http://some.geoserver.org:80/geoserver/wfs"); + expect(action.newProperties.search.type).toBe("wfs"); + expect(action.newProperties.tileGridStrategy).toEqual('custom'); + expect(action.newProperties.tileGrids).toExist(); + break; + case TEST_TIMEOUT: + break; + default: + expect(true).toBe(false); + } + }); + done(); + }, { + catalog: { + delayAutoSearch: 50, + selectedService: "wmsCatalog", + services: { + "wmsCatalog": { + type: "wms", + url: "base/web/client/test-resources/wms/GetCapabilities-1.1.1.xml" + } + }, + pageSize: 2 + } + }); + }); it('addLayerAndDescribeEpic multiple urls', (done) => { const layer = { type: 'wms', diff --git a/web/client/epics/catalog.js b/web/client/epics/catalog.js index 52b961a957..e144d7b661 100644 --- a/web/client/epics/catalog.js +++ b/web/client/epics/catalog.js @@ -62,6 +62,7 @@ import { updateServiceData } from '../utils/CatalogUtils'; import { getCapabilities, describeLayers, flatLayers } from '../api/WMS'; +import {generateGeoServerWMTSUrl} from '../utils/WMTSUtils'; import CoordinatesUtils from '../utils/CoordinatesUtils'; import ConfigUtils from '../utils/ConfigUtils'; import {getCapabilitiesUrl, getLayerId, getLayerUrl, removeWorkspace, DEFAULT_GROUP_ID} from '../utils/LayersUtils'; @@ -75,6 +76,8 @@ import { extractGeometryType } from '../utils/WFSLayerUtils'; import { createDefaultStyle } from '../utils/StyleUtils'; import { removeDuplicateLines } from '../utils/StringUtils'; import { logError } from '../utils/DebugUtils'; +import { isProjectionAvailable } from '../utils/ProjectionUtils'; +import {getLayerTileMatrixSetsInfo} from '../api/WMTS'; const onErrorRecordSearch = (isNewService, errObj) => { logError({message: errObj}); @@ -270,8 +273,28 @@ export default (API) => ({ actions.push(zoomToExtent(layer.bbox.bounds, layer.bbox.crs)); } if (layer.type === 'wms') { - return Rx.Observable.defer(() => describeLayers(getLayerUrl(layer), layer.name)) - .switchMap(results => { + // * fetch the api of cashe option if layer has 'remoteTileGrids' property with true value + return Rx.Observable.forkJoin( + Rx.Observable.defer(() => describeLayers(getLayerUrl(layer), layer.name)), + (!layer?.remoteTileGrids) ? + Rx.Observable.of(null) : + Rx.Observable.defer(() => getLayerTileMatrixSetsInfo(generateGeoServerWMTSUrl(layer), layer.name, layer)) + .catch(() => Rx.Observable.of(null)) + ) + .switchMap(([results, tileGridData]) => { + let tileGridProperties = {}; + if (tileGridData) { + const filteredTileGrids = tileGridData.tileGrids.filter(({ crs }) => isProjectionAvailable(CoordinatesUtils.normalizeSRS(crs))); + tileGridProperties = tileGridData !== undefined ? { + tiled: true, + tileGrids: tileGridData.tileGrids, + tileGridStrategy: 'custom', + tileGridCacheSupport: filteredTileGrids?.length > 0 ? + tileGridData.formats ? {formats: tileGridData.formats} : {} + : undefined + } : {}; + + } if (results) { let description = find(results, (desc) => desc.name === layer.name ); if (description && description.owsType === 'WFS') { @@ -280,9 +303,12 @@ export default (API) => ({ search: { url: filteredUrl, type: 'wfs' - } + }, ...tileGridProperties })); } + return Rx.Observable.of(changeLayerProperties(id, { + ...tileGridProperties + })); } return Rx.Observable.empty(); }) diff --git a/web/client/plugins/widgetbuilder/MapBuilder.jsx b/web/client/plugins/widgetbuilder/MapBuilder.jsx index d157398840..a483864317 100644 --- a/web/client/plugins/widgetbuilder/MapBuilder.jsx +++ b/web/client/plugins/widgetbuilder/MapBuilder.jsx @@ -25,6 +25,10 @@ import mapToolbar from './enhancers/mapToolbar'; import MapLayerSelectorComp from './MapLayerSelector'; import MapSelector from './MapSelector'; import { catalogEditorEnhancer } from './enhancers/catalogEditorEnhancer'; +import { getLayerTileMatrixSetsInfo } from '../../api/WMTS'; +import { generateGeoServerWMTSUrl } from '../../utils/WMTSUtils'; +import { isProjectionAvailable } from '../../utils/ProjectionUtils'; +import { normalizeSRS } from '../../utils/CoordinatesUtils'; const Toolbar = mapToolbar(ToolbarComp); @@ -51,8 +55,23 @@ const chooseMapEnhancer = compose( manageLayers, withHandlers({ onLayerChoice: ({ toggleLayerSelector = () => { }, addLayer = () => { } }) => (layer) => { - addLayer(layer); - toggleLayerSelector(false); + // fetching 'tileGridData' if layer has truthy flag 'remoteTileGrids' and adding the required props to layer object + let tileGridPromise = layer.type === 'wms' && layer.remoteTileGrids ? getLayerTileMatrixSetsInfo(generateGeoServerWMTSUrl(layer), layer.name, layer) : new Promise((resolve) => resolve(null)); + tileGridPromise.then((tileGridData) => { + let tileGridProperties = {}; + if (tileGridData) { + const filteredTileGrids = tileGridData.tileGrids.filter(({ crs }) => isProjectionAvailable(normalizeSRS(crs))); + tileGridProperties = tileGridData !== undefined ? { + tileGrids: tileGridData.tileGrids, + tileGridStrategy: 'custom', + tileGridCacheSupport: filteredTileGrids?.length > 0 ? + tileGridData.formats ? {formats: tileGridData.formats} : {} + : undefined + } : {}; + } + addLayer({...layer, ...tileGridProperties}); + toggleLayerSelector(false); + }); } }), layerSelector diff --git a/web/client/translations/data.de-DE.json b/web/client/translations/data.de-DE.json index e7273ccf9f..c77f45b9c2 100644 --- a/web/client/translations/data.de-DE.json +++ b/web/client/translations/data.de-DE.json @@ -207,6 +207,10 @@ "label": "Lokalisierten Stil aktivieren", "tooltip": "Hinweis: Diese Einstellung benötigt spezifische Konfigurationen im GeoServer" }, + "useCacheOptionInfo": { + "label": "Verwenden Sie entfernte benutzerdefinierte Kachelraster", + "info": "Stellen Sie sicher, dass WMTS aktiviert ist, um benutzerdefinierte Kachelraster verwenden zu können" + }, "format": { "title": "Format", "tile": "Fliese", diff --git a/web/client/translations/data.en-US.json b/web/client/translations/data.en-US.json index 9ae8c5d6bc..9d11b3fdf8 100644 --- a/web/client/translations/data.en-US.json +++ b/web/client/translations/data.en-US.json @@ -207,6 +207,10 @@ "label": "Enable localized style", "tooltip": "Note: This parameter requires specific configurations on GeoServer" }, + "useCacheOptionInfo": { + "label": "Use remote custom tile grids", + "info": "Make sure to have WMTS enabled in order to use custom tile grids" + }, "format": { "title": "Format", "tile": "Tile", diff --git a/web/client/translations/data.es-ES.json b/web/client/translations/data.es-ES.json index 0e79662d5a..4f2e87f2a4 100644 --- a/web/client/translations/data.es-ES.json +++ b/web/client/translations/data.es-ES.json @@ -207,6 +207,10 @@ "label": "Habilitar estilo localizado", "tooltip": "Nota: este parámetro requiere configuraciones específicas en GeoServer" }, + "useCacheOptionInfo": { + "label": "Utilice cuadrículas de mosaicos personalizadas remotas", + "info": "Asegúrese de que WMTS esté habilitado para poder usar cuadrículas de mosaicos personalizadas" + }, "format": { "title": "Formato", "tile": "Teja", diff --git a/web/client/translations/data.fr-FR.json b/web/client/translations/data.fr-FR.json index de5f28047a..7e626abc34 100644 --- a/web/client/translations/data.fr-FR.json +++ b/web/client/translations/data.fr-FR.json @@ -207,6 +207,10 @@ "label": "Activer le style localisé", "tooltip": "Remarque: ce paramètre nécessite des configurations spécifiques sur GeoServer" }, + "useCacheOptionInfo": { + "label": "Utiliser des grilles de carreaux personnalisées à distance", + "info": "Assurez-vous que WMTS est activé afin d'utiliser des grilles de tuiles personnalisées" + }, "format": { "title": "Format", "tile": "Tile", diff --git a/web/client/translations/data.it-IT.json b/web/client/translations/data.it-IT.json index 632f7d7086..a35362ca1e 100644 --- a/web/client/translations/data.it-IT.json +++ b/web/client/translations/data.it-IT.json @@ -207,6 +207,10 @@ "label": "Abilita stile localizzato", "tooltip": "Nota: questo parametro richiede configurazioni specifiche su GeoServer" }, + "useCacheOptionInfo": { + "label": "Utilizza griglie di riquadri personalizzate remote", + "info": "Assicurati che WMTS sia abilitato per utilizzare griglie di riquadri personalizzate" + }, "format": { "title": "Formato", "tile": "Tile",