(web) session filtering

This commit is contained in:
Alexey Kontsevoy 2016-03-21 18:33:05 -04:00
parent 0d622b9cc5
commit 0b37891c7b
15 changed files with 179 additions and 75 deletions

6
web/dist/app/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

6
web/dist/index.html vendored
View file

@ -14,11 +14,11 @@
<script src="/web/app/assets/js/bootstrap-3.3.6.js"></script>
<script src="/web/app/assets/js/term-0.0.7.js"></script>
<script src="/web/app/assets/js/bootstrap-datepicker-1.6.0.js"></script>
<script src="/web/app/vendor.js?ver=0.11458572266555"></script>
<script src="/web/app/styles.js?ver=0.11458572266555"></script>
<script src="/web/app/vendor.js?ver=0.11458599517104"></script>
<script src="/web/app/styles.js?ver=0.11458599517104"></script>
</head>
<body class="grv">
<div id="app"></div>
</body>
<script src="/web/app/app.js?ver=0.11458572266555"></script>
<script src="/web/app/app.js?ver=0.11458599517104"></script>
</html>

View file

@ -17,7 +17,7 @@ limitations under the License.
var { sampleData, reactor, expect, Dfd, cfg, spyOn, api } = require('./../');
var {actions, getters} = require('app/modules/sessions');
describe('modules/nodes', function () {
describe('modules/sessions', function () {
beforeEach( ()=> spyOn(api, 'get').andReturn(Dfd()) );
@ -49,18 +49,18 @@ describe('modules/nodes', function () {
it('should fetch based on the input params', function () {
let sid = 'xx';
let before = new Date();
let end = new Date();
let limit = 33;
spyOn(cfg.api, 'getFetchSessionsUrl');
actions.fetchSessions({sid, before, limit});
actions.fetchSessions({sid, end, limit});
let [actual] = cfg.api.getFetchSessionsUrl.calls[0].arguments;
expect(api.get.calls.length).toBe(1);
expect(actual.limit).toBe(limit);
expect(actual.sid).toBe(sid);
expect(new Date(actual.start).getTime()).toBe(before.getTime());
expect(actual.session_id).toBe(sid);
expect(new Date(actual.start).getTime()).toBe(end.getTime());
})
});

View file

@ -19,7 +19,7 @@ var {actions} = require('app/modules/storedSessionsFilter');
var InputSearch = require('./../inputSearch.jsx');
var {Table, Column, Cell, TextCell, SortHeaderCell, SortTypes} = require('app/components/table.jsx');
var {ButtonCell, SingleUserCell, EmptyList, DateCreatedCell} = require('./listItems');
var {DateRangePicker, CalendarNav} = require('./../datePicker.jsx');
var {DateRangePicker} = require('./../datePicker.jsx');
var moment = require('moment');
var {weekRange} = require('app/common/dateUtils');
var {isMatch} = require('app/common/objectUtils');
@ -36,6 +36,10 @@ var ArchivedSessions = React.createClass({
setTimeout(()=>actions.fetch(), 0);
},
componentWillUnmount(){
actions.removeStoredSessions();
},
onFilterChange(value){
this.state.filter = value;
this.setState(this.state);
@ -89,18 +93,22 @@ var ArchivedSessions = React.createClass({
return (
<div className="grv-sessions-stored">
<div className="grv-header">
<h1> Archived Sessions </h1>
<div className="grv-flex">
<div className="grv-flex-column"></div>
<div className="grv-flex-column">
<h1> Archived Sessions </h1>
</div>
<div className="grv-flex-column">
<InputSearch value={this.filter} onChange={this.onFilterChange}/>
</div>
</div>
<div className="grv-flex">
<div className="grv-flex-row">
</div>
<div className="grv-flex-row">
<DateRangePicker startDate={start} endDate={end} onChange={this.onRangePickerChange}/>
</div>
<div className="grv-flex-row">
<CalendarNav value={end} onValueChange={this.onCalendarNavChange}/>
</div>
<div className="grv-flex-row">
<div className="grv-search">
<InputSearch value={this.filter} onChange={this.onFilterChange}/>
</div>
</div>
</div>
</div>

View file

@ -6,7 +6,7 @@ let cfg = {
helpUrl: 'https://github.com/gravitational/teleport/blob/master/README.md',
maxSessionLoadSize: 20,
maxSessionLoadSize: 50,
routes: {
app: '/web',

View file

@ -17,7 +17,6 @@ limitations under the License.
var reactor = require('app/reactor');
var {fetchSessions} = require('./../sessions/actions');
var {fetchNodes} = require('./../nodes/actions');
var {weekRange} = require('app/common/dateUtils');
var $ = require('jQuery');
const { TLPT_APP_INIT, TLPT_APP_FAILED, TLPT_APP_READY } = require('./actionTypes');
@ -32,8 +31,7 @@ const actions = {
},
fetchNodesAndSessions() {
var [, end ] = weekRange();
return $.when(fetchNodes(), fetchSessions({before: end}));
return $.when(fetchNodes(), fetchSessions());
}
}

View file

@ -18,5 +18,6 @@ import keyMirror from 'keymirror'
export default keyMirror({
TLPT_SESSINS_RECEIVE: null,
TLPT_SESSINS_UPDATE: null
TLPT_SESSINS_UPDATE: null,
TLPT_SESSINS_REMOVE_STORED: null
})

View file

@ -16,6 +16,7 @@ limitations under the License.
var reactor = require('app/reactor');
var api = require('app/services/api');
var apiUtils = require('app/services/apiUtils');
var cfg = require('app/config');
var {showError} = require('app/modules/notifications/actions');
@ -32,20 +33,16 @@ const actions = {
});
},
fetchSessions({before, sid, limit=cfg.maxSessionLoadSize}={}){
let start = before || new Date();
fetchSessions({end, sid, limit=cfg.maxSessionLoadSize}={}){
let start = end || new Date();
let params = {
order: -1,
limit
limit,
start,
sid
};
params.start = start.toISOString();
if(sid){
params.session_id = sid;
}
return api.get(cfg.api.getFetchSessionsUrl(params))
return apiUtils.filterSessions(params)
.done((json) => {
reactor.dispatch(TLPT_SESSINS_RECEIVE, json.sessions);
})

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
var { Store, toImmutable } = require('nuclear-js');
var { TLPT_SESSINS_RECEIVE, TLPT_SESSINS_UPDATE } = require('./actionTypes');
var { TLPT_SESSINS_RECEIVE, TLPT_SESSINS_UPDATE, TLPT_SESSINS_REMOVE_STORED } = require('./actionTypes');
export default Store({
getInitialState() {
@ -25,9 +25,20 @@ export default Store({
initialize() {
this.on(TLPT_SESSINS_RECEIVE, receiveSessions);
this.on(TLPT_SESSINS_UPDATE, updateSession);
this.on(TLPT_SESSINS_REMOVE_STORED, removeStoredSessions);
}
})
function removeStoredSessions(state){
return state.withMutations(state => {
state.valueSeq().forEach(item=> {
if(item.get('active') !== true){
state.remove(item.get('id'));
}
});
});
}
function updateSession(state, json){
return state.set(json.id, toImmutable(json));
}

View file

@ -15,15 +15,28 @@ limitations under the License.
*/
var reactor = require('app/reactor');
var sessionModule = require('./../sessions');
var {filter} = require('./getters');
var {maxSessionLoadSize} = require('app/config');
var moment = require('moment');
var apiUtils = require('app/services/apiUtils');
var {showError} = require('app/modules/notifications/actions');
const logger = require('app/common/logger').create('Modules/Sessions');
const {
TLPT_STORED_SESSINS_FILTER_SET_RANGE,
TLPT_STORED_SESSINS_FILTER_SET_STATUS } = require('./actionTypes');
const {TLPT_SESSINS_RECEIVE, TLPT_SESSINS_REMOVE_STORED } = require('./../sessions/actionTypes');
/**
* Due to current limitations of the backend API, the filtering logic for the Archived list of Session
* works as follows:
* 1) each time a new date range is set, all previously retrieved inactive sessions get deleted.
* 2) hasMore flag will be determine after a consequent fetch request with new date range values.
*/
const actions = {
fetch(){
@ -38,33 +51,65 @@ const actions = {
}
},
removeStoredSessions(){
reactor.dispatch(TLPT_SESSINS_REMOVE_STORED);
},
setTimeRange(start, end){
reactor.batch(()=>{
reactor.dispatch(TLPT_STORED_SESSINS_FILTER_SET_RANGE, {start, end});
reactor.dispatch(TLPT_STORED_SESSINS_FILTER_SET_RANGE, {start, end, hasMore: false});
reactor.dispatch(TLPT_SESSINS_REMOVE_STORED);
_fetch(end);
});
}
}
function _fetch(before, sid){
reactor.dispatch(TLPT_STORED_SESSINS_FILTER_SET_STATUS, {isLoading: true, hasMore: false});
sessionModule.actions.fetchSessions({sid, before, limit: maxSessionLoadSize})
.done((json) => {
let {start} = reactor.evaluate(filter);
let {sessions } = json;
function _fetch(end, sid){
let status = {
hasMore: false,
isLoading: false
isLoading: true
}
reactor.dispatch(TLPT_STORED_SESSINS_FILTER_SET_STATUS, status);
let start = end || new Date();
let params = {
order: -1,
limit: maxSessionLoadSize,
start,
sid
};
return apiUtils.filterSessions(params).done((json) => {
let {sessions} = json;
let {start} = reactor.evaluate(filter);
status.hasMore = false;
status.isLoading = false;
if (sessions.length === maxSessionLoadSize) {
let {id, created} = sessions[sessions.length-1];
status.sid = id;
status.hasMore = moment(start).isBefore(created)
status.hasMore = moment(start).isBefore(created);
/**
* remove at least 1 item before storing the sessions, this way we ensure that
* there always will be at least one new item on the next 'fetchMore' request.
*/
sessions = sessions.slice(0, maxSessionLoadSize-1);
}
reactor.batch(()=>{
reactor.dispatch(TLPT_SESSINS_RECEIVE, sessions);
reactor.dispatch(TLPT_STORED_SESSINS_FILTER_SET_STATUS, status);
});
})
.fail((err)=>{
showError('Unable to retrieve list of sessions');
logger.error('fetching filtered set of sessions', err);
});
}
export default actions;

View file

@ -45,8 +45,6 @@ function setStatus(state, status){
return state.mergeIn(['status'], status);
}
function setRange(state, {start, end}){
return state.set('start', start)
.set('end', end)
.set('hasMore', false);
function setRange(state, newState){
return state.merge(newState);
}

View file

@ -0,0 +1,37 @@
/*
Copyright 2015 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var api = require('./api');
var cfg = require('../config');
const apiUtils = {
filterSessions({start, end, sid, limit, order=-1}){
let params = {
start: start.toISOString(),
end,
order,
limit
}
if(sid){
params.session_id = sid;
}
return api.get(cfg.api.getFetchSessionsUrl(params))
}
}
module.exports = apiUtils;

View file

@ -41,6 +41,15 @@ limitations under the License.
h1{
margin-bottom: 30px;
}
.grv-flex{
align-items: center;
}
.grv-datepicker {
margin: auto;
}
}
.grv-footer{