Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components/Insights/common/InsightCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export const InsightCard = (props: InsightCardProps) => {
onTicketInfoButtonClick={props.onJiraButtonClick}
ticketLink={props.jiraTicketInfo?.ticketLink}
isHintEnabled={props.jiraTicketInfo?.isHintEnabled}
spanCodeObjectId={props.jiraTicketInfo?.spanCodeObjectId}
/>
)}
{props.onPin && <IconButton icon={{ component: PinIcon }} />}
Expand Down
3 changes: 2 additions & 1 deletion src/components/Insights/common/InsightCard/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface InsightCardProps {
jiraTicketInfo?: {
ticketLink?: string | null;
isHintEnabled?: boolean;
spanCodeObjectId?: string;
};
onJiraButtonClick?: (event: string) => void;
onJiraButtonClick?: (spanCodeObjectId: string, event: string) => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Meta, StoryObj } from "@storybook/react";
import { EndpointNPlusOneInsight } from ".";
import { mockedEndpointNPlusOneInsight } from "./mockData";

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta<typeof EndpointNPlusOneInsight> = {
title: "Insights/common/insights/EndpointNPlusOneInsight",
component: EndpointNPlusOneInsight,
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
layout: "fullscreen"
}
};

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
insight: mockedEndpointNPlusOneInsight
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { useContext } from "react";
import { getDurationString } from "../../../../../utils/getDurationString";
import { sendTrackingEvent } from "../../../../../utils/sendTrackingEvent";
import { ConfigContext } from "../../../../common/App/ConfigContext";
import { InfoCircleIcon } from "../../../../common/icons/InfoCircleIcon";
import { Tooltip } from "../../../../common/v3/Tooltip";
import { trackingEvents } from "../../../tracking";
import { InsightType, Trace } from "../../../types";
import { InsightCard } from "../../InsightCard";
import { ColumnsContainer } from "../../InsightCard/ColumnsContainer";
import { KeyValue } from "../../InsightCard/KeyValue";
import { ContentContainer, Description, Details } from "../styles";
import * as s from "./styles";
import { EndpointNPlusOneInsightProps } from "./types";

