diff --git a/web/client/api/WFS.js b/web/client/api/WFS.js
index 6370be114cf..8ca861526e1 100644
--- a/web/client/api/WFS.js
+++ b/web/client/api/WFS.js
@@ -159,8 +159,13 @@ export const describeFeatureType = function(url, typeName) {
/**
* Fetch the supported formats of the WFS service
+<<<<<<< HEAD
* @param url
* @return {object} { infoFormats }
+=======
+ * @param {string} url WFS endpoint
+ * @returns {Promise} resolves with { infoFormats }
+>>>>>>> 58535023d (#12813: Layer settings - Trigger layer-specific capabilities request (#12816))
*/
export const getSupportedFormat = (url) => {
return getCapabilities(url)
diff --git a/web/client/api/WMS.js b/web/client/api/WMS.js
index faf4998ac89..3eb114ffeed 100644
--- a/web/client/api/WMS.js
+++ b/web/client/api/WMS.js
@@ -310,9 +310,9 @@ export const reset = () => {
/**
* Fetch the supported formats of the WMS service
- * @param url
- * @param includeGFIFormats
- * @return {object|string} formats
+ * @param {string} url WMS endpoint
+ * @param {boolean} includeGFIFormats include GFI formats
+ * @returns {object|string} formats
*/
export const getSupportedFormat = (url, includeGFIFormats = false) => {
return getCapabilities(url)
diff --git a/web/client/api/__tests__/WFS-test.js b/web/client/api/__tests__/WFS-test.js
index 9a78a036b0e..7a85a26ee31 100644
--- a/web/client/api/__tests__/WFS-test.js
+++ b/web/client/api/__tests__/WFS-test.js
@@ -197,6 +197,32 @@ describe('Test WFS ogc API functions', () => {
}).catch(done);
});
+ it('getSupportedFormat with layer-specific URL', (done) => {
+ mockAxios.onGet().reply((config) => {
+ try {
+ expect(config.url).toBe('/geoserver/workspace/layer_name/wfs?version=1.1.0&service=WFS&request=GetCapabilities');
+ } catch (e) {
+ done(e);
+ }
+ return [200, `
+
+
+
+
+ application/json
+ text/html
+
+
+
+
+ `];
+ });
+ getSupportedFormat('/geoserver/workspace/layer_name/wfs').then((data) => {
+ expect(data).toEqual({ infoFormats: ['application/json', 'text/html'] });
+ done();
+ }).catch(done);
+ });
+
it('toDescribeURL with URL array', () => {
const _url = [
'http://gs-stable.geosolutionsgroup.com:443/geoserver1',
diff --git a/web/client/api/__tests__/WMS-test.js b/web/client/api/__tests__/WMS-test.js
index dea877ed47b..d5ece7bf164 100644
--- a/web/client/api/__tests__/WMS-test.js
+++ b/web/client/api/__tests__/WMS-test.js
@@ -419,3 +419,77 @@ describe('Test get json wms graphic legend (mock axios)', () => {
});
});
});
+
+describe('Test getSupportedFormat WMS API (mock axios)', () => {
+ beforeEach((done) => {
+ mockAxios = new MockAdapter(axios);
+ setTimeout(done);
+ });
+
+ afterEach((done) => {
+ mockAxios.restore();
+ setTimeout(done);
+ });
+
+ it('getSupportedFormat returns imageFormats and infoFormats for layer-specific request', (done) => {
+ mockAxios.onGet().reply((config) => {
+ try {
+ expect(config.url).toBe('http://localhost:8080/geoserver/workspace/layer_name/wms?service=WMS&version=1.3.0&request=GetCapabilities');
+ } catch (e) {
+ done(e);
+ }
+ return [200, `
+
+
+
+
+ image/png
+ image/jpeg
+
+
+ text/html
+ application/json
+
+
+
+
+ `];
+ });
+
+ API.getSupportedFormat('http://localhost:8080/geoserver/workspace/layer_name/wms', true)
+ .then((formats) => {
+ try {
+ expect(formats.imageFormats).toEqual(['image/png', 'image/jpeg']);
+ expect(formats.infoFormats).toEqual(['text/html', 'application/json']);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ }).catch(done);
+ });
+
+ it('getSupportedFormat returns only imageFormats when includeGFIFormats is false', (done) => {
+ mockAxios.onGet().reply(200, `
+
+
+
+
+ image/png
+
+
+
+
+ `);
+
+ API.getSupportedFormat('http://localhost:8080/geoserver/wms')
+ .then((imageFormats) => {
+ try {
+ expect(imageFormats).toEqual(['image/png']);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ }).catch(done);
+ });
+});
+
diff --git a/web/client/components/TOC/fragments/settings/Display.jsx b/web/client/components/TOC/fragments/settings/Display.jsx
index dce79b55794..761d6911cac 100644
--- a/web/client/components/TOC/fragments/settings/Display.jsx
+++ b/web/client/components/TOC/fragments/settings/Display.jsx
@@ -21,7 +21,7 @@ import Message from '../../../I18N/Message';
import InfoPopover from '../../../widgets/widget/InfoPopover';
import Legend from '../../../../plugins/TOC/components/Legend';
import VisibilityLimitsForm from './VisibilityLimitsForm';
-import { ServerTypes } from '../../../../utils/LayersUtils';
+import { ServerTypes, getCapabilitiesUrl } from '../../../../utils/LayersUtils';
import {updateLayerLegendFilter} from '../../../../utils/FilterUtils';
import Select from 'react-select';
import { getSupportedFormat } from '../../../../api/WMS';
@@ -116,9 +116,9 @@ export default class extends React.Component {
});
};
- onFormatOptionsFetch = (url) => {
+ onFormatOptionsFetch = (layer) => {
this.setState({formatLoading: true});
- getSupportedFormat(url).then((imageFormats)=>{
+ getSupportedFormat(getCapabilitiesUrl(layer)).then((imageFormats)=>{
this.props.onChange("imageFormats", imageFormats);
this.setState({formatLoading: false});
});
@@ -165,7 +165,7 @@ export default class extends React.Component {
onOpen={() => {
if (!this.props.element?.imageFormats
|| this.props.element?.imageFormats?.length === 0) {
- this.onFormatOptionsFetch(this.props.element?.url);
+ this.onFormatOptionsFetch(this.props.element);
}
}}
onChange={({ value }) => {
@@ -175,7 +175,7 @@ export default class extends React.Component {
disabled={!!this.state.formatLoading}
tooltipId="layerProperties.format.refresh"
className="square-button no-border format-refresh"
- onClick={() => {this.onFormatOptionsFetch(this.props.element?.url);}}
+ onClick={() => {this.onFormatOptionsFetch(this.props.element);}}
key="format-refresh">
diff --git a/web/client/components/TOC/fragments/settings/FeatureInfo.jsx b/web/client/components/TOC/fragments/settings/FeatureInfo.jsx
index 243dc8dfc00..e336a4077aa 100644
--- a/web/client/components/TOC/fragments/settings/FeatureInfo.jsx
+++ b/web/client/components/TOC/fragments/settings/FeatureInfo.jsx
@@ -17,7 +17,28 @@ import { Glyphicon } from 'react-bootstrap';
import Message from '../../../I18N/Message';
import includes from 'lodash/includes';
import isEmpty from 'lodash/isEmpty';
+<<<<<<< HEAD
import { getDefaultInfoViewMode } from '../../../../utils/MapInfoUtils';
+=======
+import { v1 as uuidv1 } from 'uuid';
+import {
+ getDefaultInfoViewMode,
+ getLayerFeatureInfoViews,
+ isLayerFeatureInfoDisabled
+} from '../../../../utils/MapInfoUtils';
+import Message from '../../../I18N/Message';
+import { getCapabilitiesUrl } from '../../../../utils/LayersUtils';
+import FeatureInfoEditor from './FeatureInfoEditor';
+import localizedProps from '../../../misc/enhancers/localizedProps';
+import FeatureInfoRequestOptions from '../../../misc/FeatureInfoRequestOptions';
+import { isGeoServerLayer } from '../../../../utils/FeatureInfoRequestUtils';
+import ExternalDataEditor from './ExternalDataEditor';
+import PropertiesEditor from './PropertiesEditor';
+import { EXTERNAL_DATA, validateExternalDataConfiguration } from '../../../../utils/mapinfo/ExternalDataUtils';
+
+const FormControl = localizedProps('placeholder')(FormControlRB);
+const GlyphiconWithTitle = localizedProps('title')(Glyphicon);
+>>>>>>> 58535023d (#12813: Layer settings - Trigger layer-specific capabilities request (#12816))
const supportedFormatRequests = {
wms: getSupportedFormatWMS,
@@ -58,7 +79,7 @@ export default class extends React.Component {
// we dont know supported infoFormats yet
if (getSupportedFormat && this.props.element.url && !this.props.element.infoFormats || this.props.element.infoFormats?.length === 0) {
this.setState({ loading: true }); // eslint-disable-line -- TODO: need to be fixed
- getSupportedFormat(this.props.element.url, true)
+ getSupportedFormat(getCapabilitiesUrl(this.props.element), true)
.then(({ infoFormats }) => {
this.props.onChange("infoFormats", infoFormats);
this.setState({ loading: false }); // eslint-disable-line -- TODO: need to be fixed
diff --git a/web/client/utils/LayersUtils.js b/web/client/utils/LayersUtils.js
index 58df487e228..cf0caeb1828 100644
--- a/web/client/utils/LayersUtils.js
+++ b/web/client/utils/LayersUtils.js
@@ -814,8 +814,8 @@ export const getCapabilitiesUrl = (layer) => {
if (!!matchedGeoServerName) {
let urlParts = reqUrl.split(matchedGeoServerName);
if (urlParts.length === 2) {
- let layerParts = layer.name.split(":");
- if (layerParts.length === 2) {
+ let layerParts = layer?.name?.split(":");
+ if (layerParts?.length === 2) {
const [workspace, layerName] = layerParts;
const rawTail = urlParts[1] || '';
const urlTail = rawTail.replace(/^\/+/, '');
diff --git a/web/client/utils/__tests__/LayersUtils-test.js b/web/client/utils/__tests__/LayersUtils-test.js
index e4e94bfaff9..9a007f06e74 100644
--- a/web/client/utils/__tests__/LayersUtils-test.js
+++ b/web/client/utils/__tests__/LayersUtils-test.js
@@ -1609,6 +1609,17 @@ describe('LayersUtils', () => {
expect(LayersUtils.getCapabilitiesUrl(layer)).toEqual('localhost:8080/geoserver/world/layer1/ows?token=value');
});
+ it('test getCapabilitiesUrl with layer without name', () => {
+ const layer = {
+ url: 'localhost:8080/geoserver/wms',
+ params: {
+ token: 'value'
+ }
+ };
+ expect(LayersUtils.getCapabilitiesUrl(layer))
+ .toEqual('localhost:8080/geoserver/wms?token=value');
+ });
+
it('test getNestedGroupTitle', () => {
const groups = [
diff --git a/web/client/utils/mapinfo/wfs.js b/web/client/utils/mapinfo/wfs.js
index a864a7084d5..d4185f4eae2 100644
--- a/web/client/utils/mapinfo/wfs.js
+++ b/web/client/utils/mapinfo/wfs.js
@@ -9,7 +9,11 @@
import {Observable} from 'rxjs';
import {normalizeSRS} from '../CoordinatesUtils';
+<<<<<<< HEAD
import { getLayerUrl } from '../LayersUtils';
+=======
+import { getLayerUrl, getCapabilitiesUrl } from '../LayersUtils';
+>>>>>>> 58535023d (#12813: Layer settings - Trigger layer-specific capabilities request (#12816))
import { isObject } from 'lodash';
import { optionsToVendorParams } from '../VendorParamsUtils';
import { describeFeatureType, getFeature } from '../../api/WFS';
@@ -98,6 +102,23 @@ const getIdentifyGeometry = point => {
export default {
buildRequest,
+<<<<<<< HEAD
+=======
+ /**
+ * Detects the supported info formats, text/html is the only one a WFS service could be missing.
+ * @param {object} layer
+ * @param {string} infoFormat the info format needed by the identify request
+ * @return {Promise} resolves with the layer, with `infoFormats` when they could be detected
+ */
+ resolveLayer: (layer, infoFormat) => {
+ if (infoFormat !== INFO_FORMATS.HTML || layer.infoFormats?.length || !layer.url) {
+ return Promise.resolve(layer);
+ }
+ return getSupportedFormat(getCapabilitiesUrl(layer))
+ .then(({ infoFormats }) => ({ ...layer, infoFormats }))
+ .catch(() => layer);
+ },
+>>>>>>> 58535023d (#12813: Layer settings - Trigger layer-specific capabilities request (#12816))
getIdentifyFlow: (layer = {}, baseURL, defaultParams) => {
const { point, features, ...baseParams } = defaultParams || {};
if (features) {