From b500f60e69a340d699d42dedada3cb3fc8b66f6a Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Tue, 8 Aug 2017 13:06:02 +0200 Subject: [PATCH] Fixed bundle inclusion of ogc-schemas - Removed the inclusion of ogc-schemas in the default bundle - Improved error handling and generalized OGC errors management (return 200 with ExceptionReport) --- web/client/epics/featuregrid.js | 29 +++------------------- web/client/epics/wfsquery.js | 26 ++++++++------------ web/client/utils/ObservableUtils.js | 38 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 41 deletions(-) create mode 100644 web/client/utils/ObservableUtils.js diff --git a/web/client/epics/featuregrid.js b/web/client/epics/featuregrid.js index 7dfa7e6b3ba..c6c2ca95209 100644 --- a/web/client/epics/featuregrid.js +++ b/web/client/epics/featuregrid.js @@ -22,8 +22,7 @@ const {query, QUERY_CREATE, QUERY_RESULT, LAYER_SELECTED_FOR_SEARCH, FEATURE_TYP const {reset, QUERY_FORM_RESET} = require('../actions/queryform'); const {zoomToExtent} = require('../actions/map'); const {BROWSE_DATA} = require('../actions/layers'); -const {parseString} = require('xml2js'); -const {stripPrefix} = require('xml2js/lib/processors'); + const {SORT_BY, CHANGE_PAGE, SAVE_CHANGES, SAVE_SUCCESS, DELETE_SELECTED_FEATURES, featureSaving, changePage, saveSuccess, saveError, clearChanges, setLayer, clearSelection, toggleViewMode, toggleTool, CLEAR_CHANGES, START_EDITING_FEATURE, TOGGLE_MODE, MODES, geometryChanged, DELETE_GEOMETRY, deleteGeometryFeature, @@ -41,27 +40,7 @@ const {selectedFeaturesSelector, changesMapSelector, newFeaturesSelector, hasCha const {error} = require('../actions/notifications'); const {describeSelector, isDescribeLoaded, getFeatureById, wfsURL, wfsFilter, featureCollectionResultSelector} = require('../selectors/query'); const drawSupportReset = () => changeDrawingStatus("clean", "", "featureGrid", [], {}); -/** - * Intercept OGC Exception (200 response with exceptionReport) to throw error in the stream - * @param {observable} observable The observable that emits the server response - * @return {observable} The observable that returns the response or throws the error. - */ -const interceptOGCError = (observable) => observable.switchMap(response => { - if (typeof response.data === "string") { - if (response.data.indexOf("ExceptionReport") > 0) { - return Rx.Observable.bindNodeCallback( (data, callback) => parseString(data, { - tagNameProcessors: [stripPrefix], - explicitArray: false, - mergeAttrs: true - }, callback))(response.data).map(data => { - throw get(data, "ExceptionReport.Exception.ExceptionText") || "Undefined OGC Service Error"; - }); - - } - } - return Rx.Observable.of(response); -}); - +const {interceptOGCError} = require('../utils/ObservableUtils'); const setupDrawSupport = (state, original) => { const defaultFeatureProj = getDefaultFeatureProjection(); let drawOptions; let geomType; @@ -260,7 +239,7 @@ module.exports = { ).map(() => saveSuccess()) .catch((e) => Rx.Observable.of(saveError(), error({ title: "featuregrid.errorSaving", - message: e, + message: e.message || "Unknown Exception", uid: "saveError", autoDismiss: 5 }))) @@ -283,7 +262,7 @@ module.exports = { // close window .catch((e) => Rx.Observable.of(saveError(), error({ title: "featuregrid.errorSaving", - message: e, + message: e.message || "Unknown Exception", uid: "saveError" }))).concat(Rx.Observable.of( toggleTool("deleteConfirm"), diff --git a/web/client/epics/wfsquery.js b/web/client/epics/wfsquery.js index 3b5c9027210..0bad993f740 100644 --- a/web/client/epics/wfsquery.js +++ b/web/client/epics/wfsquery.js @@ -16,7 +16,8 @@ const {paginationInfo, isDescribeLoaded, describeSelector} = require('../selecto const FilterUtils = require('../utils/FilterUtils'); const assign = require('object-assign'); -const {isString, isObject} = require('lodash'); +const {isObject} = require('lodash'); +const {interceptOGCError} = require('../utils/ObservableUtils'); // this is a workaround for https://osgeo-org.atlassian.net/browse/GEOS-7233. can be removed when fixed const workaroundGEOS7233 = ({totalFeatures, features, ...rest}, {startIndex, maxFeatures}, originalSize) => { if (originalSize > totalFeatures && originalSize === startIndex + features.length && totalFeatures === features.length) { @@ -159,12 +160,6 @@ const getWFSFeature = (searchUrl, filterObj) => { })); }; -const getWFSResponseException = (response, code) => { - const {unmarshaller} = require('../utils/ogc/WFS'); - const json = isString(response.data) ? unmarshaller.unmarshalString(response.data) : null; - return json && json.value && json.value.exception && json.value.exception[0] && json.value.exception[0].TYPE_NAME === 'OWS_1_0_0.ExceptionType' && json.value.exception[0].exceptionCode === code; -}; - const getFirstAttribute = (state)=> { return state.query && state.query.featureTypes && state.query.featureTypes[state.query.typeName] && state.query.featureTypes[state.query.typeName].attributes && state.query.featureTypes[state.query.typeName].attributes[0] && state.query.featureTypes[state.query.typeName].attributes[0].attribute || null; }; @@ -178,11 +173,11 @@ const retryWithForcedSortOptions = (action, store) => { return getWFSFeature(action.searchUrl, assign(action.filterObj, { sortOptions })) + .let(interceptOGCError) .map((newResponse) => { - const newError = getWFSResponseException(newResponse, 'NoApplicableCode'); const state = store.getState(); const data = workaroundGEOS7233(newResponse.data, action.filterObj.pagination, paginationInfo.totalFeatures(state)); - return !newError ? querySearchResponse(data, action.searchUrl, action.filterObj) : queryError('No sortable request'); + return querySearchResponse(data, action.searchUrl, action.filterObj); }) .catch((e) => { return Rx.Observable.of(queryError(e)); @@ -237,19 +232,18 @@ const wfsQueryEpic = (action$, store) => .switchMap(action => { return Rx.Observable.merge( getWFSFeature(action.searchUrl, action.filterObj) + .let(interceptOGCError) .switchMap((response) => { - // try to guess if it was a missing id error and try to search again with forced sortOptions - const error = getWFSResponseException(response, 'NoApplicableCode'); - if (error) { - return retryWithForcedSortOptions(action, store); - } const state = store.getState(); const data = workaroundGEOS7233(response.data, action.filterObj.pagination, paginationInfo.totalFeatures(state)); return Rx.Observable.of(querySearchResponse(data, action.searchUrl, action.filterObj)); }) .startWith(featureLoading(true)) - .catch((e) => { - return Rx.Observable.of(queryError(e)); + .catch((error) => { + if (error.name === "OGCError" && error.code === 'NoApplicableCode') { + return retryWithForcedSortOptions(action, store); + } + return Rx.Observable.of(queryError(error)); }) .concat(Rx.Observable.of(featureLoading(false))) ); diff --git a/web/client/utils/ObservableUtils.js b/web/client/utils/ObservableUtils.js new file mode 100644 index 00000000000..4ca15e63f6b --- /dev/null +++ b/web/client/utils/ObservableUtils.js @@ -0,0 +1,38 @@ +const Rx = require('rxjs'); +const {get} = require('lodash'); +const {parseString} = require('xml2js'); +const {stripPrefix} = require('xml2js/lib/processors'); +class OGCError extends Error { + constructor(message, code) { + super(message); + this.name = 'OGCError'; + this.code = code; + } +} +/** + * Intercept OGC Exception (200 response with exceptionReport) to throw error in the stream + * @param {observable} observable The observable that emits the server response + * @return {observable} The observable that returns the response or throws the error. + */ +const interceptOGCError = (observable) => observable.switchMap(response => { + if (typeof response.data === "string") { + if (response.data.indexOf("ExceptionReport") > 0) { + return Rx.Observable.bindNodeCallback( (data, callback) => parseString(data, { + tagNameProcessors: [stripPrefix], + explicitArray: false, + mergeAttrs: true + }, callback))(response.data).map(data => { + const message = get(data, "ExceptionReport.Exception.ExceptionText"); + throw new OGCError(message || "Undefined OGC Service Error", get(data, "ExceptionReport.Exception.exceptionCode")); + }); + + } + } + return Rx.Observable.of(response); +}); + + +module.exports = { + interceptOGCError + +};