Skip to content

Commit

Permalink
#7416: Filter not applied correctly in attribute table (#7629)
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidQuartz authored Dec 6, 2021
1 parent 3bffb31 commit c87f5fa
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 36 deletions.
8 changes: 6 additions & 2 deletions web/client/epics/__tests__/layerdownload-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,16 @@ describe('layerdownload Epics', () => {
queryPanel: { enabled: false },
layerdownload: { enabled: true }
},
featuregrid: {}
featuregrid: {},
layers: {
flat: [{ id: 'test layer', layerFilter: { featureTypeName: 'test' } }],
selected: ['test layer']
}
};
testEpic(
startFeatureExportDownload,
1,
downloadFeatures('/wrong/path?', 'request body', { selectedFormat: "test-format"}),
downloadFeatures('/wrong/path?', { featureTypeName: 'test' }, { selectedFormat: "test-format"}),
epicResult,
state
);
Expand Down
63 changes: 36 additions & 27 deletions web/client/epics/layerdownload.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
* LICENSE file in the root directory of this source tree.
*/

import axios from 'axios';
import Rx from 'rxjs';
import { get, find, findIndex, pick, toPairs } from 'lodash';
import { get, find, findIndex, pick, toPairs, castArray } from 'lodash';
import { saveAs } from 'file-saver';
import { parseString } from 'xml2js';
import { stripPrefix } from 'xml2js/lib/processors';
Expand Down Expand Up @@ -62,17 +61,17 @@ import {
userSelector
} from '../selectors/security';

import { getLayerWFSCapabilities } from '../observables/wfs';
import { getLayerWFSCapabilities, getXMLFeature } from '../observables/wfs';
import { describeProcess } from '../observables/wps/describe';
import { download } from '../observables/wps/download';
import { referenceOutputExtractor, makeOutputsExtractor, getExecutionStatus } from '../observables/wps/execute';

import { cleanDuplicatedQuestionMarks } from '../utils/ConfigUtils';
import { toOGCFilter, mergeFiltersToOGC } from '../utils/FilterUtils';
import { mergeFiltersToOGC } from '../utils/FilterUtils';
import { getByOutputFormat } from '../utils/FileFormatUtils';
import { getLayerTitle } from '../utils/LayersUtils';
import { bboxToFeatureGeometry } from '../utils/CoordinatesUtils';
import { interceptOGCError } from '../utils/ObservableUtils';
import requestBuilder from '../utils/ogc/WFS/RequestBuilder';

