Skip to content

Commit

Permalink
Merge pull request #2919 from weaveworks/2823-include-timestamp-in-url
Browse files Browse the repository at this point in the history
Use timestamp in URL
  • Loading branch information
fbarl authored Nov 14, 2017
2 parents c5bdebd + 4bd7fc7 commit bf78fc5
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 40 deletions.
46 changes: 33 additions & 13 deletions client/app/scripts/actions/app-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ export function pauseTimeAtNow() {
dispatch({
type: ActionTypes.PAUSE_TIME_AT_NOW
});
updateRoute(getState);
if (!getState().get('nodesLoaded')) {
getNodes(getState, dispatch);
if (isResourceViewModeSelector(getState())) {
Expand Down Expand Up @@ -582,6 +583,7 @@ export function resumeTime() {
dispatch({
type: ActionTypes.RESUME_TIME
});
updateRoute(getState);
// After unpausing, all of the following calls will re-activate polling.
getTopologies(getState, dispatch);
getNodes(getState, dispatch, true);
Expand All @@ -592,11 +594,13 @@ export function resumeTime() {
};
}

export function startTimeTravel() {
export function startTimeTravel(timestamp = null) {
return (dispatch, getState) => {
dispatch({
type: ActionTypes.START_TIME_TRAVEL
type: ActionTypes.START_TIME_TRAVEL,
timestamp,
});
updateRoute(getState);
if (!getState().get('nodesLoaded')) {
getNodes(getState, dispatch);
if (isResourceViewModeSelector(getState())) {
Expand All @@ -623,6 +627,7 @@ export function jumpToTime(timestamp) {
type: ActionTypes.JUMP_TO_TIME,
timestamp,
});
updateRoute(getScopeState);
getNodes(getScopeState, dispatch);
getTopologies(getScopeState, dispatch);
if (isResourceViewModeSelector(getScopeState())) {
Expand Down Expand Up @@ -661,13 +666,32 @@ export function receiveTopologies(topologies) {
}

export function receiveApiDetails(apiDetails) {
return {
type: ActionTypes.RECEIVE_API_DETAILS,
capabilities: fromJS(apiDetails.capabilities),
hostname: apiDetails.hostname,
version: apiDetails.version,
newVersion: apiDetails.newVersion,
plugins: apiDetails.plugins,
return (dispatch, getState) => {
const isFirstTime = !getState().get('version');
const pausedAt = getState().get('pausedAt');

dispatch({
type: ActionTypes.RECEIVE_API_DETAILS,
capabilities: fromJS(apiDetails.capabilities || {}),
hostname: apiDetails.hostname,
version: apiDetails.version,
newVersion: apiDetails.newVersion,
plugins: apiDetails.plugins,
});

// On initial load either start time travelling at the pausedAt timestamp
// (if it was given as URL param) if time travelling is enabled, otherwise
// simply pause at the present time which is arguably the next best thing
// we could do.
// NOTE: We can't make this decision before API details are received because
// we have no prior info on whether time travel would be available.
if (isFirstTime && pausedAt) {
if (apiDetails.capabilities && apiDetails.capabilities.historic_reports) {
dispatch(startTimeTravel(pausedAt));
} else {
dispatch(pauseTimeAtNow());
}
}
};
}

Expand Down Expand Up @@ -806,10 +830,6 @@ export function shutdown() {
return (dispatch) => {
stopPolling();
teardownWebsockets();
// Exit the time travel mode before unmounting the app.
dispatch({
type: ActionTypes.RESUME_TIME
});
dispatch({
type: ActionTypes.SHUTDOWN
});
Expand Down
2 changes: 1 addition & 1 deletion client/app/scripts/components/footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class Footer extends React.Component {
</a>
}
<span className="footer-label">Version</span>
{version}
{version || '...'}
<span className="footer-label">on</span>
{hostname}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Map as makeMap } from 'immutable';
import MatchedText from '../matched-text';
import ShowMore from '../show-more';
import { formatDataType } from '../../utils/string-utils';
import { getSerializedTimeTravelTimestamp } from '../../utils/web-api-utils';


class NodeDetailsInfo extends React.Component {
Expand Down Expand Up @@ -68,7 +67,7 @@ class NodeDetailsInfo extends React.Component {

function mapStateToProps(state) {
return {
timestamp: getSerializedTimeTravelTimestamp(state),
timestamp: state.get('pausedAt'),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import NodeDetailsTableRow from './node-details-table-row';
import NodeDetailsTableHeaders from './node-details-table-headers';
import { ipToPaddedString } from '../../utils/string-utils';
import { moveElement, insertElement } from '../../utils/array-utils';
import { getSerializedTimeTravelTimestamp } from '../../utils/web-api-utils';
import {
isIP, isNumber, defaultSortDesc, getTableColumnsStyles
} from '../../utils/node-details-utils';
Expand Down Expand Up @@ -305,7 +304,7 @@ NodeDetailsTable.defaultProps = {

function mapStateToProps(state) {
return {
timestamp: getSerializedTimeTravelTimestamp(state),
timestamp: state.get('pausedAt'),
};
}

Expand Down
9 changes: 5 additions & 4 deletions client/app/scripts/components/time-travel-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class TimeTravelWrapper extends React.Component {
}

changeTimestamp(timestamp) {
this.props.jumpToTime(timestamp);
this.props.jumpToTime(moment(timestamp).utc());
}

trackTimestampEdit() {
Expand Down Expand Up @@ -61,7 +61,7 @@ class TimeTravelWrapper extends React.Component {
return (
<TimeTravel
visible={visible}
timestamp={timestamp || moment()}
timestamp={timestamp}
earliestTimestamp={this.props.earliestTimestamp}
onChangeTimestamp={this.changeTimestamp}
onTimestampInputEdit={this.trackTimestampEdit}
Expand All @@ -75,6 +75,7 @@ class TimeTravelWrapper extends React.Component {

function mapStateToProps(state, { params }) {
const scopeState = state.scope || state;
const pausedAt = scopeState.get('pausedAt');
let firstSeenConnectedAt;

// If we're in the Weave Cloud context, use firstSeeConnectedAt as the earliest timestamp.
Expand All @@ -89,8 +90,8 @@ function mapStateToProps(state, { params }) {
visible: scopeState.get('showingTimeTravel'),
topologyViewMode: scopeState.get('topologyViewMode'),
currentTopology: scopeState.get('currentTopology'),
earliestTimestamp: firstSeenConnectedAt,
timestamp: scopeState.get('pausedAt'),
earliestTimestamp: firstSeenConnectedAt && firstSeenConnectedAt.utc().format(),
timestamp: pausedAt && pausedAt.utc().format(),
};
}

Expand Down
13 changes: 8 additions & 5 deletions client/app/scripts/reducers/root.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable import/no-webpack-loader-syntax, import/no-unresolved */
import debug from 'debug';
import moment from 'moment';
import { size, each, includes, isEqual } from 'lodash';
import {
fromJS,
Expand All @@ -9,8 +10,6 @@ import {
OrderedMap as makeOrderedMap,
} from 'immutable';

import { nowInSecondsPrecision } from 'weaveworks-ui-components/lib/utils/time';

import ActionTypes from '../constants/action-types';
import {
GRAPH_VIEW_MODE,
Expand All @@ -24,6 +23,7 @@ import { isPausedSelector } from '../selectors/time-travel';
import { activeTopologyZoomCacheKeyPathSelector } from '../selectors/zooming';
import { timestampsEqual } from '../utils/time-utils';
import { applyPinnedSearches } from '../utils/search-utils';
import { deserializeTimestamp } from '../utils/web-api-utils';
import {
findTopologyById,
setTopologyUrlsById,
Expand Down Expand Up @@ -86,7 +86,7 @@ export const initialState = makeMap({
topologyOptions: makeOrderedMap(), // topologyId -> options
topologyUrlsById: makeOrderedMap(), // topologyId -> topologyUrl
topologyViewMode: GRAPH_VIEW_MODE,
version: '...',
version: null,
versionUpdate: null,
// Set some initial numerical values to prevent NaN in case of edgy race conditions.
viewport: makeMap({ width: 0, height: 0 }),
Expand Down Expand Up @@ -381,13 +381,13 @@ export function rootReducer(state = initialState, action) {
case ActionTypes.PAUSE_TIME_AT_NOW: {
state = state.set('showingTimeTravel', false);
state = state.set('timeTravelTransitioning', false);
return state.set('pausedAt', nowInSecondsPrecision());
return state.set('pausedAt', moment().utc());
}

case ActionTypes.START_TIME_TRAVEL: {
state = state.set('showingTimeTravel', true);
state = state.set('timeTravelTransitioning', false);
return state.set('pausedAt', nowInSecondsPrecision());
return state.set('pausedAt', action.timestamp || moment().utc());
}

case ActionTypes.JUMP_TO_TIME: {
Expand Down Expand Up @@ -694,6 +694,9 @@ export function rootReducer(state = initialState, action) {
pinnedMetricType: action.state.pinnedMetricType
});
state = state.set('topologyViewMode', action.state.topologyViewMode);
if (action.state.pausedAt) {
state = state.set('pausedAt', deserializeTimestamp(action.state.pausedAt));
}
if (action.state.gridSortedBy) {
state = state.set('gridSortedBy', action.state.gridSortedBy);
}
Expand Down
4 changes: 3 additions & 1 deletion client/app/scripts/utils/router-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { each } from 'lodash';

import { route } from '../actions/app-actions';
import { storageGet, storageSet } from './storage-utils';
import { serializeTimestamp } from './web-api-utils';

//
// page.js won't match the routes below if ":state" has a slash in it, so replace those before we
Expand Down Expand Up @@ -50,6 +51,7 @@ export function getUrlState(state) {
const urlState = {
controlPipe: cp ? cp.toJS() : null,
nodeDetails: nodeDetails.toJS(),
pausedAt: serializeTimestamp(state.get('pausedAt')),
topologyViewMode: state.get('topologyViewMode'),
pinnedMetricType: state.get('pinnedMetricType'),
pinnedSearches: state.get('pinnedSearches').toJS(),
Expand All @@ -59,7 +61,7 @@ export function getUrlState(state) {
gridSortedDesc: state.get('gridSortedDesc'),
topologyId: state.get('currentTopologyId'),
topologyOptions: state.get('topologyOptions').toJS(), // all options,
contrastMode: state.get('contrastMode')
contrastMode: state.get('contrastMode'),
};

if (state.get('showingNetworks')) {
Expand Down
5 changes: 2 additions & 3 deletions client/app/scripts/utils/string-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,12 @@ export function humanizedRoundedDownDuration(duration) {
// that matches the `dataType` of the field. You must return an Object
// with the keys `value` and `title` defined.
// `referenceTimestamp` is the timestamp we've time-travelled to.
export function formatDataType(field, referenceTimestampStr = null) {
export function formatDataType(field, referenceTimestamp = null) {
const formatters = {
datetime(timestampString) {
const timestamp = moment(timestampString);
const referenceTimestamp = referenceTimestampStr ? moment(referenceTimestampStr) : moment();
return {
value: timestamp.from(referenceTimestamp),
value: timestamp.from(referenceTimestamp ? moment(referenceTimestamp) : moment()),
title: timestamp.utc().toISOString()
};
},
Expand Down
12 changes: 7 additions & 5 deletions client/app/scripts/utils/web-api-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import debug from 'debug';
import moment from 'moment';
import reqwest from 'reqwest';
import { defaults } from 'lodash';
import { Map as makeMap, List } from 'immutable';
Expand Down Expand Up @@ -49,16 +50,17 @@ let firstMessageOnWebsocketAt = null;
let continuePolling = true;


export function getSerializedTimeTravelTimestamp(state) {
// The timestamp parameter will be used only if it's in the past.
if (!isPausedSelector(state)) return null;
export function serializeTimestamp(timestamp) {
return timestamp ? timestamp.toISOString() : null;
}

return state.get('pausedAt').toISOString();
export function deserializeTimestamp(str) {
return str ? moment(str) : null;
}

export function buildUrlQuery(params = makeMap(), state) {
// Attach the time travel timestamp to every request to the backend.
params = params.set('timestamp', getSerializedTimeTravelTimestamp(state));
params = params.set('timestamp', serializeTimestamp(state.get('pausedAt')));

// Ignore the entries with values `null` or `undefined`.
return params.map((value, param) => {
Expand Down
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"reselect": "3.0.1",
"reselect-map": "1.0.3",
"styled-components": "^2.2.1",
"weaveworks-ui-components": "git+https://github.com/weaveworks/ui-components.git#v0.1.51",
"weaveworks-ui-components": "git+https://github.com/weaveworks/ui-components.git#v0.1.52",
"whatwg-fetch": "2.0.3",
"xterm": "2.9.2"
},
Expand Down
6 changes: 3 additions & 3 deletions client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6581,9 +6581,9 @@ wd@^0.4.0:
underscore.string "~3.0.3"
vargs "~0.1.0"

"weaveworks-ui-components@git+https://github.com/weaveworks/ui-components.git#v0.1.51":
version "0.1.51"
resolved "git+https://github.com/weaveworks/ui-components.git#a08ea6a026f4cd58c66d4965838cf04d074604a4"
"weaveworks-ui-components@git+https://github.com/weaveworks/ui-components.git#v0.1.52":
version "0.1.52"
resolved "git+https://github.com/weaveworks/ui-components.git#48978545233bfb0d1ac87795f332221cdaa58fc9"
dependencies:
babel-cli "^6.18.0"
babel-plugin-transform-export-extensions "6.8.0"
Expand Down

0 comments on commit bf78fc5

Please sign in to comment.