Skip to content

Commit

Permalink
Make time travel work also with the Resource View.
Browse files Browse the repository at this point in the history
  • Loading branch information
fbarl committed Jun 19, 2017
1 parent 444e0c7 commit 6a87b15
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 40 deletions.
13 changes: 8 additions & 5 deletions client/app/scripts/actions/app-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ export function setResourceView() {
const firstAvailableMetricType = availableMetricTypesSelector(state).first();
dispatch(pinMetric(firstAvailableMetricType));
}
getResourceViewNodesSnapshot(getState, dispatch);
getResourceViewNodesSnapshot(getState(), dispatch);
updateRoute(getState);
}
};
Expand Down Expand Up @@ -372,7 +372,7 @@ function updateTopology(dispatch, getState) {
const state = getState();
// If we're in the resource view, get the snapshot of all the relevant node topologies.
if (isResourceViewModeSelector(state)) {
getResourceViewNodesSnapshot(getState, dispatch);
getResourceViewNodesSnapshot(state, dispatch);
}
updateRoute(getState);
// update all request workers with new options
Expand Down Expand Up @@ -420,6 +420,9 @@ export function timeTravelJumpToPast(millisecondsInPast) {
updateWebsocketChannel(scopeState, dispatch);
dispatch(resetNodesDeltaBuffer());
getTopologies(getServiceState().scope, dispatch);
if (isResourceViewModeSelector(scopeState)) {
getResourceViewNodesSnapshot(scopeState, dispatch);
}
};
}

