diff --git a/src/components/Insights/common/InsightCard/index.tsx b/src/components/Insights/common/InsightCard/index.tsx
index 060671e2f..1a8033724 100644
--- a/src/components/Insights/common/InsightCard/index.tsx
+++ b/src/components/Insights/common/InsightCard/index.tsx
@@ -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 && }
diff --git a/src/components/Insights/common/InsightCard/types.ts b/src/components/Insights/common/InsightCard/types.ts
index 928cb56dc..58eb94a14 100644
--- a/src/components/Insights/common/InsightCard/types.ts
+++ b/src/components/Insights/common/InsightCard/types.ts
@@ -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;
}
diff --git a/src/components/Insights/common/insights/EndpointNPlusOneInsight/EndpointNPlusOneInsight.stories.tsx b/src/components/Insights/common/insights/EndpointNPlusOneInsight/EndpointNPlusOneInsight.stories.tsx
new file mode 100644
index 000000000..201adf1b1
--- /dev/null
+++ b/src/components/Insights/common/insights/EndpointNPlusOneInsight/EndpointNPlusOneInsight.stories.tsx
@@ -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 = {
+ 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;
+
+export const Default: Story = {
+ args: {
+ insight: mockedEndpointNPlusOneInsight
+ }
+};
diff --git a/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx b/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx
new file mode 100644
index 000000000..1c2a605b3
--- /dev/null
+++ b/src/components/Insights/common/insights/EndpointNPlusOneInsight/index.tsx
@@ -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 (
+
+ handleTraceButtonClick(
+ {
+ name: spanName,
+ id: span.traceId
+ },
+ props.insight.type,
+ spanInfo.spanCodeObjectId
+ )
+ : undefined
+ }
+ jiraTicketInfo={{
+ ticketLink: span.ticketLink,
+ isHintEnabled: props.isJiraHintEnabled,
+ spanCodeObjectId: spanInfo.spanCodeObjectId
+ }}
+ content={
+
+
+ Assets
+ handleSpanLinkClick(spanInfo.spanCodeObjectId)}
+ />
+
+
+ {span.occurrences}
+
+
+ Requests
+
+
+
+ }
+ >
+ {span.requestPercentage}%
+
+
+ {getDurationString(span.duration)}
+
+
+
+ }
+ onRecalculate={props.onRecalculate}
+ onRefresh={props.onRefresh}
+ />
+ );
+};
diff --git a/src/components/Insights/common/insights/EndpointNPlusOneInsight/mockData.ts b/src/components/Insights/common/insights/EndpointNPlusOneInsight/mockData.ts
new file mode 100644
index 000000000..8b5cce804
--- /dev/null
+++ b/src/components/Insights/common/insights/EndpointNPlusOneInsight/mockData.ts
@@ -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: ""
+};
diff --git a/src/components/Insights/common/insights/EndpointNPlusOneInsight/styles.ts b/src/components/Insights/common/insights/EndpointNPlusOneInsight/styles.ts
new file mode 100644
index 000000000..f1cafeb5a
--- /dev/null
+++ b/src/components/Insights/common/insights/EndpointNPlusOneInsight/styles.ts
@@ -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;
+`;
diff --git a/src/components/Insights/common/insights/EndpointNPlusOneInsight/types.ts b/src/components/Insights/common/insights/EndpointNPlusOneInsight/types.ts
new file mode 100644
index 000000000..4362c2b9d
--- /dev/null
+++ b/src/components/Insights/common/insights/EndpointNPlusOneInsight/types.ts
@@ -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;
+}
diff --git a/src/components/Insights/common/insights/ExcessiveAPICallsInsight/index.tsx b/src/components/Insights/common/insights/ExcessiveAPICallsInsight/index.tsx
index 36cc36715..d228fe104 100644
--- a/src/components/Insights/common/insights/ExcessiveAPICallsInsight/index.tsx
+++ b/src/components/Insights/common/insights/ExcessiveAPICallsInsight/index.tsx
@@ -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";
@@ -38,11 +39,11 @@ export const ExcessiveAPICallsInsight = (
-
+
+
Excessive API calls to specific endpoint found
-
-
+
+
{pageItems.map((span) => {
const spanName = span.clientSpan.displayName;
const traceId = span.traceId;
@@ -81,8 +82,8 @@ export const ExcessiveAPICallsInsight = (
onPageChange={setPage}
withDescription={true}
/>
-
-
+
+
}
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
diff --git a/src/components/Insights/common/insights/ExcessiveAPICallsInsight/styles.ts b/src/components/Insights/common/insights/ExcessiveAPICallsInsight/styles.ts
index d80182dfc..39395a748 100644
--- a/src/components/Insights/common/insights/ExcessiveAPICallsInsight/styles.ts
+++ b/src/components/Insights/common/insights/ExcessiveAPICallsInsight/styles.ts
@@ -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;
`;
diff --git a/src/components/Insights/common/insights/HighNumberOfQueriesInsight/index.tsx b/src/components/Insights/common/insights/HighNumberOfQueriesInsight/index.tsx
index 49d6e1806..104dc4b77 100644
--- a/src/components/Insights/common/insights/HighNumberOfQueriesInsight/index.tsx
+++ b/src/components/Insights/common/insights/HighNumberOfQueriesInsight/index.tsx
@@ -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";
@@ -37,7 +37,7 @@ export const HighNumberOfQueriesInsight = (
+
{insight.quantile === 0.95 && (
Affecting the slowest 5% of requests.
)}
@@ -64,14 +64,15 @@ export const HighNumberOfQueriesInsight = (
-
+
}
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
diff --git a/src/components/Insights/common/insights/HighNumberOfQueriesInsight/styles.ts b/src/components/Insights/common/insights/HighNumberOfQueriesInsight/styles.ts
index 917de6764..5b16399ca 100644
--- a/src/components/Insights/common/insights/HighNumberOfQueriesInsight/styles.ts
+++ b/src/components/Insights/common/insights/HighNumberOfQueriesInsight/styles.ts
@@ -1,48 +1,9 @@
import styled from "styled-components";
-export const ContentContainer = styled.div`
- display: flex;
- flex-direction: column;
- gap: 8px;
-`;
-
-export const Stats = styled.div`
- display: flex;
- border-radius: 4px;
- gap: 20px;
- justify-content: space-between;
-`;
-
-export const Stat = styled.div`
- display: flex;
- flex-direction: column;
- gap: 8px;
- overflow: hidden;
-`;
-
-export const KeyContainer = styled.span`
- display: flex;
- align-items: center;
- gap: 4px;
-`;
-
-export const Key = styled.span`
- font-size: 14px;
- font-weight: 510;
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
-`;
-
export const IconContainer = styled.span`
display: flex;
`;
-export const ActionsContainer = styled.div`
- display: flex;
- gap: 8px;
-`;
-
export const TypicalLabel = styled.div`
display: flex;
gap: 4px;
diff --git a/src/components/Insights/common/insights/SlowEndpointInsight/index.tsx b/src/components/Insights/common/insights/SlowEndpointInsight/index.tsx
index a64c31701..273024d6c 100644
--- a/src/components/Insights/common/insights/SlowEndpointInsight/index.tsx
+++ b/src/components/Insights/common/insights/SlowEndpointInsight/index.tsx
@@ -4,6 +4,7 @@ import { Tag } from "../../../../common/v3/Tag";
import { InsightCard } from "../../InsightCard";
import { ColumnsContainer } from "../../InsightCard/ColumnsContainer";
import { KeyValue } from "../../InsightCard/KeyValue";
+import { Description } from "../styles";
import * as s from "./styles";
import { SlowEndpointInsightProps } from "./types";
@@ -19,12 +20,12 @@ export const SlowEndpointInsight = (props: SlowEndpointInsightProps) => {
content={
-
+
{`On average requests are slower than other endpoints by ${roundTo(
diff,
2
)}%`}
-
+
diff --git a/src/components/Insights/common/insights/SlowEndpointInsight/styles.ts b/src/components/Insights/common/insights/SlowEndpointInsight/styles.ts
index 7b6ffa5e6..451869f09 100644
--- a/src/components/Insights/common/insights/SlowEndpointInsight/styles.ts
+++ b/src/components/Insights/common/insights/SlowEndpointInsight/styles.ts
@@ -1,11 +1,5 @@
import styled from "styled-components";
-import { caption1RegularTypography } from "../../../../common/App/typographies";
export const ContentContainer = styled.div`
padding: 8px 0;
`;
-
-export const Description = styled.div`
- color: ${({ theme }) => theme.colors.v3.text.secondary};
- ${caption1RegularTypography}
-`;
diff --git a/src/components/Insights/common/insights/SpanNPlusOneInsight/SpanNPlusOneInsight.stories.tsx b/src/components/Insights/common/insights/SpanNPlusOneInsight/SpanNPlusOneInsight.stories.tsx
new file mode 100644
index 000000000..1f88ebf90
--- /dev/null
+++ b/src/components/Insights/common/insights/SpanNPlusOneInsight/SpanNPlusOneInsight.stories.tsx
@@ -0,0 +1,29 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { SpanNPlusOneInsight } from ".";
+import { mockedNPlusOneInsight } from "./mockData";
+
+// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
+const meta: Meta = {
+ title: "Insights/common/insights/SpanNPlusOneInsight",
+ component: SpanNPlusOneInsight,
+ 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;
+
+export const Default: Story = {
+ args: {
+ insight: mockedNPlusOneInsight
+ }
+};
+
+export const LinkedJira: Story = {
+ args: {
+ insight: { ...mockedNPlusOneInsight, ticketLink: "https://digma.ai/1" }
+ }
+};
diff --git a/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx b/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx
new file mode 100644
index 000000000..62d989b72
--- /dev/null
+++ b/src/components/Insights/common/insights/SpanNPlusOneInsight/index.tsx
@@ -0,0 +1,140 @@
+import { useContext, useState } from "react";
+import { getDurationString } from "../../../../../utils/getDurationString";
+import { sendTrackingEvent } from "../../../../../utils/sendTrackingEvent";
+import { trimEndpointScheme } from "../../../../../utils/trimEndpointScheme";
+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 { ListItem } from "../../InsightCard/ListItem";
+import { Select } from "../../InsightCard/Select";
+import { ContentContainer, Description, Details } from "../styles";
+import * as s from "./styles";
+import { SpanNPlusOneInsightProps } from "./types";
+
+export const SpanNPlusOneInsight = (props: SpanNPlusOneInsightProps) => {
+ const {
+ insight: { type, endpoints, ticketLink }
+ } = props;
+
+ const config = useContext(ConfigContext);
+ const [selectedEndpoint, setSelectedEndpoint] = useState(
+ props.insight.endpoints.length ? props.insight.endpoints[0] : null
+ );
+
+ const handleSpanLinkClick = (spanCodeObjectId?: string) => {
+ spanCodeObjectId && props.onAssetLinkClick(spanCodeObjectId, type);
+ };
+
+ const handleTraceButtonClick = (
+ trace: Trace,
+ insightType: InsightType,
+ spanCodeObjectId?: string
+ ) => {
+ props.onTraceButtonClick(trace, insightType, spanCodeObjectId);
+ };
+
+ const handleCreateJiraTicketButtonClick = (event: string) => {
+ sendTrackingEvent(trackingEvents.JIRA_TICKET_INFO_BUTTON_CLICKED, {
+ insightType: type
+ });
+ props.onJiraTicketCreate &&
+ props.onJiraTicketCreate(props.insight, undefined, event);
+ };
+
+ const spanName = props.insight.clientSpanName || undefined;
+ const spanCodeObjectId = props.insight.clientSpanCodeObjectId || undefined;
+ const traceId = props.insight.traceId;
+
+ return (
+
+ handleTraceButtonClick(
+ {
+ name: spanName,
+ id: traceId
+ },
+ props.insight.type,
+ spanCodeObjectId
+ )
+ : undefined
+ }
+ content={
+
+
+ Effected Endpoints ({endpoints.length})
+
+
+ {selectedEndpoint && (
+
+
+ {selectedEndpoint.occurrences}
+
+
+
+ Requests
+
+
+
+ }
+ >
+ {selectedEndpoint.requestPercentage}%
+
+
+ {getDurationString(selectedEndpoint.duration)}
+
+
+ )}
+
+ }
+ onRecalculate={props.onRecalculate}
+ onRefresh={props.onRefresh}
+ onJiraButtonClick={handleCreateJiraTicketButtonClick}
+ jiraTicketInfo={{
+ ticketLink,
+ isHintEnabled: props.isJiraHintEnabled,
+ spanCodeObjectId: props.insight.spanInfo?.spanCodeObjectId
+ }}
+ />
+ );
+};
diff --git a/src/components/Insights/common/insights/SpanNPlusOneInsight/mockData.ts b/src/components/Insights/common/insights/SpanNPlusOneInsight/mockData.ts
new file mode 100644
index 000000000..a098d26d0
--- /dev/null
+++ b/src/components/Insights/common/insights/SpanNPlusOneInsight/mockData.ts
@@ -0,0 +1,131 @@
+import {
+ InsightCategory,
+ InsightScope,
+ InsightType,
+ SpanNPlusOneInsight
+} from "../../../types";
+
+export const mockedNPlusOneInsight: SpanNPlusOneInsight = {
+ sourceSpanCodeObjectInsight: "sourceSpanCodeObjectInsightId",
+ id: "60b55792-8262-4c5d-9628-8cce7979ad6d",
+ 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: "N+1",
+ type: InsightType.SpanNPlusOne,
+ category: InsightCategory.Performance,
+ specifity: 2,
+ importance: 2,
+ span: {
+ name: "OwnerValidation.ValidateOwner",
+ displayName: "OwnerValidation.ValidateOwner",
+ instrumentationLibrary:
+ "io.opentelemetry.opentelemetry-instrumentation-annotations-1.16",
+ spanCodeObjectId:
+ "span:io.opentelemetry.opentelemetry-instrumentation-annotations-1.16$_$OwnerValidation.ValidateOwner",
+ methodCodeObjectId:
+ "org.springframework.samples.petclinic.domain.OwnerValidation$_$ValidateOwner",
+ kind: "Internal",
+ codeObjectId:
+ "org.springframework.samples.petclinic.domain.OwnerValidation$_$ValidateOwner"
+ },
+ occurrences: 100,
+ traceId: "00D37A4E7208E0F6E89AA7E2E37446A6",
+ clientSpanName: "select * from users where id = :id",
+ clientSpanCodeObjectId:
+ "span:OwnerController$_$1D138649EB4FFA92C0E3C8103404F2",
+ duration: {
+ value: 1.64,
+ unit: "sec",
+ raw: 1636050588.0
+ },
+ endpoints: [
+ {
+ endpointInfo: {
+ route: "HTTP POST /owners/new",
+ instrumentationLibrary: "OwnerController",
+ spanCodeObjectId:
+ "span:OwnerController$_$1D138649EB4FFA92C0E3C8103404F2",
+ entrySpanCodeObjectId:
+ "span:io.opentelemetry.tomcat-10.0$_$HTTP POST /owners/new",
+ serviceName: "spring-petclinic"
+ },
+ occurrences: 100,
+ criticality: 0.8,
+ impact: 0,
+ severity: 0,
+ requestPercentage: 50,
+ traceId: "00D37A4E7208E0F6E89AA7E2E37446A6",
+ commitId: "a1b2c3d",
+ duration: {
+ value: 1.64,
+ unit: "sec",
+ raw: 1636050588.0
+ }
+ },
+ {
+ endpointInfo: {
+ route: "HTTP GET /owners",
+ instrumentationLibrary: "OwnerController",
+ spanCodeObjectId:
+ "span:OwnerController$_$1D138649EB4FFA92C0E3C8103404F3",
+ entrySpanCodeObjectId:
+ "span:io.opentelemetry.tomcat-10.0$_$HTTP GET /owners",
+ serviceName: "spring-petclinic"
+ },
+ occurrences: 10,
+ criticality: 0.8,
+ impact: 0,
+ severity: 0,
+ requestPercentage: 80,
+ traceId: "00D37A4E7208E0F6E89AA7E2E37446A6",
+ commitId: "a1b2c3d",
+ duration: {
+ value: 3.64,
+ unit: "sec",
+ raw: 1636050588.0
+ }
+ }
+ ],
+ scope: InsightScope.Span,
+ spanInfo: {
+ name: "OwnerValidation.ValidateOwner",
+ displayName: "OwnerValidation.ValidateOwner",
+ instrumentationLibrary:
+ "io.opentelemetry.opentelemetry-instrumentation-annotations-1.16",
+ spanCodeObjectId:
+ "span:io.opentelemetry.opentelemetry-instrumentation-annotations-1.16$_$OwnerValidation.ValidateOwner",
+ methodCodeObjectId:
+ "org.springframework.samples.petclinic.domain.OwnerValidation$_$ValidateOwner",
+ kind: "Internal",
+ codeObjectId:
+ "org.springframework.samples.petclinic.domain.OwnerValidation$_$ValidateOwner"
+ },
+ shortDisplayInfo: {
+ title: "",
+ targetDisplayName: "",
+ subtitle: "",
+ description: ""
+ },
+ codeObjectId:
+ "org.springframework.samples.petclinic.domain.OwnerValidation$_$ValidateOwner",
+ decorators: [
+ {
+ title: "N+1",
+ description: "Supected NPlus One"
+ }
+ ],
+ environment: "BOB-LAPTOP[LOCAL]",
+ severity: 0.0,
+ isRecalculateEnabled: false,
+ prefixedCodeObjectId:
+ "method:org.springframework.samples.petclinic.domain.OwnerValidation$_$ValidateOwner",
+ customStartTime: null,
+ actualStartTime: "2023-07-27T08:23:56.500827Z"
+};
diff --git a/src/components/Insights/common/insights/SpanNPlusOneInsight/styles.ts b/src/components/Insights/common/insights/SpanNPlusOneInsight/styles.ts
new file mode 100644
index 000000000..4a449a8e6
--- /dev/null
+++ b/src/components/Insights/common/insights/SpanNPlusOneInsight/styles.ts
@@ -0,0 +1,14 @@
+import styled from "styled-components";
+import { footnoteRegularTypography } from "../../../../common/App/typographies";
+
+export const InfoContainer = styled.div`
+ display: flex;
+ gap: 4px;
+ align-items: center;
+`;
+
+export const SelectedItem = styled.div`
+ display: flex;
+ align-items: center;
+ ${footnoteRegularTypography}
+`;
diff --git a/src/components/Insights/common/insights/SpanNPlusOneInsight/types.ts b/src/components/Insights/common/insights/SpanNPlusOneInsight/types.ts
new file mode 100644
index 000000000..0ff7ede2b
--- /dev/null
+++ b/src/components/Insights/common/insights/SpanNPlusOneInsight/types.ts
@@ -0,0 +1,19 @@
+import {
+ InsightProps,
+ InsightType,
+ SpanNPlusOneInsight,
+ Trace
+} from "../../../types";
+
+export interface SpanNPlusOneInsightProps extends InsightProps {
+ insight: SpanNPlusOneInsight;
+ onAssetLinkClick: (
+ spanCodeObjectId: string,
+ insightType: InsightType
+ ) => void;
+ onTraceButtonClick: (
+ trace: Trace,
+ insightType: InsightType,
+ spanCodeObjectId?: string
+ ) => void;
+}
diff --git a/src/components/Insights/common/insights/SpanNexusInsight/index.tsx b/src/components/Insights/common/insights/SpanNexusInsight/index.tsx
index 75df09bab..b11267705 100644
--- a/src/components/Insights/common/insights/SpanNexusInsight/index.tsx
+++ b/src/components/Insights/common/insights/SpanNexusInsight/index.tsx
@@ -2,6 +2,7 @@ import { Tag } from "../../../../common/v3/Tag";
import { InsightCard } from "../../InsightCard";
import { ColumnsContainer } from "../../InsightCard/ColumnsContainer";
import { KeyValue } from "../../InsightCard/KeyValue";
+import { ContentContainer } from "../styles";
import * as s from "./styles";
import { SpanNexusInsightProps } from "./types";
@@ -24,7 +25,7 @@ export const SpanNexusInsight = (props: SpanNexusInsightProps) => {
+
Multiple code flows depend on this location
@@ -44,7 +45,7 @@ export const SpanNexusInsight = (props: SpanNexusInsightProps) => {
)}
-
+
}
onRecalculate={props.onRecalculate}
onRefresh={props.onRefresh}
diff --git a/src/components/Insights/common/insights/SpanNexusInsight/styles.ts b/src/components/Insights/common/insights/SpanNexusInsight/styles.ts
index 4fccda7c7..72891baa0 100644
--- a/src/components/Insights/common/insights/SpanNexusInsight/styles.ts
+++ b/src/components/Insights/common/insights/SpanNexusInsight/styles.ts
@@ -1,11 +1,5 @@
import styled from "styled-components";
-export const ContentContainer = styled.div`
- gap: 8px;
- display: flex;
- flex-direction: column;
-`;
-
export const Description = styled.div`
display: flex;
gap: 8px;
diff --git a/src/components/Insights/common/insights/styles.ts b/src/components/Insights/common/insights/styles.ts
new file mode 100644
index 000000000..1547269cb
--- /dev/null
+++ b/src/components/Insights/common/insights/styles.ts
@@ -0,0 +1,26 @@
+import styled from "styled-components";
+import { caption1RegularTypography } from "../../../common/App/typographies";
+
+export const Description = styled.div`
+ color: ${({ theme }) => theme.colors.v3.text.secondary};
+
+ ${caption1RegularTypography}
+`;
+
+export const Details = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+`;
+
+export const ContentContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+`;
+
+export const ListContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+`;
diff --git a/src/components/Insights/types.ts b/src/components/Insights/types.ts
index 43d0079ca..247f9e21c 100644
--- a/src/components/Insights/types.ts
+++ b/src/components/Insights/types.ts
@@ -615,6 +615,7 @@ export interface EndpointSpanNPlusOneInsight extends EndpointInsight {
impact: number;
severity: number;
ticketLink: string | null;
+ requestPercentage: number;
};
}
diff --git a/src/components/common/v3/JiraButton/index.tsx b/src/components/common/v3/JiraButton/index.tsx
index 154b15403..9a1f1e454 100644
--- a/src/components/common/v3/JiraButton/index.tsx
+++ b/src/components/common/v3/JiraButton/index.tsx
@@ -28,7 +28,7 @@ export const JiraButton = (props: JiraButtonProps) => {
const openTicketInfo = (event: string) => {
handleJiraButtonClick();
- props.onTicketInfoButtonClick(event);
+ props.onTicketInfoButtonClick(props.spanCodeObjectId, event);
};
const renderButton = () => (
@@ -48,7 +48,7 @@ export const JiraButton = (props: JiraButtonProps) => {
{
icon: ,
label: "Edit",
- id: "edit",
+ id: props.spanCodeObjectId ?? "",
onClick: () => openTicketInfo("edit menu item click")
}
]}
@@ -64,6 +64,7 @@ export const JiraButton = (props: JiraButtonProps) => {
component: () => (
)
diff --git a/src/components/common/v3/JiraButton/types.ts b/src/components/common/v3/JiraButton/types.ts
index 0569ff726..4e65fe787 100644
--- a/src/components/common/v3/JiraButton/types.ts
+++ b/src/components/common/v3/JiraButton/types.ts
@@ -1,5 +1,9 @@
export interface JiraButtonProps {
- onTicketInfoButtonClick(event: string): void;
+ onTicketInfoButtonClick(
+ spanCodeObjectId: string | undefined,
+ event: string
+ ): void;
ticketLink?: string | null;
isHintEnabled?: boolean;
+ spanCodeObjectId?: string;
}