From 9c1833aebd5f31f9b668400b78f1cd5cce7d2ad7 Mon Sep 17 00:00:00 2001 From: mahmoudadel54 Date: Mon, 30 Oct 2023 04:55:31 +0200 Subject: [PATCH 1/3] #9216: handle fixing not visibility of spatial filter for widgets that created by the attr table --- .../epics/__tests__/widgetsbuilder-test.js | 8 ++++---- web/client/epics/widgetsbuilder.js | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/web/client/epics/__tests__/widgetsbuilder-test.js b/web/client/epics/__tests__/widgetsbuilder-test.js index 565b72c2cf..fbe6f60980 100644 --- a/web/client/epics/__tests__/widgetsbuilder-test.js +++ b/web/client/epics/__tests__/widgetsbuilder-test.js @@ -215,8 +215,8 @@ describe('widgetsbuilder epic', () => { }); it('handleWidgetsFilterPanel', (done) => { const startActions = [openFilterEditor()]; - testEpic(handleWidgetsFilterPanel, 4, startActions, actions => { - expect(actions.length).toBe(4); + testEpic(handleWidgetsFilterPanel, 3, startActions, actions => { + expect(actions.length).toBe(3); actions.map((action) => { switch (action.type) { case SET_CONTROL_PROPERTY: @@ -274,8 +274,8 @@ describe('widgetsbuilder epic', () => { const startActions = [openFilterEditor(), search("TEST", { })]; - testEpic(handleWidgetsFilterPanel, 8, startActions, actions => { - expect(actions.length).toBe(8); + testEpic(handleWidgetsFilterPanel, 7, startActions, actions => { + expect(actions.length).toBe(7); actions.map((action) => { switch (action.type) { case SET_CONTROL_PROPERTY: diff --git a/web/client/epics/widgetsbuilder.js b/web/client/epics/widgetsbuilder.js index e917bc0e67..b1f54b670d 100644 --- a/web/client/epics/widgetsbuilder.js +++ b/web/client/epics/widgetsbuilder.js @@ -23,17 +23,17 @@ import { QUERY_FORM_SEARCH, loadFilter } from '../actions/queryform'; import { setControlProperty, TOGGLE_CONTROL } from '../actions/controls'; import { ADD_LAYER } from '../actions/layers'; import { LOCATION_CHANGE } from 'connected-react-router'; -import { featureTypeSelected } from '../actions/wfsquery'; +// import { featureTypeSelected } from '../actions/wfsquery'; import { getWidgetLayer, getEditingWidgetFilter, getWidgetFilterKey } from '../selectors/widgets'; import { wfsFilter } from '../selectors/query'; import { widgetBuilderAvailable } from '../selectors/controls'; import { generateNewTrace } from '../utils/WidgetsUtils'; -const getFTSelectedArgs = (state) => { - let layer = getWidgetLayer(state); - let url = layer.search && layer.search.url; - let typeName = layer.name; - return [url, typeName, layer.fields]; -}; +// const getFTSelectedArgs = (state) => { +// let layer = getWidgetLayer(state); +// let url = layer.search && layer.search.url; +// let typeName = layer.name; +// return [url, typeName, layer.fields]; +// }; export const openWidgetEditor = (action$, {getState = () => {}} = {}) => action$.ofType(NEW, EDIT, NEW_CHART) .filter(() => widgetBuilderAvailable(getState())) @@ -93,7 +93,7 @@ export const handleWidgetsFilterPanel = (action$, {getState = () => {}} = {}) => .switchMap(() => // open and setup query form Rx.Observable.of( - featureTypeSelected(...getFTSelectedArgs(getState())), + // featureTypeSelected(...getFTSelectedArgs(getState())), loadFilter(getEditingWidgetFilter(getState())), setControlProperty("widgetBuilder", "enabled", false), setControlProperty('queryPanel', "enabled", true) From 3f1d471398f927a9b45f1edd0f58a975bac70fe8 Mon Sep 17 00:00:00 2001 From: mahmoudadel54 Date: Mon, 6 Nov 2023 21:56:59 +0200 Subject: [PATCH 2/3] #9216: resolve the PR reviews --- web/client/actions/wfsquery.js | 15 +++++++++------ web/client/epics/featuregrid.js | 8 ++++---- web/client/epics/wfsquery.js | 2 +- web/client/epics/widgetsbuilder.js | 16 ++++++++-------- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/web/client/actions/wfsquery.js b/web/client/actions/wfsquery.js index e6ec54372f..14b41ae5a7 100644 --- a/web/client/actions/wfsquery.js +++ b/web/client/actions/wfsquery.js @@ -46,19 +46,21 @@ export function initQueryPanel() { type: INIT_QUERY_PANEL }; } -export function featureTypeSelected(url, typeName, fields = []) { +export function featureTypeSelected(url, typeName, fields = [], owner) { return { type: FEATURE_TYPE_SELECTED, url, typeName, - fields + fields, + owner }; } -export function featureTypeLoaded(typeName, featureType) { +export function featureTypeLoaded(typeName, featureType, owner) { return { type: FEATURE_TYPE_LOADED, typeName, - featureType + featureType, + owner }; } @@ -142,11 +144,12 @@ export function loadFeature(baseUrl, typeName) { }); }; } -export function createQuery(searchUrl, filterObj) { +export function createQuery(searchUrl, filterObj, owner) { return { type: QUERY_CREATE, searchUrl, - filterObj + filterObj, + owner }; } diff --git a/web/client/epics/featuregrid.js b/web/client/epics/featuregrid.js index 302d3305ba..6982b28a26 100644 --- a/web/client/epics/featuregrid.js +++ b/web/client/epics/featuregrid.js @@ -273,11 +273,11 @@ const createLoadPageFlow = (store) => ({page, size, reason} = {}) => { const createInitialQueryFlow = (action$, store, {url, name, id, fields} = {}) => { const filterObj = get(store.getState(), `featuregrid.advancedFilters["${id}"]`); - const createInitialQuery = () => createQuery(url, filterObj || { + const createInitialQuery = (action) => createQuery(url, filterObj || { featureTypeName: name, filterType: 'OGC', ogcVersion: '1.1.0' - }); + }, action.owner); return Rx.Observable.of(featureTypeSelected(url, name, fields)).merge( action$.ofType(FEATURE_TYPE_LOADED).filter(({typeName} = {}) => typeName === name) @@ -356,8 +356,8 @@ export const featureGridLayerSelectionInitialization = (action$) => */ export const featureGridStartupQuery = (action$, store) => action$.ofType(QUERY_CREATE) - .switchMap(() => Rx.Observable.of(changePage(0)) - .concat(modeSelector(store.getState()) === MODES.VIEW ? Rx.Observable.of(toggleViewMode()) : Rx.Observable.empty())); + .switchMap(({ owner }) => Rx.Observable.of(changePage(0)) + .concat(modeSelector(store.getState()) === MODES.VIEW && owner !== "widgets" ? Rx.Observable.of(toggleViewMode()) : Rx.Observable.empty())); /** * Create sorted queries on sort action * With virtualScroll active reset to page 0 but the grid will reload diff --git a/web/client/epics/wfsquery.js b/web/client/epics/wfsquery.js index c08bf8bd54..e9c3b50dbd 100644 --- a/web/client/epics/wfsquery.js +++ b/web/client/epics/wfsquery.js @@ -102,7 +102,7 @@ export const featureTypeSelectedEpic = (action$, store) => const info = extractInfo(layerDescribeSelector(state, action.typeName), action.fields); const geometry = info.geometry[0] && info.geometry[0].attribute ? info.geometry[0].attribute : 'the_geom'; - return Rx.Observable.of(featureTypeLoaded(action.typeName, info), changeSpatialAttribute(geometry), Rx.Scheduler.async); // async scheduler is needed to allow invokers of `FEATURE_TYPE_SELECTED` to intercept `FEATURE_TYPE_LOADED` action as response. + return Rx.Observable.of(featureTypeLoaded(action.typeName, info, action.owner), changeSpatialAttribute(geometry), Rx.Scheduler.async); // async scheduler is needed to allow invokers of `FEATURE_TYPE_SELECTED` to intercept `FEATURE_TYPE_LOADED` action as response. } const selectedLayer = selectedLayerSelector(state); diff --git a/web/client/epics/widgetsbuilder.js b/web/client/epics/widgetsbuilder.js index b1f54b670d..e917bc0e67 100644 --- a/web/client/epics/widgetsbuilder.js +++ b/web/client/epics/widgetsbuilder.js @@ -23,17 +23,17 @@ import { QUERY_FORM_SEARCH, loadFilter } from '../actions/queryform'; import { setControlProperty, TOGGLE_CONTROL } from '../actions/controls'; import { ADD_LAYER } from '../actions/layers'; import { LOCATION_CHANGE } from 'connected-react-router'; -// import { featureTypeSelected } from '../actions/wfsquery'; +import { featureTypeSelected } from '../actions/wfsquery'; import { getWidgetLayer, getEditingWidgetFilter, getWidgetFilterKey } from '../selectors/widgets'; import { wfsFilter } from '../selectors/query'; import { widgetBuilderAvailable } from '../selectors/controls'; import { generateNewTrace } from '../utils/WidgetsUtils'; -// const getFTSelectedArgs = (state) => { -// let layer = getWidgetLayer(state); -// let url = layer.search && layer.search.url; -// let typeName = layer.name; -// return [url, typeName, layer.fields]; -// }; +const getFTSelectedArgs = (state) => { + let layer = getWidgetLayer(state); + let url = layer.search && layer.search.url; + let typeName = layer.name; + return [url, typeName, layer.fields]; +}; export const openWidgetEditor = (action$, {getState = () => {}} = {}) => action$.ofType(NEW, EDIT, NEW_CHART) .filter(() => widgetBuilderAvailable(getState())) @@ -93,7 +93,7 @@ export const handleWidgetsFilterPanel = (action$, {getState = () => {}} = {}) => .switchMap(() => // open and setup query form Rx.Observable.of( - // featureTypeSelected(...getFTSelectedArgs(getState())), + featureTypeSelected(...getFTSelectedArgs(getState())), loadFilter(getEditingWidgetFilter(getState())), setControlProperty("widgetBuilder", "enabled", false), setControlProperty('queryPanel', "enabled", true) From 5a371284742f6b167de11d936fa6a662c5cef07f Mon Sep 17 00:00:00 2001 From: mahmoudadel54 Date: Fri, 10 Nov 2023 16:47:51 +0200 Subject: [PATCH 3/3] #9216: resolve review comments by handling required unit tests --- web/client/actions/__tests__/wfsquery-test.js | 40 ++++- web/client/epics/__tests__/wfsquery-test.js | 148 ++++++++++++++++++ .../epics/__tests__/widgetsbuilder-test.js | 1 + web/client/epics/widgetsbuilder.js | 2 +- 4 files changed, 189 insertions(+), 2 deletions(-) diff --git a/web/client/actions/__tests__/wfsquery-test.js b/web/client/actions/__tests__/wfsquery-test.js index b4c65be7e6..3fa2f53473 100644 --- a/web/client/actions/__tests__/wfsquery-test.js +++ b/web/client/actions/__tests__/wfsquery-test.js @@ -11,6 +11,7 @@ import expect from 'expect'; import { LAYER_SELECTED_FOR_SEARCH, FEATURE_TYPE_SELECTED, + FEATURE_TYPE_LOADED, FEATURE_TYPE_ERROR, FEATURE_LOADING, FEATURE_LOADED, @@ -25,6 +26,7 @@ import { initQueryPanel, layerSelectedForSearch, featureTypeSelected, + featureTypeLoaded, featureTypeError, featureLoading, featureLoaded, @@ -47,13 +49,36 @@ describe('wfsquery actions', () => { let {type} = initQueryPanel(); expect(type).toBe(INIT_QUERY_PANEL); }); - it('featureTypeSelected', () => { + it('featureTypeSelected without owner', () => { let {type, url, typeName, fields} = featureTypeSelected("/geoserver/", "topp:states", [{name: "name", alias: "alias"}]); expect(type).toBe(FEATURE_TYPE_SELECTED); expect(url).toBe("/geoserver/"); expect(typeName).toBe("topp:states"); expect(fields).toEqual([{name: "name", alias: "alias"}]); }); + + it('featureTypeSelected with owner as parameter', () => { + let {type, url, typeName, fields, owner} = featureTypeSelected("/geoserver/", "topp:states", [{name: "name", alias: "alias"}], "owner"); + expect(type).toBe(FEATURE_TYPE_SELECTED); + expect(url).toBe("/geoserver/"); + expect(typeName).toBe("topp:states"); + expect(fields).toEqual([{name: "name", alias: "alias"}]); + expect(owner).toBe("owner"); + }); + it('featureTypeLoaded without owner', () => { + let {type, typeName, featureType} = featureTypeLoaded("topp:states", "featureType"); + expect(type).toBe(FEATURE_TYPE_LOADED); + expect(typeName).toBe("topp:states"); + expect(featureType).toBe("featureType"); + }); + + it('featureTypeLoaded with owner as parameter', () => { + let {type, typeName, featureType, owner} = featureTypeLoaded("topp:states", "featureType", "owner"); + expect(type).toBe(FEATURE_TYPE_LOADED); + expect(typeName).toBe("topp:states"); + expect(featureType).toBe("featureType"); + expect(owner).toBe("owner"); + }); it('featureTypeError', () => { let {type, error, typeName} = featureTypeError("topp:states", "ERROR"); expect(type).toBe(FEATURE_TYPE_ERROR); @@ -71,6 +96,12 @@ describe('wfsquery actions', () => { expect(typeName).toBe("topp:states"); expect(feature).toBe(feature); }); + it('featureLoaded with owner as parameter', () => { + let {type, typeName, feature} = featureLoaded("topp:states", "feature"); + expect(type).toBe(FEATURE_LOADED); + expect(typeName).toBe("topp:states"); + expect(feature).toBe(feature); + }); it('featureError', () => { let {type, typeName, error} = featureError("topp:states", "ERROR"); expect(type).toBe(FEATURE_ERROR); @@ -95,6 +126,13 @@ describe('wfsquery actions', () => { expect(searchUrl).toBe("searchUrl"); expect(filterObj).toBe("filterObj"); }); + it('createQuery with owner param', () => { + let {type, searchUrl, filterObj, owner} = createQuery("searchUrl", "filterObj", "owner"); + expect(type).toBe(QUERY_CREATE); + expect(searchUrl).toBe("searchUrl"); + expect(filterObj).toBe("filterObj"); + expect(owner).toBe("owner"); + }); it('query', () => { let {type, searchUrl, filterObj} = query("searchUrl", "filterObj"); expect(type).toBe(QUERY); diff --git a/web/client/epics/__tests__/wfsquery-test.js b/web/client/epics/__tests__/wfsquery-test.js index 06939c7d5b..56c5c73a1f 100644 --- a/web/client/epics/__tests__/wfsquery-test.js +++ b/web/client/epics/__tests__/wfsquery-test.js @@ -517,4 +517,152 @@ describe('wfsquery Epics', () => { }, mockState); }); }); + + + describe('featureTypeSelectedEpic with owner', () => { + const expectedResult = require('../../test-resources/vector/feature-collection-vector.json'); + const flatLayers = [{ + id: 'layer1', + name: 'layer1 name', + title: 'layer1 title', + description: 'layer1 description', + type: 'vector', + features: expectedResult + }]; + const wmsLayer = [{ + id: 'layer2', + name: 'poi', + title: 'layer2 title', + description: 'layer2 description', + type: 'wms', + fields: [{ + name: "NAME", + alias: "Name Alias", + type: "string" + }] + }]; + it('vector layer', (done) => { + const mockState = { + query: { + data: {}, + featureTypes: [], + typeName: 'layer1', + url: '/dummy'}, + featuregrid: { + timeSync: true, + pagination: { + size: 10 + }, + open: true, + selectedLayer: "layer1", + changes: [], + mode: 'VIEW' + }, + layers: { + flat: flatLayers, + layerMetadata: { + expanded: false, + maskLoading: false + }, + settings: { + expanded: false, + node: null, + nodeType: null, + options: {} + } + } + }; + mockAxios.onPost().reply(() => {return [200, expectedResult];}); + testEpic(addTimeoutEpic(wfsQueryEpic, 500), 4, [ + query("base/web/client/test-resources/vector/feature-collection-vector.json", {pagination: {} }), + featureTypeSelected('/dummy', 'layer1', [], "widgets") + ], actions => { + expect(actions.length).toBe(4); + actions.map((action) => { + switch (action.type) { + case QUERY_RESULT: + expect(action.result.features).toEqual(expectedResult); + expect(action.result.totalFeatures).toEqual(expectedResult.length); + expect(action.result.numberMatched).toEqual(expectedResult.length); + expect(action.result.numberReturned).toEqual(expectedResult.length); + break; + case FEATURE_LOADING: + break; + case LAYER_LOAD: + break; + default: + expect(false).toBe(true); + } + }); + done(); + }, + mockState + ); + }); + + it('wms layer', (done) => { + const mockState = { + query: { + data: {}, + featureTypes: [], + typeName: 'layer1', + url: '/dummy'}, + featuregrid: { + timeSync: true, + pagination: { + size: 10 + }, + open: true, + selectedLayer: "layer1", + changes: [], + mode: 'VIEW' + }, + layers: { + flat: wmsLayer, + layerMetadata: { + expanded: false, + maskLoading: false + }, + settings: { + expanded: false, + node: null, + nodeType: null, + options: {} + } + } + }; + const wfsResults = require('../../test-resources/wfs/describe-pois.json'); + mockAxios.onGet().reply(() => [200, wfsResults]); + testEpic(featureTypeSelectedEpic, 2, + featureTypeSelected('/dummy', 'poi', wmsLayer[0].fields), ([changeSpatialAttribute, featureTypeLoaded]) => { + try { + expect(featureTypeLoaded.type).toBe(FEATURE_TYPE_LOADED); + expect(changeSpatialAttribute.type).toBe(CHANGE_SPATIAL_ATTRIBUTE); + expect(featureTypeLoaded.featureType.attributes).toEqual([{ + label: "Name Alias", + attribute: "NAME", + type: "string", + valueId: "id", + valueLabel: "name", + values: [] + }, + { + label: "THUMBNAIL", + attribute: "THUMBNAIL", + type: "string", + valueId: "id", + valueLabel: "name", + values: [] + }, + { + label: "MAINPAGE", attribute: "MAINPAGE", type: "string", valueId: "id", valueLabel: "name", values: [] + }]); + + } catch (error) { + done(error); + } + done(); + }, mockState); + }); + }); }); diff --git a/web/client/epics/__tests__/widgetsbuilder-test.js b/web/client/epics/__tests__/widgetsbuilder-test.js index fbe6f60980..e7a259f70b 100644 --- a/web/client/epics/__tests__/widgetsbuilder-test.js +++ b/web/client/epics/__tests__/widgetsbuilder-test.js @@ -236,6 +236,7 @@ describe('widgetsbuilder epic', () => { type: "string", alias: "X alias" }]); + expect(action.owner).toEqual("widgets"); break; case LOAD_FILTER: break; diff --git a/web/client/epics/widgetsbuilder.js b/web/client/epics/widgetsbuilder.js index e917bc0e67..3ca15f8bbc 100644 --- a/web/client/epics/widgetsbuilder.js +++ b/web/client/epics/widgetsbuilder.js @@ -93,7 +93,7 @@ export const handleWidgetsFilterPanel = (action$, {getState = () => {}} = {}) => .switchMap(() => // open and setup query form Rx.Observable.of( - featureTypeSelected(...getFTSelectedArgs(getState())), + featureTypeSelected(...getFTSelectedArgs(getState()).concat(["widgets"])), loadFilter(getEditingWidgetFilter(getState())), setControlProperty("widgetBuilder", "enabled", false), setControlProperty('queryPanel', "enabled", true)