Skip to content

Commit

Permalink
refactor(Topology): ♻️ Update modal for node and edge details
Browse files Browse the repository at this point in the history
  • Loading branch information
bartoval committed Nov 28, 2023
1 parent 4e5e999 commit 12e301d
Show file tree
Hide file tree
Showing 27 changed files with 595 additions and 366 deletions.
9 changes: 0 additions & 9 deletions cypress/e2e/skupper-console/sites/site.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,4 @@ context('Sites', () => {
link.click();
cy.url().should('eq', expectedUrl);
});

it('should navigate to the correct process page when clicking on a link', () => {
const expectedUrl = 'http://localhost:3000/#/sites';

const link = cy.get(`[data-testid="${getTestsIds.breadcrumbComponent()}"]`).contains('sites');
link.should('have.attr', 'href', `#/sites`);
link.click();
cy.url().should('eq', expectedUrl);
});
});
54 changes: 35 additions & 19 deletions mocks/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
ProcessGroupPairsResponse
} from 'API/REST.interfaces';

const DELAY_RESPONSE = 0;
const DELAY_RESPONSE = Number(process.env.MOCK_DELAY_RESPONSE) || 0; // in ms
const ITEM_COUNT = Number(process.env.MOCK_ITEM_COUNT) || 0;

// api prefix
const prefix = '/api/v1alpha1';
Expand All @@ -36,15 +37,13 @@ const serviceFlowPairs = require(`${path}/SERVICE_FLOW_PAIRS.json`);
const routers: ResponseWrapper<RouterResponse[]> = require(`${path}/ROUTERS.json`);
const links: ResponseWrapper<LinkResponse[]> = require(`${path}/LINKS.json`);

const PERF_TEST = false;
const ITEMS_TEST = 200;
interface ApiProps {
params: Record<string, string>;
queryParams: Record<string, string[] | string | null | undefined>;
queryParams: Record<string, string[] | string | null | number | undefined>;
}

