Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
kappu committed Apr 13, 2018
1 parent 05113f5 commit b69a0d7
Show file tree
Hide file tree
Showing 37 changed files with 1,290 additions and 52 deletions.
11 changes: 10 additions & 1 deletion web/client/actions/rulesmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ const UPDATE_ACTIVE_RULE = 'UPDATE_ACTIVE_RULE';
const UPDATE_FILTERS_VALUES = 'UPDATE_FILTERS_VALUES';
const ACTION_ERROR = 'ACTION_ERROR';
const OPTIONS_LOADED = 'OPTIONS_LOADED';
const LOADING = 'RULES_MANAGER:LOADING';

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

function rulesSelected(rules, merge, unselect) {
return {
Expand Down Expand Up @@ -225,5 +233,6 @@ module.exports = {
loadWorkspaces,
loadLayers,
actionError,
optionsLoaded
optionsLoaded,
LOADING, setLoading
};
44 changes: 23 additions & 21 deletions web/client/api/geoserver/GeoFence.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ const ConfigUtils = require('../../utils/ConfigUtils');

var Api = {

loadRules: function(rulesPage, rulesFiltersValues) {
const options = {
'params': {
'page': rulesPage - 1,
'entries': 10
}
loadRules: function(page, rulesFiltersValues, entries = 10) {
const params = {
page,
entries,
...this.assignFiltersValue(rulesFiltersValues)
};
this.assignFiltersValue(rulesFiltersValues, options);
const options = {params, 'headers': {
'Content': 'application/json'
}};
return axios.get('geofence/rest/rules', this.addBaseUrl(options))
.then(function(response) {
return response.data;
Expand All @@ -30,9 +31,8 @@ var Api = {

getRulesCount: function(rulesFiltersValues) {
const options = {
'params': {}
'params': this.assignFiltersValue(rulesFiltersValues)
};
this.assignFiltersValue(rulesFiltersValues, options);
return axios.get('geofence/rest/rules/count', this.addBaseUrl(options)).then(function(response) {
return response.data;
});
Expand Down Expand Up @@ -73,22 +73,24 @@ var Api = {
}));
},

assignFiltersValue: function(rulesFiltersValues, options) {
if (rulesFiltersValues) {
assign(options.params, {"userName": this.normalizeFilterValue(rulesFiltersValues.userName)});
assign(options.params, {"roleName": this.normalizeFilterValue(rulesFiltersValues.roleName)});
assign(options.params, {"service": this.normalizeFilterValue(rulesFiltersValues.service)});
assign(options.params, {"request": this.normalizeFilterValue(rulesFiltersValues.request)});
assign(options.params, {"workspace": this.normalizeFilterValue(rulesFiltersValues.workspace)});
assign(options.params, {"layer": this.normalizeFilterValue(rulesFiltersValues.layer)});
}
return options;
assignFiltersValue: function(rulesFiltersValues = {}) {
return Object.keys(rulesFiltersValues).map(key => ({key, normKey: this.normalizeKey(key)}))
.reduce((params, {key, normKey}) => ({...params, [normKey]: this.normalizeFilterValue(rulesFiltersValues[key])}), {});
},

normalizeFilterValue(value) {
return value === "*" ? undefined : value;
},

normalizeKey(key) {
switch (key) {
case 'username':
return 'userName';
case 'rolename':
return 'groupName';
default:
return key;
}
},
assignFilterValue: function(queryParameters, filterName, filterAny, filterValue) {
if (!filterValue) {
return;
Expand Down Expand Up @@ -135,7 +137,7 @@ var Api = {
},

addBaseUrl: function(options) {
return assign(options, {baseURL: ConfigUtils.getDefaults().geoServerUrl});
return assign(options, {baseURL: ConfigUtils.getDefaults().geoFenceUrl});
}
};

Expand Down
121 changes: 121 additions & 0 deletions web/client/components/manager/rulesmanager/rulesgrid/RulesGrid.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
const React = require('react');
const PropTypes = require('prop-types');
const RuleRenderer = require('./renderers/RuleRenderer');
const AccessFormatter = require('./formatters/AccessFormatter');

const { Draggable} = require('react-data-grid-addons');

const DataGrid = require('../../../data/grid/DataGrid');
const { Container: DraggableContainer, RowActionsCell, DropTargetRowContainer: dropTargetRowContainer } = Draggable;

const Message = require('../../../I18N/Message');

const RowRenderer = dropTargetRowContainer(RuleRenderer);

class RulesGrid extends React.Component {
static propTypes = {
rowKey: PropTypes.string.isRequired,
width: PropTypes.number,
height: PropTypes.number,
rows: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
rowsCount: PropTypes.number,
columns: PropTypes.array,
onSelect: PropTypes.func,
selectedIds: PropTypes.array,
rowGetter: PropTypes.func,
onGridScroll: PropTypes.func,
onAddFilter: PropTypes.func,
onReordeRows: PropTypes.func
};
static contextTypes = {
messages: PropTypes.object
};

static defaultProps = {
rowKey: 'id',
rows: [],
onSort: () => {},
onSelect: () => {},
selectedIds: [],
columns: [
{ key: 'priority', name: "Priority", filterable: true },
{ key: 'rolename', name: <Message msgId={"rulesmanager.role"} />, filterable: true },
{ key: 'username', name: <Message msgId={"rulesmanager.user"} />, filterable: true },
{ key: 'ipaddress', name: 'IP', filterable: false},
{ key: 'service', name: <Message msgId={"rulesmanager.service"} />, filterable: true },
{ key: 'request', name: <Message msgId={"rulesmanager.request"} />, filterable: true },
{ key: 'workspace', name: <Message msgId={"rulesmanager.workspace"} />, filterable: true },
{ key: 'layer', name: <Message msgId={"rulesmanager.layer"} />, filterable: true },
{ key: 'grant', name: <Message msgId={"rulesmanager.access"} />, formatter: AccessFormatter, filterable: false }]
};

componentDidMount() {
if (this.props.rowsCount > 0) {
this.grid.scrollListener();
}
}
componentDidUpdate = ({rowsCount}) => {
if (this.props.rowsCount > 0 && rowsCount !== this.props.rowsCount) {
this.grid.scrollListener();
}
}
onRowsSelected = (rows) => {
const selectedRules = this._getSelectedRow(this.props.rowKey, this.props.selectedIds, this.props.rows).concat(rows.map(({row}) => row).filter(({id}) => id !== "empty_row"));
if (selectedRules.length > 0) {
this.props.onSelect(selectedRules);
}
}
onRowsDeselected = (rows) => {
let rowIds = rows.map(r => r.row[this.props.rowKey]);
const selectedIds = this.props.selectedIds.filter(i => rowIds.indexOf(i) === -1);
this.props.onSelect(this._getSelectedRow(this.props.rowKey, selectedIds, this.props.rows));
}
render() {
return (
<DraggableContainer>
<DataGrid
displayFilters
ref={(grid) => { this.grid = grid; }}
enableCellSelection={false}
rowActionsCell={RowActionsCell}
columns={this.props.columns}
rowGetter={this.props.rowGetter}
rowsCount={this.props.rowsCount}
rows={this.props.rows}
minHeight={this.props.height}
minWidth={this.props.width}
rowRenderer={<RowRenderer onRowDrop={this.reorderRows}/>}
virtualScroll
onGridScroll={this.props.onGridScroll}
onAddFilter={this.props.onAddFilter}
rowSelection={{
showCheckbox: true,
enableShiftSelect: true,
onRowsSelected: this.onRowsSelected,
onRowsDeselected: this.onRowsDeselected,
selectBy: {
keys: {rowKey: this.props.rowKey, values: this.props.selectedIds}
}}}/>
</DraggableContainer>);
}

isDraggedRowSelected = (selectedRows, rowDragSource) => {
if (selectedRows && selectedRows.length > 0) {
let key = this.props.rowKey;
return selectedRows.filter(r => r[key] === rowDragSource.data[key]).length > 0;
}
return false;
};
_getSelectedRow = ( rowKey = [], selectedKeys = [], rows = {}) => Object.keys(rows).reduce((sel, key) => sel.concat(rows[key].filter(row => selectedKeys.indexOf(row[rowKey]) !== -1)), [])

reorderRows = (e) => {
if (e.rowSource.data[this.props.rowKey] === "empty_row" || e.rowTarget.data[this.props.rowKey] === "empty_row") {
return;
}
let selectedRows = this._getSelectedRow(this.props.rowKey, this.props.selectedIds, this.props.rows);
let draggedRows = this.isDraggedRowSelected(selectedRows, e.rowSource) ? selectedRows : [e.rowSource.data];
this.props.onReordeRows({rules: draggedRows, targetPriority: e.rowTarget.data.priority});
};
}

module.exports = RulesGrid;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* 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 React = require('react');
const ReactDOM = require('react-dom');

const expect = require('expect');
const RulesGrid = require('../RulesGrid');


describe('Test RulesGrid component', () => {
beforeEach((done) => {
document.body.innerHTML = '<div style="width: 100px; height: 100px" id="container"></div>';
setTimeout(done);
});
afterEach((done) => {
ReactDOM.unmountComponentAtNode(document.getElementById("container"));
document.body.innerHTML = '';
setTimeout(done);
});
it('render with defaults', () => {
ReactDOM.render(<RulesGrid rowGetter={() => {}} rowsCount={0} width={100} height={100}/>, document.getElementById("container"));
const container = document.getElementById('container');
expect(container).toExist();
const grid = container.querySelector(".react-grid-Container");
expect(grid).toExist();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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 expect = require('expect');
const filtersStream = require('../filtersStream');
const Rx = require("rxjs");
describe('rulegrid filterStream', () => {
it('debounce addFilter$', (done) => {
const setFilters = (filter) => {
expect(filter).toExist();
expect(filter).toBe("WFS");
done();
};
const addFilter$ = Rx.Observable.from(["WF", "WFS"]);
const prop$ = Rx.Observable.of({setFilters, addFilter$});
filtersStream(prop$).subscribe(() => {});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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 expect = require('expect');
const scrollStream = require('../scrollStream');
const Rx = require("rxjs");
describe('rulegrid scrollStream', () => {
it('generate pages request', (done) => {
const moreRules = (pagesRequest) => {
expect(pagesRequest).toExist();
expect(pagesRequest.pagesToLoad).toExist();
expect(pagesRequest.pagesToLoad).toEqual([0, 1]);
expect(pagesRequest.startPage).toBe(0);
expect(pagesRequest.endPage).toBe(1);
expect(pagesRequest.pages).toEqual({});
done();
};
const onGridScroll$ = Rx.Observable.of({ firstRowIdx: 0, lastRowIdx: 10});
const prop$ = Rx.Observable.of({ size: 10, moreRules, pages: {}, rowsCount: 50, vsOverScan: 5, scrollDebounce: 50, onGridScroll$});
scrollStream(prop$).subscribe(() => {});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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 expect = require('expect');
const triggerFetch = require('../triggerFetch');
const Rx = require("rxjs");
const ConfigUtils = require("../../../../../../utils/ConfigUtils");
ConfigUtils.setConfigProp("geoFenceUrl", "base/web/client/test-resources/");
describe('rulegrid triggerFetch', () => {
it('get count', (done) => {
const onLoad = ({pages, rowsCount}) => {
expect(pages).toEqual({});
expect(rowsCount).toBe(10);
done();
};
const prop$ = Rx.Observable.of({version: 0, filters: {}, setLoading: () => {}, onLoad, onLoadError: () => { }});
triggerFetch(prop$).subscribe(() => {});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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 expect = require('expect');
const virtualScrollFetch = require('../virtualScrollFetch');
const Rx = require("rxjs");
const ConfigUtils = require("../../../../../../utils/ConfigUtils");
ConfigUtils.setConfigProp("geoFenceUrl", "base/web/client/test-resources/");
const axios = require('../../../../../../libs/ajax');
const interceptors = (config) => {
if (config.url === "base/web/client/test-resources/geofence/rest/rules") {
config.url = "base/web/client/test-resources/geofence/rest/rules.xml";
}
return config;
};

describe('rulegrid virtulaScrollFetch', () => {
it('generate pages request', (done) => {
const inter = axios.interceptors.request.use(interceptors);
const onLoad = ({pages}) => {
axios.interceptors.request.eject(inter);
expect(pages).toExist();
expect(pages[0]).toExist();
expect(pages[0].length).toBe(5);
done();
};
const onLoadError = () => {
axios.interceptors.request.eject(inter);
};
const pages$ = Rx.Observable.of({ pagesToLoad: [0], startPage: 0, endPage: 0, pages: {}});
const prop$ = Rx.Observable.of({size: 5,
maxStoredPages: 5,
filters: {},
onLoad,
moreRules: () => {},
setLoading: () => {},
onLoadError
});
virtualScrollFetch(pages$)(prop$).subscribe(() => {});
});
});
Loading

0 comments on commit b69a0d7

Please sign in to comment.