diff --git a/web/client/actions/catalog.js b/web/client/actions/catalog.js index 392afa79a4..9b9c58d5ea 100644 --- a/web/client/actions/catalog.js +++ b/web/client/actions/catalog.js @@ -90,7 +90,7 @@ function textSearch(format, url, startPosition, maxRecords, text, options) { function addLayerAndDescribe(layer) { return (dispatch, getState) => { const state = getState(); - const layers = state && state.layers; + const layers = state && state.layers && state.layers.flat; const id = LayersUtils.getLayerId(layer, layers || []); dispatch(addLayer({...layer, id})); if (layer.type === 'wms') { diff --git a/web/client/actions/queryform.js b/web/client/actions/queryform.js index d2219f193e..74541a2992 100644 --- a/web/client/actions/queryform.js +++ b/web/client/actions/queryform.js @@ -17,6 +17,7 @@ const EXPAND_ATTRIBUTE_PANEL = 'EXPAND_ATTRIBUTE_PANEL'; const EXPAND_SPATIAL_PANEL = 'EXPAND_SPATIAL_PANEL'; const SELECT_SPATIAL_METHOD = 'SELECT_SPATIAL_METHOD'; const SELECT_SPATIAL_OPERATION = 'SELECT_SPATIAL_OPERATION'; +const CHANGE_SPATIAL_ATTRIBUTE = 'CHANGE_SPATIAL_ATTRIBUTE'; const REMOVE_SPATIAL_SELECT = 'REMOVE_SPATIAL_SELECT'; const SHOW_SPATIAL_DETAILS = 'SHOW_SPATIAL_DETAILS'; // const QUERY_FORM_SEARCH = 'QUERY_FORM_SEARCH'; @@ -133,6 +134,13 @@ function selectSpatialOperation(operation, fieldName) { }; } +function changeSpatialAttribute(attribute) { + return { + type: CHANGE_SPATIAL_ATTRIBUTE, + attribute + }; +} + function removeSpatialSelection() { return { type: REMOVE_SPATIAL_SELECT @@ -159,7 +167,6 @@ function changeDwithinValue(distance) { response: response }; } - function wfsLoadError(e) { return { type: WFS_LOAD_ERROR, @@ -297,6 +304,7 @@ module.exports = { EXPAND_SPATIAL_PANEL, SELECT_SPATIAL_METHOD, SELECT_SPATIAL_OPERATION, + CHANGE_SPATIAL_ATTRIBUTE, REMOVE_SPATIAL_SELECT, SHOW_SPATIAL_DETAILS, // QUERY_FORM_SEARCH, @@ -333,6 +341,7 @@ module.exports = { expandSpatialFilterPanel, selectSpatialMethod, selectSpatialOperation, + changeSpatialAttribute, removeSpatialSelection, showSpatialSelectionDetails, query, diff --git a/web/client/actions/wfsquery.js b/web/client/actions/wfsquery.js index c27a8feef0..ac601462d5 100644 --- a/web/client/actions/wfsquery.js +++ b/web/client/actions/wfsquery.js @@ -76,26 +76,6 @@ function queryError(error) { }; } -function describeFeatureType(baseUrl, typeName) { - return (dispatch) => { - return axios.get(baseUrl + '?service=WFS&version=1.1.0&request=DescribeFeatureType&typeName=' + typeName + '&outputFormat=application/json').then((response) => { - if (typeof response.data === 'object') { - dispatch(featureTypeLoaded(typeName, response.data)); - } else { - try { - JSON.parse(response.data); - } catch(e) { - dispatch(featureTypeError(typeName, 'Error from WFS: ' + e.message)); - } - - } - - }).catch((e) => { - dispatch(featureTypeError(typeName, e)); - }); - }; -} - function loadFeature(baseUrl, typeName) { return (dispatch) => { return axios.get(baseUrl + '?service=WFS&version=1.1.0&request=GetFeature&typeName=' + typeName + '&outputFormat=application/json').then((response) => { @@ -196,7 +176,8 @@ module.exports = { QUERY_ERROR, RESET_QUERY, featureTypeSelected, - describeFeatureType, + featureTypeLoaded, + featureTypeError, loadFeature, createQuery, query, diff --git a/web/client/components/data/query/QueryBuilder.jsx b/web/client/components/data/query/QueryBuilder.jsx index aa5a723d10..33786527ee 100644 --- a/web/client/components/data/query/QueryBuilder.jsx +++ b/web/client/components/data/query/QueryBuilder.jsx @@ -87,8 +87,7 @@ const QueryBuilder = React.createClass({ onUpdateLogicCombo: () => {}, onRemoveGroupField: () => {}, onChangeCascadingValue: () => {}, - onExpandAttributeFilterPanel: () => {}, - onLoadFeatureTypeConfig: () => {} + onExpandAttributeFilterPanel: () => {} }, spatialFilterActions: { onExpandSpatialFilterPanel: () => {}, @@ -107,19 +106,6 @@ const QueryBuilder = React.createClass({ } }; }, - componentDidMount() { - if (this.props.featureTypeConfigUrl && this.props.attributes.length < 1) { - this.props.attributeFilterActions.onLoadFeatureTypeConfig( - this.props.featureTypeConfigUrl, this.props.params); - } - }, - componentWillReceiveProps(props) { - let url = props.featureTypeConfigUrl; - let params = props.params !== this.props.params ? props.params : this.props.params; - if (url !== this.props.featureTypeConfigUrl) { - this.props.attributeFilterActions.onLoadFeatureTypeConfig(url, params); - } - }, render() { if (this.props.featureTypeError !== "") { return (
{this.props.featureTypeErrorText}
); diff --git a/web/client/components/data/query/__tests__/QueryBuilder-test.jsx b/web/client/components/data/query/__tests__/QueryBuilder-test.jsx index 1ad5fdaffe..734add2b4b 100644 --- a/web/client/components/data/query/__tests__/QueryBuilder-test.jsx +++ b/web/client/components/data/query/__tests__/QueryBuilder-test.jsx @@ -145,20 +145,13 @@ describe('QueryBuilder', () => { it('creates the QueryBuilder component in error state', () => { - let attributeFilterActions = { - onLoadFeatureTypeConfig: () => {} - }; - let spy = expect.spyOn(attributeFilterActions, 'onLoadFeatureTypeConfig'); - const querybuilder = ReactDOM.render(, document.getElementById("container")); expect(querybuilder).toExist(); - expect(spy.calls.length).toEqual(1); }); it('creates the QueryBuilder component with empty filter support', () => { diff --git a/web/client/epics/wfsquery.js b/web/client/epics/wfsquery.js new file mode 100644 index 0000000000..fe37e2b15b --- /dev/null +++ b/web/client/epics/wfsquery.js @@ -0,0 +1,77 @@ +/* + * Copyright 2017, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +const Rx = require('rxjs'); +const axios = require('../libs/ajax'); +const {changeSpatialAttribute} = require('../actions/queryform'); +const {FEATURE_TYPE_SELECTED, featureTypeLoaded, featureTypeError} = require('../actions/wfsquery'); + +const types = { + 'xsd:string': 'string', + 'xsd:dateTime': 'date', + 'xsd:number': 'number', + 'xsd:int': 'number' +}; +const fieldConfig = {}; +const extractInfo = (data) => { + return { + geometry: data.featureTypes[0].properties + .filter((attribute) => attribute.type.indexOf('gml:') === 0) + .map((attribute) => { + let conf = { + label: attribute.name, + attribute: attribute.name, + type: 'geometry', + valueId: "id", + valueLabel: "name", + values: [] + }; + conf = fieldConfig[attribute.name] ? {...conf, ...fieldConfig[attribute.name]} : conf; + return conf; + }), + attributes: data.featureTypes[0].properties + .filter((attribute) => attribute.type.indexOf('gml:') !== 0) + .map((attribute) => { + let conf = { + label: attribute.name, + attribute: attribute.name, + type: types[attribute.type], + valueId: "id", + valueLabel: "name", + values: [] + }; + conf = fieldConfig[attribute.name] ? {...conf, ...fieldConfig[attribute.name]} : conf; + return conf; + }) + }; +}; + +const featureTypeSelectedEpic = action$ => + action$.ofType(FEATURE_TYPE_SELECTED).switchMap(action => { + return Rx.Observable.defer( () => + axios.get(action.url + '?service=WFS&version=1.1.0&request=DescribeFeatureType&typeName=' + action.typeName + '&outputFormat=application/json')) + .map((response) => { + if (typeof response.data === 'object' && response.data.featureTypes && response.data.featureTypes[0]) { + const info = extractInfo(response.data); + const geometry = info.geometry[0] && info.geometry[0].attribute ? info.geometry[0].attribute : 'the_geom'; + return Rx.Observable.from([featureTypeLoaded(action.typeName, info), changeSpatialAttribute(geometry)]); + } + try { + JSON.parse(response.data); + } catch(e) { + return Rx.Observable.from([featureTypeError(action.typeName, 'Error from WFS: ' + e.message)]); + } + return Rx.Observable.from([featureTypeError(action.typeName, 'Error: feature types are empty')]); + }) + .mergeAll() + .catch(e => Rx.Observable.of(featureTypeError(action.typeName, e.message))); + }); + +module.exports = { + featureTypeSelectedEpic +}; diff --git a/web/client/plugins/QueryPanel.jsx b/web/client/plugins/QueryPanel.jsx index 492665f7b6..b506e16cef 100644 --- a/web/client/plugins/QueryPanel.jsx +++ b/web/client/plugins/QueryPanel.jsx @@ -24,6 +24,8 @@ const LayersUtils = require('../utils/LayersUtils'); // include application component const QueryBuilder = require('../components/data/query/QueryBuilder'); +const {featureTypeSelectedEpic} = require('../epics/wfsquery'); + const {bindActionCreators} = require('redux'); const { // QueryBuilder action functions @@ -234,5 +236,6 @@ module.exports = { reducers: { queryform: require('../reducers/queryform'), query: require('../reducers/query') - } + }, + epics: {featureTypeSelectedEpic} }; diff --git a/web/client/plugins/TOC.jsx b/web/client/plugins/TOC.jsx index 6bc20641fa..f61d991c5f 100644 --- a/web/client/plugins/TOC.jsx +++ b/web/client/plugins/TOC.jsx @@ -52,7 +52,7 @@ const { zoneChange } = require('../actions/queryform'); -const {createQuery, toggleQueryPanel, describeFeatureType} = require('../actions/wfsquery'); +const {createQuery, toggleQueryPanel} = require('../actions/wfsquery'); const { changeDrawingStatus, @@ -92,9 +92,6 @@ const SmartQueryForm = connect((state) => { return { attributeFilterActions: bindActionCreators({ - onLoadFeatureTypeConfig: (url, params) => { - return describeFeatureType(url, params.typeName); - }, onAddGroupField: addGroupField, onAddFilterField: addFilterField, onRemoveFilterField: removeFilterField, diff --git a/web/client/reducers/query.js b/web/client/reducers/query.js index 1f47ef5be0..a77565356c 100644 --- a/web/client/reducers/query.js +++ b/web/client/reducers/query.js @@ -24,32 +24,6 @@ const {RESET_CONTROLS} = require('../actions/controls'); const assign = require('object-assign'); -const types = { - 'xsd:string': 'string', - 'xsd:dateTime': 'date', - 'xsd:number': 'number', - 'xsd:int': 'number' -}; -const fieldConfig = {}; -const extractInfo = (featureType) => { - return { - attributes: featureType.featureTypes[0].properties - .filter((attribute) => attribute.type.indexOf('gml:') !== 0) - .map((attribute) => { - let conf = { - label: attribute.name, - attribute: attribute.name, - type: types[attribute.type], - valueId: "id", - valueLabel: "name", - values: [] - }; - conf = fieldConfig[attribute.name] ? {...conf, ...fieldConfig[attribute.name]} : conf; - return conf; - }) - }; -}; - const extractData = (feature) => { return ['STATE_NAME', 'STATE_ABBR', 'SUB_REGION', 'STATE_FIPS' ].map((attribute) => ({ attribute, @@ -86,7 +60,7 @@ function query(state = initialState, action) { } case FEATURE_TYPE_LOADED: { return assign({}, state, { - featureTypes: assign({}, state.featureTypes, {[action.typeName]: extractInfo(action.featureType)}) + featureTypes: assign({}, state.featureTypes, {[action.typeName]: action.featureType}) }); } case FEATURE_TYPE_ERROR: { diff --git a/web/client/reducers/queryform.js b/web/client/reducers/queryform.js index fe658a75f2..0ed268517c 100644 --- a/web/client/reducers/queryform.js +++ b/web/client/reducers/queryform.js @@ -19,6 +19,7 @@ const { EXPAND_SPATIAL_PANEL, SELECT_SPATIAL_METHOD, SELECT_SPATIAL_OPERATION, + CHANGE_SPATIAL_ATTRIBUTE, REMOVE_SPATIAL_SELECT, SHOW_SPATIAL_DETAILS, QUERY_FORM_RESET, @@ -167,6 +168,9 @@ function queryform(state = initialState, action) { case SELECT_SPATIAL_OPERATION: { return assign({}, state, {spatialField: assign({}, state.spatialField, {[action.fieldName]: action.operation})}); } + case CHANGE_SPATIAL_ATTRIBUTE: { + return assign({}, state, { spatialField: assign({}, state.spatialField, {attribute: action.attribute}) }); + } case CHANGE_DRAWING_STATUS: { if (action.owner === "queryform" && action.status === "start") { return assign({}, state, {toolbarEnabled: false});