From 21a0093c0814705c050d516d5e8939e933bee0b2 Mon Sep 17 00:00:00 2001 From: Roland Schilter Date: Wed, 12 Jul 2017 16:41:46 +0200 Subject: [PATCH] Keep topology nav visible if selected If the scope-app API unexpectedly restarts, it has no report at hand (until it gets one from the probe) and sends node count 0 to the frontend for all topologies. Once the report arrives, it will send the proper count. What happened was the frontend did hide Processes for a short time till the node count recovered. This moved the topology selection to the always visible Containers (hide_if_empty == false) while keeping the graph as is. Once the node count recovers, Processes comes back but the selection is still at Containers. We now keep the selected topology visible at all time even if the API returns a node count of 0. This recovers nicely when the correct node counts come in. Once the user selects a different topology while and a backend response arrives, it disappears. Fixes #2646 --- .../scripts/reducers/__tests__/root-test.js | 34 +++++++++++++++++++ client/app/scripts/reducers/root.js | 10 +++--- client/app/scripts/utils/topology-utils.js | 4 +-- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/client/app/scripts/reducers/__tests__/root-test.js b/client/app/scripts/reducers/__tests__/root-test.js index 4bb3d1a68c..991c366c18 100644 --- a/client/app/scripts/reducers/__tests__/root-test.js +++ b/client/app/scripts/reducers/__tests__/root-test.js @@ -280,6 +280,25 @@ describe('RootReducer', () => { }] }; + const ReceiveTopologiesHiddenAction = { + type: ActionTypes.RECEIVE_TOPOLOGIES, + topologies: [{ + url: '/topo1', + name: 'Topo1', + stats: { + node_count: 1 + } + }, { + hide_if_empty: true, + url: '/topo2', + name: 'Topo2', + stats: { + node_count: 0, + filtered_nodes: 0 + } + }] + }; + const RouteAction = { type: ActionTypes.ROUTE_TOPOLOGY, state: {} @@ -567,6 +586,21 @@ describe('RootReducer', () => { expect(isTopologyNodeCountZero(nextState)).toBeFalsy(); }); + it('keeps hidden topology visible if selected', () => { + let nextState = initialState; + nextState = reducer(nextState, ClickTopology2Action); + nextState = reducer(nextState, ReceiveTopologiesHiddenAction); + expect(nextState.get('currentTopologyId')).toEqual('topo2'); + expect(nextState.get('topologies').toJS().length).toEqual(2); + }); + + it('keeps hidden topology hidden if not selected', () => { + let nextState = initialState; + nextState = reducer(nextState, ClickTopologyAction); + nextState = reducer(nextState, ReceiveTopologiesHiddenAction); + expect(nextState.get('topologies').toJS().length).toEqual(1); + }); + // selection of relatives it('keeps relatives as a stack', () => { diff --git a/client/app/scripts/reducers/root.js b/client/app/scripts/reducers/root.js index 81e6b60075..849157bb52 100644 --- a/client/app/scripts/reducers/root.js +++ b/client/app/scripts/reducers/root.js @@ -110,17 +110,17 @@ function calcSelectType(topology) { // adds ID field to topology (based on last part of URL path) and save urls in // map for easy lookup function processTopologies(state, nextTopologies) { + // add IDs to topology objects in-place + const topologiesWithId = updateTopologyIds(nextTopologies); // filter out hidden topos - const visibleTopologies = filterHiddenTopologies(nextTopologies); + const visibleTopologies = filterHiddenTopologies(topologiesWithId, state.get('currentTopologyId')); // set `selectType` field for topology and sub_topologies options (recursive). const topologiesWithSelectType = visibleTopologies.map(calcSelectType); - // add IDs to topology objects in-place - const topologiesWithId = updateTopologyIds(topologiesWithSelectType); // cache URLs by ID state = state.set('topologyUrlsById', - setTopologyUrlsById(state.get('topologyUrlsById'), topologiesWithId)); + setTopologyUrlsById(state.get('topologyUrlsById'), topologiesWithSelectType)); - const topologiesWithFullnames = addTopologyFullname(topologiesWithId); + const topologiesWithFullnames = addTopologyFullname(topologiesWithSelectType); const immNextTopologies = fromJS(topologiesWithFullnames).sortBy(topologySorter); return state.set('topologies', immNextTopologies); } diff --git a/client/app/scripts/utils/topology-utils.js b/client/app/scripts/utils/topology-utils.js index efce0f6d12..fc2ba4d299 100644 --- a/client/app/scripts/utils/topology-utils.js +++ b/client/app/scripts/utils/topology-utils.js @@ -126,9 +126,9 @@ export function setTopologyUrlsById(topologyUrlsById, topologies) { return urlMap; } -export function filterHiddenTopologies(topologies) { +export function filterHiddenTopologies(topologies, currentTopologyId) { return topologies.filter(t => (!t.hide_if_empty || t.stats.node_count > 0 || - t.stats.filtered_nodes > 0)); + t.stats.filtered_nodes > 0 || t.id === currentTopologyId)); } export function getCurrentTopologyOptions(state) {