export const EndpointNPlusOneInsight = (
props: EndpointNPlusOneInsightProps
) => {
const config = useContext(ConfigContext);
const { span } = props.insight;

const handleSpanLinkClick = (spanCodeObjectId: string) => {
props.onAssetLinkClick(spanCodeObjectId, props.insight.type);
};

const handleTicketInfoButtonClick = (
spanCodeObjectId: string,
event: string
) => {
sendTrackingEvent(trackingEvents.JIRA_TICKET_INFO_BUTTON_CLICKED, {
insightType: props.insight.type
});
props.onJiraTicketCreate &&
props.onJiraTicketCreate(props.insight, spanCodeObjectId, event);
};

const handleTraceButtonClick = (
trace: Trace,
insightType: InsightType,
spanCodeObjectId: string
) => {
props.onTraceButtonClick(trace, insightType, spanCodeObjectId);
};

const spanInfo = span.internalSpan || span.clientSpan;
const spanName = spanInfo.displayName;

return (
<InsightCard
insight={props.insight}
onJiraButtonClick={handleTicketInfoButtonClick}
onGoToTrace={
config.isJaegerEnabled
? () =>
handleTraceButtonClick(
{
name: spanName,
id: span.traceId
},
props.insight.type,
spanInfo.spanCodeObjectId
)
: undefined
}
jiraTicketInfo={{
ticketLink: span.ticketLink,
isHintEnabled: props.isJiraHintEnabled,
spanCodeObjectId: spanInfo.spanCodeObjectId
}}
content={
<ContentContainer>
<Details>
<Description>Assets</Description>
<s.SpanListItem
name={spanName}
key={spanName}
onClick={() => handleSpanLinkClick(spanInfo.spanCodeObjectId)}
/>
</Details>
<ColumnsContainer>
<KeyValue label={"Repeats"}>{span.occurrences}</KeyValue>
<KeyValue
label={
<Tooltip
title={"The amount of requests affected by this issue."}
>
<s.InfoContainer>
<div>Requests</div>
<InfoCircleIcon color={"currentColor"} size={12} />
</s.InfoContainer>
</Tooltip>
}
>
{span.requestPercentage}%
</KeyValue>
<KeyValue label={"Duration"}>
{getDurationString(span.duration)}
</KeyValue>
</ColumnsContainer>
</ContentContainer>
}
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import {
EndpointSpanNPlusOneInsight,
InsightCategory,
InsightScope,
InsightType
} from "../../../types";

export const mockedEndpointNPlusOneInsight: EndpointSpanNPlusOneInsight = {
id: "60b55792-8262-4c5d-9628-7cce7919ad6d",
firstDetected: "2023-12-05T17:25:47.010Z",
lastDetected: "2024-01-05T13:14:47.010Z",
criticality: 0,
firstCommitId: "b3f7b3f",
lastCommitId: "a1b2c3d",
deactivatedCommitId: null,
reopenCount: 0,
ticketLink: null,
impact: 0,
name: "Suspected N+1 Query",
type: InsightType.EndpointSpanNPlusOneV2,
category: InsightCategory.Performance,
specifity: 2,
importance: 3,
span: {
occurrences: 200,
internalSpan: null,
clientSpan: {
name: "1D138649EB4FFA92C0E3C8103404F2",
displayName: "select * from users where id = :id",
instrumentationLibrary: "SampleInsightsController",
spanCodeObjectId:
"span:SampleInsightsController$_$1D138649EB4FFA92C0E3C8103404F2",
methodCodeObjectId: null,
kind: "Client",
codeObjectId: null
},
traceId: "9C510BC1E1CD59DD7E820BC3E8DFD4C4",
duration: {
value: 70.08,
unit: "μs",
raw: 70081
},
fraction: 0.08985711281727758,
criticality: 0.3,
impact: 0,
severity: 0,
ticketLink: "https://digma.ai/1",
requestPercentage: 98
},
scope: InsightScope.EntrySpan,
endpointSpan: "HTTP GET /SampleInsights/NPlusOneWithoutInternalSpan",
spanCodeObjectId:
"span:io.opentelemetry.tomcat-10.0$_$HTTP GET /SampleInsights/NPlusOneWithoutInternalSpan",
route: "epHTTP:HTTP GET /SampleInsights/NPlusOneWithoutInternalSpan",
serviceName: "PetClinic",
spanInfo: {
name: "HTTP GET /SampleInsights/NPlusOneWithoutInternalSpan",
displayName: "HTTP GET /SampleInsights/NPlusOneWithoutInternalSpan",
instrumentationLibrary: "io.opentelemetry.tomcat-10.0",
spanCodeObjectId:
"span:io.opentelemetry.tomcat-10.0$_$HTTP GET /SampleInsights/NPlusOneWithoutInternalSpan",
methodCodeObjectId:
"method:org.springframework.samples.petclinic.sample.SampleInsightsController$_$genNPlusOneWithoutInternalSpan",
kind: "Server",
codeObjectId:
"org.springframework.samples.petclinic.sample.SampleInsightsController$_$genNPlusOneWithoutInternalSpan"
},
shortDisplayInfo: {
title: "",
targetDisplayName: "",
subtitle: "",
description: ""
},
codeObjectId:
"org.springframework.samples.petclinic.sample.SampleInsightsController$_$genNPlusOneWithoutInternalSpan",
decorators: [
{
title: "N+1 Suspected",
description: "Supected NPlus One"
}
],
environment: "SAMPLE_ENV",
severity: 0,
isRecalculateEnabled: true,
prefixedCodeObjectId:
"method:org.springframework.samples.petclinic.sample.SampleInsightsController$_$genNPlusOneWithoutInternalSpan",
customStartTime: null,
actualStartTime: "2023-06-16T10:30:33.027Z",
sourceSpanCodeObjectInsight: ""
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import styled from "styled-components";
import { ListItem } from "../../InsightCard/ListItem";

export const InfoContainer = styled.div`
display: flex;
gap: 4px;
align-items: center;
`;

export const SpanListItem = styled(ListItem)`
padding: 4px;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
EndpointSpanNPlusOneInsight,
InsightProps,
InsightType,
Trace
} from "../../../types";

export interface EndpointNPlusOneInsightProps extends InsightProps {
insight: EndpointSpanNPlusOneInsight;
onAssetLinkClick: (
spanCodeObjectId: string,
insightType: InsightType
) => void;
onTraceButtonClick: (
trace: Trace,
insightType: InsightType,
spanCodeObjectId: string
) => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Button } from "../../../../common/v3/Button";
import { Pagination } from "../../../../common/v3/Pagination";
import { InsightType, Trace } from "../../../types";
import { InsightCard } from "../../InsightCard";
import { ContentContainer, Description, ListContainer } from "../styles";
import * as s from "./styles";
import { ExcessiveAPICallsInsightProps } from "./types";

Expand Down Expand Up @@ -38,11 +39,11 @@ export const ExcessiveAPICallsInsight = (
<InsightCard
insight={props.insight}
content={
<s.ContentContainer>
<s.Description>
<ContentContainer>
<Description>
Excessive API calls to specific endpoint found
</s.Description>
<s.List>
</Description>
<ListContainer>
{pageItems.map((span) => {
const spanName = span.clientSpan.displayName;
const traceId = span.traceId;
Expand Down Expand Up @@ -81,8 +82,8 @@ export const ExcessiveAPICallsInsight = (
onPageChange={setPage}
withDescription={true}
/>
</s.List>
</s.ContentContainer>
</ListContainer>
</ContentContainer>
}
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,6 @@
import styled from "styled-components";
import { caption1RegularTypography } from "../../../../common/App/typographies";
import { ListItem } from "../../InsightCard/ListItem";

export const ContentContainer = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
`;

export const List = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
`;

export const Description = styled.div`
color: ${({ theme }) => theme.colors.v3.text.tertiary};

${caption1RegularTypography}
`;

export const SpanListItem = styled(ListItem)`
padding: 4px;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { sendTrackingEvent } from "../../../../../utils/sendTrackingEvent";
import { InfoCircleIcon } from "../../../../common/icons/InfoCircleIcon";
import { Tag } from "../../../../common/v3/Tag";
import { Tooltip } from "../../../../common/v3/Tooltip";
import { Description } from "../../../styles";
import { trackingEvents } from "../../../tracking";
import { InsightType, Trace } from "../../../types";
import { InsightCard } from "../../InsightCard";
import { ColumnsContainer } from "../../InsightCard/ColumnsContainer";
import { KeyValue } from "../../InsightCard/KeyValue";
import { ContentContainer, Description } from "../styles";
import * as s from "./styles";
import { HighNumberOfQueriesInsightProps } from "./types";

Expand Down Expand Up @@ -37,7 +37,7 @@ export const HighNumberOfQueriesInsight = (
<InsightCard
insight={insight}
content={
<s.ContentContainer>
<ContentContainer>
{insight.quantile === 0.95 && (
<Description>Affecting the slowest 5% of requests.</Description>
)}
Expand All @@ -64,14 +64,15 @@ export const HighNumberOfQueriesInsight = (
<Tag content={insight.typicalCount} />
</KeyValue>
</ColumnsContainer>
</s.ContentContainer>
</ContentContainer>
}
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
onJiraButtonClick={handleCreateJiraTicketButtonClick}
jiraTicketInfo={{
ticketLink: insight.ticketLink,
isHintEnabled: props.isJiraHintEnabled
isHintEnabled: props.isJiraHintEnabled,
spanCodeObjectId: props.insight.spanInfo?.spanCodeObjectId
}}
onGoToTrace={
traceId
Expand Down
Loading