diff --git a/web/client/api/geofence/RuleService.js b/web/client/api/geofence/RuleService.js index a376f0a42d..6dfbb75628 100644 --- a/web/client/api/geofence/RuleService.js +++ b/web/client/api/geofence/RuleService.js @@ -20,7 +20,7 @@ const EMPTY_RULE = { workspace: "" }; -const cleanConstraints = (rule) => { +export const cleanConstraints = (rule) => { if (!rule.constraints) { return rule; } else if (rule.grant === "DENY") { @@ -28,9 +28,9 @@ const cleanConstraints = (rule) => { return r; } let constraints = { ...rule.constraints }; - constraints.allowedStyles = constraints.allowedStyles && constraints.allowedStyles.style || []; - constraints.attributes = constraints.attributes && constraints.attributes.attribute || []; - constraints.restrictedAreaWkt = constraints.restrictedAreaWkt || ""; + constraints.allowedStyles = constraints?.allowedStyles?.style ?? []; + constraints.attributes = constraints?.attributes?.attribute ?? []; + if (!constraints.restrictedAreaWkt) constraints.restrictedAreaWkt = null; // cannot be empty string, may cause API call to fail return { ...rule, constraints }; }; diff --git a/web/client/api/geofence/__tests__/RuleService-test.js b/web/client/api/geofence/__tests__/RuleService-test.js index cf8e58855e..30237c0070 100644 --- a/web/client/api/geofence/__tests__/RuleService-test.js +++ b/web/client/api/geofence/__tests__/RuleService-test.js @@ -14,7 +14,7 @@ import RULES from 'raw-loader!../../../test-resources/geofence/rest/rules/rules_ import axios from '../../../libs/ajax'; import GF_RULE from '../../../test-resources/geofence/rest/rules/full_rule1.json'; -import ruleServiceFactory from '../RuleService'; +import ruleServiceFactory, { cleanConstraints } from '../RuleService'; const RuleService = ruleServiceFactory({ addBaseUrl: (opts) => ({...opts, baseURL: BASE_URL}), @@ -108,4 +108,21 @@ describe('RuleService API for GeoFence StandAlone', () => { }); // TODO: updateRules, cleanCache + it("test cleanConstraints", () => { + let rule = {}; + expect(cleanConstraints(rule)).toEqual(rule); + const grant = "DENY"; + rule = {constraints: "some", grant}; + expect(cleanConstraints(rule)).toEqual({grant}); + rule = {constraints: {allowedStyles: undefined, attributes: null, restrictedAreaWkt: ""}}; + expect(cleanConstraints(rule)).toEqual({constraints: {allowedStyles: [], attributes: [], restrictedAreaWkt: null}}); + rule = {constraints: {allowedStyles: {style: {"color": "#000"}}, attributes: {attribute: [{access: "READONLY", name: "ID"}]}, restrictedAreaWkt: "POLYGON((10 10, 10, 20, 20 20, 20 10, 10 10))"}}; + expect(cleanConstraints(rule)).toEqual({ + constraints: { + allowedStyles: rule.constraints.allowedStyles.style, + attributes: rule.constraints.attributes.attribute, + restrictedAreaWkt: rule.constraints.restrictedAreaWkt + } + }); + }); }); diff --git a/web/client/components/manager/rulesmanager/ruleseditor/AttributesEditor.jsx b/web/client/components/manager/rulesmanager/ruleseditor/AttributesEditor.jsx index 961abf965e..2d4d28de54 100644 --- a/web/client/components/manager/rulesmanager/ruleseditor/AttributesEditor.jsx +++ b/web/client/components/manager/rulesmanager/ruleseditor/AttributesEditor.jsx @@ -6,9 +6,10 @@ * LICENSE file in the root directory of this source tree. */ -import { castArray } from 'lodash'; -import React from 'react'; +import React, { useEffect } from 'react'; import { Col, Grid, Row } from 'react-bootstrap'; +import castArray from 'lodash/castArray'; +import isEmpty from 'lodash/isEmpty'; import Message from '../../../I18N/Message'; import Select from '../AttributeAccessSelect'; @@ -22,12 +23,22 @@ const getAttributeValue = (name, constraints) => { export default ({attributes = [], constraints = {}, setOption = () => {}, active = false, setEditedAttributes = () => {}, editedAttributes = []}) => { const onChange = (at) => { - const {attributes: attrs} = constraints; - const attribute = ((attrs && attrs?.attribute?.length) ? attrs.attribute : (attrs?.attribute) ? [attrs.attribute] : [] || []).filter(e => e.name !== at.name).concat(at); + let {attributes: {attribute = []} = {}} = constraints ?? {}; + attribute = castArray(attribute).map(attr => at.name === attr.name ? at : attr); setOption({key: "attributes", value: {attribute}}); // add it to edited attribute if (!editedAttributes.includes(at.name)) setEditedAttributes(at.name); }; + useEffect(() => { + if (!isEmpty(attributes)) { + const _constraints = attributes.map(attr => ({name: attr.name, access: "READONLY"})); + const {attributes: {attribute = []} = {}} = constraints ?? {}; + const modifiedAttribute = _constraints.map(attr => { + return castArray(attribute).find(a=> a.name === attr.name) ?? attr; + }); + setOption({key: "attributes", value: {attribute: modifiedAttribute}}); + } + }, [attributes]); return ( diff --git a/web/client/components/manager/rulesmanager/ruleseditor/__tests__/AttributesEditor-test.jsx b/web/client/components/manager/rulesmanager/ruleseditor/__tests__/AttributesEditor-test.jsx index 140830c9ba..7e4889dbc6 100644 --- a/web/client/components/manager/rulesmanager/ruleseditor/__tests__/AttributesEditor-test.jsx +++ b/web/client/components/manager/rulesmanager/ruleseditor/__tests__/AttributesEditor-test.jsx @@ -10,6 +10,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import expect from 'expect'; +import TestUtils from "react-dom/test-utils"; import AttributesEditor from '../AttributesEditor.jsx'; const constraints = { attributes: { @@ -51,6 +52,54 @@ describe('Attributes Editor component', () => { expect(rows).toExist(); expect(rows.length).toBe(3); }); + it('render attributes on setOption', (done) => { + TestUtils.act(() => { + ReactDOM.render( { + try { + expect(value.key).toBe('attributes'); + expect(value.value).toEqual({"attribute": [{"name": "the_geom", "access": "READONLY"}, {"access": "READONLY", "name": "cat"}]}); + } catch (e) { + done(e); + } + done(); + }} + attributes={attributes} active + constraints={constraints} + />, document.getElementById("container")); + }); + const container = document.getElementById('container'); + const rows = container.querySelectorAll('.row'); + expect(rows).toBeTruthy(); + }); + it('render attributes on change value', (done) => { + TestUtils.act(() => { + ReactDOM.render( { + try { + const isModified = value.value?.attribute?.some(attr => attr.access === 'READWRITE'); + if (isModified) { + expect(value.key).toBe('attributes'); + expect(value.value).toEqual({"attribute": [{"name": "cat", "access": "READWRITE"}]}); + } + } catch (e) { + done(e); + } + done(); + }} + attributes={attributes} active + constraints={constraints} + />, document.getElementById("container")); + }); + const container = document.getElementById('container'); + const rows = container.querySelectorAll('.row'); + expect(rows).toBeTruthy(); + const rule = document.querySelectorAll('.Select-control')[1]; + expect(rule).toBeTruthy(); + TestUtils.Simulate.mouseDown(rule, { button: 0 }); + TestUtils.Simulate.keyDown(rule, { keyCode: 40, key: 'ArrowDown' }); + TestUtils.Simulate.keyDown(rule, { key: 'Enter', keyCode: 13 }); + }); it('render attributes with highlighted DD', () => { ReactDOM.render(, document.getElementById("container")); const container = document.getElementById('container');