From 76f2f13db748b7160ffa3c0dd4a0b8e42f762963 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Thu, 23 Jan 2025 16:37:41 +0100 Subject: [PATCH 1/3] Support custom metric in Affected Endpoints --- .../SpaNPlusOneInsightCard/index.tsx | 39 +++++++++++-------- .../index.tsx | 36 ++++++++--------- .../AffectedEndpointsSelector.stories.tsx | 18 +++++++-- .../EndpointOption/index.tsx | 14 ++++--- .../EndpointOption/types.ts | 9 +++-- .../AffectedEndpointsSelector/index.tsx | 23 ++++++----- .../common/AffectedEndpointsSelector/types.ts | 18 ++++++--- 7 files changed, 93 insertions(+), 64 deletions(-) diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpaNPlusOneInsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpaNPlusOneInsightCard/index.tsx index e2869ff2d..0f57a63a3 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpaNPlusOneInsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpaNPlusOneInsightCard/index.tsx @@ -21,11 +21,14 @@ import { ContentContainer, Description, Details } from "../styles"; import * as s from "./styles"; import type { SpaNPlusOneInsightCardProps } from "./types"; -const getSelectorOption = (endpoint: NPlusOneEndpointInfo): Option => ({ +const getSelectorOption = (endpoint: NPlusOneEndpointInfo): Option => ({ route: endpoint.endpointInfo.route, serviceName: endpoint.endpointInfo.serviceName, spanCodeObjectId: endpoint.endpointInfo.entrySpanCodeObjectId, - duration: endpoint.duration + metric: { + value: endpoint.occurrences, + label: "Repeats" + } }); export const SpaNPlusOneInsightCard = ({ @@ -41,21 +44,21 @@ export const SpaNPlusOneInsightCard = ({ viewMode, onDismissalChange }: SpaNPlusOneInsightCardProps) => { - const endpoints = useMemo(() => insight.endpoints ?? [], [insight.endpoints]); - const selectorOptions = useMemo( + const endpoints = useMemo( () => - endpoints - .map(getSelectorOption) - .sort((a, b) => - a.duration && b.duration ? b.duration.raw - a.duration.raw : 0 - ), - [endpoints] + (insight.endpoints ?? []).sort((a, b) => b.occurrences - a.occurrences), + [insight.endpoints] ); - const endpointWithMaxDuration = endpoints.reduce( - (acc, cur) => (acc.duration.raw >= cur.duration.raw ? acc : cur), - endpoints[0] + + const selectorOptions = useMemo( + () => endpoints.map(getSelectorOption), + [endpoints] ); - const maxDurationString = getDurationString(endpointWithMaxDuration.duration); + const endpointWithMaxDuration = + endpoints.length > 0 ? endpoints[0] : undefined; + const maxDurationString = endpointWithMaxDuration + ? getDurationString(endpointWithMaxDuration.duration) + : undefined; const { isJaegerEnabled } = useConfigSelector(); const [selectedEndpointKey, setSelectedEndpointKey] = useState< string | undefined @@ -192,9 +195,11 @@ export const SpaNPlusOneInsightCard = ({ isMarkAsReadButtonEnabled={isMarkAsReadButtonEnabled} viewMode={viewMode} mainMetric={ - - {maxDurationString} - + maxDurationString ? ( + + {maxDurationString} + + ) : undefined } onDismissalChange={onDismissalChange} /> diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanEndpointBottleneckInsightCard/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanEndpointBottleneckInsightCard/index.tsx index 0bf734510..069a7ef84 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanEndpointBottleneckInsightCard/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/SpanEndpointBottleneckInsightCard/index.tsx @@ -23,11 +23,16 @@ import { ContentContainer, Description, Details } from "../styles"; import * as s from "./styles"; import type { SpanEndpointBottleneckInsightCardProps } from "./types"; -const getSelectorOption = (endpoint: BottleneckEndpointInfo): Option => ({ +const getSelectorOption = ( + endpoint: BottleneckEndpointInfo +): Option => ({ route: trimEndpointScheme(endpoint.endpointInfo.route), serviceName: endpoint.endpointInfo.serviceName, spanCodeObjectId: endpoint.endpointInfo.spanCodeObjectId, - duration: endpoint.avgDurationWhenBeingBottleneck + metric: { + value: getDurationString(endpoint.avgDurationWhenBeingBottleneck), + label: "Requests" + } }); export const SpanEndpointBottleneckInsightCard = ({ @@ -45,30 +50,21 @@ export const SpanEndpointBottleneckInsightCard = ({ }: SpanEndpointBottleneckInsightCardProps) => { const { isJaegerEnabled } = useConfigSelector(); const slowEndpoints = useMemo( - () => insight.slowEndpoints ?? [], + () => + (insight.slowEndpoints ?? []).sort( + (a, b) => + b.avgDurationWhenBeingBottleneck.raw - + a.avgDurationWhenBeingBottleneck.raw + ), [insight.slowEndpoints] ); - const selectorOptions: Option[] = useMemo( - () => - slowEndpoints - .map(getSelectorOption) - .sort((a, b) => - a.duration && b.duration ? b.duration.raw - a.duration.raw : 0 - ), + const selectorOptions: Option[] = useMemo( + () => slowEndpoints.map(getSelectorOption), [slowEndpoints] ); const endpointWithMaxDuration = - slowEndpoints.length > 0 - ? slowEndpoints.reduce( - (acc, cur) => - acc.avgDurationWhenBeingBottleneck.raw >= - cur.avgDurationWhenBeingBottleneck.raw - ? acc - : cur, - slowEndpoints[0] - ) - : undefined; + slowEndpoints.length > 0 ? slowEndpoints[0] : undefined; const maxDurationString = endpointWithMaxDuration ? getDurationString(endpointWithMaxDuration.avgDurationWhenBeingBottleneck) : undefined; diff --git a/src/components/common/AffectedEndpointsSelector/AffectedEndpointsSelector.stories.tsx b/src/components/common/AffectedEndpointsSelector/AffectedEndpointsSelector.stories.tsx index 2c6d72294..b1e925156 100644 --- a/src/components/common/AffectedEndpointsSelector/AffectedEndpointsSelector.stories.tsx +++ b/src/components/common/AffectedEndpointsSelector/AffectedEndpointsSelector.stories.tsx @@ -23,18 +23,30 @@ export const Default: Story = { route: "test", serviceName: "someasasdasdasdasdasdasdawerereasdsadsadsadsadsadasdsadasdsadsdhfkjdhskjfgdf;lgjhdfhglkdfhgklhsdklfghkhgdfgkdfklghrthysdfhsbfheslkbyieryiobyrieuytirosynoiuybioyustest2", - spanCodeObjectId: "spanCodeObjectId1" + spanCodeObjectId: "spanCodeObjectId1", + metric: { + value: 100, + label: "100 ms" + } }, { route: "someasasdasdasdasdasdasdawerereasdsadsadsadsadsadasdsadasdsadsdhfkjdhskjfgdf;lgjhdfhglkdfhgklhsdklfghkhgdfgkdfklghrthysdfhsbfheslkbyieryiobyrieuytirosynoiuybioyustest", serviceName: "test1", - spanCodeObjectId: "spanCodeObjectId2" + spanCodeObjectId: "spanCodeObjectId2", + metric: { + value: 200, + label: "200 ms" + } }, { route: "test", serviceName: "test1", - spanCodeObjectId: "spanCodeObjectId2" + spanCodeObjectId: "spanCodeObjectId2", + metric: { + value: 300, + label: "300 ms" + } } ] } diff --git a/src/components/common/AffectedEndpointsSelector/EndpointOption/index.tsx b/src/components/common/AffectedEndpointsSelector/EndpointOption/index.tsx index 1500d2cf9..d12770d59 100644 --- a/src/components/common/AffectedEndpointsSelector/EndpointOption/index.tsx +++ b/src/components/common/AffectedEndpointsSelector/EndpointOption/index.tsx @@ -2,7 +2,7 @@ import { Tooltip } from "../../v3/Tooltip"; import * as s from "./styles"; import type { EndpointOptionProps } from "./types"; -export const EndpointOption = ({ +export const EndpointOption = ({ serviceName, route, spanCodeObjectId, @@ -10,9 +10,9 @@ export const EndpointOption = ({ selected, hideCopyIcon, onClick, - duration, - hideDuration -}: EndpointOptionProps) => { + metric, + isHeader +}: EndpointOptionProps) => { const title = `${serviceName} ${route}`; return ( @@ -34,8 +34,10 @@ export const EndpointOption = ({ )} {!hideCopyIcon && } - {!selected && !hideDuration && duration && ( - {duration} + {!selected && metric && ( + + {isHeader ? metric.label : String(metric.value)} + )} diff --git a/src/components/common/AffectedEndpointsSelector/EndpointOption/types.ts b/src/components/common/AffectedEndpointsSelector/EndpointOption/types.ts index 78f01617c..0fe951736 100644 --- a/src/components/common/AffectedEndpointsSelector/EndpointOption/types.ts +++ b/src/components/common/AffectedEndpointsSelector/EndpointOption/types.ts @@ -1,4 +1,4 @@ -export interface EndpointOptionProps { +export interface EndpointOptionProps { serviceName: string; route: string; spanCodeObjectId?: string; @@ -6,8 +6,11 @@ export interface EndpointOptionProps { onSpanLinkClick?: (spanCodeObjectId: string) => void; hideCopyIcon?: boolean; onClick?: ((spanCodeObjectId?: string) => void) | null; - duration?: string; - hideDuration?: boolean; + metric?: { + value: T; + label: string; + }; + isHeader?: boolean; } export interface EndpointNameProps { diff --git a/src/components/common/AffectedEndpointsSelector/index.tsx b/src/components/common/AffectedEndpointsSelector/index.tsx index a9860d11d..5be1268c3 100644 --- a/src/components/common/AffectedEndpointsSelector/index.tsx +++ b/src/components/common/AffectedEndpointsSelector/index.tsx @@ -1,6 +1,5 @@ import type { ReactNode } from "react"; import { DELIMITER } from "../../../constants"; -import { getDurationString } from "../../../utils/getDurationString"; import { trimEndpointScheme } from "../../../utils/trimEndpointScheme"; import { Select } from "../../Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/Select"; import type { CustomContentProps } from "../../Insights/InsightsCatalog/InsightsPage/InsightCardRenderer/insightCards/common/InsightCard/Select/types"; @@ -11,8 +10,8 @@ import type { AffectedEndpointsSelectorProps, Option } from "./types"; export const getEndpointKey = (option: Option): string => [option.serviceName, option.spanCodeObjectId].join(DELIMITER); -const renderOptions = ( - endpoints: Option[], +const renderOptions = ( + endpoints: Option[], handleLinkClick: (spanCodeObjectId?: string) => void ): { label: string; @@ -22,11 +21,11 @@ const renderOptions = ( endpoints.map((x) => { const spanCodeObjectId = x.spanCodeObjectId; const route = trimEndpointScheme(x.route); - const durationString = x.duration && getDurationString(x.duration); + return { label: route, customContent: ({ isSelected, onClick }) => ( - serviceName={x.serviceName} route={route} selected={isSelected} @@ -34,20 +33,20 @@ const renderOptions = ( onSpanLinkClick={handleLinkClick} hideCopyIcon={!isSelected} onClick={onClick} - duration={durationString} + metric={"metric" in x ? x.metric : undefined} /> ), value: getEndpointKey(x) }; }); -export const AffectedEndpointsSelector = ({ +export const AffectedEndpointsSelector = ({ onAssetLinkClick, value, options, onChange, isDisabled -}: AffectedEndpointsSelectorProps) => { +}: AffectedEndpointsSelectorProps) => { const handleSpanLinkClick = (spanCodeObjectId?: string) => { if (spanCodeObjectId) { onAssetLinkClick(spanCodeObjectId); @@ -58,6 +57,11 @@ export const AffectedEndpointsSelector = ({ onChange(value); }; + const metric = + options.length > 0 && "metric" in options[0] + ? options[0].metric + : undefined; + return (