mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-05 11:11:13 +00:00
817 lines
18 KiB
JavaScript
817 lines
18 KiB
JavaScript
import debug from 'debug';
|
|
import { find } from 'lodash';
|
|
|
|
import ActionTypes from '../constants/action-types';
|
|
import { saveGraph } from '../utils/file-utils';
|
|
import { updateRoute } from '../utils/router-utils';
|
|
import {
|
|
doControlRequest,
|
|
getAllNodes,
|
|
getResourceViewNodesSnapshot,
|
|
getNodeDetails,
|
|
getTopologies,
|
|
deletePipe,
|
|
stopPolling,
|
|
teardownWebsockets,
|
|
getNodes,
|
|
} from '../utils/web-api-utils';
|
|
import { storageSet } from '../utils/storage-utils';
|
|
import { loadTheme } from '../utils/contrast-utils';
|
|
import { isPausedSelector } from '../selectors/time-travel';
|
|
import {
|
|
availableMetricTypesSelector,
|
|
nextPinnedMetricTypeSelector,
|
|
previousPinnedMetricTypeSelector,
|
|
pinnedMetricSelector,
|
|
} from '../selectors/node-metric';
|
|
import {
|
|
isResourceViewModeSelector,
|
|
resourceViewAvailableSelector,
|
|
} 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 };
|
|
}
|
|
|
|
|
|
export function hideHelp() {
|
|
return { type: ActionTypes.HIDE_HELP };
|
|
}
|
|
|
|
|
|
export function toggleHelp() {
|
|
return (dispatch, getState) => {
|
|
if (getState().get('showingHelp')) {
|
|
dispatch(hideHelp());
|
|
} else {
|
|
dispatch(showHelp());
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
export function sortOrderChanged(sortedBy, sortedDesc) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.SORT_ORDER_CHANGED,
|
|
sortedBy,
|
|
sortedDesc
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
|
|
//
|
|
// Networks
|
|
//
|
|
|
|
|
|
export function showNetworks(visible) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.SHOW_NETWORKS,
|
|
visible
|
|
});
|
|
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
|
|
export function selectNetwork(networkId) {
|
|
return {
|
|
type: ActionTypes.SELECT_NETWORK,
|
|
networkId
|
|
};
|
|
}
|
|
|
|
export function pinNetwork(networkId) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.PIN_NETWORK,
|
|
networkId,
|
|
});
|
|
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function unpinNetwork(networkId) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.UNPIN_NETWORK,
|
|
networkId,
|
|
});
|
|
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
|
|
//
|
|
// Metrics
|
|
//
|
|
|
|
export function hoverMetric(metricType) {
|
|
return {
|
|
type: ActionTypes.HOVER_METRIC,
|
|
metricType,
|
|
};
|
|
}
|
|
|
|
export function unhoverMetric() {
|
|
return {
|
|
type: ActionTypes.UNHOVER_METRIC,
|
|
};
|
|
}
|
|
|
|
export function pinMetric(metricType) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.PIN_METRIC,
|
|
metricType,
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function unpinMetric() {
|
|
return (dispatch, getState) => {
|
|
// We always have to keep metrics pinned in the resource view.
|
|
if (!isResourceViewModeSelector(getState())) {
|
|
dispatch({
|
|
type: ActionTypes.UNPIN_METRIC,
|
|
});
|
|
updateRoute(getState);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function pinNextMetric() {
|
|
return (dispatch, getState) => {
|
|
const nextPinnedMetricType = nextPinnedMetricTypeSelector(getState());
|
|
dispatch(pinMetric(nextPinnedMetricType));
|
|
};
|
|
}
|
|
|
|
export function pinPreviousMetric() {
|
|
return (dispatch, getState) => {
|
|
const previousPinnedMetricType = previousPinnedMetricTypeSelector(getState());
|
|
dispatch(pinMetric(previousPinnedMetricType));
|
|
};
|
|
}
|
|
|
|
export function pinSearch() {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.PIN_SEARCH,
|
|
query: getState().get('searchQuery'),
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function unpinSearch(query) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.UNPIN_SEARCH,
|
|
query
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function blurSearch() {
|
|
return { type: ActionTypes.BLUR_SEARCH };
|
|
}
|
|
|
|
export function changeTopologyOption(option, value, topologyId, addOrRemove) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.CHANGE_TOPOLOGY_OPTION,
|
|
topologyId,
|
|
option,
|
|
value,
|
|
addOrRemove
|
|
});
|
|
updateRoute(getState);
|
|
// update all request workers with new options
|
|
getTopologies(getState, dispatch);
|
|
getNodes(getState, dispatch);
|
|
};
|
|
}
|
|
|
|
export function clickBackground() {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.CLICK_BACKGROUND
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function clickCloseDetails(nodeId) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.CLICK_CLOSE_DETAILS,
|
|
nodeId
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function clickCloseTerminal(pipeId, closePipe) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.CLICK_CLOSE_TERMINAL,
|
|
pipeId
|
|
});
|
|
if (closePipe) {
|
|
deletePipe(pipeId, dispatch);
|
|
}
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function clickDownloadGraph() {
|
|
return (dispatch) => {
|
|
dispatch({ type: ActionTypes.SET_EXPORTING_GRAPH, exporting: true });
|
|
saveGraph();
|
|
dispatch({ type: ActionTypes.SET_EXPORTING_GRAPH, exporting: false });
|
|
};
|
|
}
|
|
|
|
export function clickForceRelayout() {
|
|
return (dispatch) => {
|
|
dispatch({
|
|
type: ActionTypes.CLICK_FORCE_RELAYOUT,
|
|
forceRelayout: true
|
|
});
|
|
// fire only once, reset after dispatch
|
|
setTimeout(() => {
|
|
dispatch({
|
|
type: ActionTypes.CLICK_FORCE_RELAYOUT,
|
|
forceRelayout: false
|
|
});
|
|
}, 100);
|
|
};
|
|
}
|
|
|
|
export function doSearch(searchQuery) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.DO_SEARCH,
|
|
searchQuery
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function setViewportDimensions(width, height) {
|
|
return (dispatch) => {
|
|
dispatch({ type: ActionTypes.SET_VIEWPORT_DIMENSIONS, width, height });
|
|
};
|
|
}
|
|
|
|
export function setGraphView() {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.SET_VIEW_MODE,
|
|
viewMode: GRAPH_VIEW_MODE,
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function setTableView() {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.SET_VIEW_MODE,
|
|
viewMode: TABLE_VIEW_MODE,
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
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) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.CLICK_NODE,
|
|
origin,
|
|
label,
|
|
nodeId
|
|
});
|
|
updateRoute(getState);
|
|
getNodeDetails(getState, dispatch);
|
|
};
|
|
}
|
|
|
|
export function pauseTimeAtNow() {
|
|
return {
|
|
type: ActionTypes.PAUSE_TIME_AT_NOW
|
|
};
|
|
}
|
|
|
|
export function clickRelative(nodeId, topologyId, label, origin) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.CLICK_RELATIVE,
|
|
label,
|
|
origin,
|
|
nodeId,
|
|
topologyId
|
|
});
|
|
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({
|
|
type: ActionTypes.CLICK_SHOW_TOPOLOGY_FOR_NODE,
|
|
topologyId,
|
|
nodeId
|
|
});
|
|
updateTopology(dispatch, getState);
|
|
};
|
|
}
|
|
|
|
export function clickTopology(topologyId) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.CLICK_TOPOLOGY,
|
|
topologyId
|
|
});
|
|
updateTopology(dispatch, getState);
|
|
};
|
|
}
|
|
|
|
export function cacheZoomState(zoomState) {
|
|
return {
|
|
type: ActionTypes.CACHE_ZOOM_STATE,
|
|
zoomState
|
|
};
|
|
}
|
|
|
|
export function openWebsocket() {
|
|
return {
|
|
type: ActionTypes.OPEN_WEBSOCKET
|
|
};
|
|
}
|
|
|
|
export function clearControlError(nodeId) {
|
|
return {
|
|
type: ActionTypes.CLEAR_CONTROL_ERROR,
|
|
nodeId
|
|
};
|
|
}
|
|
|
|
export function closeWebsocket() {
|
|
return {
|
|
type: ActionTypes.CLOSE_WEBSOCKET
|
|
};
|
|
}
|
|
|
|
export function doControl(nodeId, control) {
|
|
return (dispatch) => {
|
|
dispatch({
|
|
type: ActionTypes.DO_CONTROL,
|
|
nodeId,
|
|
control
|
|
});
|
|
doControlRequest(nodeId, control, dispatch);
|
|
};
|
|
}
|
|
|
|
export function enterEdge(edgeId) {
|
|
return {
|
|
type: ActionTypes.ENTER_EDGE,
|
|
edgeId
|
|
};
|
|
}
|
|
|
|
export function enterNode(nodeId) {
|
|
return {
|
|
type: ActionTypes.ENTER_NODE,
|
|
nodeId
|
|
};
|
|
}
|
|
|
|
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 hitBackspace() {
|
|
return (dispatch, getState) => {
|
|
const state = getState();
|
|
// remove last pinned query if search query is empty
|
|
if (state.get('searchFocused') && !state.get('searchQuery')) {
|
|
const query = state.get('pinnedSearches').last();
|
|
if (query) {
|
|
dispatch({
|
|
type: ActionTypes.UNPIN_SEARCH,
|
|
query
|
|
});
|
|
updateRoute(getState);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
export function hitEsc() {
|
|
return (dispatch, getState) => {
|
|
const state = getState();
|
|
const controlPipe = state.get('controlPipes').last();
|
|
if (controlPipe && controlPipe.get('status') === 'PIPE_DELETED') {
|
|
dispatch({
|
|
type: ActionTypes.CLICK_CLOSE_TERMINAL,
|
|
pipeId: controlPipe.get('id')
|
|
});
|
|
updateRoute(getState);
|
|
// Don't deselect node on ESC if there is a controlPipe (keep terminal open)
|
|
} else if (state.get('searchFocused')) {
|
|
if (state.get('searchQuery')) {
|
|
dispatch(doSearch(''));
|
|
} else {
|
|
dispatch(blurSearch());
|
|
}
|
|
} else if (state.get('showingHelp')) {
|
|
dispatch(hideHelp());
|
|
} else if (state.get('nodeDetails').last() && !controlPipe) {
|
|
dispatch({ type: ActionTypes.DESELECT_NODE });
|
|
updateRoute(getState);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function leaveEdge(edgeId) {
|
|
return {
|
|
type: ActionTypes.LEAVE_EDGE,
|
|
edgeId
|
|
};
|
|
}
|
|
|
|
export function leaveNode(nodeId) {
|
|
return {
|
|
type: ActionTypes.LEAVE_NODE,
|
|
nodeId
|
|
};
|
|
}
|
|
|
|
export function receiveControlError(nodeId, err) {
|
|
return {
|
|
type: ActionTypes.DO_CONTROL_ERROR,
|
|
nodeId,
|
|
error: err
|
|
};
|
|
}
|
|
|
|
export function receiveControlSuccess(nodeId) {
|
|
return {
|
|
type: ActionTypes.DO_CONTROL_SUCCESS,
|
|
nodeId
|
|
};
|
|
}
|
|
|
|
export function receiveNodeDetails(details) {
|
|
return {
|
|
type: ActionTypes.RECEIVE_NODE_DETAILS,
|
|
details
|
|
};
|
|
}
|
|
|
|
export function receiveNodesDelta(delta) {
|
|
return (dispatch, getState) => {
|
|
if (!isPausedSelector(getState())) {
|
|
// Allow css-animation to run smoothly by scheduling it to run on the
|
|
// next tick after any potentially expensive canvas re-draws have been
|
|
// completed.
|
|
setTimeout(() => dispatch({ type: ActionTypes.SET_RECEIVED_NODES_DELTA }), 0);
|
|
|
|
// When moving in time, we will consider the transition complete
|
|
// only when the first batch of nodes delta has been received. We
|
|
// do that because we want to keep the previous state blurred instead
|
|
// of transitioning over an empty state like when switching topologies.
|
|
if (getState().get('timeTravelTransitioning')) {
|
|
dispatch({ type: ActionTypes.FINISH_TIME_TRAVEL_TRANSITION });
|
|
}
|
|
|
|
const hasChanges = delta.add || delta.update || delta.remove || delta.reset;
|
|
if (hasChanges) {
|
|
dispatch({
|
|
type: ActionTypes.RECEIVE_NODES_DELTA,
|
|
delta
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
export function resumeTime() {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.RESUME_TIME
|
|
});
|
|
// 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 startTimeTravel() {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.START_TIME_TRAVEL
|
|
});
|
|
if (!getState().get('nodesLoaded')) {
|
|
getNodes(getState, dispatch);
|
|
if (isResourceViewModeSelector(getState())) {
|
|
getResourceViewNodesSnapshot(getState(), dispatch);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
export function receiveNodes(nodes) {
|
|
return {
|
|
type: ActionTypes.RECEIVE_NODES,
|
|
nodes,
|
|
};
|
|
}
|
|
|
|
export function timeTravelStartTransition() {
|
|
return {
|
|
type: ActionTypes.TIME_TRAVEL_START_TRANSITION,
|
|
};
|
|
}
|
|
|
|
export function jumpToTime(timestamp) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.JUMP_TO_TIME,
|
|
timestamp,
|
|
});
|
|
getTopologies(getState, dispatch);
|
|
getNodes(getState, dispatch);
|
|
if (isResourceViewModeSelector(getState())) {
|
|
getResourceViewNodesSnapshot(getState(), dispatch);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function receiveNodesForTopology(nodes, topologyId) {
|
|
return {
|
|
type: ActionTypes.RECEIVE_NODES_FOR_TOPOLOGY,
|
|
nodes,
|
|
topologyId
|
|
};
|
|
}
|
|
|
|
export function receiveTopologies(topologies) {
|
|
return (dispatch, getState) => {
|
|
const firstLoad = !getState().get('topologiesLoaded');
|
|
dispatch({
|
|
type: ActionTypes.RECEIVE_TOPOLOGIES,
|
|
topologies
|
|
});
|
|
getNodes(getState, dispatch);
|
|
// Populate search matches on first load
|
|
const state = getState();
|
|
if (firstLoad && state.get('searchQuery')) {
|
|
dispatch(focusSearch());
|
|
}
|
|
// Fetch all the relevant nodes once on first load
|
|
if (firstLoad && isResourceViewModeSelector(state)) {
|
|
getResourceViewNodesSnapshot(state, dispatch);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function receiveApiDetails(apiDetails) {
|
|
return {
|
|
type: ActionTypes.RECEIVE_API_DETAILS,
|
|
hostname: apiDetails.hostname,
|
|
version: apiDetails.version,
|
|
newVersion: apiDetails.newVersion,
|
|
plugins: apiDetails.plugins,
|
|
};
|
|
}
|
|
|
|
export function receiveControlNodeRemoved(nodeId) {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.RECEIVE_CONTROL_NODE_REMOVED,
|
|
nodeId
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function receiveControlPipeFromParams(pipeId, rawTty, resizeTtyControl) {
|
|
// TODO add nodeId
|
|
return {
|
|
type: ActionTypes.RECEIVE_CONTROL_PIPE,
|
|
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({
|
|
type: ActionTypes.RECEIVE_CONTROL_PIPE,
|
|
nodeId,
|
|
pipeId,
|
|
rawTty,
|
|
resizeTtyControl,
|
|
control
|
|
});
|
|
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function receiveControlPipeStatus(pipeId, status) {
|
|
return {
|
|
type: ActionTypes.RECEIVE_CONTROL_PIPE_STATUS,
|
|
pipeId,
|
|
status
|
|
};
|
|
}
|
|
|
|
export function receiveError(errorUrl) {
|
|
return {
|
|
errorUrl,
|
|
type: ActionTypes.RECEIVE_ERROR
|
|
};
|
|
}
|
|
|
|
export function receiveNotFound(nodeId) {
|
|
return {
|
|
nodeId,
|
|
type: ActionTypes.RECEIVE_NOT_FOUND
|
|
};
|
|
}
|
|
|
|
export function setContrastMode(enabled) {
|
|
return (dispatch) => {
|
|
loadTheme(enabled ? 'contrast' : 'normal');
|
|
dispatch({
|
|
type: ActionTypes.TOGGLE_CONTRAST_MODE,
|
|
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
|
|
});
|
|
// 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});
|
|
storageSet('scopeViewState', '');
|
|
window.location.href = window.location.href.split('#')[0];
|
|
};
|
|
}
|
|
|
|
export function toggleTroubleshootingMenu(ev) {
|
|
if (ev) { ev.preventDefault(); ev.stopPropagation(); }
|
|
return {
|
|
type: ActionTypes.TOGGLE_TROUBLESHOOTING_MENU
|
|
};
|
|
}
|
|
|
|
export function changeInstance() {
|
|
return (dispatch, getState) => {
|
|
dispatch({
|
|
type: ActionTypes.CHANGE_INSTANCE
|
|
});
|
|
updateRoute(getState);
|
|
};
|
|
}
|
|
|
|
export function shutdown() {
|
|
stopPolling();
|
|
teardownWebsockets();
|
|
return {
|
|
type: ActionTypes.SHUTDOWN
|
|
};
|
|
}
|
|
|
|
export function getImagesForService(orgId, serviceId) {
|
|
return (dispatch, getState, { api }) => {
|
|
dispatch({
|
|
type: ActionTypes.REQUEST_SERVICE_IMAGES,
|
|
serviceId
|
|
});
|
|
|
|
api.getFluxImages(orgId, serviceId)
|
|
.then((services) => {
|
|
dispatch({
|
|
type: ActionTypes.RECEIVE_SERVICE_IMAGES,
|
|
service: find(services, s => s.ID === serviceId),
|
|
serviceId
|
|
});
|
|
}, ({ errors }) => {
|
|
dispatch({
|
|
type: ActionTypes.RECEIVE_SERVICE_IMAGES,
|
|
errors
|
|
});
|
|
});
|
|
};
|
|
}
|