mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 10:13:21 +00:00
(web) session filtering
This commit is contained in:
parent
0d622b9cc5
commit
0b37891c7b
6
web/dist/app/app.js
vendored
6
web/dist/app/app.js
vendored
File diff suppressed because one or more lines are too long
6
web/dist/app/styles.js
vendored
6
web/dist/app/styles.js
vendored
File diff suppressed because one or more lines are too long
26
web/dist/app/vendor.js
vendored
26
web/dist/app/vendor.js
vendored
File diff suppressed because one or more lines are too long
6
web/dist/index.html
vendored
6
web/dist/index.html
vendored
|
@ -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>
|
||||
|
|
|
@ -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());
|
||||
})
|
||||
|
||||
});
|
||||
|
|
|
@ -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,21 +93,25 @@ 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>
|
||||
</div>
|
||||
|
||||
<div className="grv-content">
|
||||
{data.length === 0 && !status.isLoading ? <EmptyList text="No matching archived sessions found."/> :
|
||||
|
|
|
@ -6,7 +6,7 @@ let cfg = {
|
|||
|
||||
helpUrl: 'https://github.com/gravitational/teleport/blob/master/README.md',
|
||||
|
||||
maxSessionLoadSize: 20,
|
||||
maxSessionLoadSize: 50,
|
||||
|
||||
routes: {
|
||||
app: '/web',
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
|
@ -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);
|
||||
})
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
let status = {
|
||||
hasMore: false,
|
||||
isLoading: false
|
||||
}
|
||||
function _fetch(end, sid){
|
||||
let status = {
|
||||
hasMore: false,
|
||||
isLoading: true
|
||||
}
|
||||
|
||||
if (sessions.length === maxSessionLoadSize) {
|
||||
let {id, created} = sessions[sessions.length-1];
|
||||
status.sid = id;
|
||||
status.hasMore = moment(start).isBefore(created)
|
||||
}
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
37
web/src/app/services/apiUtils.js
Normal file
37
web/src/app/services/apiUtils.js
Normal 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;
|
|
@ -41,6 +41,15 @@ limitations under the License.
|
|||
h1{
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.grv-flex{
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.grv-datepicker {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.grv-footer{
|
||||
|
|
Loading…
Reference in a new issue