diff --git a/mocks/data/ADDRESSES.json b/mocks/data/ADDRESSES.json index ef2bd8dc..ce5a6d1a 100644 --- a/mocks/data/ADDRESSES.json +++ b/mocks/data/ADDRESSES.json @@ -23,18 +23,7 @@ "currentFlows": 0 } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202292177172, - "timeRangeEnd": 1674203192177172, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203192177175, - "elapsed": 770, "count": 2, "timeRangeCount": 2, "totalCount": 2 diff --git a/mocks/data/ADDRESS_FLOW_PAIRS.json b/mocks/data/ADDRESS_FLOW_PAIRS.json index ccda98bf..175df290 100644 --- a/mocks/data/ADDRESS_FLOW_PAIRS.json +++ b/mocks/data/ADDRESS_FLOW_PAIRS.json @@ -151,18 +151,7 @@ "processAggregateId": "process-op-to-process-2-xy" } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202689930069, - "timeRangeEnd": 1674203589930069, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203589930073, - "elapsed": 149, "count": 3, "timeRangeCount": 3, "totalCount": 3 diff --git a/mocks/data/ADDRESS_PROCESSES.json b/mocks/data/ADDRESS_PROCESSES.json index 9d455eff..9768e06d 100644 --- a/mocks/data/ADDRESS_PROCESSES.json +++ b/mocks/data/ADDRESS_PROCESSES.json @@ -31,19 +31,8 @@ "processRole": "external" } ], - "queryParams": { - "offset": 0, - "limit": 10, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202685914945, - "timeRangeEnd": 1674203585914945, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203585914961, - "elapsed": 97, - "count": 1, - "timeRangeCount": 1, - "totalCount": 1 + "count": 2, + "timeRangeCount": 2, + "totalCount": 2 } diff --git a/mocks/data/COLLECTORS.json b/mocks/data/COLLECTORS.json index 46b79ce4..af6bcd84 100644 --- a/mocks/data/COLLECTORS.json +++ b/mocks/data/COLLECTORS.json @@ -4,25 +4,10 @@ "recType": "COLLECTOR", "identity": "8fbf901a-28b9-4b9d-a2ba-55affed5ee2c", "startTime": 1679429189022217, - "endTime": 0, - "PrometheusHost": "", - "PrometheusAuthMethod": "", - "PrometheusUser": "", - "PrometheusPassword": "" + "endTime": 0 } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1679519682125413, - "timeRangeEnd": 1679520582125413, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1679520582125418, - "elapsed": 24, "count": 1, "timeRangeCount": 1, "totalCount": 1 diff --git a/mocks/data/FLOW_PAIRS.json b/mocks/data/FLOW_PAIRS.json index 1014ddfe..b73887d4 100644 --- a/mocks/data/FLOW_PAIRS.json +++ b/mocks/data/FLOW_PAIRS.json @@ -151,18 +151,7 @@ "processAggregateId": "process-op-to-process-2-xy" } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202689930069, - "timeRangeEnd": 1674203589930069, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203589930073, - "elapsed": 149, "count": 3, "timeRangeCount": 3, "totalCount": 3 diff --git a/mocks/data/HOSTS.json b/mocks/data/HOSTS.json index 21532a72..cc600927 100644 --- a/mocks/data/HOSTS.json +++ b/mocks/data/HOSTS.json @@ -1,17 +1,6 @@ { "results": [], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202267815874, - "timeRangeEnd": 1674203167815874, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203167815878, - "elapsed": 19, "count": 0, "timeRangeCount": 0, "totalCount": 0 diff --git a/mocks/data/LINKS.json b/mocks/data/LINKS.json index 9ebffd88..b787ce12 100644 --- a/mocks/data/LINKS.json +++ b/mocks/data/LINKS.json @@ -67,18 +67,7 @@ "direction": "outgoing" } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202313097192, - "timeRangeEnd": 1674203213097192, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203213097194, - "elapsed": 65, "count": 6, "timeRangeCount": 6, "totalCount": 6 diff --git a/mocks/data/PROCESSES.json b/mocks/data/PROCESSES.json index a6440ead..a7d2d174 100644 --- a/mocks/data/PROCESSES.json +++ b/mocks/data/PROCESSES.json @@ -91,18 +91,7 @@ "processRole": "external" } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202236115671, - "timeRangeEnd": 1674203136115671, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203136115674, - "elapsed": 1347, "count": 6, "timeRangeCount": 6, "totalCount": 6 diff --git a/mocks/data/PROCESS_GROUPS.json b/mocks/data/PROCESS_GROUPS.json index 8bda9ed9..897cf4f5 100644 --- a/mocks/data/PROCESS_GROUPS.json +++ b/mocks/data/PROCESS_GROUPS.json @@ -25,18 +25,7 @@ "processGroupRole": "external" } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202366943924, - "timeRangeEnd": 1674203266943924, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203266943928, - "elapsed": 511, "count": 3, "timeRangeCount": 3, "totalCount": 3 diff --git a/mocks/data/PROCESS_GROUP_PAIRS.json b/mocks/data/PROCESS_GROUP_PAIRS.json index 73558b2a..ab50b42a 100644 --- a/mocks/data/PROCESS_GROUP_PAIRS.json +++ b/mocks/data/PROCESS_GROUP_PAIRS.json @@ -25,19 +25,8 @@ "destinationName": "component 1" } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202522056487, - "timeRangeEnd": 1674203422056487, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203422056491, - "elapsed": 421, - "count": 1, - "timeRangeCount": 1, - "totalCount": 1 + "count": 2, + "timeRangeCount": 2, + "totalCount": 2 } diff --git a/mocks/data/PROCESS_PAIRS.json b/mocks/data/PROCESS_PAIRS.json index bf638a27..258f0992 100644 --- a/mocks/data/PROCESS_PAIRS.json +++ b/mocks/data/PROCESS_PAIRS.json @@ -79,19 +79,8 @@ "protocol": "http1" } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202548995043, - "timeRangeEnd": 1674203448995043, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203448995047, - "elapsed": 412, - "count": 3, - "timeRangeCount": 3, - "totalCount": 3 + "count": 6, + "timeRangeCount": 6, + "totalCount": 6 } diff --git a/mocks/data/ROUTERS.json b/mocks/data/ROUTERS.json index 457b7949..0ef89b1a 100644 --- a/mocks/data/ROUTERS.json +++ b/mocks/data/ROUTERS.json @@ -40,18 +40,7 @@ "buildVersion": "UNKNOWN" } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202218894130, - "timeRangeEnd": 1674203118894130, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203118894133, - "elapsed": 64, "count": 3, "timeRangeCount": 3, "totalCount": 3 diff --git a/mocks/data/SITES.json b/mocks/data/SITES.json index ebe80389..40342e5d 100644 --- a/mocks/data/SITES.json +++ b/mocks/data/SITES.json @@ -6,7 +6,8 @@ "startTime": 1674048683000000, "endTime": 0, "name": "site 1", - "nameSpace": "config-grpc-site-1-test" + "nameSpace": "config-grpc-site-1-test", + "siteVersion": "1.5.0" }, { "recType": "SITE", @@ -14,7 +15,8 @@ "startTime": 1674048662000000, "endTime": 0, "name": "site 2", - "nameSpace": "config-grpc-site-2-test" + "nameSpace": "config-grpc-site-2-test", + "siteVersion": "1.5.0" }, { "recType": "SITE", @@ -22,7 +24,8 @@ "startTime": 1674048705000000, "endTime": 0, "name": "site 3", - "nameSpace": "config-grpc-site-3-test" + "nameSpace": "config-grpc-site-3-test", + "siteVersion": "1.5.0" }, { "recType": "SITE", @@ -30,7 +33,8 @@ "startTime": 1674048705000000, "endTime": 0, "name": "site 4", - "nameSpace": "config-grpc-site-4-test" + "nameSpace": "config-grpc-site-4-test", + "siteVersion": "1.5.0" }, { "recType": "SITE", @@ -38,21 +42,11 @@ "startTime": 1674048705000000, "endTime": 0, "name": "site 5", - "nameSpace": "config-grpc-site-5-test" + "nameSpace": "config-grpc-site-5-test", + "siteVersion": "1.5.0" } ], - "queryParams": { - "offset": -1, - "limit": -1, - "sortBy": "identity.asc", - "filter": "", - "timeRangeStart": 1674202183865932, - "timeRangeEnd": 1674203083865932, - "timeRangeOperation": 0 - }, "status": "", - "timestamp": 1674203083865935, - "elapsed": 105, "count": 5, "timeRangeCount": 5, "totalCount": 5 diff --git a/mocks/server.ts b/mocks/server.ts index 359017bd..7aa82f83 100644 --- a/mocks/server.ts +++ b/mocks/server.ts @@ -48,7 +48,8 @@ for (let i = 0; i < ITEMS_TEST; i++) { startTime: 1674048705000000, endTime: 0, name: `site ${i}`, - nameSpace: `config-grpc-site-${i}-test` + nameSpace: `config-grpc-site-${i}-test`, + siteVersion: 'x.x.x' }); } diff --git a/src/API/REST.interfaces.ts b/src/API/REST.interfaces.ts index 91beacea..636d9c25 100644 --- a/src/API/REST.interfaces.ts +++ b/src/API/REST.interfaces.ts @@ -30,13 +30,10 @@ export interface HTTPError extends AxiosError { export type ResponseWrapper = { results: T; // Type based on the Response interface - totalCount: number; + status: string; // this field is for debug scope. Empty value => OK. In case we have some internal BE error that is not a http status this field is not empty. For example a value can be `Malformed sortBy query` count: number; timeRangeCount: number; - status: string; - timestamp: number; - elapsed: number; - queryParams: RequestOptions; + totalCount: number; }; /* Response Interfaces */ @@ -52,6 +49,7 @@ interface BaseResponse { export interface SiteResponse extends BaseResponse { name: string; nameSpace: string; + siteVersion: string; } export interface ProcessGroupResponse extends BaseResponse { @@ -166,8 +164,4 @@ export interface CollectorsResponse { identity: string; startTime: number; endTime: number; - PrometheusHost: string; - PrometheusAuthMethod: string; - PrometheusUser: string; - PrometheusPassword: string; } diff --git a/src/App.tsx b/src/App.tsx index a22da032..0541f9ef 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,7 +3,6 @@ import { Suspense } from 'react'; import { Page } from '@patternfly/react-core'; import { useQuery } from '@tanstack/react-query'; import { ErrorBoundary } from 'react-error-boundary'; -import { useLocation } from 'react-router-dom'; import { RESTApi } from '@API/REST.api'; import { setCollectorStartTime } from '@config/config'; @@ -12,12 +11,9 @@ import AppContent from '@layout/AppContent'; import Header from '@layout/Header'; import SideBar from '@layout/SideBar'; import Console from '@pages/shared/Errors/Console'; -import { TopologyRoutesPaths } from '@pages/Topology/Topology.enum'; import { routes } from 'routes'; const App = function () { - const { pathname } = useLocation(); - const { data: collector } = useQuery(['app-getPrometheusURL'], () => RESTApi.fetchCollectors()); if (collector) { @@ -28,7 +24,7 @@ const App = function () { } sidebar={} - breadcrumb={pathname !== TopologyRoutesPaths.Topology && } + breadcrumb={} isManagedSidebar className="app-main-container" > diff --git a/src/core/components/Graph/Graph.interfaces.ts b/src/core/components/Graph/Graph.interfaces.ts index 49f912ab..56cbe2ae 100644 --- a/src/core/components/Graph/Graph.interfaces.ts +++ b/src/core/components/Graph/Graph.interfaces.ts @@ -9,8 +9,8 @@ export interface GraphNode { img?: string; }; style?: Record; - x?: number; - y?: number; + x: number | null; + y: number | null; } export interface GraphCombo { @@ -46,8 +46,20 @@ export interface GraphReactAdaptorProps { }; } -export interface LocalStorageData { - id: string; +export interface LocalStorageDataSavedPayload { x: number; y: number; } + +export interface LocalStorageDataSaved { + [key: string]: LocalStorageDataSavedPayload; +} + +export interface LocalStorageData extends LocalStorageDataSavedPayload { + id: string; +} + +export interface LocalStorageDataWithNullXY extends Omit { + x: number | null; + y: number | null; +} diff --git a/src/core/components/Graph/GraphReactAdaptor.tsx b/src/core/components/Graph/GraphReactAdaptor.tsx index fe7b6462..c0f94a81 100644 --- a/src/core/components/Graph/GraphReactAdaptor.tsx +++ b/src/core/components/Graph/GraphReactAdaptor.tsx @@ -1,6 +1,6 @@ import { FC, memo, useCallback, useEffect, useRef, useState } from 'react'; -import G6, { Graph, ICombo, IEdge, INode, NodeConfig } from '@antv/g6'; +import G6, { Graph, ICombo, IEdge, INode } from '@antv/g6'; import { GraphEdge, @@ -98,7 +98,7 @@ const GraphReactAdaptor: FC = memo( if ($node && nodes.length && !topologyGraphRef.current) { const legend = legendData ? createLegend(legendData) : ''; - const topologyGraph = new G6.Graph({ + topologyGraphRef.current = new G6.Graph({ container: $node, width: $node.scrollWidth, height: $node.scrollHeight, @@ -136,6 +136,7 @@ const GraphReactAdaptor: FC = memo( defaultEdge: DEFAULT_EDGE_CONFIG, nodeStateStyles: DEFAULT_NODE_STATE_CONFIG }); + const topologyGraph = topologyGraphRef.current; topologyGraph.on('node:click', ({ item }) => { if (item) { @@ -162,8 +163,16 @@ const GraphReactAdaptor: FC = memo( if (item) { topologyGraph.updateItem(item, { style: { cursor: 'pointer' } }); - const { id, x, y } = item.getModel() as NodeConfig; - GraphController.saveDataInLocalStorage([{ id, x, y }]); + const updatedNodes = GraphController.fromNodesToLocalStorageData( + [item as INode], + ({ id, x, y }: LocalStorageData) => ({ + id, + x, + y + }) + ); + + GraphController.saveDataInLocalStorage(updatedNodes); } }); @@ -293,16 +302,31 @@ const GraphReactAdaptor: FC = memo( topologyGraph.updateItem(combo, { style: { cursor: 'pointer' } }); // Retrieve the nodes contained within the combo box and store their positions in memory - const { nodes: comboNodes } = topologyGraph.getComboChildren(combo); + const updatedNodes = GraphController.fromNodesToLocalStorageData( + combo.getNodes(), + ({ id, x, y }: LocalStorageData) => ({ id, x, y }) + ); - comboNodes.forEach((comboNode) => { - const { id, x, y } = comboNode.getModel() as NodeConfig; - GraphController.saveDataInLocalStorage([{ id, x, y }]); - }); + GraphController.saveDataInLocalStorage(updatedNodes); } }); } + topologyGraph.on('canvas:dragend', ({ dx, dy }: { dx?: number; dy?: number }) => { + if (dx !== undefined && dy !== undefined) { + const updatedNodes = GraphController.fromNodesToLocalStorageData( + topologyGraph.getNodes(), + ({ id, x, y }: LocalStorageData) => ({ + id, + x: x + dx, + y: y + dy + }) + ); + + GraphController.saveDataInLocalStorage(updatedNodes); + } + }); + topologyGraph.on('wheelzoom', () => { const zoomValue = topologyGraph.getZoom(); @@ -312,13 +336,10 @@ const GraphReactAdaptor: FC = memo( }); topologyGraph.on('afterlayout', () => { - if (!topologyGraphRef.current) { - topologyGraphRef.current = topologyGraph; - prevNodesRef.current = nodes; - prevEdgesRef.current = edges; - prevCombosRef.current = combos; - setIsGraphLoaded(true); - } + prevNodesRef.current = nodes; + prevEdgesRef.current = edges; + prevCombosRef.current = combos; + setIsGraphLoaded(true); }); registerCustomBehaviours(); @@ -355,24 +376,12 @@ const GraphReactAdaptor: FC = memo( const graphInstance = topologyGraphRef.current; if (isGraphLoaded && graphInstance) { - const updateNodes = graphInstance - .getNodes() - .map((node) => { - const model = node.getModel(); - - if (model.id) { - return { - id: model.id, - x: model.x, - y: model.y - }; - } - - return undefined; - }) - .filter(Boolean) as LocalStorageData[]; + const updatedNodes = GraphController.fromNodesToLocalStorageData( + graphInstance.getNodes(), + ({ id, x, y }: LocalStorageData) => ({ id, x, y }) + ); - GraphController.saveDataInLocalStorage(updateNodes); + GraphController.saveDataInLocalStorage(updatedNodes); } }, [isGraphLoaded]); diff --git a/src/core/components/Graph/services.ts b/src/core/components/Graph/services.ts index 3011f354..19a507c7 100644 --- a/src/core/components/Graph/services.ts +++ b/src/core/components/Graph/services.ts @@ -1,30 +1,44 @@ -import { GraphCombo, GraphEdge, GraphNode, LocalStorageData } from './Graph.interfaces'; +import { INode, NodeConfig } from '@antv/g6'; + +import { + GraphCombo, + GraphEdge, + GraphNode, + LocalStorageData, + LocalStorageDataSaved, + LocalStorageDataSavedPayload, + LocalStorageDataWithNullXY +} from './Graph.interfaces'; const prefixLocalStorageItem = 'skupper'; export const GraphController = { - saveDataInLocalStorage: (topologyNodes: Partial[]) => { - topologyNodes.forEach(({ id, x, y }) => { - if (id && x && y) { - //save the position of the node to the local storage - localStorage.setItem(`${prefixLocalStorageItem}-${id}`, JSON.stringify({ x, y })); + saveDataInLocalStorage: (topologyNodes: LocalStorageData[]) => { + const cache = JSON.parse(localStorage.getItem(prefixLocalStorageItem) || '{}'); + + const topologyMap = topologyNodes.reduce((acc, { id, x, y }) => { + if (id) { + acc[id] = { x, y }; } - }); + + return acc; + }, {} as LocalStorageDataSaved); + + localStorage.setItem(prefixLocalStorageItem, JSON.stringify({ ...cache, ...topologyMap })); }, - getPositionFromLocalStorage(id: string): LocalStorageData { - const positions = localStorage.getItem(`${prefixLocalStorageItem}-${id}`); + getPositionFromLocalStorage(id: string): LocalStorageDataWithNullXY { + const cache = JSON.parse(localStorage.getItem(prefixLocalStorageItem) || '{}'); + const positions = cache[id] as LocalStorageDataSavedPayload | undefined; - const x = positions ? JSON.parse(positions).x : null; - const y = positions ? JSON.parse(positions).y : null; + const x = positions ? positions.x : null; + const y = positions ? positions.y : null; return { id, x, y }; }, cleanPositionsFromLocalStorage() { - Object.keys(localStorage) - .filter((x) => x.startsWith(prefixLocalStorageItem)) - .forEach((x) => localStorage.removeItem(x)); + localStorage.removeItem(prefixLocalStorageItem); }, cleanPositionsControlsFromLocalStorage(suffix: string) { @@ -42,6 +56,21 @@ export const GraphController = { return edges.filter(({ source, target }) => availableNodesMap[source] && availableNodesMap[target]); }, + + fromNodesToLocalStorageData(nodes: INode[], setLocalStorageData: Function) { + return nodes + .map((node) => { + const { id, x, y } = node.getModel() as NodeConfig; + + if (id && x !== undefined && y !== undefined) { + return setLocalStorageData({ id, x, y }); + } + + return undefined; + }) + .filter(Boolean) as LocalStorageData[]; + }, + getG6Model: ({ nodes, edges, combos }: { nodes: GraphNode[]; edges: GraphEdge[]; combos?: GraphCombo[] }) => ({ nodes: JSON.parse( JSON.stringify( @@ -56,6 +85,7 @@ export const GraphController = { edges: JSON.parse(JSON.stringify(GraphController.sanitizeEdges(nodes, edges))), combos: combos ? JSON.parse(JSON.stringify(combos)) : undefined }), + calculateMaxIteration(nodeCount: number) { if (nodeCount > 900) { return 10; diff --git a/src/core/components/Wrapper.tsx b/src/core/components/Wrapper.tsx index 1ada8c36..a386b9b1 100644 --- a/src/core/components/Wrapper.tsx +++ b/src/core/components/Wrapper.tsx @@ -1,4 +1,4 @@ -import { ReactElement } from 'react'; +import { ReactElement, useEffect, useState } from 'react'; import { QueryCache, QueryClient, QueryClientConfig, QueryClientProvider } from '@tanstack/react-query'; import { HashRouter, useLocation, useNavigate } from 'react-router-dom'; @@ -16,27 +16,41 @@ const QueryClientContext = function ({ const navigate = useNavigate(); const location = useLocation(); - const queryClient = new QueryClient({ - ...queryClientConfig, - ...config, - queryCache: new QueryCache({ - onError: (error) => { - handleError(error as { httpStatus?: string }); + const [queryClient, setQueryClient] = useState(); + + useEffect(() => { + const handleError = function ({ + httpStatus, + message, + code + }: { + httpStatus?: string; + message?: string; + code?: string; + }) { + if (httpStatus) { + navigate(ErrorRoutesPaths.error.HttpError, { state: { message, code } }); + } else if (!httpStatus) { + navigate(ErrorRoutesPaths.error.ErrConnection, { + state: { pathname: location.pathname, message, code } + }); } - }) - }); - - function handleError({ httpStatus, message, code }: { httpStatus?: string; message?: string; code?: string }) { - if (httpStatus) { - navigate(ErrorRoutesPaths.error.HttpError, { state: { message, code } }); - } else if (!httpStatus) { - navigate(ErrorRoutesPaths.error.ErrConnection, { - state: { pathname: location.pathname, message, code } - }); - } - } - - return {children}; + }; + + const queryClientInstance = new QueryClient({ + ...queryClientConfig, + ...config, + queryCache: new QueryCache({ + onError: (error) => { + handleError(error as { httpStatus?: string }); + } + }) + }); + + setQueryClient(queryClientInstance); + }, []); + + return queryClient ? {children} : null; }; export const Wrapper = function ({ children, config }: { config?: QueryClientConfig; children: ReactElement }) { diff --git a/src/pages/Sites/Sites.constant.ts b/src/pages/Sites/Sites.constant.ts index 2a210d2c..ecd4c20e 100644 --- a/src/pages/Sites/Sites.constant.ts +++ b/src/pages/Sites/Sites.constant.ts @@ -1,17 +1,34 @@ import { SiteResponse } from '@API/REST.interfaces'; +import { SKColumn } from '@core/components/SkTable/SkTable.interface'; +import { timeAgo } from '@core/utils/timeAgo'; -import { SiteLabels, SitesRoutesPaths, SitesTableColumns } from './Sites.enum'; +import { Labels, SitesRoutesPaths } from './Sites.enum'; export const SitesPaths = { path: SitesRoutesPaths.Sites, - name: SiteLabels.Section + name: Labels.Section }; -export const siteColumns = [ +export const siteColumns: SKColumn[] = [ { - name: SitesTableColumns.Name, + name: Labels.Name, prop: 'name' as keyof SiteResponse, customCellName: 'linkCell' + }, + { + name: Labels.Namespace, + prop: 'nameSpace' as keyof SiteResponse + }, + { + name: Labels.SiteVersion, + prop: 'siteVersion' as keyof SiteResponse, + width: 10 + }, + { + name: Labels.Created, + prop: 'startTime' as keyof SiteResponse, + format: timeAgo, + width: 10 } ]; diff --git a/src/pages/Sites/Sites.enum.ts b/src/pages/Sites/Sites.enum.ts index 9c39e118..dc594a60 100644 --- a/src/pages/Sites/Sites.enum.ts +++ b/src/pages/Sites/Sites.enum.ts @@ -2,7 +2,7 @@ export enum SitesRoutesPaths { Sites = '/sites' } -export enum SiteLabels { +export enum Labels { Section = 'Sites', Description = 'Set of applications running under the same geographical area', Details = 'Details', @@ -11,13 +11,7 @@ export enum SiteLabels { Processes = 'Processes', Name = 'Name', Namespace = 'Namespace', - GoToTopology = 'Go to the Topology view' -} - -export enum SitesTableColumns { - Name = 'Name', - NameSpace = 'Namespace', - NumHosts = 'Hosts', - NumProcesses = 'Processes', - NumSitesLinked = 'Links' + GoToTopology = 'Go to the Topology view', + SiteVersion = 'Version', + Created = 'Created' } diff --git a/src/pages/Sites/views/Site.tsx b/src/pages/Sites/views/Site.tsx index 81fe703f..76a10e93 100644 --- a/src/pages/Sites/views/Site.tsx +++ b/src/pages/Sites/views/Site.tsx @@ -29,7 +29,7 @@ import { TopologyRoutesPaths, TopologyURLFilters, TopologyViews } from '@pages/T import SitesController from '../services'; import { QueriesSites } from '../services/services.enum'; -import { SitesRoutesPaths, SiteLabels } from '../Sites.enum'; +import { SitesRoutesPaths, Labels } from '../Sites.enum'; const processQueryParams = { endTime: 0 }; @@ -75,7 +75,7 @@ const Site = function () { return null; } - const { name, nameSpace } = site; + const { name, nameSpace, siteVersion } = site; const { targetIds } = SitesController.bindLinksWithSiteIds([site], links, routers)[0]; const linkedSites = sites.filter(({ identity }) => targetIds.includes(identity)); @@ -95,13 +95,15 @@ const Site = function () { - {SiteLabels.Details} + {Labels.Details} - {SiteLabels.Namespace} + {Labels.Namespace} {nameSpace} + {Labels.SiteVersion} + {siteVersion} @@ -111,7 +113,7 @@ const Site = function () { - {SiteLabels.Links} + {Labels.Links} {(!!linkedSites.length && ( @@ -133,7 +135,7 @@ const Site = function () { - {SiteLabels.Hosts} + {Labels.Hosts} {(!!hosts.length && ( @@ -150,7 +152,7 @@ const Site = function () { - {SiteLabels.Processes} + {Labels.Processes} {(!!processes.length && ( diff --git a/src/pages/Sites/views/Sites.tsx b/src/pages/Sites/views/Sites.tsx index 7eec25ab..bfcd3d49 100644 --- a/src/pages/Sites/views/Sites.tsx +++ b/src/pages/Sites/views/Sites.tsx @@ -13,7 +13,7 @@ import { SiteResponse } from 'API/REST.interfaces'; import { QueriesSites } from '../services/services.enum'; import { siteColumns } from '../Sites.constant'; -import { SiteLabels, SitesRoutesPaths } from '../Sites.enum'; +import { Labels, SitesRoutesPaths } from '../Sites.enum'; const Sites = function () { const { data: sites, isLoading } = useQuery([QueriesSites.GetSites], () => RESTApi.fetchSites()); @@ -29,7 +29,7 @@ const Sites = function () { return (
- + - entities.map(({ identity, name: label }, index) => { + entities.map(({ identity, name, siteVersion }, index) => { const { x, y } = GraphController.getPositionFromLocalStorage(identity); const color = getColor(index); const img = siteSVG; @@ -42,7 +42,7 @@ export const TopologyController = { shadowColor: color }; - return convertEntityToNode({ id: identity, label, x, y, img, nodeConfig: { style } }); + return convertEntityToNode({ id: identity, label: `${name} (${siteVersion})`, x, y, img, nodeConfig: { style } }); }), convertProcessGroupsToNodes: (entities: ProcessGroupResponse[]): GraphNode[] => @@ -152,6 +152,7 @@ export const TopologyController = { ]) ); }, + addMetricsToLinks: ( links: GraphEdge[], byteRateByProcessPairs?: PrometheusApiSingleResult[],