From 3eb909a4f1257085810dad25715375f09d5e937d Mon Sep 17 00:00:00 2001 From: ognyan Date: Fri, 29 Jul 2022 16:32:48 +0300 Subject: [PATCH] ui: Fixed the inverted data series after switching between multiple nodes --- ui/src/components/NodeListTable.js | 64 +++++++++++----------- ui/src/hooks.js | 30 ++++++++-- ui/src/services/platformlibrary/metrics.js | 16 +++++- ui/src/services/utils.js | 11 ++++ 4 files changed, 85 insertions(+), 36 deletions(-) diff --git a/ui/src/components/NodeListTable.js b/ui/src/components/NodeListTable.js index 4d65b76b67..df7ad66227 100644 --- a/ui/src/components/NodeListTable.js +++ b/ui/src/components/NodeListTable.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { useHistory } from 'react-router'; import { useLocation, useRouteMatch } from 'react-router-dom'; import styled from 'styled-components'; @@ -452,39 +452,41 @@ const NodeListTable = (props) => { sortType: 'status', }, ], - // eslint-disable-next-line react-hooks/exhaustive-deps - [], + [intl], ); // handle the row selection by updating the URL - const onClickRow = (row) => { - const nodeName = row.values.name.name; - - const isTabSelected = - location.pathname.endsWith('overview') || - location.pathname.endsWith('alerts') || - location.pathname.endsWith('metrics') || - location.pathname.endsWith('volumes') || - location.pathname.endsWith('pods') || - location.pathname.endsWith('partitions') || - location.pathname.endsWith('details'); - - if (isTabSelected) { - const newPath = location.pathname.replace( - /\/nodes\/[^/]*\//, - `/nodes/${nodeName}/`, - ); - history.push({ - pathname: newPath, - search: query.toString(), - }); - } else { - history.push({ - pathname: `${path}/${nodeName}/overview`, - search: query.toString(), - }); - } - }; + const onClickRow = useCallback( + (row) => { + const nodeName = row.values.name.name; + + const isTabSelected = + location.pathname.endsWith('overview') || + location.pathname.endsWith('alerts') || + location.pathname.endsWith('metrics') || + location.pathname.endsWith('volumes') || + location.pathname.endsWith('pods') || + location.pathname.endsWith('partitions') || + location.pathname.endsWith('details'); + + if (isTabSelected) { + const newPath = location.pathname.replace( + /\/nodes\/[^/]*\//, + `/nodes/${nodeName}/`, + ); + history.push({ + pathname: newPath, + search: query.toString(), + }); + } else { + history.push({ + pathname: `${path}/${nodeName}/overview`, + search: query.toString(), + }); + } + }, + [history, location.pathname, path, query], + ); return ( diff --git a/ui/src/hooks.js b/ui/src/hooks.js index f8feda20b3..82a986f590 100644 --- a/ui/src/hooks.js +++ b/ui/src/hooks.js @@ -190,7 +190,18 @@ export const useChartSeries = ({ ); const isLoading = queries.find((query) => query.isLoading); - const queriesData = queries.map((query) => query.data); + const queriesData = queries + .map((query) => { + return query.data; + }) + /* useQueries is running the requests in paralel and given that + * in transformPrometheusDataToSeries (which is a generic function used by multiple charts) + * we make an assumption on the order of responses + * then we need to make sure that the average query is the second one in the array + * That is achieved by giving a key param to the response object (e.g. 'cpuUsage' and 'cpuUsageAvg') + * and sorting the array alphanumerically on its 'key' property + */ + .sort((query1, query2) => (query1.key > query2.key ? 1 : -1)); useEffect(() => { if (!isLoading && !queries.find((query) => !query.data)) { @@ -245,9 +256,20 @@ export const useSymetricalChartSeries = ({ const isLoading = aboveQueries.find((query) => query.isLoading) || belowQueries.find((query) => query.isLoading); - const queriesAboveData = aboveQueries.map((query) => query.data); - - const queriesBelowData = belowQueries.map((query) => query.data); + const queriesAboveData = aboveQueries + .map((query) => query.data) + /* useQueries is running the requests in paralel and given that + * in transformPrometheusDataToSeries (which is a generic function used by multiple charts) + * we make an assumption on the order of responses + * then we need to make sure that the average query is the second one in the array + * That is achieved by giving a key param to the response object (e.g. 'IOPSRead' and 'IOPSReadAvg') + * and sorting the array alphanumerically on its 'key' property + */ + .sort((query1, query2) => (query1.key > query2.key ? 1 : -1)); + + const queriesBelowData = belowQueries + .map((query) => query.data) + .sort((query1, query2) => (query1.key > query2.key ? 1 : -1)); useEffect(() => { if ( !isLoading && diff --git a/ui/src/services/platformlibrary/metrics.js b/ui/src/services/platformlibrary/metrics.js index 6305679d32..4a41fb97b0 100644 --- a/ui/src/services/platformlibrary/metrics.js +++ b/ui/src/services/platformlibrary/metrics.js @@ -7,7 +7,7 @@ import { queryPromtheusMetrics } from '../prometheus/fetchMetrics'; import type { NodesState } from '../../ducks/app/nodes'; import { queryPrometheus, queryPrometheusRange } from '../prometheus/api'; import { addMissingDataPoint } from '@scality/core-ui/dist/components/linetemporalchart/ChartUtil'; -import { getNaNSegments, getSegments } from '../utils'; +import { generateSelectWithKey, getNaNSegments, getSegments } from '../utils'; import { getFormattedLokiAlert } from '../loki/api'; import { NAN_STRING } from '@scality/core-ui/dist/components/constants'; @@ -84,6 +84,7 @@ export const getCPUUsageQuery = ( refetchOnMount: false, refetchOnWindowFocus: false, enabled: instanceIP !== '', + ...generateSelectWithKey('cpuUsage'), }; }; @@ -162,6 +163,7 @@ export const getCPUUsageAvgQuery = ( refetchOnMount: false, refetchOnWindowFocus: false, enabled: showAvg, + ...generateSelectWithKey('cpuUsage', true), }; }; @@ -184,6 +186,7 @@ export const getSystemLoadQuery = ( }, refetchOnMount: false, refetchOnWindowFocus: false, + ...generateSelectWithKey('systemLoad'), }; }; @@ -260,6 +263,7 @@ export const getSystemLoadAvgQuery = ( }, refetchOnMount: false, refetchOnWindowFocus: false, + ...generateSelectWithKey('systemLoad', true), }; }; @@ -282,6 +286,7 @@ export const getMemoryQuery = ( }, refetchOnMount: false, refetchOnWindowFocus: false, + ...generateSelectWithKey('memory'), }; }; @@ -360,6 +365,7 @@ export const getMemoryAvgQuery = ( refetchOnMount: false, refetchOnWindowFocus: false, enabled: showAvg, + ...generateSelectWithKey('memory', true), }; }; @@ -397,6 +403,7 @@ export const getControlPlaneBandWidthInQuery = ( refetchOnMount: false, refetchOnWindowFocus: false, enabled: planeInterface !== '', + ...generateSelectWithKey('controlPlaneBandwidthIn'), }; }; @@ -420,6 +427,7 @@ export const getControlPlaneBandWidthOutQuery = ( refetchOnMount: false, refetchOnWindowFocus: false, enabled: planeInterface !== '', + ...generateSelectWithKey('controlPlaneBandwidthOut'), }; }; @@ -461,6 +469,7 @@ export const getControlPlaneBandWidthAvgInQuery = ( refetchOnMount: false, refetchOnWindowFocus: false, enabled: !!(showAvg && Object.keys(nodesIPsInfo).length), + ...generateSelectWithKey('controlPlaneBandwidthIn', true), }; }; @@ -503,6 +512,7 @@ export const getControlPlaneBandWidthAvgOutQuery = ( refetchOnMount: false, refetchOnWindowFocus: false, enabled: !!(showAvg && Object.keys(nodesIPsInfo).length), + ...generateSelectWithKey('controlPlaneBandwidthOut', true), }; }; @@ -780,6 +790,7 @@ export const getIOPSWriteQuery = ( }, refetchOnMount: false, refetchOnWindowFocus: false, + ...generateSelectWithKey('IOPSWrite'), }; }; @@ -802,6 +813,7 @@ export const getIOPSReadQuery = ( }, refetchOnMount: false, refetchOnWindowFocus: false, + ...generateSelectWithKey('IOPSRead'), }; }; @@ -826,6 +838,7 @@ export const getIOPSWriteAvgQuery = ( refetchOnMount: false, refetchOnWindowFocus: false, enabled: showAvg, + ...generateSelectWithKey('IOPSWrite', true), }; }; @@ -850,6 +863,7 @@ export const getIOPSReadAvgQuery = ( refetchOnMount: false, refetchOnWindowFocus: false, enabled: showAvg, + ...generateSelectWithKey('IOPSRead', true), }; }; diff --git a/ui/src/services/utils.js b/ui/src/services/utils.js index 9d2a84ab1c..4108a7e193 100644 --- a/ui/src/services/utils.js +++ b/ui/src/services/utils.js @@ -577,3 +577,14 @@ export const formatDateToMid1 = (isoDate: string): string => { const minute = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); return `${year}-${month}-${day} ${hour}:${minute}`; }; + +/* A convenience function for creating select post-request processing + * (used by react-query, see https://tanstack.com/query/v4/docs/reference/useQuery) + * It's useful when utilizing useQueries to perform multiple requests in paralel. + * Then you're not sure in which order the responses will arrive + * (useQueries returns an array and the order of response objects is not always the + * same as the order in which the request were performed) + */ +export const generateSelectWithKey = (key, isAverage) => ({ + select: (data) => ({ ...data, key: !isAverage ? key : `${key}Avg` }), +});