diff --git a/client/.eslintrc b/client/.eslintrc
index 519fa33617..81022730e9 100644
--- a/client/.eslintrc
+++ b/client/.eslintrc
@@ -31,6 +31,7 @@
"no-param-reassign": 0,
"no-restricted-properties": 0,
"object-curly-spacing": 0,
+ "react/destructuring-assignment": 0,
"react/jsx-closing-bracket-location": 0,
"react/jsx-filename-extension": [
2,
diff --git a/client/.nvmrc b/client/.nvmrc
index a244f6f35f..6fe1005059 100644
--- a/client/.nvmrc
+++ b/client/.nvmrc
@@ -1 +1 @@
-v8.9.0
+v8.10.0
diff --git a/client/app/scripts/actions/app-actions.js b/client/app/scripts/actions/app-actions.js
index 0587ca8b6f..ec5bb0acc7 100644
--- a/client/app/scripts/actions/app-actions.js
+++ b/client/app/scripts/actions/app-actions.js
@@ -1,42 +1,19 @@
-import debug from 'debug';
-import { fromJS } from 'immutable';
-
import ActionTypes from '../constants/action-types';
import { saveGraph } from '../utils/file-utils';
import { clearStoredViewState, updateRoute } from '../utils/router-utils';
-import {
- doControlRequest,
- getAllNodes,
- getResourceViewNodesSnapshot,
- getNodeDetails,
- getTopologies,
- deletePipe,
- stopPolling,
- teardownWebsockets,
- getNodes,
-} from '../utils/web-api-utils';
import { isPausedSelector } from '../selectors/time-travel';
import {
- availableMetricTypesSelector,
nextPinnedMetricTypeSelector,
previousPinnedMetricTypeSelector,
- pinnedMetricSelector,
} from '../selectors/node-metric';
-import {
- isResourceViewModeSelector,
- resourceViewAvailableSelector,
-} from '../selectors/topology';
+import { isResourceViewModeSelector } from '../selectors/topology';
import {
GRAPH_VIEW_MODE,
TABLE_VIEW_MODE,
- RESOURCE_VIEW_MODE,
} from '../constants/naming';
-const log = debug('scope:app-actions');
-
-
export function showHelp() {
return { type: ActionTypes.SHOW_HELP };
}
@@ -181,41 +158,10 @@ export function updateSearch(searchQuery = '', pinnedSearches = []) {
};
}
-export function focusSearch() {
- return (dispatch, getState) => {
- dispatch({ type: ActionTypes.FOCUS_SEARCH });
- // update nodes cache to allow search across all topologies,
- // wait a second until animation is over
- // NOTE: This will cause matching recalculation (and rerendering)
- // of all the nodes in the topology, instead applying it only on
- // the nodes delta. The solution would be to implement deeper
- // search selectors with per-node caching instead of per-topology.
- setTimeout(() => {
- getAllNodes(getState(), dispatch);
- }, 1200);
- };
-}
-
export function blurSearch() {
return { type: ActionTypes.BLUR_SEARCH };
}
-export function changeTopologyOption(option, value, topologyId, addOrRemove) {
- return (dispatch, getState) => {
- dispatch({
- addOrRemove,
- option,
- topologyId,
- type: ActionTypes.CHANGE_TOPOLOGY_OPTION,
- value
- });
- updateRoute(getState);
- // update all request workers with new options
- getTopologies(getState, dispatch);
- getNodes(getState, dispatch);
- };
-}
-
export function clickBackground() {
return (dispatch, getState) => {
dispatch({
@@ -225,18 +171,6 @@ export function clickBackground() {
};
}
-export function clickCloseDetails(nodeId) {
- return (dispatch, getState) => {
- dispatch({
- nodeId,
- type: ActionTypes.CLICK_CLOSE_DETAILS
- });
- // Pull the most recent details for the next details panel that comes into focus.
- getNodeDetails(getState, dispatch);
- updateRoute(getState);
- };
-}
-
export function closeTerminal(pipeId) {
return (dispatch, getState) => {
dispatch({
@@ -297,102 +231,6 @@ export function setTableView() {
};
}
-export function setResourceView() {
- return (dispatch, getState) => {
- if (resourceViewAvailableSelector(getState())) {
- dispatch({
- type: ActionTypes.SET_VIEW_MODE,
- viewMode: RESOURCE_VIEW_MODE,
- });
- // Pin the first metric if none of the visible ones is pinned.
- const state = getState();
- if (!pinnedMetricSelector(state)) {
- const firstAvailableMetricType = availableMetricTypesSelector(state).first();
- dispatch(pinMetric(firstAvailableMetricType));
- }
- getResourceViewNodesSnapshot(getState(), dispatch);
- updateRoute(getState);
- }
- };
-}
-
-export function clickNode(nodeId, label, origin, topologyId = null) {
- return (dispatch, getState) => {
- dispatch({
- label,
- nodeId,
- origin,
- topologyId,
- type: ActionTypes.CLICK_NODE,
- });
- updateRoute(getState);
- getNodeDetails(getState, dispatch);
- };
-}
-
-export function pauseTimeAtNow() {
- return (dispatch, getState) => {
- dispatch({
- type: ActionTypes.PAUSE_TIME_AT_NOW
- });
- updateRoute(getState);
- if (!getState().get('nodesLoaded')) {
- getNodes(getState, dispatch);
- if (isResourceViewModeSelector(getState())) {
- getResourceViewNodesSnapshot(getState(), dispatch);
- }
- }
- };
-}
-
-export function clickRelative(nodeId, topologyId, label, origin) {
- return (dispatch, getState) => {
- dispatch({
- label,
- nodeId,
- origin,
- topologyId,
- type: ActionTypes.CLICK_RELATIVE
- });
- updateRoute(getState);
- getNodeDetails(getState, dispatch);
- };
-}
-
-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(state, dispatch);
- }
- updateRoute(getState);
- // NOTE: This is currently not needed for our static resource
- // view, but we'll need it here later and it's simpler to just
- // keep it than to redo the nodes delta updating logic.
- getNodes(getState, dispatch);
-}
-
-export function clickShowTopologyForNode(topologyId, nodeId) {
- return (dispatch, getState) => {
- dispatch({
- nodeId,
- topologyId,
- type: ActionTypes.CLICK_SHOW_TOPOLOGY_FOR_NODE
- });
- updateTopology(dispatch, getState);
- };
-}
-
-export function clickTopology(topologyId) {
- return (dispatch, getState) => {
- dispatch({
- topologyId,
- type: ActionTypes.CLICK_TOPOLOGY
- });
- updateTopology(dispatch, getState);
- };
-}
-
export function cacheZoomState(zoomState) {
return {
type: ActionTypes.CACHE_ZOOM_STATE,
@@ -420,17 +258,6 @@ export function closeWebsocket() {
};
}
-export function doControl(nodeId, control) {
- return (dispatch) => {
- dispatch({
- control,
- nodeId,
- type: ActionTypes.DO_CONTROL
- });
- doControlRequest(nodeId, control, dispatch);
- };
-}
-
export function enterEdge(edgeId) {
return {
edgeId,
@@ -528,23 +355,6 @@ export function receiveNodesDelta(delta) {
};
}
-export function resumeTime() {
- return (dispatch, getState) => {
- if (isPausedSelector(getState())) {
- 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);
- if (isResourceViewModeSelector(getState())) {
- getResourceViewNodesSnapshot(getState(), dispatch);
- }
- }
- };
-}
-
export function receiveNodes(nodes) {
return {
nodes,
@@ -552,26 +362,6 @@ export function receiveNodes(nodes) {
};
}
-export function jumpToTime(timestamp) {
- return (dispatch, getState) => {
- dispatch({
- timestamp,
- type: ActionTypes.JUMP_TO_TIME,
- });
- updateRoute(getState);
- getTopologies(getState, dispatch);
- if (!getState().get('nodesLoaded')) {
- getNodes(getState, dispatch);
- if (isResourceViewModeSelector(getState())) {
- getResourceViewNodesSnapshot(getState(), dispatch);
- }
- } else {
- // Get most recent details before freezing the state.
- getNodeDetails(getState, dispatch);
- }
- };
-}
-
export function receiveNodesForTopology(nodes, topologyId) {
return {
nodes,
@@ -580,53 +370,6 @@ export function receiveNodesForTopology(nodes, topologyId) {
};
}
-export function receiveTopologies(topologies) {
- return (dispatch, getState) => {
- const firstLoad = !getState().get('topologiesLoaded');
- dispatch({
- topologies,
- type: ActionTypes.RECEIVE_TOPOLOGIES
- });
- getNodes(getState, dispatch);
- // Populate search matches on first load
- const state = getState();
- // Fetch all the relevant nodes once on first load
- if (firstLoad && isResourceViewModeSelector(state)) {
- getResourceViewNodesSnapshot(state, dispatch);
- }
- };
-}
-
-export function receiveApiDetails(apiDetails) {
- return (dispatch, getState) => {
- const isFirstTime = !getState().get('version');
- const pausedAt = getState().get('pausedAt');
-
- dispatch({
- capabilities: fromJS(apiDetails.capabilities || {}),
- hostname: apiDetails.hostname,
- newVersion: apiDetails.newVersion,
- plugins: apiDetails.plugins,
- type: ActionTypes.RECEIVE_API_DETAILS,
- version: apiDetails.version,
- });
-
- // 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(jumpToTime(pausedAt));
- } else {
- dispatch(pauseTimeAtNow());
- }
- }
- };
-}
-
export function receiveControlNodeRemoved(nodeId) {
return (dispatch, getState) => {
dispatch({
@@ -647,34 +390,6 @@ export function receiveControlPipeFromParams(pipeId, rawTty, resizeTtyControl) {
};
}
-export function receiveControlPipe(pipeId, nodeId, rawTty, resizeTtyControl, control) {
- return (dispatch, getState) => {
- const state = getState();
- if (state.get('nodeDetails').last()
- && nodeId !== state.get('nodeDetails').last().id) {
- log('Node was deselected before we could set up control!');
- deletePipe(pipeId, dispatch);
- return;
- }
-
- const controlPipe = state.get('controlPipes').last();
- if (controlPipe && controlPipe.get('id') !== pipeId) {
- deletePipe(controlPipe.get('id'), dispatch);
- }
-
- dispatch({
- control,
- nodeId,
- pipeId,
- rawTty,
- resizeTtyControl,
- type: ActionTypes.RECEIVE_CONTROL_PIPE
- });
-
- updateRoute(getState);
- };
-}
-
export function receiveControlPipeStatus(pipeId, status) {
return {
pipeId,
@@ -708,44 +423,9 @@ export function setContrastMode(enabled) {
};
}
-export function getTopologiesWithInitialPoll() {
- return (dispatch, getState) => {
- getTopologies(getState, dispatch, true);
- };
-}
-
-export function route(urlState) {
- return (dispatch, getState) => {
- dispatch({
- state: urlState,
- type: ActionTypes.ROUTE_TOPOLOGY
- });
- // Handle Time Travel state update through separate actions as it's more complex.
- // This is mostly to handle switching contexts Explore <-> Monitor in WC while
- // the timestamp keeps changing - e.g. if we were Time Travelling in Scope and
- // then went live in Monitor, switching back to Explore should properly close
- // the Time Travel etc, not just update the pausedAt state directly.
- if (!urlState.pausedAt) {
- dispatch(resumeTime());
- } else {
- dispatch(jumpToTime(urlState.pausedAt));
- }
- // update all request workers with new options
- getTopologies(getState, dispatch);
- getNodes(getState, dispatch);
- // If we are landing on the resource view page, we need to fetch not only all the
- // nodes for the current topology, but also the nodes of all the topologies that make
- // the layers in the resource view.
- const state = getState();
- if (isResourceViewModeSelector(state)) {
- getResourceViewNodesSnapshot(state, dispatch);
- }
- };
-}
-
export function resetLocalViewState() {
return (dispatch) => {
- dispatch({type: ActionTypes.RESET_LOCAL_VIEW_STATE});
+ dispatch({ type: ActionTypes.RESET_LOCAL_VIEW_STATE });
clearStoredViewState();
// eslint-disable-next-line prefer-destructuring
window.location.href = window.location.href.split('#')[0];
@@ -768,16 +448,6 @@ export function changeInstance() {
};
}
-export function shutdown() {
- return (dispatch) => {
- stopPolling();
- teardownWebsockets();
- dispatch({
- type: ActionTypes.SHUTDOWN
- });
- };
-}
-
export function setMonitorState(monitor) {
return {
monitor,
diff --git a/client/app/scripts/actions/request-actions.js b/client/app/scripts/actions/request-actions.js
new file mode 100644
index 0000000000..82a7e7d9fd
--- /dev/null
+++ b/client/app/scripts/actions/request-actions.js
@@ -0,0 +1,608 @@
+/*
+
+This file consists of functions that both dispatch actions to Redux and also make API requests.
+
+TODO: Refactor all the methods below so that the split between actions and
+requests is more clear, and make user components make explicit calls to requests
+and dispatch actions when handling request promises.
+
+*/
+import debug from 'debug';
+import { fromJS } from 'immutable';
+
+import ActionTypes from '../constants/action-types';
+import { RESOURCE_VIEW_MODE } from '../constants/naming';
+import {
+ API_REFRESH_INTERVAL,
+ TOPOLOGY_REFRESH_INTERVAL,
+} from '../constants/timer';
+import { updateRoute } from '../utils/router-utils';
+import { getCurrentTopologyUrl } from '../utils/topology-utils';
+import {
+ doRequest,
+ getApiPath,
+ getAllNodes,
+ getNodesOnce,
+ deletePipe,
+ getNodeDetails,
+ getResourceViewNodesSnapshot,
+ topologiesUrl,
+ buildWebsocketUrl,
+} from '../utils/web-api-utils';
+import {
+ availableMetricTypesSelector,
+ pinnedMetricSelector,
+} from '../selectors/node-metric';
+import {
+ isResourceViewModeSelector,
+ resourceViewAvailableSelector,
+ activeTopologyOptionsSelector,
+} from '../selectors/topology';
+import { isPausedSelector } from '../selectors/time-travel';
+
+import {
+ receiveControlNodeRemoved,
+ receiveControlPipeStatus,
+ receiveControlSuccess,
+ receiveControlError,
+ receiveError,
+ pinMetric,
+ openWebsocket,
+ closeWebsocket,
+ receiveNodesDelta,
+ clearControlError,
+ blurSearch,
+} from './app-actions';
+
+
+const log = debug('scope:app-actions');
+const reconnectTimerInterval = 5000;
+const FIRST_RENDER_TOO_LONG_THRESHOLD = 100; // ms
+
+let socket;
+let topologyTimer = 0;
+let controlErrorTimer = 0;
+let reconnectTimer = 0;
+let apiDetailsTimer = 0;
+let continuePolling = true;
+let firstMessageOnWebsocketAt = null;
+let createWebsocketAt = null;
+let currentUrl = null;
+
+function createWebsocket(websocketUrl, getState, dispatch) {
+ if (socket) {
+ socket.onclose = null;
+ socket.onerror = null;
+ socket.close();
+ // onclose() is not called, but that's fine since we're opening a new one
+ // right away
+ }
+
+ // profiling
+ createWebsocketAt = new Date();
+ firstMessageOnWebsocketAt = null;
+
+ socket = new WebSocket(websocketUrl);
+
+ socket.onopen = () => {
+ log(`Opening websocket to ${websocketUrl}`);
+ dispatch(openWebsocket());
+ };
+
+ socket.onclose = () => {
+ clearTimeout(reconnectTimer);
+ log(`Closing websocket to ${websocketUrl}`, socket.readyState);
+ socket = null;
+ dispatch(closeWebsocket());
+
+ if (continuePolling && !isPausedSelector(getState())) {
+ reconnectTimer = setTimeout(() => {
+ createWebsocket(websocketUrl, getState, dispatch);
+ }, reconnectTimerInterval);
+ }
+ };
+
+ socket.onerror = () => {
+ log(`Error in websocket to ${websocketUrl}`);
+ dispatch(receiveError(websocketUrl));
+ };
+
+ socket.onmessage = (event) => {
+ const msg = JSON.parse(event.data);
+ dispatch(receiveNodesDelta(msg));
+
+ // profiling (receiveNodesDelta triggers synchronous render)
+ if (!firstMessageOnWebsocketAt) {
+ firstMessageOnWebsocketAt = new Date();
+ const timeToFirstMessage = firstMessageOnWebsocketAt - createWebsocketAt;
+ if (timeToFirstMessage > FIRST_RENDER_TOO_LONG_THRESHOLD) {
+ log(
+ 'Time (ms) to first nodes render after websocket was created',
+ firstMessageOnWebsocketAt - createWebsocketAt
+ );
+ }
+ }
+ };
+}
+
+function teardownWebsockets() {
+ clearTimeout(reconnectTimer);
+ if (socket) {
+ socket.onerror = null;
+ socket.onclose = null;
+ socket.onmessage = null;
+ socket.onopen = null;
+ socket.close();
+ socket = null;
+ currentUrl = null;
+ }
+}
+
+function updateWebsocketChannel(getState, dispatch, forceRequest) {
+ const topologyUrl = getCurrentTopologyUrl(getState());
+ const topologyOptions = activeTopologyOptionsSelector(getState());
+ const websocketUrl = buildWebsocketUrl(topologyUrl, topologyOptions, getState());
+ // 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
+ // and no socket exists, or if we get a new url.
+ if (topologyUrl && (!socket || isNewUrl || forceRequest)) {
+ createWebsocket(websocketUrl, getState, dispatch);
+ currentUrl = websocketUrl;
+ }
+}
+
+function getNodes(getState, dispatch, forceRequest = false) {
+ if (isPausedSelector(getState())) {
+ getNodesOnce(getState, dispatch);
+ } else {
+ updateWebsocketChannel(getState, dispatch, forceRequest);
+ }
+ getNodeDetails(getState, dispatch);
+}
+
+export function pauseTimeAtNow() {
+ return (dispatch, getState) => {
+ dispatch({
+ type: ActionTypes.PAUSE_TIME_AT_NOW
+ });
+ updateRoute(getState);
+ if (!getState().get('nodesLoaded')) {
+ getNodes(getState, dispatch);
+ if (isResourceViewModeSelector(getState())) {
+ getResourceViewNodesSnapshot(getState(), dispatch);
+ }
+ }
+ };
+}
+
+function receiveTopologies(topologies) {
+ return (dispatch, getState) => {
+ const firstLoad = !getState().get('topologiesLoaded');
+ dispatch({
+ topologies,
+ type: ActionTypes.RECEIVE_TOPOLOGIES
+ });
+ getNodes(getState, dispatch);
+ // Populate search matches on first load
+ const state = getState();
+ // Fetch all the relevant nodes once on first load
+ if (firstLoad && isResourceViewModeSelector(state)) {
+ getResourceViewNodesSnapshot(state, dispatch);
+ }
+ };
+}
+
+function getTopologiesOnce(getState, dispatch) {
+ const url = topologiesUrl(getState());
+ doRequest({
+ error: (req) => {
+ log(`Error in topology request: ${req.responseText}`);
+ dispatch(receiveError(url));
+ },
+ success: (res) => {
+ dispatch(receiveTopologies(res));
+ },
+ url
+ });
+}
+
+function pollTopologies(getState, dispatch, initialPoll = false) {
+ // Used to resume polling when navigating between pages in Weave Cloud.
+ continuePolling = initialPoll === true ? true : continuePolling;
+ clearTimeout(topologyTimer);
+ // NOTE: getState is called every time to make sure the up-to-date state is used.
+ const url = topologiesUrl(getState());
+ doRequest({
+ error: (req) => {
+ log(`Error in topology request: ${req.responseText}`);
+ dispatch(receiveError(url));
+ // Only retry in stand-alone mode
+ if (continuePolling && !isPausedSelector(getState())) {
+ topologyTimer = setTimeout(() => {
+ pollTopologies(getState, dispatch);
+ }, TOPOLOGY_REFRESH_INTERVAL);
+ }
+ },
+ success: (res) => {
+ if (continuePolling && !isPausedSelector(getState())) {
+ dispatch(receiveTopologies(res));
+ topologyTimer = setTimeout(() => {
+ pollTopologies(getState, dispatch);
+ }, TOPOLOGY_REFRESH_INTERVAL);
+ }
+ },
+ url
+ });
+}
+
+function getTopologies(getState, dispatch, forceRequest) {
+ if (isPausedSelector(getState())) {
+ getTopologiesOnce(getState, dispatch);
+ } else {
+ pollTopologies(getState, dispatch, forceRequest);
+ }
+}
+
+export function jumpToTime(timestamp) {
+ return (dispatch, getState) => {
+ dispatch({
+ timestamp,
+ type: ActionTypes.JUMP_TO_TIME,
+ });
+ updateRoute(getState);
+ getTopologies(getState, dispatch);
+ if (!getState().get('nodesLoaded')) {
+ getNodes(getState, dispatch);
+ if (isResourceViewModeSelector(getState())) {
+ getResourceViewNodesSnapshot(getState(), dispatch);
+ }
+ } else {
+ // Get most recent details before freezing the state.
+ getNodeDetails(getState, dispatch);
+ }
+ };
+}
+
+export function receiveApiDetails(apiDetails) {
+ return (dispatch, getState) => {
+ const isFirstTime = !getState().get('version');
+ const pausedAt = getState().get('pausedAt');
+
+ dispatch({
+ capabilities: fromJS(apiDetails.capabilities || {}),
+ hostname: apiDetails.hostname,
+ newVersion: apiDetails.newVersion,
+ plugins: apiDetails.plugins,
+ type: ActionTypes.RECEIVE_API_DETAILS,
+ version: apiDetails.version,
+ });
+
+ // 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(jumpToTime(pausedAt));
+ } else {
+ dispatch(pauseTimeAtNow());
+ }
+ }
+ };
+}
+
+export function getApiDetails(dispatch) {
+ clearTimeout(apiDetailsTimer);
+ const url = `${getApiPath()}/api`;
+ doRequest({
+ error: (req) => {
+ log(`Error in api details request: ${req.responseText}`);
+ receiveError(url);
+ if (continuePolling) {
+ apiDetailsTimer = setTimeout(() => {
+ getApiDetails(dispatch);
+ }, API_REFRESH_INTERVAL / 2);
+ }
+ },
+ success: (res) => {
+ dispatch(receiveApiDetails(res));
+ if (continuePolling) {
+ apiDetailsTimer = setTimeout(() => {
+ getApiDetails(dispatch);
+ }, API_REFRESH_INTERVAL);
+ }
+ },
+ url
+ });
+}
+
+function stopPolling() {
+ clearTimeout(apiDetailsTimer);
+ clearTimeout(topologyTimer);
+ continuePolling = false;
+}
+
+export function focusSearch() {
+ return (dispatch, getState) => {
+ dispatch({ type: ActionTypes.FOCUS_SEARCH });
+ // update nodes cache to allow search across all topologies,
+ // wait a second until animation is over
+ // NOTE: This will cause matching recalculation (and rerendering)
+ // of all the nodes in the topology, instead applying it only on
+ // the nodes delta. The solution would be to implement deeper
+ // search selectors with per-node caching instead of per-topology.
+ setTimeout(() => {
+ getAllNodes(getState(), dispatch);
+ }, 1200);
+ };
+}
+
+export function getPipeStatus(pipeId, dispatch) {
+ const url = `${getApiPath()}/api/pipe/${encodeURIComponent(pipeId)}/check`;
+ doRequest({
+ complete: (res) => {
+ const status = {
+ 204: 'PIPE_ALIVE',
+ 404: 'PIPE_DELETED'
+ }[res.status];
+
+ if (!status) {
+ log('Unexpected pipe status:', res.status);
+ return;
+ }
+
+ dispatch(receiveControlPipeStatus(pipeId, status));
+ },
+ method: 'GET',
+ url
+ });
+}
+
+export function receiveControlPipe(pipeId, nodeId, rawTty, resizeTtyControl, control) {
+ return (dispatch, getState) => {
+ const state = getState();
+ if (state.get('nodeDetails').last()
+ && nodeId !== state.get('nodeDetails').last().id) {
+ log('Node was deselected before we could set up control!');
+ deletePipe(pipeId, dispatch);
+ return;
+ }
+
+ const controlPipe = state.get('controlPipes').last();
+ if (controlPipe && controlPipe.get('id') !== pipeId) {
+ deletePipe(controlPipe.get('id'), dispatch);
+ }
+
+ dispatch({
+ control,
+ nodeId,
+ pipeId,
+ rawTty,
+ resizeTtyControl,
+ type: ActionTypes.RECEIVE_CONTROL_PIPE
+ });
+
+ updateRoute(getState);
+ };
+}
+
+function doControlRequest(nodeId, control, dispatch) {
+ clearTimeout(controlErrorTimer);
+ const url = `${getApiPath()}/api/control/${encodeURIComponent(control.probeId)}/`
+ + `${encodeURIComponent(control.nodeId)}/${control.id}`;
+ doRequest({
+ error: (err) => {
+ dispatch(receiveControlError(nodeId, err.response));
+ controlErrorTimer = setTimeout(() => {
+ dispatch(clearControlError(nodeId));
+ }, 10000);
+ },
+ method: 'POST',
+ success: (res) => {
+ dispatch(receiveControlSuccess(nodeId));
+ if (res) {
+ if (res.pipe) {
+ dispatch(blurSearch());
+ const resizeTtyControl = res.resize_tty_control
+ && { id: res.resize_tty_control, nodeId: control.nodeId, probeId: control.probeId };
+ dispatch(receiveControlPipe(
+ res.pipe,
+ nodeId,
+ res.raw_tty,
+ resizeTtyControl,
+ control
+ ));
+ }
+ if (res.removedNode) {
+ dispatch(receiveControlNodeRemoved(nodeId));
+ }
+ }
+ },
+ url
+ });
+}
+
+export function doControl(nodeId, control) {
+ return (dispatch) => {
+ dispatch({
+ control,
+ nodeId,
+ type: ActionTypes.DO_CONTROL
+ });
+ doControlRequest(nodeId, control, dispatch);
+ };
+}
+
+export function shutdown() {
+ return (dispatch) => {
+ stopPolling();
+ teardownWebsockets();
+ dispatch({
+ type: ActionTypes.SHUTDOWN
+ });
+ };
+}
+
+export function setResourceView() {
+ return (dispatch, getState) => {
+ if (resourceViewAvailableSelector(getState())) {
+ dispatch({
+ type: ActionTypes.SET_VIEW_MODE,
+ viewMode: RESOURCE_VIEW_MODE,
+ });
+ // Pin the first metric if none of the visible ones is pinned.
+ const state = getState();
+ if (!pinnedMetricSelector(state)) {
+ const firstAvailableMetricType = availableMetricTypesSelector(state).first();
+ dispatch(pinMetric(firstAvailableMetricType));
+ }
+ getResourceViewNodesSnapshot(getState(), dispatch);
+ updateRoute(getState);
+ }
+ };
+}
+
+export function changeTopologyOption(option, value, topologyId, addOrRemove) {
+ return (dispatch, getState) => {
+ dispatch({
+ addOrRemove,
+ option,
+ topologyId,
+ type: ActionTypes.CHANGE_TOPOLOGY_OPTION,
+ value
+ });
+ updateRoute(getState);
+ // update all request workers with new options
+ getTopologies(getState, dispatch);
+ getNodes(getState, dispatch);
+ };
+}
+
+export function getTopologiesWithInitialPoll() {
+ return (dispatch, getState) => {
+ getTopologies(getState, dispatch, true);
+ };
+}
+
+export function resumeTime() {
+ return (dispatch, getState) => {
+ if (isPausedSelector(getState())) {
+ 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);
+ if (isResourceViewModeSelector(getState())) {
+ getResourceViewNodesSnapshot(getState(), dispatch);
+ }
+ }
+ };
+}
+
+export function route(urlState) {
+ return (dispatch, getState) => {
+ dispatch({
+ state: urlState,
+ type: ActionTypes.ROUTE_TOPOLOGY
+ });
+ // Handle Time Travel state update through separate actions as it's more complex.
+ // This is mostly to handle switching contexts Explore <-> Monitor in WC while
+ // the timestamp keeps changing - e.g. if we were Time Travelling in Scope and
+ // then went live in Monitor, switching back to Explore should properly close
+ // the Time Travel etc, not just update the pausedAt state directly.
+ if (!urlState.pausedAt) {
+ dispatch(resumeTime());
+ } else {
+ dispatch(jumpToTime(urlState.pausedAt));
+ }
+ // update all request workers with new options
+ getTopologies(getState, dispatch);
+ getNodes(getState, dispatch);
+ // If we are landing on the resource view page, we need to fetch not only all the
+ // nodes for the current topology, but also the nodes of all the topologies that make
+ // the layers in the resource view.
+ const state = getState();
+ if (isResourceViewModeSelector(state)) {
+ getResourceViewNodesSnapshot(state, dispatch);
+ }
+ };
+}
+
+export function clickCloseDetails(nodeId) {
+ return (dispatch, getState) => {
+ dispatch({
+ nodeId,
+ type: ActionTypes.CLICK_CLOSE_DETAILS
+ });
+ // Pull the most recent details for the next details panel that comes into focus.
+ getNodeDetails(getState, dispatch);
+ updateRoute(getState);
+ };
+}
+
+export function clickNode(nodeId, label, origin, topologyId = null) {
+ return (dispatch, getState) => {
+ dispatch({
+ label,
+ nodeId,
+ origin,
+ topologyId,
+ type: ActionTypes.CLICK_NODE,
+ });
+ updateRoute(getState);
+ getNodeDetails(getState, dispatch);
+ };
+}
+
+export function clickRelative(nodeId, topologyId, label, origin) {
+ return (dispatch, getState) => {
+ dispatch({
+ label,
+ nodeId,
+ origin,
+ topologyId,
+ type: ActionTypes.CLICK_RELATIVE
+ });
+ updateRoute(getState);
+ getNodeDetails(getState, dispatch);
+ };
+}
+
+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(state, dispatch);
+ }
+ updateRoute(getState);
+ // NOTE: This is currently not needed for our static resource
+ // view, but we'll need it here later and it's simpler to just
+ // keep it than to redo the nodes delta updating logic.
+ getNodes(getState, dispatch);
+}
+
+export function clickShowTopologyForNode(topologyId, nodeId) {
+ return (dispatch, getState) => {
+ dispatch({
+ nodeId,
+ topologyId,
+ type: ActionTypes.CLICK_SHOW_TOPOLOGY_FOR_NODE
+ });
+ updateTopology(dispatch, getState);
+ };
+}
+
+export function clickTopology(topologyId) {
+ return (dispatch, getState) => {
+ dispatch({
+ topologyId,
+ type: ActionTypes.CLICK_TOPOLOGY
+ });
+ updateTopology(dispatch, getState);
+ };
+}
diff --git a/client/app/scripts/charts/edge-container.js b/client/app/scripts/charts/edge-container.js
index 2ad0f81999..9d357d9459 100644
--- a/client/app/scripts/charts/edge-container.js
+++ b/client/app/scripts/charts/edge-container.js
@@ -88,12 +88,11 @@ export default class EdgeContainer extends React.PureComponent {
...waypointsMap.toJS(),
}}>
{
- ({ interpolatedThickness, ...interpolatedWaypoints }) =>
- transformedEdge(
- forwardedProps,
- waypointsMapToArray(fromJS(interpolatedWaypoints)),
- interpolatedThickness
- )
+ ({ interpolatedThickness, ...interpolatedWaypoints }) => transformedEdge(
+ forwardedProps,
+ waypointsMapToArray(fromJS(interpolatedWaypoints)),
+ interpolatedThickness
+ )
}
);
diff --git a/client/app/scripts/charts/node-container.js b/client/app/scripts/charts/node-container.js
index 636412465f..c7dd158401 100644
--- a/client/app/scripts/charts/node-container.js
+++ b/client/app/scripts/charts/node-container.js
@@ -7,7 +7,8 @@ import {
getMetricValue,
getMetricColor,
} from '../utils/metric-utils';
-import { clickNode, enterNode, leaveNode } from '../actions/app-actions';
+import { clickNode } from '../actions/request-actions';
+import { enterNode, leaveNode } from '../actions/app-actions';
import { trackAnalyticsEvent } from '../utils/tracking-utils';
import { getNodeColor } from '../utils/color-utils';
import MatchedResults from '../components/matched-results';
diff --git a/client/app/scripts/charts/nodes-chart-elements.js b/client/app/scripts/charts/nodes-chart-elements.js
index 52c3e3b1fc..741fbb9d4e 100644
--- a/client/app/scripts/charts/nodes-chart-elements.js
+++ b/client/app/scripts/charts/nodes-chart-elements.js
@@ -63,9 +63,9 @@ class NodesChartElements extends React.Component {
nodeDisplayLayer(node) {
if (node.get('id') === this.props.mouseOverNodeId) {
return HOVERED_NODES_LAYER;
- } else if (node.get('blurred') && !node.get('focused')) {
+ } if (node.get('blurred') && !node.get('focused')) {
return BLURRED_NODES_LAYER;
- } else if (node.get('highlighted')) {
+ } if (node.get('highlighted')) {
return HIGHLIGHTED_NODES_LAYER;
}
return NORMAL_NODES_LAYER;
@@ -74,9 +74,9 @@ class NodesChartElements extends React.Component {
edgeDisplayLayer(edge) {
if (edge.get('id') === this.props.mouseOverEdgeId) {
return HOVERED_EDGES_LAYER;
- } else if (edge.get('blurred') && !edge.get('focused')) {
+ } if (edge.get('blurred') && !edge.get('focused')) {
return BLURRED_EDGES_LAYER;
- } else if (edge.get('highlighted')) {
+ } if (edge.get('highlighted')) {
return HIGHLIGHTED_EDGES_LAYER;
}
return NORMAL_EDGES_LAYER;
@@ -140,8 +140,8 @@ class NodesChartElements extends React.Component {
const sourceInNetwork = selectedNetworkNodesIds.contains(edge.get('source'));
const targetInNetwork = selectedNetworkNodesIds.contains(edge.get('target'));
const notInNetwork = this.props.selectedNetwork && (!sourceInNetwork || !targetInNetwork);
- return edge.set('blurred', !edge.get('highlighted') && !edge.get('focused') &&
- (otherNodesSelected || notMatched || notInNetwork));
+ return edge.set('blurred', !edge.get('highlighted') && !edge.get('focused')
+ && (otherNodesSelected || notMatched || notInNetwork));
}
edgeScaleDecorator(edge) {
diff --git a/client/app/scripts/charts/nodes-grid.js b/client/app/scripts/charts/nodes-grid.js
index ffaa8126a9..8cd2c6d6cf 100644
--- a/client/app/scripts/charts/nodes-grid.js
+++ b/client/app/scripts/charts/nodes-grid.js
@@ -1,4 +1,4 @@
-/* eslint react/jsx-no-bind: "off", no-multi-comp: "off" */
+/* eslint react/jsx-no-bind: "off" */
import React from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';
@@ -6,7 +6,8 @@ import { List as makeList, Map as makeMap } from 'immutable';
import capitalize from 'lodash/capitalize';
import NodeDetailsTable from '../components/node-details/node-details-table';
-import { clickNode, sortOrderChanged } from '../actions/app-actions';
+import { clickNode } from '../actions/request-actions';
+import { sortOrderChanged } from '../actions/app-actions';
import { shownNodesSelector } from '../selectors/node-filters';
import { trackAnalyticsEvent } from '../utils/tracking-utils';
import { findTopologyById } from '../utils/topology-utils';
@@ -55,7 +56,11 @@ function getColumns(nodes, topologies) {
.toList()
.flatMap((n) => {
const metadata = (n.get('metadata') || makeList())
- .map(m => makeMap({ dataType: m.get('dataType'), id: m.get('id'), label: m.get('label') }));
+ .map(m => makeMap({
+ dataType: m.get('dataType'),
+ id: m.get('id'),
+ label: m.get('label')
+ }));
return metadata;
})
.toSet()
@@ -68,7 +73,10 @@ function getColumns(nodes, topologies) {
.toList()
.flatMap((n) => {
const metadata = (n.get('parents') || makeList())
- .map(m => makeMap({ id: m.get('topologyId'), label: topologyLabel(topologies, m.get('topologyId')) }));
+ .map(m => makeMap({
+ id: m.get('topologyId'),
+ label: topologyLabel(topologies, m.get('topologyId'))
+ }));
return metadata;
})
.toSet()
@@ -91,7 +99,9 @@ function renderIdCell({
- {label} {showSubLabel && {labelMinor} }
+ {label}
+ {' '}
+ {showSubLabel && {labelMinor} }
);
@@ -126,8 +136,9 @@ class NodesGrid extends React.Component {
const {
nodes, gridSortedBy, gridSortedDesc, searchNodeMatches, searchQuery, windowHeight, topologies
} = this.props;
- const height =
- this.tableRef ? windowHeight - this.tableRef.getBoundingClientRect().top - 30 : 0;
+ const height = this.tableRef
+ ? windowHeight - this.tableRef.getBoundingClientRect().top - 30
+ : 0;
const cmpStyle = {
height,
paddingLeft: 40,
@@ -152,20 +163,22 @@ class NodesGrid extends React.Component {
return (
- {nodes.size > 0 && }
+ {nodes.size > 0 && (
+
+ )}
);
}
diff --git a/client/app/scripts/charts/nodes-layout.js b/client/app/scripts/charts/nodes-layout.js
index 2d0d7321cd..cf7ed85bdd 100644
--- a/client/app/scripts/charts/nodes-layout.js
+++ b/client/app/scripts/charts/nodes-layout.js
@@ -408,7 +408,7 @@ function copyLayoutProperties(layout, nodeCache, edgeCache) {
if (edgeCache.has(edge.get('id'))
&& hasSameEndpoints(edgeCache.get(edge.get('id')), result.nodes)) {
return edge.merge(edgeCache.get(edge.get('id')));
- } else if (nodeCache.get(edge.get('source')) && nodeCache.get(edge.get('target'))) {
+ } if (nodeCache.get(edge.get('source')) && nodeCache.get(edge.get('target'))) {
return setSimpleEdgePoints(edge, nodeCache);
}
return edge;
diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js
index 0b616cfc60..b9a4ebc741 100644
--- a/client/app/scripts/components/app.js
+++ b/client/app/scripts/components/app.js
@@ -18,9 +18,7 @@ import Status from './status';
import Topologies from './topologies';
import TopologyOptions from './topology-options';
import Overlay from './overlay';
-import { getApiDetails } from '../utils/web-api-utils';
import {
- focusSearch,
pinNextMetric,
pinPreviousMetric,
hitEsc,
@@ -29,12 +27,16 @@ import {
setGraphView,
setMonitorState,
setTableView,
- setResourceView,
setStoreViewState,
- shutdown,
setViewportDimensions,
- getTopologiesWithInitialPoll,
} from '../actions/app-actions';
+import {
+ focusSearch,
+ getApiDetails,
+ setResourceView,
+ getTopologiesWithInitialPoll,
+ shutdown,
+} from '../actions/request-actions';
import Details from './details';
import Nodes from './nodes';
import TimeControl from './time-control';
@@ -42,7 +44,8 @@ import TimeTravelWrapper from './time-travel-wrapper';
import ViewModeSelector from './view-mode-selector';
import NetworkSelector from './networks-selector';
import DebugToolbar, { showingDebugToolbar, toggleDebugToolbar } from './debug-toolbar';
-import { getRouter, getUrlState } from '../utils/router-utils';
+import { getUrlState } from '../utils/router-utils';
+import { getRouter } from '../router';
import { trackAnalyticsEvent } from '../utils/tracking-utils';
import { availableNetworksSelector } from '../selectors/node-networks';
import { timeTravelSupportedSelector } from '../selectors/time-travel';
@@ -210,19 +213,23 @@ class App extends React.Component {
{showingTroubleshootingMenu && }
- {showingDetails && }
+ />
+ )}
{timeTravelSupported && this.props.renderTimeTravel()}
- {!isIframe &&
+ {!isIframe
+ && (
+ )
}
diff --git a/client/app/scripts/components/cloud-feature.js b/client/app/scripts/components/cloud-feature.js
index 0708a37eff..440f745117 100644
--- a/client/app/scripts/components/cloud-feature.js
+++ b/client/app/scripts/components/cloud-feature.js
@@ -26,6 +26,8 @@ class CloudFeature extends React.Component {
}
}
+/* eslint-disable react/forbid-prop-types */
+// TODO: Remove this component as part of https://github.com/weaveworks/scope/issues/3278.
CloudFeature.contextTypes = {
router: PropTypes.object,
serviceStore: PropTypes.object,
@@ -36,5 +38,6 @@ CloudFeature.childContextTypes = {
router: PropTypes.object,
store: PropTypes.object
};
+/* eslint-enable react/forbid-prop-types */
export default connect()(CloudFeature);
diff --git a/client/app/scripts/components/debug-toolbar.js b/client/app/scripts/components/debug-toolbar.js
index fa71ffb802..bb84c609df 100644
--- a/client/app/scripts/components/debug-toolbar.js
+++ b/client/app/scripts/components/debug-toolbar.js
@@ -1,7 +1,9 @@
/* eslint react/jsx-no-bind: "off" */
import React from 'react';
import { connect } from 'react-redux';
-import { sampleSize, sample, random, range, flattenDeep, times } from 'lodash';
+import {
+ sampleSize, sample, random, range, flattenDeep, times
+} from 'lodash';
import { fromJS, Set as makeSet } from 'immutable';
import { hsl } from 'd3-color';
import debug from 'debug';
@@ -17,7 +19,7 @@ const STACK_VARIANTS = [false, true];
const METRIC_FILLS = [0, 0.1, 50, 99.9, 100];
const NETWORKS = [
'be', 'fe', 'zb', 'db', 're', 'gh', 'jk', 'lol', 'nw'
-].map(n => ({colorKey: n, id: n, label: n}));
+].map(n => ({ colorKey: n, id: n, label: n }));
const INTERNET = 'the-internet';
const LOREM = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
@@ -58,7 +60,7 @@ const deltaAdd = (name, adjacency = [], shape = 'circle', stack = false, network
function addMetrics(availableMetrics, node, v) {
const metrics = availableMetrics.size > 0 ? availableMetrics : fromJS([
- {id: 'host_cpu_usage_percent', label: 'CPU'}
+ { id: 'host_cpu_usage_percent', label: 'CPU' }
]);
return Object.assign({}, node, {
@@ -151,13 +153,13 @@ class DebugToolbar extends React.Component {
}
onChange(ev) {
- this.setState({nodesToAdd: parseInt(ev.target.value, 10)});
+ this.setState({ nodesToAdd: parseInt(ev.target.value, 10) });
}
toggleColors() {
- this.setState({
- showColors: !this.state.showColors
- });
+ this.setState(prevState => ({
+ showColors: !prevState.showColors
+ }));
}
asyncDispatch(v) {
@@ -283,46 +285,54 @@ class DebugToolbar extends React.Component {
Add nodes
- this.addNodes(1)}>+1
- this.addNodes(10)}>+10
+ this.addNodes(1)}>+1
+ this.addNodes(10)}>+10
- this.addNodes(this.state.nodesToAdd)}>+
- this.asyncDispatch(addAllVariants)}>Variants
- this.asyncDispatch(addAllMetricVariants(availableMetrics))}>
+ this.addNodes(this.state.nodesToAdd)}>+
+ this.asyncDispatch(addAllVariants)}>
+ Variants
+
+ this.asyncDispatch(addAllMetricVariants(availableMetrics))}>
Metric Variants
- this.addNodes(1, LOREM)}>Long name
- this.addInternetNode()}>Internet
- this.removeNode()}>Remove node
- this.updateAdjacencies()}>Update adj.
+ this.addNodes(1, LOREM)}>Long name
+ this.addInternetNode()}>Internet
+ this.removeNode()}>Remove node
+ this.updateAdjacencies()}>Update adj.
Logging
- enableLog('*')}>scope:*
- enableLog('dispatcher')}>scope:dispatcher
- enableLog('app-key-press')}>scope:app-key-press
- enableLog('terminal')}>scope:terminal
- disableLog()}>Disable log
+ enableLog('*')}>scope:*
+ enableLog('dispatcher')}>scope:dispatcher
+ enableLog('app-key-press')}>
+ scope:app-key-press
+
+ enableLog('terminal')}>scope:terminal
+ disableLog()}>Disable log
Colors
- toggle
+ toggle
- {this.state.showColors &&
-
-
- {LABEL_PREFIXES.map(r => (
-
-
-
- ))}
-
-
}
+ {this.state.showColors
+ && (
+
+
+ {LABEL_PREFIXES.map(r => (
+
+
+
+ ))}
+
+
+ )}
{this.state.showColors && [getNodeColor, getNodeColorDark].map(fn => (
@@ -330,7 +340,7 @@ class DebugToolbar extends React.Component {
{LABEL_PREFIXES.map(r => (
{LABEL_PREFIXES.map(c => (
-
+
))}
))}
@@ -340,14 +350,20 @@ class DebugToolbar extends React.Component {
State
- this.setLoading(true)}>Set doing initial load
- this.setLoading(false)}>Stop
+ this.setLoading(true)}>
+ Set doing initial load
+
+ this.setLoading(false)}>Stop
Short-lived nodes
- this.setShortLived()}>Toggle short-lived nodes
- this.setIntermittent()}>Toggle intermittent nodes
+ this.setShortLived()}>
+ Toggle short-lived nodes
+
+ this.setIntermittent()}>
+ Toggle intermittent nodes
+
);
diff --git a/client/app/scripts/components/embedded-terminal.js b/client/app/scripts/components/embedded-terminal.js
index 734a3c2bce..37188e196c 100644
--- a/client/app/scripts/components/embedded-terminal.js
+++ b/client/app/scripts/components/embedded-terminal.js
@@ -31,8 +31,8 @@ class EmeddedTerminal extends React.Component {
}
getTransform() {
- const dx = this.state.mounted ? 0 :
- window.innerWidth - DETAILS_PANEL_WIDTH - DETAILS_PANEL_MARGINS.right;
+ const dx = this.state.mounted ? 0
+ : window.innerWidth - DETAILS_PANEL_WIDTH - DETAILS_PANEL_MARGINS.right;
return `translateX(${dx}px)`;
}
@@ -56,7 +56,7 @@ class EmeddedTerminal extends React.Component {
+ style={{transform: this.getTransform()}}>
- {versionUpdate &&
-
- Update available: {versionUpdate.get('version')}
-
+ {versionUpdate
+ && (
+
+ Update available:
+ {' '}
+ {versionUpdate.get('version')}
+
+ )
}
Version
{version || '...'}
@@ -64,15 +68,21 @@ class Footer extends React.Component {
-
+
-
+
diff --git a/client/app/scripts/components/help-panel.js b/client/app/scripts/components/help-panel.js
index 445b8a4a43..a41aecc3ca 100644
--- a/client/app/scripts/components/help-panel.js
+++ b/client/app/scripts/components/help-panel.js
@@ -7,26 +7,26 @@ import { hideHelp } from '../actions/app-actions';
const GENERAL_SHORTCUTS = [
- {key: 'esc', label: 'Close active panel'},
- {key: '/', label: 'Activate search field'},
- {key: '?', label: 'Toggle shortcut menu'},
- {key: 'g', label: 'Switch to Graph view'},
- {key: 't', label: 'Switch to Table view'},
- {key: 'r', label: 'Switch to Resources view'},
+ { key: 'esc', label: 'Close active panel' },
+ { key: '/', label: 'Activate search field' },
+ { key: '?', label: 'Toggle shortcut menu' },
+ { key: 'g', label: 'Switch to Graph view' },
+ { key: 't', label: 'Switch to Table view' },
+ { key: 'r', label: 'Switch to Resources view' },
];
const CANVAS_METRIC_SHORTCUTS = [
- {key: '<', label: 'Select and pin previous metric'},
- {key: '>', label: 'Select and pin next metric'},
- {key: 'q', label: 'Unpin current metric'},
+ { key: '<', label: 'Select and pin previous metric' },
+ { key: '>', label: 'Select and pin next metric' },
+ { key: 'q', label: 'Unpin current metric' },
];
function renderShortcuts(cuts) {
return (
- {cuts.map(({key, label}) => (
+ {cuts.map(({ key, label }) => (
{key}
{label}
@@ -51,9 +51,16 @@ function renderShortcutPanel() {
const BASIC_SEARCHES = [
- {label: 'All fields for foo', term: 'foo'},
+ { label: 'All fields for foo', term: 'foo' },
{
- label:
Any field matching pid for the value 12345 ,
+ label: (
+
+ Any field matching
+ pid
+ {' '}
+ for the value 12345
+
+ ),
term: 'pid: 12345'
},
];
@@ -65,16 +72,37 @@ const REGEX_SEARCHES = [
term: 'foo|bar'
},
{
- label:
command field for foobar or foobaz,
+ label: (
+
+ command
+ {' '}
+ field for foobar or foobaz
+
+ ),
term: 'command: foo(bar|baz)'
},
];
const METRIC_SEARCHES = [
- {label:
CPU greater than 4%, term: 'cpu > 4%'},
{
- label:
Memory less than 10 megabytes,
+ label: (
+
+ CPU
+ {' '}
+ greater than 4%
+
+ ),
+ term: 'cpu > 4%'
+ },
+ {
+ label: (
+
+ Memory
+ {' '}
+ less than 10 megabytes
+
+ ),
term: 'memory < 10mb'
},
];
@@ -83,7 +111,7 @@ const METRIC_SEARCHES = [
function renderSearches(searches) {
return (
- {searches.map(({term, label}) => (
+ {searches.map(({ term, label }) => (
@@ -117,7 +145,7 @@ function renderSearchPanel() {
function renderFieldsPanel(currentTopologyName, searchableFields) {
const none = (
-
None
+
None
);
const currentTopology = (
@@ -129,8 +157,14 @@ function renderFieldsPanel(currentTopologyName, searchableFields) {
Fields and Metrics
- Searchable fields and metrics in the
- currently selected {currentTopology} topology:
+ Searchable fields and metrics in the
+ {' '}
+
+ currently selected
+ {' '}
+ {currentTopology}
+ {' '}
+ topology:
@@ -162,7 +196,7 @@ function HelpPanel({
}) {
return (
-
+
Help
diff --git a/client/app/scripts/components/matched-results.js b/client/app/scripts/components/matched-results.js
index 7817f3f4cf..61f2659101 100644
--- a/client/app/scripts/components/matched-results.js
+++ b/client/app/scripts/components/matched-results.js
@@ -7,7 +7,8 @@ const Match = (searchTerms, match) => (
- {match.label}:
+ {match.label}
+:
Match(searchTerms, matches.get(fieldId)))
}
- {moreFieldMatches &&
+ {moreFieldMatches
+ && (
{`${moreFieldMatches.size} more matches`}
+ )
}
);
diff --git a/client/app/scripts/components/metric-selector.js b/client/app/scripts/components/metric-selector.js
index bca5589d96..debfb0d5e2 100644
--- a/client/app/scripts/components/metric-selector.js
+++ b/client/app/scripts/components/metric-selector.js
@@ -22,7 +22,8 @@ class MetricSelector extends React.Component {
return (
- {hasMetrics &&
+ {hasMetrics
+ && (
{availableMetrics.map(metric => (
))}
+ )
}
);
diff --git a/client/app/scripts/components/node-details.js b/client/app/scripts/components/node-details.js
index 50dc20d1a1..bff3c839c4 100644
--- a/client/app/scripts/components/node-details.js
+++ b/client/app/scripts/components/node-details.js
@@ -6,7 +6,7 @@ import { connect } from 'react-redux';
import { Map as makeMap } from 'immutable';
import { noop } from 'lodash';
-import { clickCloseDetails, clickShowTopologyForNode } from '../actions/app-actions';
+import { clickCloseDetails, clickShowTopologyForNode } from '../actions/request-actions';
import { brightenColor, getNeutralColor, getNodeColorDark } from '../utils/color-utils';
import { isGenericTable, isPropertyList } from '../utils/node-details-utils';
import { resetDocumentTitle, setDocumentTitle } from '../utils/title-utils';
@@ -56,13 +56,18 @@ class NodeDetails extends React.Component {
return (
- {showSwitchTopology &&
+ {showSwitchTopology
+ && (
- Show in {this.props.topologyId.replace(/-/g, ' ')}
+
+Show in
+ {this.props.topologyId.replace(/-/g, ' ')}
+
+ )
}
- {this.props.label} not found!
+ {this.props.label}
+ {' '}
+not found!
@@ -176,14 +183,17 @@ class NodeDetails extends React.Component {
- {details.parents && }
+ relatives={details.parents} />
+ )}
- {showControls &&
+ {showControls
+ && (
+ )
}
- {details.metrics &&
+ {details.metrics
+ && (
+ )
}
- {details.metadata &&
+ {details.metadata
+ && (
+ )
}
{details.connections && details.connections.filter(cs => cs.connections.length > 0)
@@ -219,7 +234,7 @@ class NodeDetails extends React.Component {
nodeIdKey="nodeId"
/>
- ))}
+ ))}
{details.children && details.children.map(children => (
@@ -233,11 +248,13 @@ class NodeDetails extends React.Component {
{table.label && table.label.length > 0 && table.label}
- {table.truncationCount > 0 &&
+ {table.truncationCount > 0
+ && (
+ )
}
{this.renderTable(table)}
@@ -266,7 +283,7 @@ class NodeDetails extends React.Component {
matches={nodeMatches.get('tables')}
/>
);
- } else if (isPropertyList(table)) {
+ } if (isPropertyList(table)) {
return (
{
// (that is, first by row and then by column), the indexes we are interested in are of the
// form columnIndex + n * columns.length, where n >= 0. Therefore we take only the values
// at the index which divided by columns.length gives a reminder columnIndex.
- const filteredValues = values.filter((element, index) =>
- index % columns.length === columnIndex);
+ const filteredValues = values.filter(
+ (element, index) => index % columns.length === columnIndex
+ );
// Array comparison
expect(filteredValues).toEqual(expectedValues);
}
@@ -91,7 +92,7 @@ describe('NodeDetailsTable', () => {
sortedBy="kubernetes_ip"
nodeIdKey="id"
nodes={nodes}
- />
+ />
));
@@ -127,7 +128,7 @@ describe('NodeDetailsTable', () => {
sortedBy="kubernetes_namespace"
nodeIdKey="id"
nodes={nodes}
- />
+ />
));
diff --git a/client/app/scripts/components/node-details/node-details-control-button.js b/client/app/scripts/components/node-details/node-details-control-button.js
index 8bca5fbb37..b79f8c3040 100644
--- a/client/app/scripts/components/node-details/node-details-control-button.js
+++ b/client/app/scripts/components/node-details/node-details-control-button.js
@@ -4,7 +4,7 @@ import { isEmpty } from 'lodash';
import classNames from 'classnames';
import { trackAnalyticsEvent } from '../../utils/tracking-utils';
-import { doControl } from '../../actions/app-actions';
+import { doControl } from '../../actions/request-actions';
class NodeDetailsControlButton extends React.Component {
constructor(props, context) {
diff --git a/client/app/scripts/components/node-details/node-details-controls.js b/client/app/scripts/components/node-details/node-details-controls.js
index 41e08794f2..ca15e14989 100644
--- a/client/app/scripts/components/node-details/node-details-controls.js
+++ b/client/app/scripts/components/node-details/node-details-controls.js
@@ -15,11 +15,13 @@ export default function NodeDetailsControls({
return (
- {error &&
+ {error
+ && (
{error}
+ )
}
{sortBy(controls, 'rank').map(control => (
diff --git a/client/app/scripts/components/node-details/node-details-generic-table.js b/client/app/scripts/components/node-details/node-details-generic-table.js
index 0b933ef5b6..0f9bfe5444 100644
--- a/client/app/scripts/components/node-details/node-details-generic-table.js
+++ b/client/app/scripts/components/node-details/node-details-generic-table.js
@@ -46,9 +46,9 @@ export default class NodeDetailsGenericTable extends React.Component {
}
handleLimitClick() {
- this.setState({
- limit: this.state.limit ? 0 : NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT
- });
+ this.setState(prevState => ({
+ limit: prevState.limit ? 0 : NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT
+ }));
}
render() {
@@ -62,8 +62,11 @@ export default class NodeDetailsGenericTable extends React.Component {
// If there are rows that would be hidden behind 'show more', keep them
// expanded if any of them match the search query; otherwise hide them.
if (this.state.limit > 0 && rows.length > this.state.limit) {
- const hasHiddenMatch = rows.slice(this.state.limit).some(row =>
- columns.some(column => matches.has(genericTableEntryKey(row, column))));
+ const hasHiddenMatch = rows
+ .slice(this.state.limit)
+ .some(
+ row => columns.some(column => matches.has(genericTableEntryKey(row, column)))
+ );
if (!hasHiddenMatch) {
notShown = rows.length - NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT;
rows = rows.slice(0, this.state.limit);
@@ -94,15 +97,17 @@ export default class NodeDetailsGenericTable extends React.Component {
title={value}
key={column.id}
style={styles[index]}>
- {column.dataType === 'link' ?
-
- {value}
- :
-
+ {column.dataType === 'link'
+ ? (
+
+ {value}
+
+ )
+ :
}
);
diff --git a/client/app/scripts/components/node-details/node-details-health.js b/client/app/scripts/components/node-details/node-details-health.js
index e73b2b0d5b..4c724b8784 100644
--- a/client/app/scripts/components/node-details/node-details-health.js
+++ b/client/app/scripts/components/node-details/node-details-health.js
@@ -13,8 +13,9 @@ export default class NodeDetailsHealth extends React.Component {
}
handleClickMore() {
- const expanded = !this.state.expanded;
- this.setState({expanded});
+ this.setState(prevState => ({
+ expanded: !prevState.expanded
+ }));
}
render() {
@@ -38,18 +39,22 @@ export default class NodeDetailsHealth extends React.Component {
return (
- {shownWithData.map(item => ( ))}
+ {shownWithData.map(item => (
+
+ ))}
- {shownEmpty.map(item => ( ))}
+ {shownEmpty.map(item => (
+
+ ))}
({
+ expanded: !prevState.expanded
+ }));
}
render() {
@@ -47,18 +48,22 @@ class NodeDetailsInfo extends React.Component {
{field.label}
- {field.dataType === 'link' ?
-
- {value}
- :
-
+ {field.dataType === 'link'
+ ? (
+
+ {value}
+
+ )
+ : (
+
+ )
}
diff --git a/client/app/scripts/components/node-details/node-details-property-list.js b/client/app/scripts/components/node-details/node-details-property-list.js
index 92a7fbc22e..95585fe16a 100644
--- a/client/app/scripts/components/node-details/node-details-property-list.js
+++ b/client/app/scripts/components/node-details/node-details-property-list.js
@@ -9,10 +9,12 @@ import ShowMore from '../show-more';
const Controls = controls => (
- {sortBy(controls, 'rank').map(control => ( ))}
+ {sortBy(controls, 'rank').map(control => (
+
+ ))}
);
@@ -26,8 +28,9 @@ export default class NodeDetailsPropertyList extends React.Component {
}
handleLimitClick() {
- const limit = this.state.limit ? 0 : NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT;
- this.setState({limit});
+ this.setState(prevState => ({
+ limit: prevState.limit ? 0 : NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT
+ }));
}
render() {
diff --git a/client/app/scripts/components/node-details/node-details-relatives-link.js b/client/app/scripts/components/node-details/node-details-relatives-link.js
index 2750fb86ed..77650b0c0a 100644
--- a/client/app/scripts/components/node-details/node-details-relatives-link.js
+++ b/client/app/scripts/components/node-details/node-details-relatives-link.js
@@ -1,7 +1,7 @@
import React from 'react';
import { connect } from 'react-redux';
-import { clickRelative } from '../../actions/app-actions';
+import { clickRelative } from '../../actions/request-actions';
import { trackAnalyticsEvent } from '../../utils/tracking-utils';
import MatchedText from '../matched-text';
diff --git a/client/app/scripts/components/node-details/node-details-relatives.js b/client/app/scripts/components/node-details/node-details-relatives.js
index ec9d6dedf1..e4af56a0dd 100644
--- a/client/app/scripts/components/node-details/node-details-relatives.js
+++ b/client/app/scripts/components/node-details/node-details-relatives.js
@@ -15,8 +15,9 @@ export default class NodeDetailsRelatives extends React.Component {
handleLimitClick(ev) {
ev.preventDefault();
- const limit = this.state.limit ? 0 : NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT;
- this.setState({limit});
+ this.setState(prevState => ({
+ limit: prevState.limit ? 0 : NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT
+ }));
}
render() {
@@ -37,13 +38,16 @@ export default class NodeDetailsRelatives extends React.Component {
))}
- {showLimitAction &&
-
- {limitActionText}
-
+ {...relative} />
+ ))}
+ {showLimitAction
+ && (
+
+ {limitActionText}
+
+ )
}
);
diff --git a/client/app/scripts/components/node-details/node-details-table-headers.js b/client/app/scripts/components/node-details/node-details-table-headers.js
index 37a4a77792..48a049de5a 100644
--- a/client/app/scripts/components/node-details/node-details-table-headers.js
+++ b/client/app/scripts/components/node-details/node-details-table-headers.js
@@ -33,9 +33,9 @@ export default class NodeDetailsTableHeaders extends React.Component {
}
const style = colStyles[index];
- const label =
- (style.width === NODE_DETAILS_TABLE_CW.XS && NODE_DETAILS_TABLE_XS_LABEL[header.id]) ?
- NODE_DETAILS_TABLE_XS_LABEL[header.id] : header.label;
+ const label = (
+ style.width === NODE_DETAILS_TABLE_CW.XS && NODE_DETAILS_TABLE_XS_LABEL[header.id]
+ ) ? NODE_DETAILS_TABLE_XS_LABEL[header.id] : header.label;
return (
diff --git a/client/app/scripts/components/node-details/node-details-table-node-link.js b/client/app/scripts/components/node-details/node-details-table-node-link.js
index 1c3303b9b5..3d4f3f614b 100644
--- a/client/app/scripts/components/node-details/node-details-table-node-link.js
+++ b/client/app/scripts/components/node-details/node-details-table-node-link.js
@@ -1,9 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
-import { clickRelative } from '../../actions/app-actions';
+import { clickRelative } from '../../actions/request-actions';
import { trackAnalyticsEvent } from '../../utils/tracking-utils';
-import { dismissRowClickProps } from './node-details-table-row';
+import { dismissRowClickProps } from '../../utils/dom-utils';
class NodeDetailsTableNodeLink extends React.Component {
diff --git a/client/app/scripts/components/node-details/node-details-table-node-metric-link.js b/client/app/scripts/components/node-details/node-details-table-node-metric-link.js
index 3749fce826..e097adf479 100644
--- a/client/app/scripts/components/node-details/node-details-table-node-metric-link.js
+++ b/client/app/scripts/components/node-details/node-details-table-node-metric-link.js
@@ -3,7 +3,7 @@ import React from 'react';
import CloudLink from '../cloud-link';
import { formatMetric } from '../../utils/string-utils';
import { trackAnalyticsEvent } from '../../utils/tracking-utils';
-import { dismissRowClickProps } from './node-details-table-row';
+import { dismissRowClickProps } from '../../utils/dom-utils';
class NodeDetailsTableNodeMetricLink extends React.Component {
constructor(props) {
diff --git a/client/app/scripts/components/node-details/node-details-table-row.js b/client/app/scripts/components/node-details/node-details-table-row.js
index 15553d4a9a..8e452a4d5c 100644
--- a/client/app/scripts/components/node-details/node-details-table-row.js
+++ b/client/app/scripts/components/node-details/node-details-table-row.js
@@ -54,14 +54,17 @@ function renderValues(node, columns = [], columnStyles = [], timestamp = null, t
title={title}
style={style}
key={field.id}>
- {field.dataType === 'link' ?
- {value}
- :
- value}
+ {field.dataType === 'link'
+ ? (
+
+ {value}
+
+ )
+ : value}
);
}
@@ -72,13 +75,14 @@ function renderValues(node, columns = [], columnStyles = [], timestamp = null, t
title={field.value}
style={style}
key={field.id}>
- {intersperse(field.relatives.map(relative =>
- (
(
+ )), ' ')}
+ />
+ )), ' ')}
);
}
@@ -98,19 +102,6 @@ function renderValues(node, columns = [], columnStyles = [], timestamp = null, t
});
}
-/**
- * Table row children may react to onClick events but the row
- * itself does detect a click by looking at onMouseUp. To stop
- * the bubbling of clicks on child elements we need to dismiss
- * the onMouseUp event.
- */
-export const dismissRowClickProps = {
- onMouseUp: (ev) => {
- ev.preventDefault();
- ev.stopPropagation();
- }
-};
-
export default class NodeDetailsTableRow extends React.Component {
constructor(props, context) {
super(props, context);
@@ -150,8 +141,8 @@ export default class NodeDetailsTableRow extends React.Component {
const { pageX, pageY } = ev;
const { originX, originY } = this.mouseDrag;
const movedTheMouseTooMuch = (
- Math.abs(originX - pageX) > thresholdPx ||
- Math.abs(originY - pageY) > thresholdPx
+ Math.abs(originX - pageX) > thresholdPx
+ || Math.abs(originY - pageY) > thresholdPx
);
if (movedTheMouseTooMuch && originX && originY) {
return;
@@ -182,7 +173,7 @@ export default class NodeDetailsTableRow extends React.Component {
onMouseLeave={this.onMouseLeave}
className={className}>
- {this.props.renderIdCell(Object.assign(node, {nodeId, topologyId}))}
+ {this.props.renderIdCell(Object.assign(node, { nodeId, topologyId }))}
{values}
diff --git a/client/app/scripts/components/node-details/node-details-table.js b/client/app/scripts/components/node-details/node-details-table.js
index d235541d4e..4af2e1b8dd 100644
--- a/client/app/scripts/components/node-details/node-details-table.js
+++ b/client/app/scripts/components/node-details/node-details-table.js
@@ -1,7 +1,9 @@
import React from 'react';
import classNames from 'classnames';
import { connect } from 'react-redux';
-import { find, get, union, sortBy, groupBy, concat, debounce } from 'lodash';
+import {
+ find, get, union, sortBy, groupBy, concat, debounce
+} from 'lodash';
import { NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT } from '../../constants/limits';
import { TABLE_ROW_FOCUS_DEBOUNCE_INTERVAL } from '../../constants/timer';
@@ -18,7 +20,7 @@ import {
function getDefaultSortedBy(columns, nodes) {
// default sorter specified by columns
- const defaultSortColumn = find(columns, {defaultSort: true});
+ const defaultSortColumn = find(columns, { defaultSort: true });
if (defaultSortColumn) {
return defaultSortColumn.id;
}
@@ -49,7 +51,7 @@ function getNodeValue(node, header) {
if (isIP(header)) {
// Format the IPs so that they are sorted numerically.
return ipToPaddedString(field.value);
- } else if (isNumeric(header)) {
+ } if (isNumeric(header)) {
return parseFloat(field.value);
}
return field.value;
@@ -125,7 +127,7 @@ function getSortedNodes(nodes, sortedByHeader, sortedDesc) {
// have a minimal height. That prevents auto-scroll under a focus if the
// number of table rows shrinks.
function minHeightConstraint(height = 0) {
- return ;
+ return ;
}
@@ -158,8 +160,9 @@ class NodeDetailsTable extends React.Component {
}
handleLimitClick() {
- const limit = this.state.limit ? 0 : this.props.limit;
- this.setState({ limit });
+ this.setState(prevState => ({
+ limit: prevState.limit ? 0 : this.props.limit
+ }));
}
focusRow(rowIndex, node) {
@@ -202,7 +205,7 @@ class NodeDetailsTable extends React.Component {
getColumnHeaders() {
const columns = this.props.columns || [];
- return [{id: 'label', label: this.props.label}].concat(columns);
+ return [{ id: 'label', label: this.props.label }].concat(columns);
}
componentDidMount() {
@@ -218,8 +221,8 @@ class NodeDetailsTable extends React.Component {
const sortedBy = this.state.sortedBy || getDefaultSortedBy(columns, this.props.nodes);
const sortedByHeader = this.getColumnHeaders().find(h => h.id === sortedBy);
- const sortedDesc = (this.state.sortedDesc === null) ?
- defaultSortDesc(sortedByHeader) : this.state.sortedDesc;
+ const sortedDesc = (this.state.sortedDesc === null)
+ ? defaultSortDesc(sortedByHeader) : this.state.sortedDesc;
let nodes = getSortedNodes(this.props.nodes, sortedByHeader, sortedDesc);
@@ -261,12 +264,14 @@ class NodeDetailsTable extends React.Component {
- {this.props.nodes && this.props.nodes.length > 0 && }
+ {this.props.nodes && this.props.nodes.length > 0 && (
+
+ )}
{},
+ onSortChange: () => { },
sortedBy: null,
sortedDesc: null,
};
diff --git a/client/app/scripts/components/nodes-resources/node-resources-layer.js b/client/app/scripts/components/nodes-resources/node-resources-layer.js
index 96a47f7aa5..ef21627af4 100644
--- a/client/app/scripts/components/nodes-resources/node-resources-layer.js
+++ b/client/app/scripts/components/nodes-resources/node-resources-layer.js
@@ -35,11 +35,13 @@ class NodesResourcesLayer extends React.Component {
/>
))}
- {!layoutNodes.isEmpty() && }
+ />
+ )}
);
}
diff --git a/client/app/scripts/components/nodes-resources/node-resources-metric-box-info.js b/client/app/scripts/components/nodes-resources/node-resources-metric-box-info.js
index e165401678..6596759271 100644
--- a/client/app/scripts/components/nodes-resources/node-resources-metric-box-info.js
+++ b/client/app/scripts/components/nodes-resources/node-resources-metric-box-info.js
@@ -13,11 +13,21 @@ export default class NodeResourcesMetricBoxInfo extends React.Component {
{showExtendedInfo ? humanizedRelativeConsumption : humanizedAbsoluteConsumption}
- used
- {showExtendedInfo &&
+
+ {' '}
+used
+ {showExtendedInfo
+ && (
- {' - '}({humanizedAbsoluteConsumption} / {humanizedTotalCapacity} )
+ {' - '}
+(
+ {humanizedAbsoluteConsumption}
+ {' '}
+/
+ {humanizedTotalCapacity}
+)
+ )
}
);
diff --git a/client/app/scripts/components/nodes-resources/node-resources-metric-box.js b/client/app/scripts/components/nodes-resources/node-resources-metric-box.js
index 161257e1bf..073afaee26 100644
--- a/client/app/scripts/components/nodes-resources/node-resources-metric-box.js
+++ b/client/app/scripts/components/nodes-resources/node-resources-metric-box.js
@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import theme from 'weaveworks-ui-components/lib/theme';
import NodeResourcesMetricBoxInfo from './node-resources-metric-box-info';
-import { clickNode } from '../../actions/app-actions';
+import { clickNode } from '../../actions/request-actions';
import { trackAnalyticsEvent } from '../../utils/tracking-utils';
import { applyTransform } from '../../utils/transform-utils';
import { RESOURCE_VIEW_MODE } from '../../constants/naming';
@@ -112,9 +112,9 @@ class NodeResourcesMetricBox extends React.Component {
// TODO: Show `+ 31 nodes` kind of tag in their stead.
if (!showNode) return null;
- const resourceUsageTooltipInfo = showCapacity ?
- metricSummary.get('humanizedRelativeConsumption') :
- metricSummary.get('humanizedAbsoluteConsumption');
+ const resourceUsageTooltipInfo = showCapacity
+ ? metricSummary.get('humanizedRelativeConsumption')
+ : metricSummary.get('humanizedAbsoluteConsumption');
return (
- {label} - {type} usage at {resourceUsageTooltipInfo}
- {showCapacity &&
+ {label}
+ {' '}
+-
+ {' '}
+ {type}
+ {' '}
+usage at
+ {' '}
+ {resourceUsageTooltipInfo}
+
+ {showCapacity && (
+ }
+ />
+ )}
- {showInfo && }
+ />
+ )}
);
}
diff --git a/client/app/scripts/components/nodes.js b/client/app/scripts/components/nodes.js
index e7ec56c52a..cfd61ec7ca 100644
--- a/client/app/scripts/components/nodes.js
+++ b/client/app/scripts/components/nodes.js
@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import NodesChart from '../charts/nodes-chart';
import NodesGrid from '../charts/nodes-grid';
-import NodesResources from '../components/nodes-resources';
+import NodesResources from './nodes-resources';
import NodesError from '../charts/nodes-error';
import DelayedShow from '../utils/delayed-show';
import { Loading, getNodeType } from './loading';
@@ -44,9 +44,9 @@ class Nodes extends React.Component {
return (
Nothing to show. This can have any of these reasons:
- {topologyNodeCountZero ?
- renderCauses(NODES_STATS_COUNT_ZERO_CAUSES) :
- renderCauses(NODES_NOT_DISPLAYED_CAUSES)}
+ {topologyNodeCountZero
+ ? renderCauses(NODES_STATS_COUNT_ZERO_CAUSES)
+ : renderCauses(NODES_NOT_DISPLAYED_CAUSES)}
);
}
diff --git a/client/app/scripts/components/plugins.js b/client/app/scripts/components/plugins.js
index 7537553497..3a56f9f50e 100644
--- a/client/app/scripts/components/plugins.js
+++ b/client/app/scripts/components/plugins.js
@@ -10,7 +10,16 @@ const Plugin = ({
}) => {
const error = status !== 'ok';
const className = classNames({ error });
- const tip = (Description: {description} Status: {status} );
+ const tip = (
+
+Description:
+ {description}
+
+Status:
+ {' '}
+ {status}
+
+ );
// Inner span to hold styling so we don't effect the "before:content"
return (
diff --git a/client/app/scripts/components/search.js b/client/app/scripts/components/search.js
index 193da88019..bd0b091521 100644
--- a/client/app/scripts/components/search.js
+++ b/client/app/scripts/components/search.js
@@ -4,7 +4,12 @@ import { isEmpty } from 'lodash';
import { Search } from 'weaveworks-ui-components';
import styled from 'styled-components';
-import { blurSearch, focusSearch, updateSearch, toggleHelp } from '../actions/app-actions';
+import {
+ blurSearch, updateSearch, toggleHelp
+} from '../actions/app-actions';
+import {
+ focusSearch
+} from '../actions/request-actions';
import { searchMatchCountByTopologySelector } from '../selectors/search';
import { isResourceViewModeSelector } from '../selectors/topology';
import { slugify } from '../utils/string-utils';
@@ -107,7 +112,9 @@ class SearchComponent extends React.Component {
onBlur={this.props.blurSearch}
/>
- {searchHint}
diff --git a/client/app/scripts/components/show-more.js b/client/app/scripts/components/show-more.js
index 15868ef625..3744dabe60 100644
--- a/client/app/scripts/components/show-more.js
+++ b/client/app/scripts/components/show-more.js
@@ -24,7 +24,9 @@ export default class ShowMore extends React.PureComponent {
}
return (
- {limitActionText}
+ {limitActionText}
+ {' '}
+
);
}
diff --git a/client/app/scripts/components/sparkline.js b/client/app/scripts/components/sparkline.js
index 9ac381a61c..e5b814fbdc 100644
--- a/client/app/scripts/components/sparkline.js
+++ b/client/app/scripts/components/sparkline.js
@@ -72,8 +72,8 @@ export default class Sparkline extends React.Component {
const min = formatMetricSvg(d3Min(data, d => d.value), this.props);
const max = formatMetricSvg(d3Max(data, d => d.value), this.props);
const mean = formatMetricSvg(d3Mean(data, d => d.value), this.props);
- const title = `Last ${Math.round((lastDate - firstDate) / 1000)} seconds, ` +
- `${data.length} samples, min: ${min}, max: ${max}, mean: ${mean}`;
+ const title = `Last ${Math.round((lastDate - firstDate) / 1000)} seconds, `
+ + `${data.length} samples, min: ${min}, max: ${max}, mean: ${mean}`;
return {
data, lastX, lastY, title
@@ -122,7 +122,8 @@ export default class Sparkline extends React.Component {
strokeDasharray={strokeDasharray}
d={this.line(graph.data)}
/>
- {hasData && }
+ />
+ )}
);
diff --git a/client/app/scripts/components/terminal-app.js b/client/app/scripts/components/terminal-app.js
index 98484f6ef6..d8819aa5ff 100644
--- a/client/app/scripts/components/terminal-app.js
+++ b/client/app/scripts/components/terminal-app.js
@@ -51,12 +51,14 @@ class TerminalApp extends React.Component {
return (
- {this.props.controlPipe && }
+ embedded={false} />
+ )}
);
}
diff --git a/client/app/scripts/components/terminal.js b/client/app/scripts/components/terminal.js
index 5e8d874d90..5dda94fd7d 100644
--- a/client/app/scripts/components/terminal.js
+++ b/client/app/scripts/components/terminal.js
@@ -8,9 +8,12 @@ import { Terminal as Term } from 'xterm';
import * as fit from 'xterm/lib/addons/fit/fit';
import { closeTerminal } from '../actions/app-actions';
+import { getPipeStatus } from '../actions/request-actions';
import { getNeutralColor } from '../utils/color-utils';
import { setDocumentTitle } from '../utils/title-utils';
-import { getPipeStatus, deletePipe, doResizeTty, getWebsocketUrl, basePath } from '../utils/web-api-utils';
+import {
+ deletePipe, doResizeTty, getWebsocketUrl, basePath
+} from '../utils/web-api-utils';
const log = debug('scope:terminal');
@@ -327,6 +330,7 @@ class Terminal extends React.Component {
);
}
+
getControlStatusIcon() {
const icon = this.props.controlStatus && this.props.controlStatus.get('control').icon;
return (
@@ -339,8 +343,7 @@ class Terminal extends React.Component {
}
function mapStateToProps(state, ownProps) {
- const controlStatus = state.get('controlPipes').find(pipe =>
- pipe.get('nodeId') === ownProps.pipe.get('nodeId'));
+ const controlStatus = state.get('controlPipes').find(pipe => pipe.get('nodeId') === ownProps.pipe.get('nodeId'));
return { controlStatus };
}
diff --git a/client/app/scripts/components/time-control.js b/client/app/scripts/components/time-control.js
index 59593447ea..b868599bf6 100644
--- a/client/app/scripts/components/time-control.js
+++ b/client/app/scripts/components/time-control.js
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { TimestampTag } from 'weaveworks-ui-components';
import { trackAnalyticsEvent } from '../utils/tracking-utils';
-import { pauseTimeAtNow, resumeTime } from '../actions/app-actions';
+import { pauseTimeAtNow, resumeTime } from '../actions/request-actions';
import { isPausedSelector, timeTravelSupportedSelector } from '../selectors/time-travel';
@@ -64,10 +64,14 @@ class TimeControl extends React.Component {
- {isPaused &&
+ {isPaused
+ && (
- Showing state from
+ Showing state from
+ {' '}
+
+ )
}
);
diff --git a/client/app/scripts/components/time-travel-wrapper.js b/client/app/scripts/components/time-travel-wrapper.js
index ee749ece49..ba3c1fe4a4 100644
--- a/client/app/scripts/components/time-travel-wrapper.js
+++ b/client/app/scripts/components/time-travel-wrapper.js
@@ -2,7 +2,7 @@ import React from 'react';
import { connect } from 'react-redux';
import { TimeTravel } from 'weaveworks-ui-components';
-import { jumpToTime, resumeTime, pauseTimeAtNow } from '../actions/app-actions';
+import { jumpToTime, resumeTime, pauseTimeAtNow } from '../actions/request-actions';
class TimeTravelWrapper extends React.Component {
handleLiveModeChange = (showingLive) => {
diff --git a/client/app/scripts/components/topologies.js b/client/app/scripts/components/topologies.js
index aa74286c39..617efe2574 100644
--- a/client/app/scripts/components/topologies.js
+++ b/client/app/scripts/components/topologies.js
@@ -5,7 +5,7 @@ import classnames from 'classnames';
import { trackAnalyticsEvent } from '../utils/tracking-utils';
import { searchMatchCountByTopologySelector } from '../selectors/search';
import { isResourceViewModeSelector } from '../selectors/topology';
-import { clickTopology } from '../actions/app-actions';
+import { clickTopology } from '../actions/request-actions';
function basicTopologyInfo(topology, searchMatchCount) {
diff --git a/client/app/scripts/components/topology-options.js b/client/app/scripts/components/topology-options.js
index 29896023b3..bbc0c5f435 100644
--- a/client/app/scripts/components/topology-options.js
+++ b/client/app/scripts/components/topology-options.js
@@ -7,7 +7,7 @@ import { trackAnalyticsEvent } from '../utils/tracking-utils';
import { getCurrentTopologyOptions } from '../utils/topology-utils';
import { activeTopologyOptionsSelector } from '../selectors/topology';
import TopologyOptionAction from './topology-option-action';
-import { changeTopologyOption } from '../actions/app-actions';
+import { changeTopologyOption } from '../actions/request-actions';
class TopologyOptions extends React.Component {
constructor(props, context) {
@@ -101,7 +101,8 @@ class TopologyOptions extends React.Component {
return (
- {option.get('selectType') === 'union' &&
+ {option.get('selectType') === 'union'
+ && (
+ )
}
{option.get('options').map(item => (
Save raw data as JSON
- {pausedAt && ({pausedAt}) }
+ {pausedAt && (
+
+ {' '}
+ (
+ {pausedAt}
+ )
+
+ )}
({
+ expanded: !prevState.expanded
+ }));
}
render() {
diff --git a/client/app/scripts/components/zoom-control.js b/client/app/scripts/components/zoom-control.js
index a39204586e..36bdfa08de 100644
--- a/client/app/scripts/components/zoom-control.js
+++ b/client/app/scripts/components/zoom-control.js
@@ -56,11 +56,11 @@ export default class ZoomControl extends React.Component {
return (
-
+
-
+
diff --git a/client/app/scripts/components/zoomable-canvas.js b/client/app/scripts/components/zoomable-canvas.js
index f9801ae2b4..51b51c73e8 100644
--- a/client/app/scripts/components/zoomable-canvas.js
+++ b/client/app/scripts/components/zoomable-canvas.js
@@ -8,8 +8,8 @@ import { drag } from 'd3-drag';
import { event as d3Event, select } from 'd3-selection';
import { zoomFactor } from 'weaveworks-ui-components/lib/utils/zooming';
-import Logo from '../components/logo';
-import ZoomControl from '../components/zoom-control';
+import Logo from './logo';
+import ZoomControl from './zoom-control';
import { cacheZoomState } from '../actions/app-actions';
import { applyTransform, inverseTransform } from '../utils/transform-utils';
import { activeTopologyZoomCacheKeyPathSelector } from '../selectors/zooming';
@@ -116,12 +116,14 @@ class ZoomableCanvas extends React.Component {
{this.props.children(this.state)}
- {this.canChangeZoom() && }
+ />
+ )}
);
}
diff --git a/client/app/scripts/decorators/node.js b/client/app/scripts/decorators/node.js
index 2126605c05..ecafa27be5 100644
--- a/client/app/scripts/decorators/node.js
+++ b/client/app/scripts/decorators/node.js
@@ -14,9 +14,9 @@ export function nodeResourceViewColorDecorator(node) {
// Decorates the resource node with dimensions taken from its metric summary.
export function nodeResourceBoxDecorator(node) {
const metricSummary = node.get('metricSummary', makeMap());
- const width = metricSummary.get('showCapacity') ?
- metricSummary.get('totalCapacity') :
- metricSummary.get('absoluteConsumption');
+ const width = metricSummary.get('showCapacity')
+ ? metricSummary.get('totalCapacity')
+ : metricSummary.get('absoluteConsumption');
const height = RESOURCES_LAYER_HEIGHT;
return node.merge(makeMap({ height, width }));
diff --git a/client/app/scripts/reducers/root.js b/client/app/scripts/reducers/root.js
index 9ebebae6f4..2cfd47d47b 100644
--- a/client/app/scripts/reducers/root.js
+++ b/client/app/scripts/reducers/root.js
@@ -1,7 +1,9 @@
/* 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 {
+ size, each, includes, isEqual
+} from 'lodash';
import {
fromJS,
is as isDeepEqual,
diff --git a/client/app/scripts/router.js b/client/app/scripts/router.js
new file mode 100644
index 0000000000..2b20190cc7
--- /dev/null
+++ b/client/app/scripts/router.js
@@ -0,0 +1,64 @@
+import page from 'page';
+import stableStringify from 'json-stable-stringify';
+import { each } from 'lodash';
+
+import { route } from './actions/request-actions';
+import { storageGet, storageSet } from './utils/storage-utils';
+import {
+ decodeURL, encodeURL, isStoreViewStateEnabled, STORAGE_STATE_KEY
+} from './utils/router-utils';
+
+// Temporarily detect old topology options to avoid breaking things between releases
+// Related to https://github.com/weaveworks/scope/pull/2404
+function detectOldOptions(topologyOptions) {
+ let bad = false;
+ each(topologyOptions, (topology) => {
+ each(topology, (option) => {
+ if (typeof option === 'string') {
+ bad = true;
+ }
+ });
+ });
+ return bad;
+}
+
+export function getRouter(initialState) {
+ return (dispatch, getState) => {
+ // strip any trailing '/'s.
+ page.base(window.location.pathname.replace(/\/$/, ''));
+
+ page('/', () => {
+ // recover from storage state on empty URL
+ const storageState = storageGet(STORAGE_STATE_KEY);
+ if (storageState && isStoreViewStateEnabled(getState())) {
+ const parsedState = JSON.parse(decodeURL(storageState));
+ const dirtyOptions = detectOldOptions(parsedState.topologyOptions);
+ if (dirtyOptions) {
+ dispatch(route(initialState));
+ } else {
+ const mergedState = Object.assign(initialState, parsedState);
+ // push storage state to URL
+ window.location.hash = `!/state/${stableStringify(mergedState)}`;
+ dispatch(route(mergedState));
+ }
+ } else {
+ dispatch(route(initialState));
+ }
+ });
+
+ page('/state/:state', (ctx) => {
+ const state = JSON.parse(decodeURL(ctx.params.state));
+ const dirtyOptions = detectOldOptions(state.topologyOptions);
+ const nextState = dirtyOptions ? initialState : state;
+
+ // back up state in storage and redirect
+ if (isStoreViewStateEnabled(getState())) {
+ storageSet(STORAGE_STATE_KEY, encodeURL(stableStringify(state)));
+ }
+
+ dispatch(route(nextState));
+ });
+
+ return page;
+ };
+}
diff --git a/client/app/scripts/selectors/node-metric.js b/client/app/scripts/selectors/node-metric.js
index 4058534fd1..d788d99d4f 100644
--- a/client/app/scripts/selectors/node-metric.js
+++ b/client/app/scripts/selectors/node-metric.js
@@ -3,7 +3,7 @@ import { createMapSelector, createListSelector } from 'reselect-map';
import { fromJS, Map as makeMap, List as makeList } from 'immutable';
import { modulo } from '../utils/math-utils';
-import { isGraphViewModeSelector, isResourceViewModeSelector } from '../selectors/topology';
+import { isGraphViewModeSelector, isResourceViewModeSelector } from './topology';
import { RESOURCE_VIEW_METRICS } from '../constants/resources';
@@ -93,8 +93,7 @@ const selectedMetricIdSelector = createSelector(
availableMetricsSelector,
selectedMetricTypeSelector,
],
- (availableMetrics, metricType) =>
- (availableMetrics.find(m => m.get('label') === metricType) || makeMap()).get('id')
+ (availableMetrics, metricType) => (availableMetrics.find(m => m.get('label') === metricType) || makeMap()).get('id')
);
const topCardNodeSelector = createSelector(
diff --git a/client/app/scripts/selectors/resource-view/layout.js b/client/app/scripts/selectors/resource-view/layout.js
index 44d41783b0..c2f6b42cd0 100644
--- a/client/app/scripts/selectors/resource-view/layout.js
+++ b/client/app/scripts/selectors/resource-view/layout.js
@@ -22,9 +22,9 @@ const log = debug('scope:nodes-layout');
// Used for ordering the resource nodes.
const resourceNodeConsumptionComparator = (node) => {
const metricSummary = node.get('metricSummary');
- return metricSummary.get('showCapacity') ?
- -metricSummary.get('relativeConsumption') :
- -metricSummary.get('absoluteConsumption');
+ return metricSummary.get('showCapacity')
+ ? -metricSummary.get('relativeConsumption')
+ : -metricSummary.get('absoluteConsumption');
};
// A list of topologies shown in the resource view of the active topology (bottom to top).
@@ -85,8 +85,10 @@ const decoratedNodesByTopologySelector = createSelector(
const isBaseLayer = (index === 0);
const nodeParentDecorator = nodeParentDecoratorByTopologyId(parentLayerTopologyId);
- const nodeMetricSummaryDecorator =
- nodeMetricSummaryDecoratorByType(pinnedMetricType, showCapacity);
+ const nodeMetricSummaryDecorator = nodeMetricSummaryDecoratorByType(
+ pinnedMetricType,
+ showCapacity
+ );
// Color the node, deduce its anchor point, dimensions and info about its pinned metric.
const decoratedTopologyNodes = (topologyNodes || makeMap())
@@ -158,8 +160,8 @@ export const layoutNodesByTopologyIdSelector = createSelector(
// We fix it by shrinking all the children to by a factor to perfectly fit into the parent.
if (totalChildrenWidth > parentWidth) {
const shrinkFactor = parentWidth / totalChildrenWidth;
- log(`Inconsistent data: Children of ${parentNodeId} reported to use more ` +
- `resource than the node itself - shrinking by factor ${shrinkFactor}`);
+ log(`Inconsistent data: Children of ${parentNodeId} reported to use more `
+ + `resource than the node itself - shrinking by factor ${shrinkFactor}`);
// Shrink all the children.
nodesBucket.forEach((_, nodeId) => {
let node = positionedNodes.get(nodeId);
diff --git a/client/app/scripts/selectors/search.js b/client/app/scripts/selectors/search.js
index 00b16b1bbf..7e0194c663 100644
--- a/client/app/scripts/selectors/search.js
+++ b/client/app/scripts/selectors/search.js
@@ -2,7 +2,9 @@ import { createSelector } from 'reselect';
import { createMapSelector } from 'reselect-map';
import { Map as makeMap } from 'immutable';
-import { parseQuery, searchNode, searchTopology, getSearchableFields } from '../utils/search-utils';
+import {
+ parseQuery, searchNode, searchTopology, getSearchableFields
+} from '../utils/search-utils';
const parsedSearchQuerySelector = createSelector(
diff --git a/client/app/scripts/utils/__tests__/web-api-utils-test.js b/client/app/scripts/utils/__tests__/web-api-utils-test.js
index da32769c4d..1b78c1fc88 100644
--- a/client/app/scripts/utils/__tests__/web-api-utils-test.js
+++ b/client/app/scripts/utils/__tests__/web-api-utils-test.js
@@ -1,6 +1,8 @@
import { Map as makeMap, OrderedMap as makeOrderedMap } from 'immutable';
-import { buildUrlQuery, basePath, getApiPath, getWebsocketUrl } from '../web-api-utils';
+import {
+ buildUrlQuery, basePath, getApiPath, getWebsocketUrl
+} from '../web-api-utils';
describe('WebApiUtils', () => {
diff --git a/client/app/scripts/utils/array-utils.js b/client/app/scripts/utils/array-utils.js
index 4088b82a33..dfcfeef7bf 100644
--- a/client/app/scripts/utils/array-utils.js
+++ b/client/app/scripts/utils/array-utils.js
@@ -7,8 +7,8 @@ export function uniformSelect(array, size) {
return array;
}
- return range(size).map(index =>
- array[parseInt(index * (array.length / (size - (1 - 1e-9))), 10)]);
+ return range(size)
+ .map(index => array[parseInt(index * (array.length / (size - (1 - 1e-9))), 10)]);
}
export function insertElement(array, index, element) {
diff --git a/client/app/scripts/utils/dom-utils.js b/client/app/scripts/utils/dom-utils.js
index b993a51361..1ab2bcf367 100644
--- a/client/app/scripts/utils/dom-utils.js
+++ b/client/app/scripts/utils/dom-utils.js
@@ -14,3 +14,16 @@ export function encodeIdAttribute(id) {
export function decodeIdAttribute(id) {
return id.replace(/__u(\d+)__/gm, (m, d) => String.fromCharCode(d));
}
+
+/**
+ * Table row children may react to onClick events but the row
+ * itself does detect a click by looking at onMouseUp. To stop
+ * the bubbling of clicks on child elements we need to dismiss
+ * the onMouseUp event.
+ */
+export const dismissRowClickProps = {
+ onMouseUp: (ev) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+};
diff --git a/client/app/scripts/utils/hash-utils.js b/client/app/scripts/utils/hash-utils.js
index 31cbbb2166..76c358d386 100644
--- a/client/app/scripts/utils/hash-utils.js
+++ b/client/app/scripts/utils/hash-utils.js
@@ -1,4 +1,6 @@
-import { isPlainObject, mapValues, isEmpty, omitBy } from 'lodash';
+import {
+ isPlainObject, mapValues, isEmpty, omitBy
+} from 'lodash';
export function hashDifferenceDeep(A, B) {
diff --git a/client/app/scripts/utils/metric-utils.js b/client/app/scripts/utils/metric-utils.js
index 8bdad12c2c..d7731f46c5 100644
--- a/client/app/scripts/utils/metric-utils.js
+++ b/client/app/scripts/utils/metric-utils.js
@@ -57,12 +57,12 @@ export function getMetricColor(metric) {
: metric && metric.get('id');
if (/mem/.test(metricId)) {
return 'steelBlue';
- } else if (/cpu/.test(metricId)) {
+ } if (/cpu/.test(metricId)) {
return colors('cpu').toString();
- } else if (/files/.test(metricId)) {
+ } if (/files/.test(metricId)) {
// purple
return '#9467bd';
- } else if (/load/.test(metricId)) {
+ } if (/load/.test(metricId)) {
return colors('load').toString();
}
return 'steelBlue';
diff --git a/client/app/scripts/utils/router-utils.js b/client/app/scripts/utils/router-utils.js
index 58f376ffae..aa339ed521 100644
--- a/client/app/scripts/utils/router-utils.js
+++ b/client/app/scripts/utils/router-utils.js
@@ -1,11 +1,10 @@
import page from 'page';
import stableStringify from 'json-stable-stringify';
import { fromJS, is as isDeepEqual } from 'immutable';
-import { each, omit, omitBy, isEmpty } from 'lodash';
+import { omit, omitBy, isEmpty } from 'lodash';
-import { route } from '../actions/app-actions';
import { hashDifferenceDeep } from './hash-utils';
-import { storageGet, storageSet } from './storage-utils';
+import { storageSet } from './storage-utils';
import { getDefaultTopologyOptions, initialState as initialRootState } from '../reducers/root';
@@ -17,9 +16,9 @@ const SLASH = '/';
const SLASH_REPLACEMENT = '';
const PERCENT = '%';
const PERCENT_REPLACEMENT = '';
-const STORAGE_STATE_KEY = 'scopeViewState';
+export const STORAGE_STATE_KEY = 'scopeViewState';
-function encodeURL(url) {
+export function encodeURL(url) {
return url
.replace(new RegExp(PERCENT, 'g'), PERCENT_REPLACEMENT)
.replace(new RegExp(SLASH, 'g'), SLASH_REPLACEMENT);
@@ -41,7 +40,7 @@ export function clearStoredViewState() {
storageSet(STORAGE_STATE_KEY, '');
}
-function isStoreViewStateEnabled(state) {
+export function isStoreViewStateEnabled(state) {
return state.get('storeViewState');
}
@@ -135,59 +134,3 @@ export function updateRoute(getState) {
page.show(`/state/${stateUrl}`, state, dispatch);
}
}
-
-// Temporarily detect old topology options to avoid breaking things between releases
-// Related to https://github.com/weaveworks/scope/pull/2404
-function detectOldOptions(topologyOptions) {
- let bad = false;
- each(topologyOptions, (topology) => {
- each(topology, (option) => {
- if (typeof option === 'string') {
- bad = true;
- }
- });
- });
- return bad;
-}
-
-
-export function getRouter(initialState) {
- return (dispatch, getState) => {
- // strip any trailing '/'s.
- page.base(window.location.pathname.replace(/\/$/, ''));
-
- page('/', () => {
- // recover from storage state on empty URL
- const storageState = storageGet(STORAGE_STATE_KEY);
- if (storageState && isStoreViewStateEnabled(getState())) {
- const parsedState = JSON.parse(decodeURL(storageState));
- const dirtyOptions = detectOldOptions(parsedState.topologyOptions);
- if (dirtyOptions) {
- dispatch(route(initialState));
- } else {
- const mergedState = Object.assign(initialState, parsedState);
- // push storage state to URL
- window.location.hash = `!/state/${stableStringify(mergedState)}`;
- dispatch(route(mergedState));
- }
- } else {
- dispatch(route(initialState));
- }
- });
-
- page('/state/:state', (ctx) => {
- const state = JSON.parse(decodeURL(ctx.params.state));
- const dirtyOptions = detectOldOptions(state.topologyOptions);
- const nextState = dirtyOptions ? initialState : state;
-
- // back up state in storage and redirect
- if (isStoreViewStateEnabled(getState())) {
- storageSet(STORAGE_STATE_KEY, encodeURL(stableStringify(state)));
- }
-
- dispatch(route(nextState));
- });
-
- return page;
- };
-}
diff --git a/client/app/scripts/utils/topology-utils.js b/client/app/scripts/utils/topology-utils.js
index 932f4f967a..2c6991164f 100644
--- a/client/app/scripts/utils/topology-utils.js
+++ b/client/app/scripts/utils/topology-utils.js
@@ -128,9 +128,9 @@ export function setTopologyUrlsById(topologyUrlsById, topologies) {
export function filterHiddenTopologies(topologies, currentTopology) {
currentTopology = currentTopology || makeMap();
- return topologies.filter(t => (!t.hide_if_empty || t.stats.node_count > 0 ||
- t.stats.filtered_nodes > 0 || t.id === currentTopology.get('id') ||
- t.id === currentTopology.get('parentId')));
+ return topologies.filter(t => (!t.hide_if_empty || t.stats.node_count > 0
+ || t.stats.filtered_nodes > 0 || t.id === currentTopology.get('id')
+ || t.id === currentTopology.get('parentId')));
}
export function getCurrentTopologyOptions(state) {
diff --git a/client/app/scripts/utils/web-api-utils.js b/client/app/scripts/utils/web-api-utils.js
index dfbb2096d1..7a2b576345 100644
--- a/client/app/scripts/utils/web-api-utils.js
+++ b/client/app/scripts/utils/web-api-utils.js
@@ -4,25 +4,18 @@ import { defaults } from 'lodash';
import { Map as makeMap, List } from 'immutable';
import {
- blurSearch, clearControlError, closeWebsocket, openWebsocket, receiveError,
- receiveApiDetails, receiveNodesDelta, receiveNodeDetails, receiveControlError,
- receiveControlNodeRemoved, receiveControlPipe, receiveControlPipeStatus,
- receiveControlSuccess, receiveTopologies, receiveNotFound,
- receiveNodesForTopology, receiveNodes,
+ receiveError,
+ receiveNodeDetails,
+ receiveNotFound, receiveNodesForTopology, receiveNodes,
} from '../actions/app-actions';
-import { getCurrentTopologyUrl } from '../utils/topology-utils';
+import { getCurrentTopologyUrl } from './topology-utils';
import { layersTopologyIdsSelector } from '../selectors/resource-view/layout';
import { activeTopologyOptionsSelector } from '../selectors/topology';
-import { isPausedSelector } from '../selectors/time-travel';
-
-import { API_REFRESH_INTERVAL, TOPOLOGY_REFRESH_INTERVAL } from '../constants/timer';
const log = debug('scope:web-api-utils');
-const reconnectTimerInterval = 5000;
const updateFrequency = '5s';
-const FIRST_RENDER_TOO_LONG_THRESHOLD = 100; // ms
const csrfToken = (() => {
// Check for token at window level or parent level (for iframe);
/* eslint-disable no-underscore-dangle */
@@ -38,16 +31,6 @@ const csrfToken = (() => {
return token;
})();
-let socket;
-let reconnectTimer = 0;
-let topologyTimer = 0;
-let apiDetailsTimer = 0;
-let controlErrorTimer = 0;
-let currentUrl = null;
-let createWebsocketAt = null;
-let firstMessageOnWebsocketAt = null;
-let continuePolling = true;
-
export function buildUrlQuery(params = makeMap(), state = null) {
// Attach the time travel timestamp to every request to the backend.
if (state) {
@@ -105,7 +88,7 @@ export function getReportUrl(timestamp) {
return `${getApiPath()}/api/report?${buildUrlQuery(makeMap({ timestamp }))}`;
}
-function topologiesUrl(state) {
+export function topologiesUrl(state) {
const activeTopologyOptions = activeTopologyOptionsSelector(state);
const optionsQuery = buildUrlQuery(activeTopologyOptions, state);
return `${getApiPath()}/api/topology?${optionsQuery}`;
@@ -116,73 +99,17 @@ export function getWebsocketUrl(host = window.location.host, pathname = window.l
return `${wsProto}://${host}${getApiPath(pathname)}`;
}
-function buildWebsocketUrl(topologyUrl, topologyOptions = makeMap(), state) {
+export function buildWebsocketUrl(topologyUrl, topologyOptions = makeMap(), state) {
topologyOptions = topologyOptions.set('t', updateFrequency);
const optionsQuery = buildUrlQuery(topologyOptions, state);
return `${getWebsocketUrl()}${topologyUrl}/ws?${optionsQuery}`;
}
-function createWebsocket(websocketUrl, getState, dispatch) {
- if (socket) {
- socket.onclose = null;
- socket.onerror = null;
- socket.close();
- // onclose() is not called, but that's fine since we're opening a new one
- // right away
- }
-
- // profiling
- createWebsocketAt = new Date();
- firstMessageOnWebsocketAt = null;
-
- socket = new WebSocket(websocketUrl);
-
- socket.onopen = () => {
- log(`Opening websocket to ${websocketUrl}`);
- dispatch(openWebsocket());
- };
-
- socket.onclose = () => {
- clearTimeout(reconnectTimer);
- log(`Closing websocket to ${websocketUrl}`, socket.readyState);
- socket = null;
- dispatch(closeWebsocket());
-
- if (continuePolling && !isPausedSelector(getState())) {
- reconnectTimer = setTimeout(() => {
- createWebsocket(websocketUrl, getState, dispatch);
- }, reconnectTimerInterval);
- }
- };
-
- socket.onerror = () => {
- log(`Error in websocket to ${websocketUrl}`);
- dispatch(receiveError(websocketUrl));
- };
-
- socket.onmessage = (event) => {
- const msg = JSON.parse(event.data);
- dispatch(receiveNodesDelta(msg));
-
- // profiling (receiveNodesDelta triggers synchronous render)
- if (!firstMessageOnWebsocketAt) {
- firstMessageOnWebsocketAt = new Date();
- const timeToFirstMessage = firstMessageOnWebsocketAt - createWebsocketAt;
- if (timeToFirstMessage > FIRST_RENDER_TOO_LONG_THRESHOLD) {
- log(
- 'Time (ms) to first nodes render after websocket was created',
- firstMessageOnWebsocketAt - createWebsocketAt
- );
- }
- }
- };
-}
-
/**
* XHR wrapper. Applies a CSRF token (if it exists) and content-type to all requests.
* Any opts that get passed in will override the defaults.
*/
-function doRequest(opts) {
+export function doRequest(opts) {
const config = defaults(opts, {
contentType: 'application/json',
type: 'json'
@@ -212,7 +139,7 @@ function getNodesForTopologies(state, dispatch, topologyIds, topologyOptions = m
);
}
-function getNodesOnce(getState, dispatch) {
+export function getNodesOnce(getState, dispatch) {
const state = getState();
const topologyUrl = getCurrentTopologyUrl(state);
const topologyOptions = activeTopologyOptionsSelector(state);
@@ -248,63 +175,6 @@ export function getResourceViewNodesSnapshot(state, dispatch) {
getNodesForTopologies(state, dispatch, topologyIds);
}
-function pollTopologies(getState, dispatch, initialPoll = false) {
- // Used to resume polling when navigating between pages in Weave Cloud.
- continuePolling = initialPoll === true ? true : continuePolling;
- clearTimeout(topologyTimer);
- // NOTE: getState is called every time to make sure the up-to-date state is used.
- const url = topologiesUrl(getState());
- doRequest({
- error: (req) => {
- log(`Error in topology request: ${req.responseText}`);
- dispatch(receiveError(url));
- // Only retry in stand-alone mode
- if (continuePolling && !isPausedSelector(getState())) {
- topologyTimer = setTimeout(() => {
- pollTopologies(getState, dispatch);
- }, TOPOLOGY_REFRESH_INTERVAL);
- }
- },
- success: (res) => {
- if (continuePolling && !isPausedSelector(getState())) {
- dispatch(receiveTopologies(res));
- topologyTimer = setTimeout(() => {
- pollTopologies(getState, dispatch);
- }, TOPOLOGY_REFRESH_INTERVAL);
- }
- },
- url
- });
-}
-
-function getTopologiesOnce(getState, dispatch) {
- const url = topologiesUrl(getState());
- doRequest({
- error: (req) => {
- log(`Error in topology request: ${req.responseText}`);
- dispatch(receiveError(url));
- },
- success: (res) => {
- dispatch(receiveTopologies(res));
- },
- url
- });
-}
-
-function updateWebsocketChannel(getState, dispatch, forceRequest) {
- const topologyUrl = getCurrentTopologyUrl(getState());
- const topologyOptions = activeTopologyOptionsSelector(getState());
- const websocketUrl = buildWebsocketUrl(topologyUrl, topologyOptions, getState());
- // 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
- // and no socket exists, or if we get a new url.
- if (topologyUrl && (!socket || isNewUrl || forceRequest)) {
- createWebsocket(websocketUrl, getState, dispatch);
- currentUrl = websocketUrl;
- }
-}
-
export function getNodeDetails(getState, dispatch) {
const state = getState();
const nodeMap = state.get('nodeDetails');
@@ -351,91 +221,12 @@ export function getNodeDetails(getState, dispatch) {
}
}
-export function getTopologies(getState, dispatch, forceRequest) {
- if (isPausedSelector(getState())) {
- getTopologiesOnce(getState, dispatch);
- } else {
- pollTopologies(getState, dispatch, forceRequest);
- }
-}
-
-export function getNodes(getState, dispatch, forceRequest = false) {
- if (isPausedSelector(getState())) {
- getNodesOnce(getState, dispatch);
- } else {
- updateWebsocketChannel(getState, dispatch, forceRequest);
- }
- getNodeDetails(getState, dispatch);
-}
-
-export function getApiDetails(dispatch) {
- clearTimeout(apiDetailsTimer);
- const url = `${getApiPath()}/api`;
- doRequest({
- error: (req) => {
- log(`Error in api details request: ${req.responseText}`);
- receiveError(url);
- if (continuePolling) {
- apiDetailsTimer = setTimeout(() => {
- getApiDetails(dispatch);
- }, API_REFRESH_INTERVAL / 2);
- }
- },
- success: (res) => {
- dispatch(receiveApiDetails(res));
- if (continuePolling) {
- apiDetailsTimer = setTimeout(() => {
- getApiDetails(dispatch);
- }, API_REFRESH_INTERVAL);
- }
- },
- url
- });
-}
-
-export function doControlRequest(nodeId, control, dispatch) {
- clearTimeout(controlErrorTimer);
- const url = `${getApiPath()}/api/control/${encodeURIComponent(control.probeId)}/`
- + `${encodeURIComponent(control.nodeId)}/${control.id}`;
- doRequest({
- error: (err) => {
- dispatch(receiveControlError(nodeId, err.response));
- controlErrorTimer = setTimeout(() => {
- dispatch(clearControlError(nodeId));
- }, 10000);
- },
- method: 'POST',
- success: (res) => {
- dispatch(receiveControlSuccess(nodeId));
- if (res) {
- if (res.pipe) {
- dispatch(blurSearch());
- const resizeTtyControl = res.resize_tty_control &&
- {id: res.resize_tty_control, nodeId: control.nodeId, probeId: control.probeId};
- dispatch(receiveControlPipe(
- res.pipe,
- nodeId,
- res.raw_tty,
- resizeTtyControl,
- control
- ));
- }
- if (res.removedNode) {
- dispatch(receiveControlNodeRemoved(nodeId));
- }
- }
- },
- url
- });
-}
-
-
export function doResizeTty(pipeId, control, cols, rows) {
const url = `${getApiPath()}/api/control/${encodeURIComponent(control.probeId)}/`
+ `${encodeURIComponent(control.nodeId)}/${control.id}`;
return doRequest({
- data: JSON.stringify({height: rows.toString(), pipeID: pipeId, width: cols.toString()}),
+ data: JSON.stringify({ height: rows.toString(), pipeID: pipeId, width: cols.toString() }),
method: 'POST',
url,
})
@@ -459,44 +250,3 @@ export function deletePipe(pipeId, dispatch) {
url
});
}
-
-
-export function getPipeStatus(pipeId, dispatch) {
- const url = `${getApiPath()}/api/pipe/${encodeURIComponent(pipeId)}/check`;
- doRequest({
- complete: (res) => {
- const status = {
- 204: 'PIPE_ALIVE',
- 404: 'PIPE_DELETED'
- }[res.status];
-
- if (!status) {
- log('Unexpected pipe status:', res.status);
- return;
- }
-
- dispatch(receiveControlPipeStatus(pipeId, status));
- },
- method: 'GET',
- url
- });
-}
-
-export function stopPolling() {
- clearTimeout(apiDetailsTimer);
- clearTimeout(topologyTimer);
- continuePolling = false;
-}
-
-export function teardownWebsockets() {
- clearTimeout(reconnectTimer);
- if (socket) {
- socket.onerror = null;
- socket.onclose = null;
- socket.onmessage = null;
- socket.onopen = null;
- socket.close();
- socket = null;
- currentUrl = null;
- }
-}
diff --git a/client/package.json b/client/package.json
index 616d660a87..c9a3b8eac1 100644
--- a/client/package.json
+++ b/client/package.json
@@ -8,7 +8,7 @@
"main": "index.js",
"dependencies": {
"@babel/polyfill": "^7.0.0",
- "babel-plugin-lodash": "3.3.2",
+ "babel-plugin-lodash": "3.3.4",
"classnames": "2.2.5",
"d3-array": "1.2.1",
"d3-color": "1.0.3",
@@ -56,19 +56,19 @@
"@babel/preset-react": "^7.0.0",
"@fortawesome/fontawesome-free": "^5.5.0",
"autoprefixer": "7.1.5",
- "babel-eslint": "8.2.1",
+ "babel-eslint": "10.0.2",
"babel-jest": "24.8.0",
"babel-loader": "^8.0.0",
"babel-plugin-transform-class-properties": "6.24.1",
"babel-plugin-transform-object-rest-spread": "6.26.0",
"clean-webpack-plugin": "0.1.17",
"css-loader": "2.1.1",
- "eslint": "5.16.0",
- "eslint-config-airbnb": "16.1.0",
- "eslint-loader": "2.1.2",
- "eslint-plugin-import": "2.17.3",
- "eslint-plugin-jsx-a11y": "6.2.1",
- "eslint-plugin-react": "7.13.0",
+ "eslint": "6.1.0",
+ "eslint-config-airbnb": "17.1.1",
+ "eslint-loader": "2.2.1",
+ "eslint-plugin-import": "2.18.2",
+ "eslint-plugin-jsx-a11y": "6.2.3",
+ "eslint-plugin-react": "7.14.3",
"express": "4.16.4",
"file-loader": "1.1.11",
"html-webpack-plugin": "3.2.0",
@@ -130,6 +130,6 @@
]
},
"engines": {
- "node": "^8.9.0"
+ "node": "^8.10.0"
}
}
diff --git a/client/yarn.lock b/client/yarn.lock
index 5ded5422fc..5b7d62de24 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -18,14 +18,6 @@
optionalDependencies:
chokidar "^2.0.3"
-"@babel/code-frame@7.0.0-beta.36":
- version "7.0.0-beta.36"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.36.tgz#2349d7ec04b3a06945ae173280ef8579b63728e4"
- dependencies:
- chalk "^2.0.0"
- esutils "^2.0.2"
- js-tokens "^3.0.0"
-
"@babel/code-frame@7.0.0-beta.46":
version "7.0.0-beta.46"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.46.tgz#e0d002100805daab1461c0fcb32a07e304f3a4f4"
@@ -180,14 +172,6 @@
"@babel/traverse" "^7.1.0"
"@babel/types" "^7.0.0"
-"@babel/helper-function-name@7.0.0-beta.36":
- version "7.0.0-beta.36"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.36.tgz#366e3bc35147721b69009f803907c4d53212e88d"
- dependencies:
- "@babel/helper-get-function-arity" "7.0.0-beta.36"
- "@babel/template" "7.0.0-beta.36"
- "@babel/types" "7.0.0-beta.36"
-
"@babel/helper-function-name@7.0.0-beta.46":
version "7.0.0-beta.46"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.46.tgz#d0c4eed2e220e180f91b02e008dcc4594afe1d39"
@@ -204,12 +188,6 @@
"@babel/template" "^7.1.0"
"@babel/types" "^7.0.0"
-"@babel/helper-get-function-arity@7.0.0-beta.36":
- version "7.0.0-beta.36"
- resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.36.tgz#f5383bac9a96b274828b10d98900e84ee43e32b8"
- dependencies:
- "@babel/types" "7.0.0-beta.36"
-
"@babel/helper-get-function-arity@7.0.0-beta.46":
version "7.0.0-beta.46"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.46.tgz#7161bfe449b4183dbe25d1fe5579338b7429e5f2"
@@ -234,9 +212,10 @@
dependencies:
"@babel/types" "^7.0.0"
-"@babel/helper-module-imports@^7.0.0":
+"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.0.0-beta.49":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d"
+ integrity sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==
dependencies:
"@babel/types" "^7.0.0"
@@ -735,14 +714,11 @@
dependencies:
regenerator-runtime "^0.12.0"
-"@babel/template@7.0.0-beta.36":
- version "7.0.0-beta.36"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.36.tgz#02e903de5d68bd7899bce3c5b5447e59529abb00"
+"@babel/runtime@^7.4.5":
+ version "7.4.5"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12"
dependencies:
- "@babel/code-frame" "7.0.0-beta.36"
- "@babel/types" "7.0.0-beta.36"
- babylon "7.0.0-beta.36"
- lodash "^4.2.0"
+ regenerator-runtime "^0.13.2"
"@babel/template@7.0.0-beta.46":
version "7.0.0-beta.46"
@@ -769,19 +745,6 @@
"@babel/parser" "^7.2.2"
"@babel/types" "^7.2.2"
-"@babel/traverse@7.0.0-beta.36":
- version "7.0.0-beta.36"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.36.tgz#1dc6f8750e89b6b979de5fe44aa993b1a2192261"
- dependencies:
- "@babel/code-frame" "7.0.0-beta.36"
- "@babel/helper-function-name" "7.0.0-beta.36"
- "@babel/types" "7.0.0-beta.36"
- babylon "7.0.0-beta.36"
- debug "^3.0.1"
- globals "^11.1.0"
- invariant "^2.2.0"
- lodash "^4.2.0"
-
"@babel/traverse@7.0.0-beta.46", "@babel/traverse@^7.0.0-beta.42":
version "7.0.0-beta.46"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.46.tgz#29a0c0395b3642f0297e6f8e475bde89f9343755"
@@ -825,14 +788,6 @@
globals "^11.1.0"
lodash "^4.17.11"
-"@babel/types@7.0.0-beta.36":
- version "7.0.0-beta.36"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.36.tgz#64f2004353de42adb72f9ebb4665fc35b5499d23"
- dependencies:
- esutils "^2.0.2"
- lodash "^4.2.0"
- to-fast-properties "^2.0.0"
-
"@babel/types@7.0.0-beta.46":
version "7.0.0-beta.46"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.46.tgz#eb84399a699af9fcb244440cce78e1acbeb40e0c"
@@ -849,6 +804,15 @@
lodash "^4.17.11"
to-fast-properties "^2.0.0"
+"@babel/types@^7.0.0-beta.49":
+ version "7.5.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.0.tgz#e47d43840c2e7f9105bc4d3a2c371b4d0c7832ab"
+ integrity sha512-UFpDVqRABKsW01bvw7/wSUe56uy6RXM5+VJibVVAybDGxEW25jdwiFJEf7ASvSaC7sN7rbE/l3cLp2izav+CtQ==
+ dependencies:
+ esutils "^2.0.2"
+ lodash "^4.17.11"
+ to-fast-properties "^2.0.0"
+
"@babel/types@^7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.4.0.tgz#670724f77d24cce6cc7d8cf64599d511d164894c"
@@ -1204,7 +1168,7 @@ ajv@^5.0.0, ajv@^5.2.3:
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
-ajv@^6.1.0, ajv@^6.5.5, ajv@^6.9.1:
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.5.5, ajv@^6.9.1:
version "6.10.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1"
dependencies:
@@ -1292,7 +1256,6 @@ argparse@^1.0.7:
aria-query@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc"
- integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=
dependencies:
ast-types-flow "0.0.7"
commander "^2.11.0"
@@ -1463,7 +1426,6 @@ aws4@^1.8.0:
axobject-query@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9"
- integrity sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==
dependencies:
ast-types-flow "0.0.7"
@@ -1475,15 +1437,15 @@ babel-code-frame@^6.26.0:
esutils "^2.0.2"
js-tokens "^3.0.2"
-babel-eslint@8.2.1:
- version "8.2.1"
- resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.1.tgz#136888f3c109edc65376c23ebf494f36a3e03951"
+babel-eslint@10.0.2:
+ version "10.0.2"
+ resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.2.tgz#182d5ac204579ff0881684b040560fdcc1558456"
dependencies:
- "@babel/code-frame" "7.0.0-beta.36"
- "@babel/traverse" "7.0.0-beta.36"
- "@babel/types" "7.0.0-beta.36"
- babylon "7.0.0-beta.36"
- eslint-scope "~3.7.1"
+ "@babel/code-frame" "^7.0.0"
+ "@babel/parser" "^7.0.0"
+ "@babel/traverse" "^7.0.0"
+ "@babel/types" "^7.0.0"
+ eslint-scope "3.7.1"
eslint-visitor-keys "^1.0.0"
babel-helper-function-name@^6.24.1:
@@ -1503,13 +1465,6 @@ babel-helper-get-function-arity@^6.24.1:
babel-runtime "^6.22.0"
babel-types "^6.24.1"
-babel-helper-module-imports@^7.0.0-beta.3:
- version "7.0.0-beta.3"
- resolved "https://registry.yarnpkg.com/babel-helper-module-imports/-/babel-helper-module-imports-7.0.0-beta.3.tgz#e15764e3af9c8e11810c09f78f498a2bdc71585a"
- dependencies:
- babel-types "7.0.0-beta.3"
- lodash "^4.2.0"
-
babel-jest@24.8.0, babel-jest@^24.8.0:
version "24.8.0"
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.8.0.tgz#5c15ff2b28e20b0f45df43fe6b7f2aae93dba589"
@@ -1551,14 +1506,15 @@ babel-plugin-jest-hoist@^24.6.0:
dependencies:
"@types/babel__traverse" "^7.0.6"
-babel-plugin-lodash@3.3.2:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/babel-plugin-lodash/-/babel-plugin-lodash-3.3.2.tgz#da3a5b49ba27447f54463f6c4fa81396ccdd463f"
+babel-plugin-lodash@3.3.4:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/babel-plugin-lodash/-/babel-plugin-lodash-3.3.4.tgz#4f6844358a1340baed182adbeffa8df9967bc196"
+ integrity sha512-yDZLjK7TCkWl1gpBeBGmuaDIFhZKmkoL+Cu2MUUjv5VxUZx/z7tBGBCBcQs5RI1Bkz5LLmNdjx7paOyQtMovyg==
dependencies:
- babel-helper-module-imports "^7.0.0-beta.3"
- babel-types "^6.26.0"
+ "@babel/helper-module-imports" "^7.0.0-beta.49"
+ "@babel/types" "^7.0.0-beta.49"
glob "^7.1.1"
- lodash "^4.17.4"
+ lodash "^4.17.10"
require-package-name "^2.0.1"
babel-plugin-syntax-class-properties@^6.8.0:
@@ -1623,14 +1579,6 @@ babel-traverse@^6.24.1, babel-traverse@^6.26.0:
invariant "^2.2.2"
lodash "^4.17.4"
-babel-types@7.0.0-beta.3:
- version "7.0.0-beta.3"
- resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-7.0.0-beta.3.tgz#cd927ca70e0ae8ab05f4aab83778cfb3e6eb20b4"
- dependencies:
- esutils "^2.0.2"
- lodash "^4.2.0"
- to-fast-properties "^2.0.0"
-
babel-types@^6.24.1, babel-types@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
@@ -1640,10 +1588,6 @@ babel-types@^6.24.1, babel-types@^6.26.0:
lodash "^4.17.4"
to-fast-properties "^1.0.3"
-babylon@7.0.0-beta.36:
- version "7.0.0-beta.36"
- resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.36.tgz#3a3683ba6a9a1e02b0aa507c8e63435e39305b9e"
-
babylon@7.0.0-beta.46, babylon@^7.0.0-beta.42:
version "7.0.0-beta.46"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.46.tgz#b6ddaba81bbb130313932757ff9c195d527088b6"
@@ -2296,6 +2240,11 @@ concat-stream@^1.5.0:
readable-stream "^2.2.2"
typedarray "^0.0.6"
+confusing-browser-globals@^1.0.5:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.7.tgz#5ae852bd541a910e7ffb2dbb864a2d21a36ad29b"
+ integrity sha512-cgHI1azax5ATrZ8rJ+ODDML9Fvu67PimB6aNxBrc/QwSaDaM9eTfIEUHx3bBLJJ82ioSb+/5zfsMCCEJax3ByQ==
+
console-browserify@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
@@ -2630,7 +2579,6 @@ dagre@0.8.4:
damerau-levenshtein@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz#780cf7144eb2e8dbd1c3bb83ae31100ccc31a414"
- integrity sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==
dashdash@^1.12.0:
version "1.14.1"
@@ -2656,7 +2604,7 @@ debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.
dependencies:
ms "2.0.0"
-debug@3.1.0, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0:
+debug@3.1.0, debug@^3.0.0, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
@@ -2702,6 +2650,12 @@ define-properties@^1.1.2:
foreach "^2.0.5"
object-keys "^1.0.8"
+define-properties@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+ dependencies:
+ object-keys "^1.0.12"
+
define-property@^0.2.5:
version "0.2.5"
resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
@@ -2803,7 +2757,6 @@ doctrine@1.5.0, doctrine@^1.2.2:
doctrine@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
- integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
dependencies:
esutils "^2.0.2"
@@ -2995,7 +2948,7 @@ error-ex@^1.2.0, error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
-es-abstract@^1.11.0, es-abstract@^1.5.1:
+es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.1:
version "1.13.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9"
dependencies:
@@ -3113,29 +3066,35 @@ escope@^3.6.0:
esrecurse "^4.1.0"
estraverse "^4.1.1"
-eslint-config-airbnb-base@^12.1.0:
- version "12.1.0"
- resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz#386441e54a12ccd957b0a92564a4bafebd747944"
+eslint-config-airbnb-base@^13.2.0:
+ version "13.2.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.2.0.tgz#f6ea81459ff4dec2dda200c35f1d8f7419d57943"
+ integrity sha512-1mg/7eoB4AUeB0X1c/ho4vb2gYkNH8Trr/EgCT/aGmKhhG+F6vF5s8+iRBlWAzFIAphxIdp3YfEKgEl0f9Xg+w==
dependencies:
- eslint-restricted-globals "^0.1.1"
+ confusing-browser-globals "^1.0.5"
+ object.assign "^4.1.0"
+ object.entries "^1.1.0"
-eslint-config-airbnb@16.1.0:
- version "16.1.0"
- resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-16.1.0.tgz#2546bfb02cc9fe92284bf1723ccf2e87bc45ca46"
+eslint-config-airbnb@17.1.1:
+ version "17.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-17.1.1.tgz#2272e0b86bb1e2b138cdf88d07a3b6f4cda3d626"
+ integrity sha512-xCu//8a/aWqagKljt+1/qAM62BYZeNq04HmdevG5yUGWpja0I/xhqd6GdLRch5oetEGFiJAnvtGuTEAese53Qg==
dependencies:
- eslint-config-airbnb-base "^12.1.0"
+ eslint-config-airbnb-base "^13.2.0"
+ object.assign "^4.1.0"
+ object.entries "^1.1.0"
eslint-import-resolver-node@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a"
- integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==
dependencies:
debug "^2.6.9"
resolve "^1.5.0"
-eslint-loader@2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.1.2.tgz#453542a1230d6ffac90e4e7cb9cadba9d851be68"
+eslint-loader@2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.2.1.tgz#28b9c12da54057af0845e2a6112701a2f6bf8337"
+ integrity sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg==
dependencies:
loader-fs-cache "^1.0.0"
loader-utils "^1.0.2"
@@ -3146,15 +3105,14 @@ eslint-loader@2.1.2:
eslint-module-utils@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz#8b93499e9b00eab80ccb6614e69f03678e84e09a"
- integrity sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw==
dependencies:
debug "^2.6.8"
pkg-dir "^2.0.0"
-eslint-plugin-import@2.17.3:
- version "2.17.3"
- resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.17.3.tgz#00548b4434c18faebaba04b24ae6198f280de189"
- integrity sha512-qeVf/UwXFJbeyLbxuY8RgqDyEKCkqV7YC+E5S5uOjAp4tOc8zj01JP3ucoBM8JcEqd1qRasJSg6LLlisirfy0Q==
+eslint-plugin-import@2.18.2:
+ version "2.18.2"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6"
+ integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==
dependencies:
array-includes "^3.0.3"
contains-path "^0.1.0"
@@ -3163,16 +3121,16 @@ eslint-plugin-import@2.17.3:
eslint-import-resolver-node "^0.3.2"
eslint-module-utils "^2.4.0"
has "^1.0.3"
- lodash "^4.17.11"
minimatch "^3.0.4"
+ object.values "^1.1.0"
read-pkg-up "^2.0.0"
resolve "^1.11.0"
-eslint-plugin-jsx-a11y@6.2.1:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.1.tgz#4ebba9f339b600ff415ae4166e3e2e008831cf0c"
- integrity sha512-cjN2ObWrRz0TTw7vEcGQrx+YltMvZoOEx4hWU8eEERDnBIU00OTq7Vr+jA7DFKxiwLNv4tTh5Pq2GUNEa8b6+w==
+eslint-plugin-jsx-a11y@6.2.3:
+ version "6.2.3"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa"
dependencies:
+ "@babel/runtime" "^7.4.5"
aria-query "^3.0.0"
array-includes "^3.0.3"
ast-types-flow "^0.0.7"
@@ -3180,42 +3138,34 @@ eslint-plugin-jsx-a11y@6.2.1:
damerau-levenshtein "^1.0.4"
emoji-regex "^7.0.2"
has "^1.0.3"
- jsx-ast-utils "^2.0.1"
+ jsx-ast-utils "^2.2.1"
-eslint-plugin-react@7.13.0:
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.13.0.tgz#bc13fd7101de67996ea51b33873cd9dc2b7e5758"
- integrity sha512-uA5LrHylu8lW/eAH3bEQe9YdzpPaFd9yAJTwTi/i/BKTD7j6aQMKVAdGM/ML72zD6womuSK7EiGtMKuK06lWjQ==
+eslint-plugin-react@7.14.3:
+ version "7.14.3"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13"
+ integrity sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==
dependencies:
array-includes "^3.0.3"
doctrine "^2.1.0"
has "^1.0.3"
jsx-ast-utils "^2.1.0"
+ object.entries "^1.1.0"
object.fromentries "^2.0.0"
+ object.values "^1.1.0"
prop-types "^15.7.2"
resolve "^1.10.1"
-eslint-restricted-globals@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
-
-eslint-scope@^3.7.1:
+eslint-scope@3.7.1, eslint-scope@^3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
dependencies:
esrecurse "^4.1.0"
estraverse "^4.1.1"
-eslint-scope@^4.0.3:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
- dependencies:
- esrecurse "^4.1.0"
- estraverse "^4.1.1"
-
-eslint-scope@~3.7.1:
- version "3.7.3"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535"
+eslint-scope@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9"
+ integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==
dependencies:
esrecurse "^4.1.0"
estraverse "^4.1.1"
@@ -3228,46 +3178,48 @@ eslint-visitor-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
-eslint@5.16.0:
- version "5.16.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea"
+eslint@6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.1.0.tgz#06438a4a278b1d84fb107d24eaaa35471986e646"
+ integrity sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ==
dependencies:
"@babel/code-frame" "^7.0.0"
- ajv "^6.9.1"
+ ajv "^6.10.0"
chalk "^2.1.0"
cross-spawn "^6.0.5"
debug "^4.0.1"
doctrine "^3.0.0"
- eslint-scope "^4.0.3"
+ eslint-scope "^5.0.0"
eslint-utils "^1.3.1"
eslint-visitor-keys "^1.0.0"
- espree "^5.0.1"
+ espree "^6.0.0"
esquery "^1.0.1"
esutils "^2.0.2"
file-entry-cache "^5.0.1"
functional-red-black-tree "^1.0.1"
- glob "^7.1.2"
+ glob-parent "^5.0.0"
globals "^11.7.0"
ignore "^4.0.6"
import-fresh "^3.0.0"
imurmurhash "^0.1.4"
- inquirer "^6.2.2"
- js-yaml "^3.13.0"
+ inquirer "^6.4.1"
+ is-glob "^4.0.0"
+ js-yaml "^3.13.1"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.3.0"
- lodash "^4.17.11"
+ lodash "^4.17.14"
minimatch "^3.0.4"
mkdirp "^0.5.1"
natural-compare "^1.4.0"
optionator "^0.8.2"
- path-is-inside "^1.0.2"
progress "^2.0.0"
regexpp "^2.0.1"
- semver "^5.5.1"
- strip-ansi "^4.0.0"
- strip-json-comments "^2.0.1"
+ semver "^6.1.2"
+ strip-ansi "^5.2.0"
+ strip-json-comments "^3.0.1"
table "^5.2.3"
text-table "^0.2.0"
+ v8-compile-cache "^2.0.3"
eslint@^2.7.0:
version "2.13.1"
@@ -3314,9 +3266,9 @@ espree@^3.1.6:
acorn "^5.5.0"
acorn-jsx "^3.0.0"
-espree@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a"
+espree@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-6.0.0.tgz#716fc1f5a245ef5b9a7fdb1d7b0d3f02322e75f6"
dependencies:
acorn "^6.0.7"
acorn-jsx "^5.0.0"
@@ -3960,6 +3912,13 @@ glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
+glob-parent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954"
+ integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==
+ dependencies:
+ is-glob "^4.0.1"
+
glob-to-regexp@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
@@ -4525,9 +4484,10 @@ inquirer@^0.12.0:
strip-ansi "^3.0.0"
through "^2.3.6"
-inquirer@^6.2.2:
- version "6.3.1"
- resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.3.1.tgz#7a413b5e7950811013a3db491c61d1f3b776e8e7"
+inquirer@^6.4.1:
+ version "6.5.0"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42"
+ integrity sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==
dependencies:
ansi-escapes "^3.2.0"
chalk "^2.4.2"
@@ -4535,7 +4495,7 @@ inquirer@^6.2.2:
cli-width "^2.0.0"
external-editor "^3.0.3"
figures "^2.0.0"
- lodash "^4.17.11"
+ lodash "^4.17.12"
mute-stream "0.0.7"
run-async "^2.2.0"
rxjs "^6.4.0"
@@ -4750,6 +4710,13 @@ is-glob@^4.0.0:
dependencies:
is-extglob "^2.1.1"
+is-glob@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
+ integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
+ dependencies:
+ is-extglob "^2.1.1"
+
is-hexadecimal@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz#6e084bbc92061fbb0971ec58b6ce6d404e24da69"
@@ -5311,7 +5278,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
-js-yaml@^3.13.0, js-yaml@^3.13.1:
+js-yaml@^3.13.1:
version "3.13.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
dependencies:
@@ -5441,19 +5408,19 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
-jsx-ast-utils@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f"
- dependencies:
- array-includes "^3.0.3"
-
jsx-ast-utils@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.1.0.tgz#0ee4e2c971fb9601c67b5641b71be80faecf0b36"
- integrity sha512-yDGDG2DS4JcqhA6blsuYbtsT09xL8AoLuUR2Gb5exrw7UEM19sBcOTq+YBBhrNbl0PUC4R4LnFu+dHg2HKeVvA==
dependencies:
array-includes "^3.0.3"
+jsx-ast-utils@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb"
+ dependencies:
+ array-includes "^3.0.3"
+ object.assign "^4.1.0"
+
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -5647,6 +5614,11 @@ lodash@^4.0.0, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lod
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+lodash@^4.17.12, lodash@^4.17.14:
+ version "4.17.15"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+ integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
log-symbols@^2.0.0, log-symbols@^2.1.0, log-symbols@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
@@ -6372,10 +6344,18 @@ object.assign@^4.1.0:
has-symbols "^1.0.0"
object-keys "^1.0.11"
+object.entries@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519"
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.12.0"
+ function-bind "^1.1.1"
+ has "^1.0.3"
+
object.fromentries@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab"
- integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==
dependencies:
define-properties "^1.1.2"
es-abstract "^1.11.0"
@@ -6402,6 +6382,15 @@ object.pick@^1.3.0:
dependencies:
isobject "^3.0.1"
+object.values@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9"
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.12.0"
+ function-bind "^1.1.1"
+ has "^1.0.3"
+
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
@@ -6642,7 +6631,7 @@ path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
-path-is-inside@^1.0.1, path-is-inside@^1.0.2:
+path-is-inside@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
@@ -7521,6 +7510,10 @@ regenerator-runtime@^0.12.0:
version "0.12.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
+regenerator-runtime@^0.13.2:
+ version "0.13.2"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447"
+
regenerator-transform@^0.13.4:
version "0.13.4"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb"
@@ -8021,9 +8014,10 @@ semver@^5.5.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
-semver@^5.5.1:
- version "5.7.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
+semver@^6.1.2:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+ integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@~5.3.0:
version "5.3.0"
@@ -8467,14 +8461,19 @@ strip-indent@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
-strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+strip-json-comments@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7"
+ integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==
strip-json-comments@~1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91"
+strip-json-comments@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+
style-loader@0.21.0:
version "0.21.0"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.21.0.tgz#68c52e5eb2afc9ca92b6274be277ee59aea3a852"
@@ -9181,6 +9180,11 @@ v8-compile-cache@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c"
+v8-compile-cache@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe"
+ integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==
+
validate-npm-package-license@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"