const mockSitesForPerf: SiteResponse[] = [];
for (let i = 0; i < ITEMS_TEST; i++) {
for (let i = 0; i < ITEM_COUNT; i++) {
mockSitesForPerf.push({
recType: 'SITE',
identity: `sitePerf${i}`,
Expand Down Expand Up @@ -76,8 +75,8 @@ mockSitesForPerf.forEach((site, index) => {

const mockLinksForPerf: LinkResponse[] = [];
mockRoutersForPerf.forEach((_, index) => {
const idx1 = Math.floor(Math.random() * ITEMS_TEST);
const idx2 = Math.floor(Math.random() * ITEMS_TEST);
const idx1 = Math.floor(Math.random() * ITEM_COUNT);
const idx2 = Math.floor(Math.random() * ITEM_COUNT);

const router1 = mockRoutersForPerf[idx1];
const router2 = mockRoutersForPerf[idx2];
Expand Down Expand Up @@ -109,7 +108,7 @@ mockRoutersForPerf.forEach((_, index) => {
});

const mockProcessesForPerf: ProcessResponse[] = [];
for (let i = 0; i < ITEMS_TEST; i++) {
for (let i = 0; i < ITEM_COUNT; i++) {
// const parent = Math.floor(Math.random() * (4 - 1 + 1) + 1);
const process = processes.results[i % processes.results.length];

Expand All @@ -125,7 +124,7 @@ for (let i = 0; i < ITEMS_TEST; i++) {
}

const mockProcessPairsForPerf: ProcessPairsResponse[] = [];
for (let i = 0; i < ITEMS_TEST; i++) {
for (let i = 0; i < ITEM_COUNT; i++) {
const sourceIndex = Math.floor(Math.random() * mockProcessesForPerf.length);
const destinationIndex = Math.floor(Math.random() * mockProcessesForPerf.length);

Expand All @@ -146,7 +145,7 @@ export const MockApi = {
get404Error: () => new Response(404),
getCollectors: () => collectors,
getSites: () => {
const sitesForPerfTests = PERF_TEST ? mockSitesForPerf : [];
const sitesForPerfTests = ITEM_COUNT ? mockSitesForPerf : [];
const results = [...sites.results, ...sitesForPerfTests];

return { ...sites, results };
Expand All @@ -155,13 +154,13 @@ export const MockApi = {
results: sites.results.find(({ identity }) => identity === id) || []
}),
getRouters: () => {
const routersForPerfTests = PERF_TEST ? mockRoutersForPerf : [];
const routersForPerfTests = ITEM_COUNT ? mockRoutersForPerf : [];
const results = [...routers.results, ...routersForPerfTests];

return { ...routers, results };
},
getLinks: () => {
const linksForPerfTests = PERF_TEST ? mockLinksForPerf : [];
const linksForPerfTests = ITEM_COUNT ? mockLinksForPerf : [];
const results = [...links.results, ...linksForPerfTests];

return { ...links, results };
Expand All @@ -173,25 +172,42 @@ export const MockApi = {
return { results };
},
getProcesses: (_: unknown, { queryParams }: ApiProps) => {
const processesForPerfTests = PERF_TEST ? mockProcessesForPerf : [];
const processesForPerfTests = ITEM_COUNT ? mockProcessesForPerf : [];
const results = [...processes.results, ...processesForPerfTests];

if (queryParams && !Object.keys(queryParams).length) {
return { ...processes, results };
return {
...processes,
results,
count: results.length,
totalCount: results.length,
timeRangeCount: results.length
};
}

const resultFIltered = results.filter(
const filteredResults = results.filter(
(result) =>
result.processRole === queryParams.processRole ||
result.groupIdentity === queryParams.groupIdentity ||
result.parent === queryParams.parent
);

return { ...processes, results: resultFIltered };
const paginatedResults = filteredResults.slice(
Number(queryParams.offset || 0),
Number(queryParams.offset || 0) + Number(queryParams.limit || filteredResults.length - 1)
);

return {
...processes,
results: paginatedResults,
count: filteredResults.length,
totalCount: filteredResults.length,
timeRangeCount: filteredResults.length
};
},

getProcessPairs: (_: unknown, { queryParams }: ApiProps) => {
const processesForPerfTests = PERF_TEST ? mockProcessPairsForPerf : [];
const processesForPerfTests = ITEM_COUNT ? mockProcessPairsForPerf : [];
const results = [...processPairs.results, ...processesForPerfTests];

if (queryParams && !Object.keys(queryParams).length) {
Expand All @@ -207,7 +223,7 @@ export const MockApi = {
},

getPrometheusQuery: (_: unknown, { queryParams }: ApiProps) => {
if (queryParams.query === 'sum by(destProcess, sourceProcess,direction)(rate(octets_total[1m]))') {
if ((queryParams.query as string)?.includes('sum by(destProcess, sourceProcess, direction)(rate(octets_total')) {
return {
data: {
resultType: 'vector',
Expand Down Expand Up @@ -330,7 +346,7 @@ export function loadMockServer() {
}));

this.get(`${prefix}/processes/:id`, (_, { params: { id } }) => ({
results: (PERF_TEST ? mockProcessesForPerf : processes.results).find(
results: MockApi.getProcesses(null, { params: {}, queryParams: {} }).results.find(
({ identity }: ProcessResponse) => identity === id
)
}));
Expand Down
27 changes: 21 additions & 6 deletions src/API/Prometheus.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,31 +125,46 @@ export const PrometheusApi = {
return result;
},

fetchAllProcessPairsBytes: async (groupBy: string): Promise<PrometheusMetricSingleData[]> => {
fetchAllProcessPairsBytes: async (
groupBy: string,
filters?: PrometheusLabels
): Promise<PrometheusMetricSingleData[]> => {
const queryFilterString = filters ? convertToPrometheusQueryParams(filters) : undefined;

const {
data: { result }
} = await axiosFetch<PrometheusResponse<PrometheusMetricSingleData[]>>(gePrometheusQueryPATH('single'), {
params: { query: queries.getAllPairsBytes(groupBy) }
params: { query: queries.getAllPairsBytes(groupBy, queryFilterString) }
});

return result;
},

fetchAllProcessPairsByteRates: async (groupBy: string): Promise<PrometheusMetricSingleData[]> => {
fetchAllProcessPairsByteRates: async (
groupBy: string,
filters?: PrometheusLabels
): Promise<PrometheusMetricSingleData[]> => {
const queryFilterString = filters ? convertToPrometheusQueryParams(filters) : undefined;

const {
data: { result }
} = await axiosFetch<PrometheusResponse<PrometheusMetricSingleData[]>>(gePrometheusQueryPATH('single'), {
params: { query: queries.getAllPairsByteRates(groupBy) }
params: { query: queries.getAllPairsByteRates(groupBy, queryFilterString) }
});

return result;
},

fetchAllProcessPairsLatencies: async (groupBy: string): Promise<PrometheusMetricSingleData[]> => {
fetchAllProcessPairsLatencies: async (
groupBy: string,
filters?: PrometheusLabels
): Promise<PrometheusMetricSingleData[]> => {
const queryFilterString = filters ? convertToPrometheusQueryParams(filters) : undefined;

const {
data: { result }
} = await axiosFetch<PrometheusResponse<PrometheusMetricSingleData[]>>(gePrometheusQueryPATH('single'), {
params: { query: queries.getAllPairsLatencies(groupBy) }
params: { query: queries.getAllPairsLatencies(groupBy, queryFilterString) }
});

return result;
Expand Down
18 changes: 15 additions & 3 deletions src/API/Prometheus.queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,27 @@ export const queries = {
},

// topology metrics
getAllPairsBytes(groupBy: string) {
getAllPairsBytes(groupBy: string, params?: string) {
if (params) {
return `sum by(${groupBy})(octets_total{${params}})`;
}

return `sum by(${groupBy})(octets_total)`;
},

getAllPairsByteRates(groupBy: string) {
getAllPairsByteRates(groupBy: string, params?: string) {
if (params) {
return `sum by(${groupBy})(rate(octets_total{${params}}[1m]))`;
}

return `sum by(${groupBy})(rate(octets_total[1m]))`;
},

getAllPairsLatencies(groupBy: string) {
getAllPairsLatencies(groupBy: string, params?: string) {
if (params) {
return `sum by(${groupBy})(rate(flow_latency_microseconds_sum{${params}}[1m]))`;
}

return `sum by(${groupBy})(rate(flow_latency_microseconds_sum[1m]))`;
},

Expand Down
13 changes: 10 additions & 3 deletions src/core/components/Graph/ReactAdaptor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ const GraphReactAdaptor: FC<GraphReactAdaptorProps> = memo(
if (!graphInstance) {
return;
}
const nodeFound = graphInstance.find('node', (node) => node.getModel().id === id);

if (nodeFound) {
graphInstance.focusItem(nodeFound, true, { duration: 100 });
if (id) {
handleMouseEnter(id);
graphInstance.focusItem(id, true, { duration: 100 });

return;
}

handleNodeMouseLeave({ currentTarget: graphInstance });
}
}));
Expand Down Expand Up @@ -184,6 +184,8 @@ const GraphReactAdaptor: FC<GraphReactAdaptorProps> = memo(
/** Simulate a MouseEnter event, regardless of whether a node or edge is preselected */
const handleMouseEnter = useCallback(
(id?: string) => {
isHoverState.current = true;

const graphInstance = topologyGraphRef.current;

if (graphInstance && id) {
Expand Down Expand Up @@ -337,6 +339,11 @@ const GraphReactAdaptor: FC<GraphReactAdaptorProps> = memo(

const handleAfterRender = useCallback(() => {
handleMouseEnter(itemSelectedRef.current);

if (itemSelectedRef.current) {
topologyGraphRef.current?.focusItem(itemSelectedRef.current);
}

setIsGraphLoaded(true);
}, [handleMouseEnter]);

Expand Down
10 changes: 5 additions & 5 deletions src/core/components/HighlightValueCell/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useRef } from 'react';
import { useMemo, useRef } from 'react';

import { VarColors } from '@config/colors';

Expand All @@ -7,23 +7,23 @@ import { HighlightValueCellProps } from './HighightValueCell.interfaces';
const HighlightValueCell = function <T>({ value, format }: HighlightValueCellProps<T>) {
const prevValueRef = useRef<number>();

const isValueUpdated = useCallback(() => {
const isValueUpdated = useMemo(() => {
if (!prevValueRef.current) {
prevValueRef.current = value;

return false;
}

if (value !== prevValueRef.current) {
if (format(value) !== format(prevValueRef.current)) {
prevValueRef.current = value;

return true;
}

return false;
}, [value]);
}, [format, value]);

return isValueUpdated() ? (
return isValueUpdated ? (
<div
data-testid="highlighted-value"
style={{
Expand Down
7 changes: 5 additions & 2 deletions src/core/components/SkBreadcrumb/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Breadcrumb, BreadcrumbHeading, BreadcrumbItem } from '@patternfly/react-core';
import { Link, useLocation } from 'react-router-dom';
import { Link, useLocation, useSearchParams } from 'react-router-dom';

import { getTestsIds } from '@config/testIds';
import { getIdAndNameFromUrlParams } from '@core/utils/getIdAndNameFromUrlParams';

const SkBreadcrumb = function () {
const { pathname } = useLocation();
const [searchParams] = useSearchParams();

const paths = pathname.split('/').filter(Boolean);
const pathsNormalized = paths.map((path) => getIdAndNameFromUrlParams(path.replace(/%20/g, ' '))); //sanitize %20 url space
Expand All @@ -15,11 +16,13 @@ const SkBreadcrumb = function () {
return null;
}

const queryParams = searchParams.size > 0 ? `?${searchParams.toString()}` : '';

return (
<Breadcrumb data-testid={getTestsIds.breadcrumbComponent()}>
{pathsNormalized.map((path, index) => (
<BreadcrumbItem key={path.name} className="sk-capitalize">
<Link to={[...paths].slice(0, index + 1).join('/')}>{path.name}</Link>
<Link to={`${[...paths].slice(0, index + 1).join('/')}${queryParams}`}>{path.name}</Link>
</BreadcrumbItem>
))}

Expand Down
Loading

0 comments on commit 12e301d

Please sign in to comment.