diff --git a/web/client/epics/__tests__/featuregrid-test.js b/web/client/epics/__tests__/featuregrid-test.js
index fad283631f..72c7193a99 100644
--- a/web/client/epics/__tests__/featuregrid-test.js
+++ b/web/client/epics/__tests__/featuregrid-test.js
@@ -60,7 +60,10 @@ import {
launchUpdateFilterFunc,
LAUNCH_UPDATE_FILTER_FUNC,
setLayer,
- setViewportFilter, SET_VIEWPORT_FILTER
+ setViewportFilter, SET_VIEWPORT_FILTER,
+ SAVING,
+ saveChanges,
+ SAVE_SUCCESS
} from '../../actions/featuregrid';
import { SET_HIGHLIGHT_FEATURES_PATH } from '../../actions/highlight';
@@ -141,12 +144,16 @@ import {
toggleSnappingOffOnFeatureGridViewMode,
closeFeatureGridOnDrawingToolOpen,
setViewportFilterEpic,
- deactivateViewportFilterEpic, resetViewportFilter
+ deactivateViewportFilterEpic, resetViewportFilter,
+ savePendingFeatureGridChanges
} from '../featuregrid';
import { onLocationChanged } from 'connected-react-router';
import { TEST_TIMEOUT, testEpic, addTimeoutEpic } from './epicTestUtils';
import { getDefaultFeatureProjection } from '../../utils/FeatureGridUtils';
import { isEmpty, isNil } from 'lodash';
+import axios from "../../libs/ajax";
+import MockAdapter from "axios-mock-adapter";
+
const filterObj = {
featureTypeName: 'TEST',
groupFields: [
@@ -1822,6 +1829,95 @@ describe('featuregrid Epics', () => {
}));
});
describe('updateSelectedOnSaveOrCloseFeatureGrid', () => {
+ let mockAxios;
+ beforeEach(() => {
+ mockAxios = new MockAdapter(axios);
+ });
+ afterEach(() => {
+ mockAxios.restore();
+ });
+ it("test savePendingFeatureGridChanges", (done) => {
+ const stateFeaturegrid = {
+ query: {
+ featureTypes: {
+ "mapstore:TEST_LAYER": {
+ "original": {
+ "elementFormDefault": "qualified",
+ "targetNamespace": "http://localhost:8080/geoserver/mapstore",
+ "targetPrefix": "mapstore",
+ "featureTypes": [
+ {
+ "typeName": "TEST_LAYER",
+ "properties": [
+ {
+ "name": "Integer",
+ "maxOccurs": 1,
+ "minOccurs": 0,
+ "nillable": true,
+ "type": "xsd:int",
+ "localType": "int"
+ },
+ {
+ "name": "Long",
+ "maxOccurs": 1,
+ "minOccurs": 0,
+ "nillable": true,
+ "type": "xsd:int",
+ "localType": "int"
+ },
+ {
+ "name": "Point",
+ "maxOccurs": 1,
+ "minOccurs": 0,
+ "nillable": true,
+ "type": "gml:Point",
+ "localType": "Point"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ },
+ filterObj: {
+ featureTypeName: "mapstore:TEST_LAYER"
+ },
+ searchUrl: "https://localhost:8080/geoserver/wfs?authkey=29031b3b8afc"
+ },
+ featuregrid: {
+ open: true,
+ selectedLayer: "TEST_LAYER",
+ mode: 'EDIT',
+ select: [{id: 'TEST_LAYER', geometry_name: "Point"}],
+ changes: [
+ {
+ "id": "TEST_LAYER.13",
+ "updated": {
+ "Integer": 50
+ }
+ },
+ {
+ "id": "TEST_LAYER.13",
+ "updated": {
+ "Long": 55
+ }
+ }
+ ]
+ }
+ };
+ const payloadSample = `Integer50,Long55`;
+ mockAxios.onPost(stateFeaturegrid.query.searchUrl, payloadSample).replyOnce(200);
+ testEpic(
+ savePendingFeatureGridChanges,
+ 2,
+ saveChanges(),
+ ([a, b]) => {
+ expect(a.type).toEqual(SAVING);
+ expect(b.type).toEqual(SAVE_SUCCESS);
+ done();
+ }, stateFeaturegrid
+ );
+ });
it('on Save', (done) => {
testEpic(
updateSelectedOnSaveOrCloseFeatureGrid,
diff --git a/web/client/epics/featuregrid.js b/web/client/epics/featuregrid.js
index 16b9a27a94..a8738f19f9 100644
--- a/web/client/epics/featuregrid.js
+++ b/web/client/epics/featuregrid.js
@@ -11,8 +11,7 @@ import {get, head, isEmpty, find, castArray, includes, reduce} from 'lodash';
import { LOCATION_CHANGE } from 'connected-react-router';
import axios from '../libs/ajax';
import bbox from '@turf/bbox';
-import { fidFilter } from '../utils/ogc/Filter/filter';
-import { getDefaultFeatureProjection, getPagesToLoad, gridUpdateToQueryUpdate, updatePages } from '../utils/FeatureGridUtils';
+import { createChangesTransaction, getDefaultFeatureProjection, getPagesToLoad, gridUpdateToQueryUpdate, updatePages } from '../utils/FeatureGridUtils';
import assign from 'object-assign';
import {
@@ -232,15 +231,6 @@ const addPagination = (filterObj, pagination) => ({
pagination
});
-const createChangesTransaction = (changes, newFeatures, {insert, update, propertyChange, getPropertyName, transaction})=>
- transaction(
- newFeatures.map(f => insert(f)),
- Object.keys(changes).map( id =>
- Object.keys(changes[id]).map(name =>
- update([propertyChange(getPropertyName(name), changes[id][name]), fidFilter("ogc", id)])
- )
- )
- );
const createDeleteTransaction = (features, {transaction, deleteFeature}) => transaction(
features.map(deleteFeature)
);
diff --git a/web/client/utils/FeatureGridUtils.js b/web/client/utils/FeatureGridUtils.js
index b5406acd93..b35c34341f 100644
--- a/web/client/utils/FeatureGridUtils.js
+++ b/web/client/utils/FeatureGridUtils.js
@@ -18,6 +18,7 @@ import {
} from './ogc/WFS/base';
import { applyDefaultToLocalizedString } from '../components/I18N/LocalizedString';
+import { fidFilter } from './ogc/Filter/filter';
const getGeometryName = (describe) => get(findGeometryProperty(describe), "name");
const getPropertyName = (name, describe) => name === "geometry" ? getGeometryName(describe) : name;
@@ -392,3 +393,17 @@ export const supportsFeatureEditing = (layer) => includes(supportedEditLayerType
* @returns {boolean} flag
*/
export const areLayerFeaturesEditable = (layer) => !layer?.disableFeaturesEditing && supportsFeatureEditing(layer);
+/**
+ * Create wfs-t xml payload for insert/edit features in featuregrid
+ * @param {object} changes object that contains updates e.g: {LAYER_NAME.id: {"FIELD1": 55, "FIELD2":"edit 02"}}
+ * @param {object[]} newFeatures array of new inserted features
+ * @param {object} wfsutils object of wfs utils that includes insert/update/propertyChange/getPropertyName/transaction
+ * @returns {string} wfs-transaction xml payload
+ */
+export const createChangesTransaction = (changes, newFeatures, {insert, update, propertyChange, getPropertyName: getPropertyNameFunc, transaction})=>
+ transaction(
+ newFeatures.map(f => insert(f)),
+ Object.keys(changes).map( id =>{
+ return update(Object.keys(changes[id]).map(prop => propertyChange(getPropertyNameFunc(prop), changes[id][prop])), fidFilter("ogc", id));
+ })
+ );
diff --git a/web/client/utils/__tests__/FeatureGridUtils-test.js b/web/client/utils/__tests__/FeatureGridUtils-test.js
index 53f140dcd3..5609f2b76d 100644
--- a/web/client/utils/__tests__/FeatureGridUtils-test.js
+++ b/web/client/utils/__tests__/FeatureGridUtils-test.js
@@ -16,8 +16,10 @@ import {
getAttributesNames,
featureTypeToGridColumns,
supportsFeatureEditing,
- areLayerFeaturesEditable
+ areLayerFeaturesEditable,
+ createChangesTransaction
} from '../FeatureGridUtils';
+import requestBuilder from "../ogc/WFST/RequestBuilder";
describe('FeatureGridUtils', () => {
@@ -447,4 +449,57 @@ describe('FeatureGridUtils', () => {
expect(areLayerFeaturesEditable({type: "wmts"})).toBeFalsy();
});
});
+ describe('test featuregrid transactions utils', ()=>{
+ const describeFeatureType = {
+ "elementFormDefault": "qualified",
+ "targetNamespace": "http://localhost:8080/geoserver/mapstore",
+ "targetPrefix": "mapstore",
+ "featureTypes": [
+ {
+ "typeName": "TEST_LAYER",
+ "properties": [
+ {
+ "name": "Integer",
+ "maxOccurs": 1,
+ "minOccurs": 0,
+ "nillable": true,
+ "type": "xsd:int",
+ "localType": "int"
+ },
+ {
+ "name": "Long",
+ "maxOccurs": 1,
+ "minOccurs": 0,
+ "nillable": true,
+ "type": "xsd:int",
+ "localType": "int"
+ },
+ {
+ "name": "Point",
+ "maxOccurs": 1,
+ "minOccurs": 0,
+ "nillable": true,
+ "type": "gml:Point",
+ "localType": "Point"
+ }
+ ]
+ }
+ ]
+
+ };
+ it('test createChangesTransaction for single edit', (done) => {
+ const singleChanges = {"TEST_LAYER.13": { "Integer": 50}};
+ const transactionPayload = createChangesTransaction(singleChanges, [], requestBuilder(describeFeatureType));
+ const samplePayload = `Integer50`;
+ expect(transactionPayload).toEqual(samplePayload);
+ done();
+ });
+ it('test createChangesTransaction for multi-edit', (done) => {
+ const multiChanges = {"TEST_LAYER.13": { "Integer": 50, "Long": 55 }};
+ const transactionPayload = createChangesTransaction(multiChanges, [], requestBuilder(describeFeatureType));
+ const multieditPayload = `Integer50,Long55`;
+ expect(transactionPayload).toEqual(multieditPayload);
+ done();
+ });
+ });
});