Skip to content

Commit

Permalink
first implementation of ruleseditor
Browse files Browse the repository at this point in the history
  • Loading branch information
kappu committed Apr 27, 2018
1 parent b14a08d commit 7901108
Show file tree
Hide file tree
Showing 44 changed files with 1,735 additions and 137 deletions.
28 changes: 27 additions & 1 deletion web/client/actions/__tests__/rulesmanager-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,35 @@ const { RULES_SELECTED, RULES_LOADED, UPDATE_ACTIVE_RULE,
ACTION_ERROR, OPTIONS_LOADED, UPDATE_FILTERS_VALUES,
rulesSelected, rulesLoaded, updateActiveRule,
actionError, optionsLoaded, updateFiltersValues,
SET_FILTER, setFilter} = require('../rulesmanager');
SET_FILTER, setFilter,
SAVE_RULE, saveRule, cleanEditing, CLEAN_EDITING,
onEditRule, EDIT_RULE, delRules, DELETE_RULES} = require('../rulesmanager');

describe('test rules manager actions', () => {
it('save rule', () => {
const rule = {};
const action = saveRule(rule);
expect(action).toExist();
expect(action.type).toBe(SAVE_RULE);
expect(action.rule).toBe(rule);
});
it('clean editing', () => {
const action = cleanEditing();
expect(action).toExist();
expect(action.type).toBe(CLEAN_EDITING);
});
it('on edit rule', () => {
const action = onEditRule();
expect(action).toExist();
expect(action.type).toBe(EDIT_RULE);
expect(action.createNew).toBe(false);
expect(action.targetPriority).toBe(0);
});
it('delete rules', () => {
const action = delRules();
expect(action).toExist();
expect(action.type).toBe(DELETE_RULES);
});
it('set Filter', () => {
const action = setFilter("key", "value");
expect(action).toExist();
Expand Down
44 changes: 39 additions & 5 deletions web/client/actions/rulesmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ const ACTION_ERROR = 'ACTION_ERROR';
const OPTIONS_LOADED = 'OPTIONS_LOADED';
const LOADING = 'RULES_MANAGER:LOADING';
const SET_FILTER = "RULES_MANAGER:SET_FILTER";
const EDIT_RULE = "RULES_MANAGER:EDIT_RULE";
const CLEAN_EDITING = "RULES_MANAGER:CLEAN_EDITING";
const SAVE_RULE = "RULES_MANAGER:SAVE_RULE";
const RULE_SAVED = "RULES_MANAGER:RULE_SAVED";
const DELETE_RULES = "RULES_MANAGER: DELETE_RULES";

function delRules(ids) {
return {
type: DELETE_RULES,
ids
};
}

function setFilter(key, value) {
return {
Expand All @@ -28,19 +40,35 @@ function setFilter(key, value) {
value
};
}

function onEditRule(targetPriority = 0, createNew = false) {
return {
type: EDIT_RULE,
createNew,
targetPriority
};
}

function cleanEditing() {
return {
type: CLEAN_EDITING
};
}

function setLoading(loading) {
return {
type: LOADING,
loading
};
}

function rulesSelected(rules, merge, unselect) {
function rulesSelected(rules, merge, unselect, targetPosition) {
return {
type: RULES_SELECTED,
rules: rules,
merge: merge,
unselect: unselect
rules,
merge,
unselect,
targetPosition
};
}

Expand Down Expand Up @@ -219,6 +247,8 @@ function updateRule() {
};
}

const saveRule = (rule) => ({type: SAVE_RULE, rule});

module.exports = {
RULES_SELECTED,
RULES_LOADED,
Expand All @@ -243,5 +273,9 @@ module.exports = {
actionError,
optionsLoaded,
LOADING, setLoading,
SET_FILTER, setFilter
SET_FILTER, setFilter,
EDIT_RULE, onEditRule,
CLEAN_EDITING, cleanEditing,
SAVE_RULE, saveRule, RULE_SAVED,
DELETE_RULES, delRules
};
27 changes: 22 additions & 5 deletions web/client/api/geoserver/GeoFence.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,16 @@ const axios = require('../../libs/ajax');
const assign = require('object-assign');

const ConfigUtils = require('../../utils/ConfigUtils');

const EMPTY_RULE = {
constraints: {},
ipaddress: "",
layer: "",
request: "",
rolename: "",
service: "",
username: "",
workspace: ""
};
var Api = {

loadRules: function(page, rulesFiltersValues, entries = 10) {
Expand Down Expand Up @@ -55,18 +64,26 @@ var Api = {
},

addRule: function(rule) {
if (!rule.access) {
rule.access = "ALLOW";
const newRule = {...rule};
if (!newRule.instance) {
const {id: instanceId} = ConfigUtils.getDefaults().geoFenceGeoServerInstance;
newRule.instance = {id: instanceId};
}
if (!newRule.grant) {
newRule.grant = "ALLOW";
}
return axios.post('geofence/rest/rules', rule, this.addBaseUrl({
return axios.post('geofence/rest/rules', newRule, this.addBaseUrl({
'headers': {
'Content': 'application/json'
}
}));
},

updateRule: function(rule) {
return axios.post('geofence/rest/rules/id/' + rule.id, rule, this.addBaseUrl({
// id, priority and grant aren't updatable
const {id, priority, grant, position, ...others} = rule;
const newRule = {...EMPTY_RULE, ...others};
return axios.put(`geofence/rest/rules/id/${id}`, newRule, this.addBaseUrl({
'headers': {
'Content': 'application/json'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,29 @@ const stop = stream$ => stream$.filter(() => false);

const sameParentFilter = ({parentsFilter: f1}, {parentsFilter: f2}) => f1 === f2;
const sameFilter = ({val: f1}, {val: f2}) => f1 === f2;
const sameEmptyRequest = ({emptyReq: f1}, {emptyReq: f2}) => f1 === f2;

// ParentFilter change so resets the data
const resetStream = prop$ => prop$.distinctUntilChanged((oP, nP) => sameParentFilter(oP, nP)).skip(1).do(({resetCombo}) => resetCombo()).let(stop);

// Trigger first loading when value change
const triggerEmptyLoadDataStream = prop$ => prop$.distinctUntilChanged((oP, nP) => sameEmptyRequest(oP, nP))
.skip(1)
.debounceTime(300)
.switchMap(({emptySearch, loadingErrorMsg, nextPage, prevPage, onError, parentsFilter = {}, size = 10, pagination, loadData, setData = () => {}}) => {
return loadData(emptySearch, 0, size, parentsFilter, true)
.do(({count, data}) => {
setData({
pagination: {...pagination, loadNextPage: nextPage, loadPrevPage: prevPage, firstPage: true, lastPage: Math.ceil(count / size) <= 1},
data,
page: 0,
count
});
}).let(stop).startWith({busy: true}).catch((e) => Rx.Observable.of(e).do(() => {
onError(loadingErrorMsg);
}).mapTo({busy: false})).concat(Rx.Observable.of({busy: false}));
});

// Trigger first loading when value change
const triggerLoadDataStream = prop$ => prop$.distinctUntilChanged((oP, nP) => sameFilter(oP, nP))
.debounceTime(300)
Expand Down Expand Up @@ -55,7 +74,7 @@ const dataStreamFactory = prop$ => {
setData, loadingErrorMsg})), (pageStep, other) => ({ pageStep, ...other}));

const $p = prop$.map(o => ({ ...o, nextPage, prevPage, nextPage$, prevPage$}));
return triggerLoadDataStream($p).merge(loadPageStream(page$), resetStream($p)).startWith({busy: false});
return triggerLoadDataStream($p).merge(triggerEmptyLoadDataStream($p), loadPageStream(page$), resetStream($p)).startWith({busy: false});
};


Expand All @@ -75,8 +94,8 @@ const dataStreamFactory = prop$ => {

module.exports = compose(
defaultProps({
emptySearch: "%",
stopPropagation: true,
emitOnReset: false,
paginated: true,
size: 5,
loadData: () => Rx.Observable.of({data: [], count: 0}),
Expand All @@ -94,17 +113,15 @@ module.exports = compose(
val: selected,
page: 0,
data: initialData,
emptyReq: 0,
pagination: {
paginated: paginated,
firstPage: false,
lastPage: false,
loadPrevPage: () => {},
loadNextPage: () => {}
}}), {
resetCombo: ({emitOnReset, onValueSelected}, {initialData = []}) => () => {
if (emitOnReset) {
onValueSelected();
}
resetCombo: ({onValueSelected}, { initialData = []}) => () => {
return {
data: initialData,
page: 0,
Expand All @@ -117,14 +134,17 @@ module.exports = compose(
page,
count
}),
onChange: (state, {initialData = []}) => (val = "") => {
return val.length === 0 && {val, data: initialData} || {val};
onChange: (state, {initialData = [], valueField}) => (val = "") => {
const currentVal = isObject(val) && val[valueField] || val;
return currentVal.length === 0 && {val: currentVal, data: initialData} || {val: currentVal};
},
onToggle: ({ val = ""}, {selected, onValueSelected, initialData = []}) => (open) => {
onToggle: ({ val = "", data, emptyReq}, {selected, onValueSelected, initialData = []}) => (open) => {
if (!open && val === "" && selected) {
onValueSelected();
}else if (!open && val !== selected) {
return {val: selected, data: initialData};
}else if (open && val.length === 0 && data.length === 0) {
return {emptyReq: emptyReq + 1};
}
},
onSelect: (state, {onValueSelected, selected, valueField}) => (select) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const React = require('react');
const {Grid} = require('react-bootstrap');
const Selectors = require("./attributeselectors");

module.exports = ({rule = {}, setOption= () => {}, active = true}) => {
return (
<Grid className="ms-rule-editor" fluid style={{width: '100%', display: active ? 'block' : 'none'}}>
{ !!rule.id && (<Selectors.Priority key="priority" selected={rule.priority} setOption={setOption}/>) }
<Selectors.Role key="rolename" selected={rule.rolename} setOption={setOption}/>
<Selectors.User key="username" selected={rule.username} setOption={setOption}/>
<Selectors.Ip key="ip-address" selected={rule.ipaddress} setOption={setOption}/>
<Selectors.Service key="service" selected={rule.service} setOption={setOption}/>
<Selectors.Request key="request" selected={rule.request} service={rule.service} setOption={setOption}/>
<Selectors.Workspace key="workspace" selected={rule.workspace} setOption={setOption}/>
<Selectors.Layer key="layer" selected={rule.layer} workspace={rule.workspace} setOption={setOption}/>
<Selectors.Access key="access" selected={rule.grant} workspace={rule.workspace} setOption={setOption}/>
</Grid>
);
};


31 changes: 31 additions & 0 deletions web/client/components/manager/rulesmanager/ruleseditor/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const React = require('react');
const Toolbar = require('../../../misc/toolbar/Toolbar');
const {NavItem, Nav} = require('react-bootstrap');
const Message = require('../../../I18N/Message');

module.exports = ({onNavChange = () => {}, onExit = () => {}, disableSave = true, onSave = () => {}, activeTab = "1", detailsActive = false}) => {
const buttons = [{
glyph: '1-close',
tooltipId: 'rulesmanager.tooltip.close',
onClick: onExit
},
{
glyph: 'floppy-disk',
tooltipId: 'rulesmanager.tooltip.save',
onClick: onSave,
disabled: disableSave
}];
return (<div className="ms-panel-header-container">
<div className="ms-toolbar-container">
<Toolbar btnDefaultProps={{ className: 'square-button-md', bsStyle: 'primary', tooltipPosition: 'bottom'}} buttons={buttons}/>
</div>
<Nav bsStyle="tabs" activeKey={activeTab} justified onSelect={onNavChange}>
<NavItem eventKey="1" ><Message msgId={"rulesmanager.navItems.main"}/></NavItem>
<NavItem eventKey="2" disabled={!detailsActive}><Message msgId={"rulesmanager.navItems.style"}/></NavItem>
<NavItem eventKey="3" disabled={!detailsActive}><Message msgId={"rulesmanager.navItems.filter"}/></NavItem>
<NavItem eventKey="4" disabled={!detailsActive}><Message msgId={"rulesmanager.navItems.attribute"}/></NavItem>
</Nav>
</div>);
};


Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2018, 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 rxjsConfig = require('recompose/rxjsObservableConfig').default;

const React = require('react');
const ReactDOM = require('react-dom');
const expect = require('expect');
const Provider = require('react-redux').Provider;
const Editor = require('../EditMain.jsx');

const configureMockStore = require('redux-mock-store').default;
const { setObservableConfig } = require('recompose');
setObservableConfig(rxjsConfig);
const mockStore = configureMockStore();

const renderComp = (props, store) => {
return ReactDOM.render(
<Provider store={store}>
<Editor {...props}/>
</Provider>, document.getElementById("container"));
};


describe('Rules Editor Main Editor component', () => {
let store;
beforeEach((done) => {
store = mockStore();
document.body.innerHTML = '<div id="container"></div>';
setTimeout(done);
});
afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});
it('render nothing if not active', () => {
store = mockStore({
rulesmanager: {}
});
renderComp({active: false}, store);
const container = document.getElementById('container');
const el = container.querySelector('.ms-rule-editor');
expect(el).toExist();
expect(el.style.display).toBe("none");
});
it('render default when active', () => {
store = mockStore({
rulesmanager: {}
});
renderComp({active: true}, store);
const container = document.getElementById('container');
const el = container.querySelector('.ms-rule-editor');
expect(el).toExist();
const rows = el.querySelectorAll('.row');
expect(rows).toExist();
expect(rows.length).toBe(8);
const disabledRows = el.querySelectorAll('.ms-disabled.row');
expect(disabledRows).toExist();
expect(disabledRows.length).toBe(2);
});
it('render priority selector', () => {
store = mockStore({
rulesmanager: {}
});
renderComp({active: true, rule: {id: 10}}, store);
const container = document.getElementById('container');
const el = container.querySelector('.ms-rule-editor');
expect(el).toExist();
const rows = el.querySelectorAll('.row');
expect(rows).toExist();
expect(rows.length).toBe(9);
const disabledRows = el.querySelectorAll('.ms-disabled.row');
expect(disabledRows).toExist();
expect(disabledRows.length).toBe(2);
});
});
Loading

0 comments on commit 7901108

Please sign in to comment.