const DOWNLOAD_FORMATS_LOOKUP = {
"gml3": "GML3.1",
Expand All @@ -94,6 +93,9 @@ const DOWNLOAD_FORMATS_LOOKUP = {
"excel2007": "excel2007"
};

const { getFeature: getFilterFeature, query, sortBy, propertyName } = requestBuilder({ wfsVersion: "1.1.0" });


const hasOutputFormat = (data) => {
const operation = get(data, "WFS_Capabilities.OperationsMetadata.Operation");
const getFeature = find(operation, function(o) { return o.name === 'GetFeature'; });
Expand All @@ -103,15 +105,18 @@ const hasOutputFormat = (data) => {
return toPairs(pickedObj).map(([prop, value]) => ({ name: prop, label: value }));
};

const getWFSFeature = ({url, filterObj = {}, downloadOptions = {}} = {}) => {
const data = toOGCFilter(filterObj.featureTypeName, filterObj, filterObj.ogcVersion, filterObj.sortOptions, false, null, null, downloadOptions.selectedSrs);
return Rx.Observable.defer( () =>
axios.post(cleanDuplicatedQuestionMarks(url + `?service=WFS&outputFormat=${downloadOptions.selectedFormat}`), data, {
timeout: 60000,
responseType: 'arraybuffer',
headers: {'Content-Type': 'application/xml'}
}));
const getWFSFeature = ({ url, filterObj = {}, layerFilter, downloadOptions = {}, options } = {}) => {
const { sortOptions, propertyName: pn } = options;

const data = mergeFiltersToOGC({ ogcVersion: '1.0.0', addXmlnsToRoot: true, xmlnsToAdd: ['xmlns:ogc="http://www.opengis.net/ogc"', 'xmlns:gml="http://www.opengis.net/gml"'] }, layerFilter, filterObj);

return getXMLFeature(url, getFilterFeature(query(
filterObj.featureTypeName, [...(sortOptions ? [sortBy(sortOptions.sortBy, sortOptions.sortOrder)] : []), ...(pn ? [propertyName(pn)] : []), ...(data ? castArray(data) : [])],
{ srsName: downloadOptions.selectedSrs })
), options, downloadOptions.selectedFormat);

};

const getFileName = action => {
const name = get(action, "filterObj.featureTypeName");
const format = getByOutputFormat(get(action, "downloadOptions.selectedFormat"));
Expand Down Expand Up @@ -233,45 +238,49 @@ export const startFeatureExportDownload = (action$, store) =>
const mapBbox = mapBboxSelector(state);
const currentLocale = currentLocaleSelector(state);

const { layerFilter } = layer;

const wfsFlow = () => getWFSFeature({
url: action.url,
downloadOptions: action.downloadOptions,
filterObj: {
...action.filterObj,
filterObj: action.filterObj,
layerFilter,
options: {
pagination: !virtualScroll && get(action, "downloadOptions.singlePage") ? action.filterObj && action.filterObj.pagination : null
}
})
.do(({data, headers}) => {
.do(({ data, headers }) => {
if (headers["content-type"] === "application/xml") { // TODO add expected mimetypes in the case you want application/dxf
let xml = String.fromCharCode.apply(null, new Uint8Array(data));
if (xml.indexOf("<ows:ExceptionReport") === 0 ) {
if (xml.indexOf("<ows:ExceptionReport") === 0) {
throw xml;
}
}
})
.catch( () => {
.catch(() => {
return getWFSFeature({
url: action.url,
downloadOptions: action.downloadOptions,
filterObj: {
...action.filterObj,
filterObj: action.filterObj,
layerFilter,
options: {
pagination: !virtualScroll && get(action, "downloadOptions.singlePage") ? action.filterObj && action.filterObj.pagination : null,
sortOptions: getDefaultSortOptions(getFirstAttribute(store.getState()))
}
}).do(({data, headers}) => {
}).do(({ data, headers }) => {
if (headers["content-type"] === "application/xml") { // TODO add expected mimetypes in the case you want application/dxf
let xml = String.fromCharCode.apply(null, new Uint8Array(data));
if (xml.indexOf("<ows:ExceptionReport") === 0 ) {
if (xml.indexOf("<ows:ExceptionReport") === 0) {
throw xml;
}
}
saveAs(new Blob([data], {type: headers && headers["content-type"]}), getFileName(action));
saveAs(new Blob([data], { type: headers && headers["content-type"] }), getFileName(action));
});
}).do(({data, headers}) => {
saveAs(new Blob([data], {type: headers && headers["content-type"]}), getFileName(action));
}).do(({ data, headers }) => {
saveAs(new Blob([data], { type: headers && headers["content-type"] }), getFileName(action));
})
.map( () => onDownloadFinished() )
.catch( (e) => Rx.Observable.of(
.map(() => onDownloadFinished())
.catch((e) => Rx.Observable.of(
error({
error: e,
title: "layerdownload.error.title",
Expand Down
55 changes: 49 additions & 6 deletions web/client/observables/wfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,27 +120,26 @@ const getFeaturesFiltered = (features, filterObj) => {
};

/**
* Get Features in json format. Intercepts request with 200 errors and workarounds GEOS-7233 if `totalFeatures` is passed
* Get data and query string for WFS query for either JSON or XML formats
* @param {string} searchUrl URL of WFS service
* @param {object} filterObj FilterObject
* @param {number} totalFeatures optional number to use in case of a previews request, needed to workaround GEOS-7233.
* @return {Observable} a stream that emits the GeoJSON or an error.
* @param {*} downloadOption selected format for query
* @returns {Object} The data and query string
*/
export const getJSONFeature = (searchUrl, filterObj, options = {}) => {
const getFeatureUtilities = (searchUrl, filterObj, options = {}, downloadOption = 'json') => {
const data = getWFSFilterData(filterObj, options);

const urlParsedObj = urlUtil.parse(searchUrl, true);
let params = isObject(urlParsedObj.query) ? urlParsedObj.query : {};
params.service = 'WFS';
params.outputFormat = 'json';
params.outputFormat = downloadOption;
const queryString = urlUtil.format({
protocol: urlParsedObj.protocol,
host: urlParsedObj.host,
pathname: urlParsedObj.pathname,
query: params
});


if (options.layer && options.layer.type === 'vector') {
return Rx.Observable.defer(() => new Promise((resolve) => {
let features = createFeatureCollection(options.layer.features);
Expand All @@ -149,6 +148,50 @@ export const getJSONFeature = (searchUrl, filterObj, options = {}) => {
}));
}

return {
data,
queryString
};
};

/**
* Get Features in xml format.
* @param {string} searchUrl URL of WFS service
* @param {object} filterObj FilterObject
* @param {*} downloadOption selected format for download
* @returns {Observable} a stream that emits the XML
*/
export const getXMLFeature = (searchUrl, filterObj, options = {}, downloadOption) => {

if (options.layer && options.layer.type === 'vector') {
return getFeatureUtilities(searchUrl, filterObj, options, downloadOption);
}

const { data, queryString } = getFeatureUtilities(searchUrl, filterObj, options, downloadOption);

return Rx.Observable.defer(() =>
axios.post(queryString, data, {
timeout: 60000,
responseType: 'arraybuffer',
headers: { 'Accept': `application/xml`, 'Content-Type': `application/xml` }
}));
};

/**
* Get Features in json format. Intercepts request with 200 errors and workarounds GEOS-7233 if `totalFeatures` is passed
* @param {string} searchUrl URL of WFS service
* @param {object} filterObj FilterObject
* @param {number} totalFeatures optional number to use in case of a previews request, needed to workaround GEOS-7233.
* @return {Observable} a stream that emits the GeoJSON or an error.
*/
export const getJSONFeature = (searchUrl, filterObj, options = {}) => {

if (options.layer && options.layer.type === 'vector') {
return getFeatureUtilities(searchUrl, filterObj, options);
}

const { data, queryString } = getFeatureUtilities(searchUrl, filterObj, options);

return Rx.Observable.defer(() =>
axios.post(queryString, data, {
timeout: 60000,
Expand Down
2 changes: 1 addition & 1 deletion web/client/utils/ogc/WFS/RequestBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ module.exports = function({wfsVersion = "1.1.0", gmlVersion, filterNS, wfsNS = "
sortBy: (property, order = "ASC") =>
`<${wfsNS}:SortBy><${wfsNS}:SortProperty>${propertyName(property)}<${wfsNS}:SortOrder>${order}</${wfsNS}:SortOrder></${wfsNS}:SortProperty></${wfsNS}:SortBy>`,
query: (featureName, content, {srsName = "EPSG:4326"} = {}) =>
`<${wfsNS}:Query ${wfsVersion === "2.0" ? "typeNames" : "typeName"}="${featureName}" srsName="${srsName}">`
`<${wfsNS}:Query ${wfsVersion === "2.0" ? "typeNames" : "typeName"}="${featureName}" ${srsName !== 'native' ? `srsName="${srsName}"` : ''}>`
+ `${Array.isArray(content) ? content.join("") : content}`
+ `</${wfsNS}:Query>`
};
Expand Down

0 comments on commit c87f5fa

Please sign in to comment.