Expand Down Expand Up @@ -484,7 +487,7 @@ export function focusSearch() {
// the nodes delta. The solution would be to implement deeper
// search selectors with per-node caching instead of per-topology.
setTimeout(() => {
getAllNodes(getState, dispatch);
getAllNodes(getState(), dispatch);
}, 1200);
};
}
Expand Down Expand Up @@ -654,7 +657,7 @@ export function receiveTopologies(topologies) {
}
// Fetch all the relevant nodes once on first load
if (firstLoad && isResourceViewModeSelector(state)) {
getResourceViewNodesSnapshot(getState, dispatch);
getResourceViewNodesSnapshot(state, dispatch);
}
};
}
Expand Down Expand Up @@ -769,7 +772,7 @@ export function route(urlState) {
// nodes for the current topology, but also the nodes of all the topologies that make
// the layers in the resource view.
if (isResourceViewModeSelector(state)) {
getResourceViewNodesSnapshot(getState, dispatch);
getResourceViewNodesSnapshot(state, dispatch);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Map as makeMap } from 'immutable';
import MatchedText from '../matched-text';
import ShowMore from '../show-more';
import { formatDataType } from '../../utils/string-utils';
import { getWebsocketQueryTimestamp } from '../../utils/web-api-utils';
import { getSerializedTimeTravelTimestamp } from '../../utils/web-api-utils';


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

function mapStateToProps(state) {
return {
timestamp: getWebsocketQueryTimestamp(state),
timestamp: getSerializedTimeTravelTimestamp(state),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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 { getWebsocketQueryTimestamp } from '../../utils/web-api-utils';
import { getSerializedTimeTravelTimestamp } from '../../utils/web-api-utils';
import {
isIP, isNumber, defaultSortDesc, getTableColumnsStyles
} from '../../utils/node-details-utils';
Expand Down Expand Up @@ -293,7 +293,7 @@ NodeDetailsTable.defaultProps = {

function mapStateToProps(state) {
return {
timestamp: getWebsocketQueryTimestamp(state),
timestamp: getSerializedTimeTravelTimestamp(state),
};
}

Expand Down
1 change: 1 addition & 0 deletions client/app/scripts/utils/topology-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export function isTopologyNodeCountZero(state) {
export function isNodesDisplayEmpty(state) {
// Consider a topology in the resource view empty if it has no pinned metric.
if (isResourceViewModeSelector(state)) {
// TODO: Check for empty displays here after Scope v1.5.0 has been released.
return !pinnedMetricSelector(state);
}
// Otherwise (in graph and table view), we only look at the nodes content.
Expand Down
58 changes: 27 additions & 31 deletions client/app/scripts/utils/web-api-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,18 @@ let createWebsocketAt = null;
let firstMessageOnWebsocketAt = null;
let continuePolling = true;

export function buildUrlQuery(params) {
if (!params) return '';

export function getSerializedTimeTravelTimestamp(state) {
// The timestamp parameter will be used only if it's in the past.
if (isNowSelector(state)) return null;

const millisecondsInPast = state.get('timeTravelMillisecondsInPast');
return moment().utc().subtract(millisecondsInPast).toISOString();
}

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

// Ignore the entries with values `null` or `undefined`.
return params.map((value, param) => {
Expand Down Expand Up @@ -98,12 +108,11 @@ export function getWebsocketUrl(host = window.location.host, pathname = window.l
return `${wsProto}://${host}${process.env.SCOPE_API_PREFIX || ''}${basePath(pathname)}`;
}

function buildWebsocketUrl(topologyUrl, topologyOptions = makeMap(), queryTimestamp) {
function buildWebsocketUrl(topologyUrl, topologyOptions = makeMap(), state) {
const query = buildUrlQuery(fromJS({
t: updateFrequency,
timestamp: queryTimestamp,
...topologyOptions.toJS(),
}));
}), state);
return `${getWebsocketUrl()}${topologyUrl}/ws?${query}`;
}

Expand Down Expand Up @@ -180,12 +189,12 @@ function doRequest(opts) {
/**
* Does a one-time fetch of all the nodes for a custom list of topologies.
*/
function getNodesForTopologies(getState, dispatch, topologyIds, topologyOptions = makeMap()) {
function getNodesForTopologies(state, dispatch, topologyIds, topologyOptions = makeMap()) {
// fetch sequentially
getState().get('topologyUrlsById')
state.get('topologyUrlsById')
.filter((_, topologyId) => topologyIds.contains(topologyId))
.reduce((sequence, topologyUrl, topologyId) => sequence.then(() => {
const optionsQuery = buildUrlQuery(topologyOptions.get(topologyId));
const optionsQuery = buildUrlQuery(topologyOptions.get(topologyId), state);
return doRequest({ url: `${getApiPath()}${topologyUrl}?${optionsQuery}` });
})
.then(json => dispatch(receiveNodesForTopology(json.nodes, topologyId))),
Expand All @@ -195,39 +204,28 @@ function getNodesForTopologies(getState, dispatch, topologyIds, topologyOptions
/**
* Gets nodes for all topologies (for search).
*/
export function getAllNodes(getState, dispatch) {
const state = getState();
export function getAllNodes(state, dispatch) {
const topologyOptions = state.get('topologyOptions');
const topologyIds = state.get('topologyUrlsById').keySeq();
getNodesForTopologies(getState, dispatch, topologyIds, topologyOptions);
getNodesForTopologies(state, dispatch, topologyIds, topologyOptions);
}

/**
* One-time update of all the nodes of topologies that appear in the current resource view.
* TODO: Replace the one-time snapshot with periodic polling.
*/
export function getResourceViewNodesSnapshot(getState, dispatch) {
const topologyIds = layersTopologyIdsSelector(getState());
getNodesForTopologies(getState, dispatch, topologyIds);
}

export function getWebsocketQueryTimestamp(state) {
// The timestamp query parameter will be used only if it's in the past.
if (isNowSelector(state)) return null;

const millisecondsInPast = state.get('timeTravelMillisecondsInPast');
return moment().utc().subtract(millisecondsInPast).toISOString();
export function getResourceViewNodesSnapshot(state, dispatch) {
const topologyIds = layersTopologyIdsSelector(state);
getNodesForTopologies(state, dispatch, topologyIds);
}

export function getTopologies(state, dispatch, initialPoll = false) {
// TODO: Remove this once TimeTravel is out of the feature flag.
state = state.scope || state;
let options = activeTopologyOptionsSelector(state);
options = options.set('timestamp', getWebsocketQueryTimestamp(state));

// Used to resume polling when navigating between pages in Weave Cloud.
continuePolling = initialPoll === true ? true : continuePolling;
clearTimeout(topologyTimer);
const optionsQuery = buildUrlQuery(options);
const optionsQuery = buildUrlQuery(activeTopologyOptionsSelector(state), state);
const url = `${getApiPath()}/api/topology?${optionsQuery}`;
doRequest({
url,
Expand Down Expand Up @@ -255,8 +253,7 @@ export function getTopologies(state, dispatch, initialPoll = false) {
export function updateWebsocketChannel(state, dispatch) {
const topologyUrl = getCurrentTopologyUrl(state);
const topologyOptions = activeTopologyOptionsSelector(state);
const queryTimestamp = getWebsocketQueryTimestamp(state);
const websocketUrl = buildWebsocketUrl(topologyUrl, topologyOptions, queryTimestamp);
const websocketUrl = buildWebsocketUrl(topologyUrl, topologyOptions, state);
// Only recreate websocket if url changed or if forced (weave cloud instance reload);
const isNewUrl = websocketUrl !== currentUrl;
// `topologyUrl` can be undefined initially, so only create a socket if it is truthy
Expand All @@ -271,7 +268,7 @@ export function getNodeDetails(state, dispatch) {
const nodeMap = state.get('nodeDetails');
const topologyUrlsById = state.get('topologyUrlsById');
const currentTopologyId = state.get('currentTopologyId');
let options = activeTopologyOptionsSelector(state);
const options = activeTopologyOptionsSelector(state);

// get details for all opened nodes
const obj = nodeMap.last();
Expand All @@ -280,8 +277,7 @@ export function getNodeDetails(state, dispatch) {
let urlComponents = [getApiPath(), topologyUrl, '/', encodeURIComponent(obj.id)];
if (currentTopologyId === obj.topologyId) {
// Only forward filters for nodes in the current topology
options = options.set('timestamp', getWebsocketQueryTimestamp(state));
const optionsQuery = buildUrlQuery(options);
const optionsQuery = buildUrlQuery(options, state);
urlComponents = urlComponents.concat(['?', optionsQuery]);
}
const url = urlComponents.join('');
Expand Down

0 comments on commit 6a87b15

Please sign in to comment.