Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use timestamp in URL #2919

Merged
merged 1 commit into from
Nov 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -6666,9 +6666,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