diff --git a/knip.config.ts b/knip.config.ts index 98196dc9026407..82ce4425cf66bf 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -57,8 +57,6 @@ const config: KnipConfig = { // helper files for stories - it's fine that they are only used in tests '!static/app/**/__stories__/*.{js,mjs,ts,tsx}!', '!static/app/stories/**/*.{js,mjs,ts,tsx}!', - // TEMPORARY! Abdullah Khan: WILL BE REMOVING IN STACKED PRs. Trying to merge PRs in smaller batches. - '!static/app/views/performance/newTraceDetails/traceModels/traceTreeNode/**/*.{js,mjs,ts,tsx}!', ], compilers: { mdx: async text => String(await compile(text)), diff --git a/static/app/components/events/eventEntries.tsx b/static/app/components/events/eventEntries.tsx index 698599d8c33dc2..5860e9f9577d92 100644 --- a/static/app/components/events/eventEntries.tsx +++ b/static/app/components/events/eventEntries.tsx @@ -139,7 +139,7 @@ function EventEntries({ // Because replays are not an interface, we need to manually insert the replay section // into the array of entries. The long-term solution here is to move the ordering // logic to this component, similar to how GroupEventDetailsContent works. -export function partitionEntriesForReplay(entries: Entry[]) { +function partitionEntriesForReplay(entries: Entry[]) { let replayIndex = 0; for (const [i, entry] of entries.entries()) { diff --git a/static/app/views/insights/agents/components/aiSpanList.tsx b/static/app/views/insights/agents/components/aiSpanList.tsx index 4e8b3bbabf8f58..f2a3b589c528ab 100644 --- a/static/app/views/insights/agents/components/aiSpanList.tsx +++ b/static/app/views/insights/agents/components/aiSpanList.tsx @@ -29,8 +29,8 @@ import { isTransactionNode, isTransactionNodeEquivalent, } from 'sentry/views/performance/newTraceDetails/traceGuards'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; function getNodeTimeBounds(node: AITraceSpanNode | AITraceSpanNode[]) { let startTime = 0; @@ -69,16 +69,6 @@ function getNodeTimeBounds(node: AITraceSpanNode | AITraceSpanNode[]) { }; } -function getClosestNode( - node: AITraceSpanNode, - predicate: (node: TraceTreeNode) => node is T -): T | null { - if (predicate(node)) { - return node; - } - return TraceTree.ParentNode(node, predicate) as T | null; -} - export function AISpanList({ nodes, selectedNodeKey, @@ -89,12 +79,11 @@ export function AISpanList({ selectedNodeKey: string | null; }) { const nodesByTransaction = useMemo(() => { - const result: Map< - TraceTreeNode, - AITraceSpanNode[] - > = new Map(); + const result: Map = new Map(); for (const node of nodes) { - const transaction = getClosestNode(node, isTransactionNodeEquivalent); + const transaction = node.findParent(p => + isTransactionNodeEquivalent(p) + ); if (!transaction) { continue; } @@ -132,7 +121,7 @@ function TransactionWrapper({ nodes: AITraceSpanNode[]; onSelectNode: (node: AITraceSpanNode) => void; selectedNodeKey: string | null; - transaction: TraceTreeNode; + transaction: TransactionNode | EapSpanNode; }) { const [isExpanded, setIsExpanded] = useState(true); const theme = useTheme(); @@ -142,7 +131,7 @@ function TransactionWrapper({ const nodeAiRunParentsMap = useMemo>(() => { const parents: Record = {}; for (const node of nodes) { - const parent = getClosestNode(node, getIsAiRunNode); + const parent = node.findParent(p => getIsAiRunNode(p)); if (parent) { parents[getNodeId(node)] = parent; } @@ -242,7 +231,7 @@ interface TraceBounds { } function calculateRelativeTiming( - node: TraceTreeNode, + node: AITraceSpanNode, traceBounds: TraceBounds ): {leftPercent: number; widthPercent: number} { if (!node.value) return {leftPercent: 0, widthPercent: 0}; diff --git a/static/app/views/insights/agents/hooks/useAITrace.tsx b/static/app/views/insights/agents/hooks/useAITrace.tsx index 79b6ee8bb131cf..b28fb74631375e 100644 --- a/static/app/views/insights/agents/hooks/useAITrace.tsx +++ b/static/app/views/insights/agents/hooks/useAITrace.tsx @@ -13,7 +13,7 @@ import { isTransactionNodeEquivalent, } from 'sentry/views/performance/newTraceDetails/traceGuards'; import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; import {DEFAULT_TRACE_VIEW_PREFERENCES} from 'sentry/views/performance/newTraceDetails/traceState/tracePreferences'; import {useTraceQueryParams} from 'sentry/views/performance/newTraceDetails/useTraceQueryParams'; @@ -69,11 +69,13 @@ export function useAITrace(traceSlug: string): UseAITraceResult { tree.build(); - const fetchableTransactions = TraceTree.FindAll(tree.root, node => { - return isTransactionNode(node) && node.canFetch && node.value !== null; - }).filter((node): node is TraceTreeNode => - isTransactionNode(node) - ); + const fetchableTransactions = tree.root + .findAllChildren(node => { + return ( + isTransactionNode(node) && node.canFetchChildren && node.value !== null + ); + }) + .filter((node): node is TransactionNode => isTransactionNode(node)); const uniqueTransactions = fetchableTransactions.filter( (node, index, array) => @@ -81,7 +83,7 @@ export function useAITrace(traceSlug: string): UseAITraceResult { ); const zoomPromises = uniqueTransactions.map(node => - tree.zoom(node, true, { + tree.fetchNodeSubTree(true, node, { api, organization, preferences: DEFAULT_TRACE_VIEW_PREFERENCES, @@ -91,7 +93,7 @@ export function useAITrace(traceSlug: string): UseAITraceResult { await Promise.all(zoomPromises); // Keep only transactions that include AI spans and the AI spans themselves - const flattenedNodes = TraceTree.FindAll(tree.root, node => { + const flattenedNodes = tree.root.findAllChildren(node => { if ( !isTransactionNodeEquivalent(node) && !isSpanNode(node) && @@ -101,7 +103,7 @@ export function useAITrace(traceSlug: string): UseAITraceResult { } return getIsAiNode(node); - }) as AITraceSpanNode[]; + }); setNodes(flattenedNodes); setIsLoading(false); diff --git a/static/app/views/insights/agents/utils/aiTraceNodes.tsx b/static/app/views/insights/agents/utils/aiTraceNodes.tsx index 8eeca45b145540..4126dd05ab08ac 100644 --- a/static/app/views/insights/agents/utils/aiTraceNodes.tsx +++ b/static/app/views/insights/agents/utils/aiTraceNodes.tsx @@ -13,8 +13,7 @@ import { isSpanNode, isTransactionNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; // TODO(aknaus): Remove the special handling for tags once the endpoint returns the correct type function getAttributeValue( @@ -37,7 +36,7 @@ function getAttributeValue( } export function ensureAttributeObject( - node: TraceTreeNode, + node: AITraceSpanNode, event?: EventTransaction, attributes?: TraceItemResponseAttribute[] ) { @@ -66,7 +65,7 @@ export function ensureAttributeObject( export function getTraceNodeAttribute( name: string, - node: TraceTreeNode, + node: AITraceSpanNode, event?: EventTransaction, attributes?: TraceItemResponseAttribute[] ): string | number | boolean | undefined { @@ -75,7 +74,7 @@ export function getTraceNodeAttribute( } function createGetIsAiNode(predicate: ({op}: {op?: string}) => boolean) { - return (node: TraceTreeNode): node is AITraceSpanNode => { + return (node: BaseNode): node is AITraceSpanNode => { if (!isTransactionNode(node) && !isSpanNode(node) && !isEAPSpanNode(node)) { return false; } diff --git a/static/app/views/insights/agents/utils/getNodeId.tsx b/static/app/views/insights/agents/utils/getNodeId.tsx index 23ca2125e9e79c..9ff1370818c6a5 100644 --- a/static/app/views/insights/agents/utils/getNodeId.tsx +++ b/static/app/views/insights/agents/utils/getNodeId.tsx @@ -7,7 +7,7 @@ import { export function getNodeId(node: AITraceSpanNode): string { if (isEAPSpanNode(node)) { - return node.metadata.event_id as string; + return node.value.event_id; } if (isTransactionNode(node)) { return node.value.event_id; diff --git a/static/app/views/insights/agents/utils/types.tsx b/static/app/views/insights/agents/utils/types.tsx index 8f844dbfcd15e3..871698d9da8a52 100644 --- a/static/app/views/insights/agents/utils/types.tsx +++ b/static/app/views/insights/agents/utils/types.tsx @@ -1,6 +1,5 @@ -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; -export type AITraceSpanNode = TraceTreeNode< - TraceTree.Transaction | TraceTree.EAPSpan | TraceTree.Span ->; +export type AITraceSpanNode = TransactionNode | EapSpanNode | SpanNode; diff --git a/static/app/views/insights/mcp/utils/mcpTraceNodes.tsx b/static/app/views/insights/mcp/utils/mcpTraceNodes.tsx index ef4fcf439fbb45..968b2b9fb074fb 100644 --- a/static/app/views/insights/mcp/utils/mcpTraceNodes.tsx +++ b/static/app/views/insights/mcp/utils/mcpTraceNodes.tsx @@ -3,10 +3,9 @@ import { isSpanNode, isTransactionNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; -export function getIsMCPNode(node: TraceTreeNode) { +export function getIsMCPNode(node: BaseNode) { if (!isTransactionNode(node) && !isSpanNode(node) && !isEAPSpanNode(node)) { return false; } diff --git a/static/app/views/performance/newTraceDetails/issuesTraceWaterfall.tsx b/static/app/views/performance/newTraceDetails/issuesTraceWaterfall.tsx index e1c9f2beda5391..d930bce15bf88b 100644 --- a/static/app/views/performance/newTraceDetails/issuesTraceWaterfall.tsx +++ b/static/app/views/performance/newTraceDetails/issuesTraceWaterfall.tsx @@ -27,11 +27,10 @@ import { isTransactionNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; import {IssuesTraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/issuesTraceTree'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; import {useDividerResizeSync} from 'sentry/views/performance/newTraceDetails/useDividerResizeSync'; import {useTraceSpaceListeners} from 'sentry/views/performance/newTraceDetails/useTraceSpaceListeners'; -import type {TraceTreeNode} from './traceModels/traceTreeNode'; +import type {BaseNode} from './traceModels/traceTreeNode/baseNode'; import {useTraceState, useTraceStateDispatch} from './traceState/traceStateProvider'; import {Trace} from './trace'; import { @@ -80,9 +79,7 @@ export function IssuesTraceWaterfall(props: IssuesTraceWaterfallProps) { }); }, [props.organization, props.source]); - const previouslyFocusedNodeRef = useRef | null>( - null - ); + const previouslyFocusedNodeRef = useRef(null); const {viewManager, traceScheduler, traceView} = useTraceWaterfallModels(); @@ -95,17 +92,13 @@ export function IssuesTraceWaterfall(props: IssuesTraceWaterfallProps) { }, [props.tree.list.length, traceDispatch]); const onRowClick = useCallback( - ( - node: TraceTreeNode, - _event: React.MouseEvent, - index: number - ) => { + (node: BaseNode, _event: React.MouseEvent, index: number) => { trackAnalytics('trace.trace_layout.span_row_click', { organization, num_children: node.children.length, type: traceNodeAnalyticsName(node), project_platform: - projects.find(p => p.slug === node.metadata.project_slug)?.platform || 'other', + projects.find(p => p.slug === node.projectSlug)?.platform || 'other', ...traceNodeAdjacentAnalyticsProperties(node), }); @@ -134,7 +127,7 @@ export function IssuesTraceWaterfall(props: IssuesTraceWaterfallProps) { // Find all the nodes that match the event id from the error so that we can try and // link the user to the most specific one. - const nodes = IssuesTraceTree.FindAll(props.tree.root, n => { + const nodes = props.tree.root.findAllChildren(n => { if (isTraceErrorNode(n) || isEAPErrorNode(n)) { return n.value.event_id === props.event.eventID; } @@ -200,7 +193,7 @@ export function IssuesTraceWaterfall(props: IssuesTraceWaterfallProps) { const index = node ? IssuesTraceTree.EnforceVisibility(props.tree, node) : -1; if (node) { - const preserveNodes: Array> = [node]; + const preserveNodes: BaseNode[] = [node]; let start = index; while (--start > 0) { diff --git a/static/app/views/performance/newTraceDetails/issuesTraceWaterfallOverlay.tsx b/static/app/views/performance/newTraceDetails/issuesTraceWaterfallOverlay.tsx index 51a53cde1fda4f..50c2892f333e62 100644 --- a/static/app/views/performance/newTraceDetails/issuesTraceWaterfallOverlay.tsx +++ b/static/app/views/performance/newTraceDetails/issuesTraceWaterfallOverlay.tsx @@ -21,7 +21,7 @@ import type {VirtualizedViewManager} from './traceRenderers/virtualizedViewManag interface RowPosition { height: number; left: number; - pathToNode: ReturnType; + pathToNode: TraceTree.NodePath[]; top: number; width: number; } @@ -90,7 +90,7 @@ export function IssueTraceWaterfallOverlay({ return; } - const pathToNode = TraceTree.PathToNode(node); + const pathToNode = node.pathToNode(); if (!pathToNode) { return; @@ -155,7 +155,7 @@ export function IssueTraceWaterfallOverlay({ export function getTraceLinkForIssue( traceTarget: LocationDescriptor, - pathToNode?: ReturnType + pathToNode?: TraceTree.NodePath[] ) { if (typeof traceTarget === 'string') { return traceTarget; diff --git a/static/app/views/performance/newTraceDetails/trace.spec.tsx b/static/app/views/performance/newTraceDetails/trace.spec.tsx index 8d2af3adf59707..74be550aeb3865 100644 --- a/static/app/views/performance/newTraceDetails/trace.spec.tsx +++ b/static/app/views/performance/newTraceDetails/trace.spec.tsx @@ -1079,7 +1079,7 @@ describe('trace view', () => { }); it('scrolls to sibling autogroup node', async () => { - mockQueryString('?node=ag-http0&node=txn-1'); + mockQueryString('?node=ag-span0&node=txn-1'); const {virtualizedContainer} = await completeTestSetup(); await within(virtualizedContainer).findAllByText(/Autogrouped/i); diff --git a/static/app/views/performance/newTraceDetails/trace.tsx b/static/app/views/performance/newTraceDetails/trace.tsx index fb96b3751f5478..f69ffb31d8dc2e 100644 --- a/static/app/views/performance/newTraceDetails/trace.tsx +++ b/static/app/views/performance/newTraceDetails/trace.tsx @@ -35,7 +35,7 @@ import { } from 'sentry/views/insights/browser/webVitals/utils/scoreToStatus'; import {TraceTree} from './traceModels/traceTree'; -import type {TraceTreeNode} from './traceModels/traceTreeNode'; +import type {BaseNode} from './traceModels/traceTreeNode/baseNode'; import type {TraceEvents, TraceScheduler} from './traceRenderers/traceScheduler'; import { useVirtualizedList, @@ -65,12 +65,11 @@ import {useTraceState, useTraceStateDispatch} from './traceState/traceStateProvi import { isAutogroupedNode, isCollapsedNode, - isEAPErrorNode, isEAPSpanNode, isEAPTraceNode, + isErrorNode, isMissingInstrumentationNode, isSpanNode, - isTraceErrorNode, isTraceNode, isTransactionNode, isUptimeCheckNode, @@ -108,16 +107,16 @@ interface TraceProps { isLoading: boolean; manager: VirtualizedViewManager; onRowClick: ( - node: TraceTreeNode, + node: BaseNode, event: React.MouseEvent, index: number ) => void; onTraceSearch: ( query: string, - node: TraceTreeNode, + node: BaseNode, behavior: 'track result' | 'persist' ) => void; - previouslyFocusedNodeRef: React.MutableRefObject | null>; + previouslyFocusedNodeRef: React.MutableRefObject; rerender: () => void; scheduler: TraceScheduler; trace: TraceTree; @@ -148,7 +147,7 @@ export function Trace({ rerenderRef.current = rerender; const treePromiseStatusRef = useRef, + BaseNode, 'loading' | 'error' | 'success' > | null>(null); @@ -208,7 +207,7 @@ export function Trace({ const onNodeZoomIn = useCallback( ( event: React.MouseEvent | React.KeyboardEvent, - node: TraceTreeNode, + node: BaseNode, value: boolean ) => { if (!isTransactionNode(node) && !isSpanNode(node)) { @@ -219,7 +218,7 @@ export function Trace({ rerenderRef.current(); treeRef.current - .zoom(node, value, { + .fetchNodeSubTree(value, node, { api, organization, preferences: traceStatePreferencesRef.current, @@ -245,23 +244,19 @@ export function Trace({ const onNodeExpand = useCallback( ( event: React.MouseEvent | React.KeyboardEvent, - node: TraceTreeNode, + node: BaseNode, value: boolean ) => { event.stopPropagation(); - treeRef.current.expand(node, value); + node.expand(value, treeRef.current); rerenderRef.current(); }, [] ); const onRowKeyDown = useCallback( - ( - event: React.KeyboardEvent, - index: number, - node: TraceTreeNode - ) => { + (event: React.KeyboardEvent, index: number, node: BaseNode) => { if (!manager.list) { return; } @@ -283,13 +278,13 @@ export function Trace({ } if (event.key === 'ArrowLeft') { - if (node.zoomedIn) { + if (node.hasFetchedChildren) { onNodeZoomIn(event, node, false); } else if (node.expanded) { onNodeExpand(event, node, false); } } else if (event.key === 'ArrowRight') { - if (node.canFetch) { + if (node.canFetchChildren) { onNodeZoomIn(event, node, true); } else { onNodeExpand(event, node, true); @@ -508,29 +503,17 @@ function RenderTraceRow(props: { index: number; isSearchResult: boolean; manager: VirtualizedViewManager; - node: TraceTreeNode; - onExpand: ( - event: React.MouseEvent, - node: TraceTreeNode, - value: boolean - ) => void; + node: BaseNode; + onExpand: (event: React.MouseEvent, node: BaseNode, value: boolean) => void; onRowClick: ( - node: TraceTreeNode, + node: BaseNode, event: React.MouseEvent, index: number ) => void; - onRowKeyDown: ( - event: React.KeyboardEvent, - index: number, - node: TraceTreeNode - ) => void; - onZoomIn: ( - event: React.MouseEvent, - node: TraceTreeNode, - value: boolean - ) => void; + onRowKeyDown: (event: React.KeyboardEvent, index: number, node: BaseNode) => void; + onZoomIn: (event: React.MouseEvent, node: BaseNode, value: boolean) => void; organization: Organization; - previouslyFocusedNodeRef: React.MutableRefObject | null>; + previouslyFocusedNodeRef: React.MutableRefObject; projects: Record; searchResultsIteratorIndex: number | null; style: React.CSSProperties; @@ -607,7 +590,7 @@ function RenderTraceRow(props: { const onZoomInProp = props.onZoomIn; const onZoomIn = useCallback( (e: React.MouseEvent) => { - onZoomInProp(e, node, !node.zoomedIn); + onZoomInProp(e, node, !node.hasFetchedChildren); }, [node, onZoomInProp] ); @@ -628,7 +611,7 @@ function RenderTraceRow(props: { paddingLeft: TraceTree.Depth(node) * props.manager.row_depth_padding, }; - const rowProps: TraceRowProps> = { + const rowProps: TraceRowProps = { onExpand, onZoomIn, onRowClick, @@ -676,7 +659,7 @@ function RenderTraceRow(props: { return ; } - if (isTraceErrorNode(node) || isEAPErrorNode(node)) { + if (isErrorNode(node)) { return ; } diff --git a/static/app/views/performance/newTraceDetails/traceAnalytics.tsx b/static/app/views/performance/newTraceDetails/traceAnalytics.tsx index 9e86cca64c4de0..519813293d4e0b 100644 --- a/static/app/views/performance/newTraceDetails/traceAnalytics.tsx +++ b/static/app/views/performance/newTraceDetails/traceAnalytics.tsx @@ -5,7 +5,7 @@ import type {PlatformKey, Project} from 'sentry/types/project'; import {trackAnalytics} from 'sentry/utils/analytics'; import type {TraceDrawerActionKind} from './traceDrawer/details/utils'; -import {TraceShape, type TraceTree} from './traceModels/traceTree'; +import type {TraceShape, TraceTree} from './traceModels/traceTree'; export type TraceTreeSource = | 'trace_view' @@ -28,7 +28,7 @@ const trackTraceSuccessState = ( const trace_duration_seconds = (tree.root.space?.[1] ?? 0) / 1000; const projectSlugs = [ ...new Set( - tree.list.map(node => node.metadata.project_slug).filter(slug => slug !== undefined) + tree.list.map(node => node.projectSlug).filter(slug => slug !== undefined) ), ]; diff --git a/static/app/views/performance/newTraceDetails/traceApi/useTraceAverageTransactionDuration.tsx b/static/app/views/performance/newTraceDetails/traceApi/useTraceAverageTransactionDuration.tsx index 0e1249ddadccf9..d63beaba7c4096 100644 --- a/static/app/views/performance/newTraceDetails/traceApi/useTraceAverageTransactionDuration.tsx +++ b/static/app/views/performance/newTraceDetails/traceApi/useTraceAverageTransactionDuration.tsx @@ -4,12 +4,11 @@ import type {Organization} from 'sentry/types/organization'; import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery'; import EventView from 'sentry/utils/discover/eventView'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; type Props = { location: Location; - node: TraceTreeNode; + node: TransactionNode; organization: Organization; }; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/autogroup/index.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/autogroup/index.tsx index 4472bcf598da2f..62d9970e47ccab 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/autogroup/index.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/autogroup/index.tsx @@ -4,8 +4,8 @@ import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import type {TraceTreeNodeDetailsProps} from 'sentry/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails'; -import type {ParentAutogroupNode} from 'sentry/views/performance/newTraceDetails/traceModels/parentAutogroupNode'; -import type {SiblingAutogroupNode} from 'sentry/views/performance/newTraceDetails/traceModels/siblingAutogroupNode'; +import type {ParentAutogroupNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/parentAutogroupNode'; +import type {SiblingAutogroupNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/siblingAutogroupNode'; export function AutogroupNodeDetails( props: TraceTreeNodeDetailsProps diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/error.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/error.tsx index 3a9a72d75d559c..0bbd7ca9c756e3 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/error.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/error.tsx @@ -2,17 +2,12 @@ import {useMemo} from 'react'; import {t} from 'sentry/locale'; import type {TraceTreeNodeDetailsProps} from 'sentry/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {ErrorNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/errorNode'; import {IssueList} from './issues/issues'; import {TraceDrawerComponents} from './styles'; -export function ErrorNodeDetails( - props: TraceTreeNodeDetailsProps< - TraceTreeNode | TraceTreeNode - > -) { +export function ErrorNodeDetails(props: TraceTreeNodeDetailsProps) { const {node, organization, onTabScrollToNode} = props; const issues = useMemo(() => { return [...node.errors]; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/issues/issues.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/issues/issues.tsx index a366ddd2e33b13..676a3595cc7e43 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/issues/issues.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/issues/issues.tsx @@ -16,8 +16,8 @@ import {useApiQuery} from 'sentry/utils/queryClient'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import {isTraceOccurence} from 'sentry/views/performance/newTraceDetails/traceGuards'; import {TraceIcons} from 'sentry/views/performance/newTraceDetails/traceIcons'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; type IssueProps = { issue: TraceTree.TraceIssue; @@ -182,14 +182,14 @@ const SummaryWrapper = styled('div')` type IssueListProps = { issues: TraceTree.TraceIssue[]; - node: TraceTreeNode; + node: BaseNode; organization: Organization; }; export function IssueList({issues, node, organization}: IssueListProps) { const uniqueIssues = [ - ...TraceTree.UniqueErrorIssues(node).sort(sortIssuesByLevel), - ...TraceTree.UniqueOccurrences(node), + ...node.uniqueErrorIssues.sort(sortIssuesByLevel), + ...node.uniqueOccurrenceIssues, ]; if (!issues.length) { diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/missingInstrumentation.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/missingInstrumentation.tsx index 10e2d94eb0517b..d6c4ad7bd3a3c8 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/missingInstrumentation.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/missingInstrumentation.tsx @@ -11,17 +11,19 @@ import {useTransaction} from 'sentry/views/performance/newTraceDetails/traceApi/ import {getCustomInstrumentationLink} from 'sentry/views/performance/newTraceDetails/traceConfigurations'; import {ProfilePreview} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/profiling/profilePreview'; import type {TraceTreeNodeDetailsProps} from 'sentry/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails'; -import {isEAPSpanNode} from 'sentry/views/performance/newTraceDetails/traceGuards'; -import type {MissingInstrumentationNode} from 'sentry/views/performance/newTraceDetails/traceModels/missingInstrumentationNode'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import { + isEAPSpanNode, + isSpanNode, +} from 'sentry/views/performance/newTraceDetails/traceGuards'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; +import type {NoInstrumentationNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/noInstrumentationNode'; import {ProfileGroupProvider} from 'sentry/views/profiling/profileGroupProvider'; import {ProfileContext, ProfilesProvider} from 'sentry/views/profiling/profilesProvider'; import {TraceDrawerComponents} from './styles'; import {getProfileMeta} from './utils'; -interface BaseProps extends TraceTreeNodeDetailsProps { +interface BaseProps extends TraceTreeNodeDetailsProps { event: EventTransaction | null; profileId: string | undefined; profileMeta: ReturnType; @@ -31,7 +33,7 @@ interface BaseProps extends TraceTreeNodeDetailsProps) { +}: TraceTreeNodeDetailsProps) { const {node} = props; const {projects} = useProjects(); @@ -39,31 +41,35 @@ export function MissingInstrumentationNodeDetails({ return ; } - const event = node.previous.event ?? node.next.event ?? null; - const project = projects.find(proj => proj.slug === event?.projectSlug); - const profileMeta = getProfileMeta(event) || ''; - const profileContext = event?.contexts?.profile ?? {}; + if (isSpanNode(node.previous)) { + const event = node.previous.event; + const project = projects.find(proj => proj.slug === event?.projectSlug); + const profileMeta = getProfileMeta(event) || ''; + const profileContext = event?.contexts?.profile ?? {}; - return ( - - ); + return ( + + ); + } + + return null; } function EAPMissingInstrumentationNodeDetails({ projects, ...props -}: TraceTreeNodeDetailsProps & { +}: TraceTreeNodeDetailsProps & { projects: Project[]; }) { const {node} = props; - const previous = node.previous as TraceTreeNode; + const previous = node.previous as EapSpanNode; const { data: eventTransaction = null, diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/profiling/profilePreview.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/profiling/profilePreview.tsx index c5148b22e13124..b5f17d08f72185 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/profiling/profilePreview.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/profiling/profilePreview.tsx @@ -29,14 +29,13 @@ import useOrganization from 'sentry/utils/useOrganization'; import {SectionDivider} from 'sentry/views/issueDetails/streamline/foldSection'; import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection'; import {isEAPSpanNode} from 'sentry/views/performance/newTraceDetails/traceGuards'; -import type {MissingInstrumentationNode} from 'sentry/views/performance/newTraceDetails/traceModels/missingInstrumentationNode'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; +import type {NoInstrumentationNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/noInstrumentationNode'; import {useProfileGroup} from 'sentry/views/profiling/profileGroupProvider'; import {useProfiles} from 'sentry/views/profiling/profilesProvider'; interface SpanProfileProps { event: Readonly | null; - missingInstrumentationNode: MissingInstrumentationNode; + missingInstrumentationNode: NoInstrumentationNode; profileID: string | undefined; profilerID: string | undefined; project: Project | undefined; @@ -75,10 +74,8 @@ export function ProfilePreview({ const transactionHasProfile = useMemo(() => { return isEAPSpanNode(missingInstrumentationNode.previous) - ? (TraceTree.ParentEAPTransaction(missingInstrumentationNode)?.profiles?.length ?? - 0) > 0 - : (TraceTree.ParentTransaction(missingInstrumentationNode)?.profiles?.length ?? 0) > - 0; + ? (missingInstrumentationNode.findParentEapTransaction()?.profiles?.size ?? 0) > 0 + : (missingInstrumentationNode.findParentTransaction()?.profiles?.size ?? 0) > 0; }, [missingInstrumentationNode]); const flamegraph = useMemo(() => { diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiIOAlert.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiIOAlert.tsx index 1b7ccc3fb2c160..725d3f01506ed6 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiIOAlert.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiIOAlert.tsx @@ -19,8 +19,9 @@ import { } from 'sentry/views/insights/agents/utils/aiTraceNodes'; import {hasAIInputAttribute} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiInput'; import {hasAIOutputAttribute} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiOutput'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; type SupportedSDKLanguage = 'javascript' | 'python'; @@ -71,7 +72,7 @@ export function AIIOAlert({ attributes, event, }: { - node: TraceTreeNode; + node: EapSpanNode | SpanNode | TransactionNode; attributes?: TraceItemResponseAttribute[]; event?: EventTransaction; }) { diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiInput.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiInput.tsx index 29b1388bf341e7..e5800ee0576f9d 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiInput.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiInput.tsx @@ -16,8 +16,9 @@ import { import {SectionKey} from 'sentry/views/issueDetails/streamline/context'; import {FoldSection} from 'sentry/views/issueDetails/streamline/foldSection'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; interface AIMessage { content: React.ReactNode; @@ -125,7 +126,7 @@ function transformPrompt(prompt: string) { } export function hasAIInputAttribute( - node: TraceTreeNode, + node: EapSpanNode | SpanNode | TransactionNode, attributes?: TraceItemResponseAttribute[], event?: EventTransaction ) { @@ -167,7 +168,7 @@ export function AIInputSection({ attributes, event, }: { - node: TraceTreeNode; + node: EapSpanNode | SpanNode | TransactionNode; attributes?: TraceItemResponseAttribute[]; event?: EventTransaction; }) { diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiOutput.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiOutput.tsx index 07bc6320fe413a..211235548f2e1f 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiOutput.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/aiOutput.tsx @@ -10,8 +10,9 @@ import { import {SectionKey} from 'sentry/views/issueDetails/streamline/context'; import {FoldSection} from 'sentry/views/issueDetails/streamline/foldSection'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; function isJson(value: string) { try { @@ -31,7 +32,7 @@ function renderAIResponse(text: string) { } export function hasAIOutputAttribute( - node: TraceTreeNode, + node: EapSpanNode | SpanNode | TransactionNode, attributes?: TraceItemResponseAttribute[], event?: EventTransaction ) { @@ -48,7 +49,7 @@ export function AIOutputSection({ attributes, event, }: { - node: TraceTreeNode; + node: EapSpanNode | SpanNode | TransactionNode; attributes?: TraceItemResponseAttribute[]; event?: EventTransaction; }) { diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/attributes.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/attributes.tsx index e83d9bfa78727e..10c7bad442af86 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/attributes.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/attributes.tsx @@ -28,8 +28,8 @@ import { getTraceAttributesTreeActions, sortAttributes, } from 'sentry/views/performance/newTraceDetails/traceDrawer/details/utils'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; +import type {UptimeCheckNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/uptimeCheckNode'; import {useTraceState} from 'sentry/views/performance/newTraceDetails/traceState/traceStateProvider'; import {useOTelFriendlyUI} from 'sentry/views/performance/otlp/useOTelFriendlyUI'; import {makeReplaysPathname} from 'sentry/views/replays/pathnames'; @@ -79,7 +79,7 @@ export function Attributes({ }: { attributes: TraceItemResponseAttribute[]; location: Location; - node: TraceTreeNode; + node: EapSpanNode | UptimeCheckNode; organization: Organization; project: Project | undefined; theme: Theme; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/description.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/description.tsx index 5fea0cc3c0d45c..e4e5b9614c7821 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/description.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/description.tsx @@ -45,7 +45,7 @@ import { TraceDrawerActionKind, } from 'sentry/views/performance/newTraceDetails/traceDrawer/details/utils'; import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; import {useOTelFriendlyUI} from 'sentry/views/performance/otlp/useOTelFriendlyUI'; import {transactionSummaryRouteWithQuery} from 'sentry/views/performance/transactionSummary/utils'; import {usePerformanceGeneralProjectSettings} from 'sentry/views/performance/utils'; @@ -64,13 +64,13 @@ export function SpanDescription({ attributes: TraceItemResponseAttribute[]; avgSpanDuration: number | undefined; location: Location; - node: TraceTreeNode; + node: EapSpanNode; organization: Organization; project: Project | undefined; hideNodeActions?: boolean; }) { const {data: event} = useEventDetails({ - eventId: node.event?.eventID, + eventId: node.value.transaction_id, projectSlug: project?.slug, }); const span = node.value; @@ -140,7 +140,7 @@ export function SpanDescription({ to={getSearchInExploreTarget( organization, location, - node.event?.projectID, + node.projectId?.toString(), exploreAttributeName, exploreAttributeValue, TraceDrawerActionKind.INCLUDE @@ -177,7 +177,7 @@ export function SpanDescription({ {codeFilepath ? ( ; + node: EapSpanNode; }) { const span = node.value; @@ -292,7 +292,7 @@ function ResourceImageDescription({ ) : ( setShowLinks(true)} - projectSlug={span.project_slug ?? node.event?.projectSlug} + projectSlug={span.project_slug ?? node.projectSlug} /> )} diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/mcpInput.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/mcpInput.tsx index 682505797def87..bcc860ed08c7e9 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/mcpInput.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/mcpInput.tsx @@ -2,12 +2,14 @@ import {t} from 'sentry/locale'; import type {EventTransaction} from 'sentry/types/event'; import type {TraceItemResponseAttribute} from 'sentry/views/explore/hooks/useTraceItemDetails'; import {ensureAttributeObject} from 'sentry/views/insights/agents/utils/aiTraceNodes'; +import type {AITraceSpanNode} from 'sentry/views/insights/agents/utils/types'; import {getIsMCPNode} from 'sentry/views/insights/mcp/utils/mcpTraceNodes'; import {SectionKey} from 'sentry/views/issueDetails/streamline/context'; import {FoldSection} from 'sentry/views/issueDetails/streamline/foldSection'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; const ARGUMENTS_KEY_PREFIX = 'mcp.request.argument.'; @@ -19,7 +21,7 @@ function shortenKey(key: string) { } function getInputAttributes( - node: TraceTreeNode, + node: AITraceSpanNode, event?: EventTransaction, attributes?: TraceItemResponseAttribute[] ): Array<[string, string | number | boolean]> { @@ -39,7 +41,7 @@ export function MCPInputSection({ attributes, event, }: { - node: TraceTreeNode; + node: EapSpanNode | SpanNode | TransactionNode; attributes?: TraceItemResponseAttribute[]; event?: EventTransaction; }) { diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/mcpOutput.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/mcpOutput.tsx index afb2486faeee33..21cedd8fb58a5b 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/mcpOutput.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/mcpOutput.tsx @@ -6,8 +6,9 @@ import {getIsMCPNode} from 'sentry/views/insights/mcp/utils/mcpTraceNodes'; import {SectionKey} from 'sentry/views/issueDetails/streamline/context'; import {FoldSection} from 'sentry/views/issueDetails/streamline/foldSection'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; const TOOL_OUTPUT_ATTRIBUTE = 'mcp.tool.result.content'; const PROMPT_OUTPUT_PREFIX = 'mcp.prompt.result.'; @@ -17,7 +18,7 @@ export function MCPOutputSection({ attributes, event, }: { - node: TraceTreeNode; + node: EapSpanNode | SpanNode | TransactionNode; attributes?: TraceItemResponseAttribute[]; event?: EventTransaction; }) { diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/traceSpanLinks.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/traceSpanLinks.tsx index b849156ae95c2d..c36ae58e3e9827 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/traceSpanLinks.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/traceSpanLinks.tsx @@ -20,16 +20,17 @@ import { import {SectionKey} from 'sentry/views/issueDetails/streamline/context'; import {FoldSection} from 'sentry/views/issueDetails/streamline/foldSection'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; import {useTraceStateDispatch} from 'sentry/views/performance/newTraceDetails/traceState/traceStateProvider'; import {TraceLayoutTabKeys} from 'sentry/views/performance/newTraceDetails/useTraceLayoutTabs'; interface TraceSpanLinksProps { links: TraceItemResponseLink[]; location: Location; - node: TraceTreeNode; - onTabScrollToNode: (node: TraceTreeNode) => void; + node: EapSpanNode; + onTabScrollToNode: (node: BaseNode) => void; organization: Organization; theme: Theme; traceId: string; @@ -125,7 +126,7 @@ export function TraceSpanLinks({ } // If the link is to the same trace, we look for and navigate to the span in the same trace waterfall - const spanNode = TraceTree.FindByID(tree.root, link.itemId); + const spanNode = tree.root.findChild(c => c.matchById(link.itemId)); if (spanNode) { onTabScrollToNode(spanNode); } diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/index.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/index.tsx index 8c9a9633b5c539..c484c1682c608d 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/index.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/index.tsx @@ -51,7 +51,9 @@ import { isEAPTransactionNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; import {ProfileGroupProvider} from 'sentry/views/profiling/profileGroupProvider'; import {ProfileContext, ProfilesProvider} from 'sentry/views/profiling/profilesProvider'; @@ -73,8 +75,8 @@ function SpanNodeDetailHeader({ onTabScrollToNode, hideNodeActions, }: { - node: TraceTreeNode | TraceTreeNode; - onTabScrollToNode: (node: TraceTreeNode) => void; + node: SpanNode | EapSpanNode; + onTabScrollToNode: (node: BaseNode) => void; organization: Organization; hideNodeActions?: boolean; }) { @@ -108,8 +110,8 @@ function SpanSections({ onParentClick, }: { location: Location; - node: TraceTreeNode; - onParentClick: (node: TraceTreeNode) => void; + node: SpanNode; + onParentClick: (node: BaseNode) => void; organization: Organization; project: Project | undefined; }) { @@ -150,30 +152,24 @@ function SpanSections({ } export function SpanNodeDetails( - props: TraceTreeNodeDetailsProps< - TraceTreeNode | TraceTreeNode - > + props: TraceTreeNodeDetailsProps ) { const {node, organization} = props; const location = useLocation(); const theme = useTheme(); const {projects} = useProjects(); - const issues = TraceTree.UniqueIssues(node); + const issues = node.uniqueIssues; const parentTransaction = isEAPSpanNode(node) - ? isEAPTransactionNode(node) + ? node.value.is_transaction ? node - : TraceTree.ParentEAPTransaction(node) - : TraceTree.ParentTransaction(node); + : node.findParentEapTransaction() + : node.findParentTransaction(); const profileId = parentTransaction?.value.profile_id; const profilerId = parentTransaction?.value.profiler_id; - const profilerStart = parentTransaction?.value.start_timestamp; - const profilerEnd = parentTransaction - ? 'timestamp' in parentTransaction.value - ? parentTransaction.value.timestamp - : parentTransaction.value.end_timestamp - : undefined; + const profilerStart = parentTransaction?.startTimestamp; + const profilerEnd = parentTransaction?.endTimestamp; const profileMeta = useMemo(() => { if (profileId) { @@ -191,9 +187,7 @@ export function SpanNodeDetails( return ''; }, [profileId, profilerId, profilerStart, profilerEnd]); - const project = projects.find( - proj => proj.slug === (node.value.project_slug ?? node.event?.projectSlug) - ); + const project = projects.find(proj => proj.slug === node.projectSlug); const spanId = isEAPSpanNode(node) ? node.value.event_id : node.value.span_id; @@ -259,7 +253,7 @@ function SpanNodeDetailsContent({ issues, location, onParentClick, -}: TraceTreeNodeDetailsProps> & { +}: TraceTreeNodeDetailsProps & { issues: TraceTree.TraceIssue[]; location: Location; project: Project | undefined; @@ -351,9 +345,7 @@ function useAvgSpanDuration( return result.data?.[0]?.['avg(span.duration)']; } -type EAPSpanNodeDetailsProps = TraceTreeNodeDetailsProps< - TraceTreeNode -> & { +type EAPSpanNodeDetailsProps = TraceTreeNodeDetailsProps & { issues: TraceTree.TraceIssue[]; location: Location; project: Project | undefined; @@ -369,7 +361,7 @@ function EAPSpanNodeDetails(props: EAPSpanNodeDetailsProps) { } = useTraceItemDetails({ traceItemId: node.value.event_id, projectId: node.value.project_id.toString(), - traceId: node.metadata.replayTraceSlug ?? traceId, + traceId: node.extra?.replayTraceSlug ?? traceId, traceItemType: TraceItemDataset.SPANS, referrer: 'api.explore.log-item-details', // TODO: change to span details enabled: true, @@ -378,8 +370,7 @@ function EAPSpanNodeDetails(props: EAPSpanNodeDetailsProps) { // EAP spans with is_transaction=false don't have an associated transaction_id that maps to the nodestore transaction. // In that case we use the transaction id attached to the direct parent EAP span where is_transaction=true. const transaction_event_id = - node.value.transaction_id ?? - TraceTree.ParentEAPTransaction(node)?.value.transaction_id; + node.value.transaction_id ?? node.findParentEapTransaction()?.value.transaction_id; const {data: eventTransaction, isLoading: isEventTransactionLoading} = useTransaction({ event_id: transaction_event_id, project_slug: node.value.project_slug, @@ -512,7 +503,7 @@ function EAPSpanNodeDetailsContent({ theme={theme} location={location} organization={organization} - traceId={node.metadata.replayTraceSlug ?? traceId} + traceId={node.extra?.replayTraceSlug ?? traceId} onTabScrollToNode={onTabScrollToNode} /> ) : null} diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/alerts.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/alerts.tsx index 77e85690002950..c2785cd011fc62 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/alerts.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/alerts.tsx @@ -1,10 +1,9 @@ import {Alert} from 'sentry/components/core/alert'; import {isOrphanSpan} from 'sentry/components/events/interfaces/spans/utils'; import {t} from 'sentry/locale'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; -function Alerts({node}: {node: TraceTreeNode}) { +function Alerts({node}: {node: SpanNode}) { if (!isOrphanSpan(node.value)) { return null; } diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/ancestry.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/ancestry.tsx index e945ab61851d39..03c9a81b8d7bec 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/ancestry.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/ancestry.tsx @@ -10,8 +10,9 @@ import { type SectionCardKeyValueList, } from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import {isTransactionNode} from 'sentry/views/performance/newTraceDetails/traceGuards'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; import {getTraceTabTitle} from 'sentry/views/performance/newTraceDetails/traceState/traceTabs'; export function useSpanAncestryAndGroupingItems({ @@ -19,14 +20,14 @@ export function useSpanAncestryAndGroupingItems({ onParentClick, }: { location: Location; - node: TraceTreeNode; - onParentClick: (node: TraceTreeNode) => void; + node: SpanNode; + onParentClick: (node: BaseNode) => void; organization: Organization; }): SectionCardKeyValueList { - const parentTransaction = useMemo(() => TraceTree.ParentTransaction(node), [node]); + const parentTransaction = useMemo(() => node.findParentTransaction(), [node]); const childTransactions = useMemo(() => { - const transactions: Array> = []; - TraceTree.ForEachChild(node, c => { + const transactions: TransactionNode[] = []; + node.forEachChild(c => { if (isTransactionNode(c)) { transactions.push(c); } diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/description.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/description.tsx index 50307e7f64f68c..fa98bf953b21f0 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/description.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/description.tsx @@ -42,7 +42,7 @@ import { TraceDrawerActionKind, } from 'sentry/views/performance/newTraceDetails/traceDrawer/details/utils'; import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; import {usePerformanceGeneralProjectSettings} from 'sentry/views/performance/utils'; const formatter = new SQLishFormatter(); @@ -55,7 +55,7 @@ export function SpanDescription({ hideNodeActions, }: { location: Location; - node: TraceTreeNode; + node: SpanNode; organization: Organization; project: Project | undefined; hideNodeActions?: boolean; @@ -226,7 +226,7 @@ function ResourceImageDescription({ node, }: { formattedDescription: string; - node: TraceTreeNode; + node: SpanNode; }) { const projectID = node.event?.projectID ? Number(node.event?.projectID) : undefined; const span = node.value; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/generalInfo.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/generalInfo.tsx index 85ace89ee6fa80..cdfcbad2609cb6 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/generalInfo.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/generalInfo.tsx @@ -22,18 +22,19 @@ import { type SectionCardKeyValueList, } from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; import {useSpanAncestryAndGroupingItems} from './ancestry'; type GeneralnfoProps = { location: Location; - node: TraceTreeNode; - onParentClick: (node: TraceTreeNode) => void; + node: SpanNode; + onParentClick: (node: BaseNode) => void; organization: Organization; }; -function SpanDuration({node}: {node: TraceTreeNode}) { +function SpanDuration({node}: {node: SpanNode}) { const span = node.value; const startTimestamp: number = span.start_timestamp; const endTimestamp: number = span.timestamp; @@ -51,7 +52,7 @@ function SpanDuration({node}: {node: TraceTreeNode}) { ); } -function SpanSelfTime({node}: {node: TraceTreeNode}) { +function SpanSelfTime({node}: {node: SpanNode}) { const span = node.value; const startTimestamp: number = span.start_timestamp; const endTimestamp: number = span.timestamp; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/keys.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/keys.tsx index b905f89d8b21e7..1d675587a4bae3 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/keys.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/keys.tsx @@ -24,8 +24,7 @@ import { } from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import {TraceDrawerActionValueKind} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/utils'; import {isSpanNode} from 'sentry/views/performance/newTraceDetails/traceGuards'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; import {getPerformanceDuration} from 'sentry/views/performance/utils/getPerformanceDuration'; const SIZE_DATA_KEYS = [ @@ -71,13 +70,13 @@ function partitionSizes(data: RawSpanType['data']): { }; } -function getSpanAggregateMeasurements(node: TraceTreeNode) { +function getSpanAggregateMeasurements(node: SpanNode) { if (!/^ai\.pipeline($|\.)/.test(node.value.op ?? '')) { return []; } let sum = 0; - TraceTree.ForEachChild(node, n => { + node.forEachChild(n => { if ( isSpanNode(n) && typeof n?.value?.measurements?.ai_total_tokens_used?.value === 'number' @@ -94,19 +93,17 @@ function getSpanAggregateMeasurements(node: TraceTreeNode) { ]; } -export function hasSpanKeys(node: TraceTreeNode, theme: Theme) { - const span = node.value; - const {sizeKeys, nonSizeKeys} = partitionSizes(span?.data ?? {}); +export function hasSpanKeys(node: SpanNode, theme: Theme) { + const {sizeKeys, nonSizeKeys} = partitionSizes(node.value?.data ?? {}); const allZeroSizes = SIZE_DATA_KEYS.map(key => sizeKeys[key]).every( value => value === 0 ); - const unknownKeys = Object.keys(span).filter(key => { + const unknownKeys = Object.keys(node.value).filter(key => { return !isHiddenDataKey(key) && !rawSpanKeys.has(key as any); }); - const timingKeys = getSpanSubTimings(span, theme) ?? []; + const timingKeys = getSpanSubTimings(node.value, theme) ?? []; const aggregateMeasurements: SectionCardKeyValueList = getSpanAggregateMeasurements(node); - return ( allZeroSizes || unknownKeys.length > 0 || @@ -117,7 +114,7 @@ export function hasSpanKeys(node: TraceTreeNode, theme: Theme) { ); } -export function SpanKeys({node}: {node: TraceTreeNode}) { +export function SpanKeys({node}: {node: SpanNode}) { const theme = useTheme(); const span = node.value; const projectIds = node.event?.projectID; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/measurements.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/measurements.tsx index 55354ab5676d50..3ce1a6e92a939a 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/measurements.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/measurements.tsx @@ -24,7 +24,7 @@ import { } from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import {TraceDrawerActionValueKind} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/utils'; import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; export function hasSpanMeasurements(span: TraceTree.Span) { return !!span.measurements && Object.keys(span.measurements).length > 0; @@ -36,7 +36,7 @@ function Measurements({ organization, }: { location: Location; - node: TraceTreeNode; + node: SpanNode; organization: Organization; }) { const theme = useTheme(); diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/tags.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/tags.tsx index 8da91a5a22797b..acc0dc35c12a1c 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/tags.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/span/sections/tags.tsx @@ -5,14 +5,13 @@ import { type SectionCardKeyValueList, } from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import {TraceDrawerActionValueKind} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/utils'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {SpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode'; export function hasSpanTags(span: RawSpanType) { return !!span.tags && Object.keys(span.tags).length > 0; } -export function Tags({node}: {node: TraceTreeNode}) { +export function Tags({node}: {node: SpanNode}) { const span = node.value; const tags: Record | undefined = span?.tags; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/styles.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/styles.tsx index 1903ec473914e3..3e68483cc02b56 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/styles.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/styles.tsx @@ -68,11 +68,8 @@ import { isSpanNode, isTransactionNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; -import type {MissingInstrumentationNode} from 'sentry/views/performance/newTraceDetails/traceModels/missingInstrumentationNode'; -import type {ParentAutogroupNode} from 'sentry/views/performance/newTraceDetails/traceModels/parentAutogroupNode'; -import type {SiblingAutogroupNode} from 'sentry/views/performance/newTraceDetails/traceModels/siblingAutogroupNode'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; +import type {EapSpanNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/eapSpanNode'; import { useTraceState, useTraceStateDispatch, @@ -329,7 +326,7 @@ const getDurationComparison = ( type DurationProps = { baseline: number | undefined; duration: number; - node: TraceTreeNode; + node: BaseNode; baseDescription?: string; ratio?: number; }; @@ -412,7 +409,7 @@ type HighlightProps = { avgDuration: number | undefined; bodyContent: React.ReactNode; headerContent: React.ReactNode; - node: TraceTreeNode; + node: BaseNode; project: Project | undefined; transaction: EventTransaction | undefined; hideNodeActions?: boolean; @@ -591,11 +588,11 @@ function HighLightEAPOpsBreakdown({ node, onRowClick, }: { - node: TraceTreeNode; + node: EapSpanNode; onRowClick: (op: string) => void; }) { const theme = useTheme(); - const breakdown = node.eapSpanOpsBreakdown; + const breakdown = node.opsBreakdown; if (breakdown.length === 0) { return null; @@ -765,13 +762,7 @@ const HighlightsRightColumn = styled('div')` overflow: hidden; `; -function IssuesLink({ - node, - children, -}: { - children: React.ReactNode; - node: TraceTreeNode; -}) { +function IssuesLink({node, children}: {children: React.ReactNode; node: BaseNode}) { const organization = useOrganization(); const params = useParams<{traceSlug?: string}>(); const traceSlug = params.traceSlug?.trim() ?? ''; @@ -840,7 +831,7 @@ const ValueTd = styled('td')` `; function getThreadIdFromNode( - node: TraceTreeNode, + node: BaseNode, transaction: EventTransaction | undefined ): string | undefined { if (isSpanNode(node) && node.value.data?.['thread.id']) { @@ -997,14 +988,8 @@ function PanelPositionDropDown({organization}: {organization: Organization}) { } function NodeActions(props: { - node: TraceTreeNode; - onTabScrollToNode: ( - node: - | TraceTreeNode - | ParentAutogroupNode - | SiblingAutogroupNode - | MissingInstrumentationNode - ) => void; + node: BaseNode; + onTabScrollToNode: (node: BaseNode) => void; organization: Organization; eventSize?: number | undefined; }) { @@ -1019,7 +1004,7 @@ function NodeActions(props: { const {data: transaction} = useTransaction({ event_id: transactionId, - project_slug: props.node.value.project_slug, + project_slug: props.node.projectSlug ?? '', organization, }); @@ -1034,7 +1019,7 @@ function NodeActions(props: { } return makeTransactionProfilingLink(profileId, { organization, - projectSlug: props.node.metadata.project_slug ?? '', + projectSlug: props.node.projectSlug ?? '', }); }, [organization, props.node]); @@ -1049,7 +1034,7 @@ function NodeActions(props: { } return makeTraceContinuousProfilingLink(props.node, profilerId, { organization, - projectSlug: props.node.metadata.project_slug ?? '', + projectSlug: props.node.projectSlug ?? '', traceId: params.traceSlug ?? '', threadId: getThreadIdFromNode(props.node, transaction), }); diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/index.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/index.tsx index 38ec757cf762fd..e98499ab324b1b 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/index.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/index.tsx @@ -32,8 +32,8 @@ import {MCPInputSection} from 'sentry/views/performance/newTraceDetails/traceDra import {MCPOutputSection} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/mcpOutput'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import type {TraceTreeNodeDetailsProps} from 'sentry/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; import {AdditionalData, hasAdditionalData} from './sections/additionalData'; import {BreadCrumbs} from './sections/breadCrumbs'; @@ -48,8 +48,8 @@ import {hasSDKContext} from './sections/sdk'; type TransactionNodeDetailHeaderProps = { event: EventTransaction; - node: TraceTreeNode; - onTabScrollToNode: (node: TraceTreeNode) => void; + node: TransactionNode; + onTabScrollToNode: (node: BaseNode) => void; organization: Organization; hideNodeActions?: boolean; }; @@ -93,7 +93,7 @@ export function TransactionNodeDetails({ onParentClick, replay, hideNodeActions, -}: TraceTreeNodeDetailsProps>) { +}: TraceTreeNodeDetailsProps) { const {projects} = useProjects(); const issues = useMemo(() => { return [...node.errors, ...node.occurrences]; @@ -137,7 +137,7 @@ export function TransactionNodeDetails({ hideNodeActions={hideNodeActions} /> - {node.canFetch ? null : ( + {node.canFetchChildren ? null : ( {tct( @@ -228,8 +228,8 @@ export function TransactionNodeDetails({ type TransactionSpecificSectionsProps = { cacheMetrics: Array>; event: EventTransaction; - node: TraceTreeNode; - onParentClick: (node: TraceTreeNode) => void; + node: TransactionNode; + onParentClick: (node: BaseNode) => void; organization: Organization; }; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/sections/generalInfo.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/sections/generalInfo.tsx index 052963e5370004..92278c27d6cf59 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/sections/generalInfo.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/sections/generalInfo.tsx @@ -16,16 +16,16 @@ import { TraceDrawerComponents, type SectionCardKeyValueList, } from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; import {getTraceTabTitle} from 'sentry/views/performance/newTraceDetails/traceState/traceTabs'; type GeneralInfoProps = { cacheMetrics: Array>; event: EventTransaction; location: Location; - node: TraceTreeNode; - onParentClick: (node: TraceTreeNode) => void; + node: TransactionNode; + onParentClick: (node: BaseNode) => void; organization: Organization; }; @@ -42,7 +42,7 @@ function GeneralInfo(props: GeneralInfoProps) { endTimestamp / 1e3 ); - const parentTransaction = TraceTree.ParentTransaction(node); + const parentTransaction = node.findParentTransaction(); const items: SectionCardKeyValueList = [ { diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/sections/highlights.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/sections/highlights.tsx index 665fa8a721f7a8..739737bfde71d1 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/sections/highlights.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/transaction/sections/highlights.tsx @@ -16,13 +16,12 @@ import {useTraceAverageTransactionDuration} from 'sentry/views/performance/newTr import {getHighlightedSpanAttributes} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/highlightedAttributes'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import {isTransactionNode} from 'sentry/views/performance/newTraceDetails/traceGuards'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; import {transactionSummaryRouteWithQuery} from 'sentry/views/performance/transactionSummary/utils'; type HighlightProps = { event: EventTransaction; - node: TraceTreeNode; + node: TransactionNode; organization: Organization; project: Project | undefined; hideNodeActions?: boolean; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/uptime/index.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/uptime/index.tsx index 872d2da8a5f523..bb2f653895a2f6 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/uptime/index.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/uptime/index.tsx @@ -16,8 +16,8 @@ import {TraceItemDataset} from 'sentry/views/explore/types'; import {Attributes} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/attributes'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import type {TraceTreeNodeDetailsProps} from 'sentry/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; +import type {UptimeCheckNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/uptimeCheckNode'; function UptimeNodeDetailsHeader({ node, @@ -25,8 +25,8 @@ function UptimeNodeDetailsHeader({ onTabScrollToNode, hideNodeActions, }: { - node: TraceTreeNode; - onTabScrollToNode: (node: TraceTreeNode) => void; + node: UptimeCheckNode; + onTabScrollToNode: (node: BaseNode) => void; organization: Organization; hideNodeActions?: boolean; }) { @@ -55,16 +55,14 @@ function UptimeNodeDetailsHeader({ ); } -export function UptimeNodeDetails( - props: TraceTreeNodeDetailsProps> -) { +export function UptimeNodeDetails(props: TraceTreeNodeDetailsProps) { const {node} = props; const location = useLocation(); const theme = useTheme(); const {projects} = useProjects(); const project = projects.find( - proj => proj.slug === (node.value.project_slug ?? node.event?.projectSlug) + proj => proj.slug === (node.value.project_slug ?? node.projectSlug) ); return ( @@ -78,9 +76,7 @@ export function UptimeNodeDetails( ); } -type UptimeSpanNodeDetailsProps = TraceTreeNodeDetailsProps< - TraceTreeNode -> & { +type UptimeSpanNodeDetailsProps = TraceTreeNodeDetailsProps & { location: Location; project: Project | undefined; theme: Theme; @@ -95,7 +91,7 @@ function UptimeSpanNodeDetails(props: UptimeSpanNodeDetailsProps) { } = useTraceItemDetails({ traceItemId: node.value.event_id, projectId: node.value.project_id.toString(), - traceId: node.metadata.replayTraceSlug ?? traceId, + traceId: node.extra?.replayTraceSlug ?? traceId, traceItemType: TraceItemDataset.UPTIME_RESULTS, referrer: 'api.explore.log-item-details', // TODO: change to span details enabled: true, diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/details/uptime/timing.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/details/uptime/timing.tsx index 14853ed765d293..1feda879444df1 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/details/uptime/timing.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/details/uptime/timing.tsx @@ -9,8 +9,7 @@ import {AttributesTree} from 'sentry/views/explore/components/traceItemAttribute import type {TraceItemResponseAttribute} from 'sentry/views/explore/hooks/useTraceItemDetails'; import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles'; import type {TraceTreeNodeDetailsProps} from 'sentry/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {UptimeCheckTimingNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/uptimeCheckTimingNode'; const UPTIME_PHASE_DESCRIPTIONS = { 'dns.lookup.duration': t( @@ -32,7 +31,7 @@ const UPTIME_PHASE_DESCRIPTIONS = { }; export function UptimeTimingDetails( - props: TraceTreeNodeDetailsProps> + props: TraceTreeNodeDetailsProps ) { const {node} = props; const {op, description, duration, start_timestamp, end_timestamp} = node.value; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/tabs/traceProfiles.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/tabs/traceProfiles.tsx index 65db1867bf76b8..b66e46a28fffb0 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/tabs/traceProfiles.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/tabs/traceProfiles.tsx @@ -19,7 +19,7 @@ import { isSpanNode, isTransactionNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; +import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; export function TraceProfiles({tree}: {tree: TraceTree}) { const {projects} = useProjects(); @@ -59,7 +59,7 @@ export function TraceProfiles({tree}: {tree: TraceTree}) { {profiles.map((node, index) => { - const profile = node.profiles?.[0]; + const profile = Array.from(node.profiles)?.[0]; if (!profile) { return null; @@ -71,7 +71,7 @@ export function TraceProfiles({tree}: {tree: TraceTree}) { } : isSpanNode(node) ? { - eventId: TraceTree.ParentTransaction(node)?.value?.event_id, + eventId: node.findParentTransaction()?.value?.event_id, } : {}; @@ -82,12 +82,12 @@ export function TraceProfiles({tree}: {tree: TraceTree}) { profilerId: profile.profiler_id, start: new Date(node.space[0]).toISOString(), end: new Date(node.space[0] + node.space[1]).toISOString(), - projectSlug: node.metadata.project_slug as string, + projectSlug: node.projectSlug ?? '', query, }) : generateProfileFlamechartRouteWithQuery({ organization, - projectSlug: node.metadata.project_slug as string, + projectSlug: node.projectSlug ?? '', profileId: profile.profile_id, query, }); diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails.tsx index 133dcaf86b1bd0..44e8bf96a719f5 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/tabs/traceTreeNodeDetails.tsx @@ -18,15 +18,15 @@ import { isUptimeCheckTimingNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; import type {VirtualizedViewManager} from 'sentry/views/performance/newTraceDetails/traceRenderers/virtualizedViewManager'; import type {ReplayRecord} from 'sentry/views/replays/types'; export interface TraceTreeNodeDetailsProps { manager: VirtualizedViewManager | null; node: T; - onParentClick: (node: TraceTreeNode) => void; - onTabScrollToNode: (node: TraceTreeNode) => void; + onParentClick: (node: BaseNode) => void; + onTabScrollToNode: (node: BaseNode) => void; organization: Organization; replay: ReplayRecord | null; traceId: string; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/traceDrawer.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/traceDrawer.tsx index c2dd69c6f360d7..a0a8c420afa8a7 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/traceDrawer.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/traceDrawer.tsx @@ -24,7 +24,7 @@ import type { TraceShape, TraceTree, } from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; import type {TraceScheduler} from 'sentry/views/performance/newTraceDetails/traceRenderers/traceScheduler'; import type {VirtualizedViewManager} from 'sentry/views/performance/newTraceDetails/traceRenderers/virtualizedViewManager'; import {makeTraceNodeBarColor} from 'sentry/views/performance/newTraceDetails/traceRow/traceBar'; @@ -48,8 +48,8 @@ import {TraceTreeNodeDetails} from './tabs/traceTreeNodeDetails'; type TraceDrawerProps = { manager: VirtualizedViewManager; meta: TraceMetaQueryResults; - onScrollToNode: (node: TraceTreeNode) => void; - onTabScrollToNode: (node: TraceTreeNode) => void; + onScrollToNode: (node: BaseNode) => void; + onTabScrollToNode: (node: BaseNode) => void; replay: ReplayRecord | null; scheduler: TraceScheduler; trace: TraceTree; @@ -206,7 +206,7 @@ export function TraceDrawer(props: TraceDrawerProps) { const {onMouseDown, size} = usePassiveResizableDrawer(resizableDrawerOptions); const onParentClick = useCallback( - (node: TraceTreeNode) => { + (node: BaseNode) => { props.onTabScrollToNode(node); traceDispatch({ type: 'activate tab', @@ -410,7 +410,7 @@ export function TraceDrawer(props: TraceDrawerProps) { interface TraceDrawerTabProps { index: number; - onTabScrollToNode: (node: TraceTreeNode) => void; + onTabScrollToNode: (node: BaseNode) => void; pinned: boolean; tab: TraceTabsReducerState['tabs'][number]; theme: Theme; diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/traceProfilingLink.spec.tsx b/static/app/views/performance/newTraceDetails/traceDrawer/traceProfilingLink.spec.tsx index 97c28dd7760a5d..0c19f1f16ca5af 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/traceProfilingLink.spec.tsx +++ b/static/app/views/performance/newTraceDetails/traceDrawer/traceProfilingLink.spec.tsx @@ -2,7 +2,7 @@ import type {LocationDescriptor} from 'history'; import {OrganizationFixture} from 'sentry-fixture/organization'; import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import {TransactionNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode'; import {makeTraceContinuousProfilingLink} from './traceProfilingLink'; @@ -24,11 +24,12 @@ function makeTransaction( } as TraceTree.Transaction; } +const organization = OrganizationFixture({slug: 'sentry'}); + describe('traceProfilingLink', () => { describe('required params', () => { - const node = new TraceTreeNode(null, makeTransaction(), { - project_slug: '', - event_id: '', + const node = new TransactionNode(null, makeTransaction(), { + organization, }); it('requires projectSlug', () => { @@ -67,7 +68,7 @@ describe('traceProfilingLink', () => { it('creates a window of time around end timestamp', () => { const timestamp = Date.now(); - const node = new TraceTreeNode( + const node = new TransactionNode( null, makeTransaction({ start_timestamp: undefined, @@ -75,8 +76,7 @@ describe('traceProfilingLink', () => { event_id: 'event', }), { - project_slug: 'project', - event_id: 'event', + organization, } ); diff --git a/static/app/views/performance/newTraceDetails/traceDrawer/traceProfilingLink.ts b/static/app/views/performance/newTraceDetails/traceDrawer/traceProfilingLink.ts index 85d47662a4b747..7521d348c59b20 100644 --- a/static/app/views/performance/newTraceDetails/traceDrawer/traceProfilingLink.ts +++ b/static/app/views/performance/newTraceDetails/traceDrawer/traceProfilingLink.ts @@ -10,10 +10,9 @@ import { isSpanNode, isTransactionNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; -function getNodeId(node: TraceTreeNode): string | undefined { +function getNodeId(node: BaseNode): string | undefined { if (isTransactionNode(node)) { return node.value.event_id; } @@ -24,11 +23,11 @@ function getNodeId(node: TraceTreeNode): string | undefined } // In the current version, a segment is a parent transaction -function getEventId(node: TraceTreeNode): string | undefined { +function getEventId(node: BaseNode): string | undefined { if (isTransactionNode(node)) { return node.value.event_id; } - return TraceTree.ParentTransaction(node)?.value?.event_id; + return node.findParentTransaction()?.value?.event_id; } export function makeTransactionProfilingLink( @@ -54,7 +53,7 @@ export function makeTransactionProfilingLink( * Generates a link to a continuous profile for a given trace element type */ export function makeTraceContinuousProfilingLink( - node: TraceTreeNode, + node: BaseNode, profilerId: string, options: { organization: Organization; @@ -70,7 +69,7 @@ export function makeTraceContinuousProfilingLink( // We compute a time offset based on the duration of the span so that // users can see some context of things that occurred before and after the span. - const transaction = isTransactionNode(node) ? node : TraceTree.ParentTransaction(node); + const transaction = isTransactionNode(node) ? node : node.findParentTransaction(); if (!transaction) { return null; } diff --git a/static/app/views/performance/newTraceDetails/traceGuards.tsx b/static/app/views/performance/newTraceDetails/traceGuards.tsx index 2ace569c76421a..bbda08422232c0 100644 --- a/static/app/views/performance/newTraceDetails/traceGuards.tsx +++ b/static/app/views/performance/newTraceDetails/traceGuards.tsx @@ -91,7 +91,7 @@ export function isEAPError(value: TraceTree.NodeValue): value is TraceTree.EAPEr ); } -export function isEAPErrorNode(node: BaseNode): node is ErrorNode { +export function isEAPErrorNode(node: BaseNode): node is BaseNode { return isEAPError(node.value); } @@ -125,10 +125,15 @@ export function isTraceError(value: TraceTree.NodeValue): value is TraceTree.Tra return !!(value && 'level' in value && 'message' in value); } -export function isTraceErrorNode(node: BaseNode): node is ErrorNode { +export function isTraceErrorNode(node: BaseNode): node is BaseNode { return isTraceError(node.value); } +// TODO Abdullah Khan: Won't be needed once we fully migrate to the new BaseNode subclass +export function isErrorNode(node: BaseNode): node is ErrorNode { + return isTraceErrorNode(node) || isEAPErrorNode(node); +} + export function isRootNode(node: BaseNode): node is RootNode { return node.value === null; } diff --git a/static/app/views/performance/newTraceDetails/traceHeader/index.spec.tsx b/static/app/views/performance/newTraceDetails/traceHeader/index.spec.tsx index dc92bac0e42932..b34bc12d8194a4 100644 --- a/static/app/views/performance/newTraceDetails/traceHeader/index.spec.tsx +++ b/static/app/views/performance/newTraceDetails/traceHeader/index.spec.tsx @@ -13,7 +13,9 @@ import { } from 'sentry/views/performance/newTraceDetails/traceHeader'; import {TraceViewSources} from 'sentry/views/performance/newTraceDetails/traceHeader/breadcrumbs'; import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import {RootNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/rootNode'; +import {TraceNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/traceNode'; +import {UptimeCheckNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/uptimeCheckNode'; import {makeUptimeCheck} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeTestUtils'; jest.mock('sentry/views/performance/newTraceDetails/traceState/traceStateProvider'); @@ -190,9 +192,8 @@ describe('TraceMetaDataHeader', () => { const tree = new TraceTree(); // Create the tree root (this is tree.root) - const treeRoot = new TraceTreeNode(null, null, { - project_slug: 'test-project', - event_id: 'tree-root', + const treeRoot = new RootNode(null, null, { + organization, }); tree.root = treeRoot; @@ -201,16 +202,14 @@ describe('TraceMetaDataHeader', () => { transactions: [], orphan_errors: [], }; - const traceNode = new TraceTreeNode(treeRoot, traceNodeValue, { - project_slug: 'test-project', - event_id: 'trace-node', + const traceNode = new TraceNode(treeRoot, traceNodeValue, { + organization, }); treeRoot.children.push(traceNode); // Add uptime check as first child of trace node - const uptimeCheckNode = new TraceTreeNode(traceNode, uptimeCheckEvent, { - project_slug: 'test-project', - event_id: 'uptime-event-id', + const uptimeCheckNode = new UptimeCheckNode(traceNode, uptimeCheckEvent, { + organization, }); traceNode.children.push(uptimeCheckNode); diff --git a/static/app/views/performance/newTraceDetails/traceHeader/meta.tsx b/static/app/views/performance/newTraceDetails/traceHeader/meta.tsx index 085dda6dbe4706..0ab6a2d5357fd1 100644 --- a/static/app/views/performance/newTraceDetails/traceHeader/meta.tsx +++ b/static/app/views/performance/newTraceDetails/traceHeader/meta.tsx @@ -15,7 +15,7 @@ import { isEAPTraceNode, isTraceError, } from 'sentry/views/performance/newTraceDetails/traceGuards'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; +import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; import {useTraceQueryParams} from 'sentry/views/performance/newTraceDetails/useTraceQueryParams'; type MetaDataProps = { @@ -79,7 +79,7 @@ export function Meta(props: MetaProps) { ? props.tree.eap_spans_count : (props.meta?.span_count ?? 0); - const uniqueIssuesCount = traceNode ? TraceTree.UniqueIssues(traceNode).length : 0; + const uniqueIssuesCount = traceNode ? traceNode.uniqueIssues.length : 0; // If there is no trace data, use the timestamp from the query params as an approximation for // the age of the trace. diff --git a/static/app/views/performance/newTraceDetails/traceModels/issuesTraceTree.spec.tsx b/static/app/views/performance/newTraceDetails/traceModels/issuesTraceTree.spec.tsx index 5ed62d8f946892..64f07b3260d54c 100644 --- a/static/app/views/performance/newTraceDetails/traceModels/issuesTraceTree.spec.tsx +++ b/static/app/views/performance/newTraceDetails/traceModels/issuesTraceTree.spec.tsx @@ -8,7 +8,6 @@ import { isTransactionNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import {DEFAULT_TRACE_VIEW_PREFERENCES} from 'sentry/views/performance/newTraceDetails/traceState/tracePreferences'; import type {BaseNode} from './traceTreeNode/baseNode'; import {IssuesTraceTree} from './issuesTraceTree'; @@ -201,7 +200,6 @@ describe('IssuesTraceTree', () => { await txn.fetchChildren(true, tree, { api: new MockApiClient(), - preferences: DEFAULT_TRACE_VIEW_PREFERENCES, }); const span = tree.root.findChild( diff --git a/static/app/views/performance/newTraceDetails/traceModels/missingInstrumentationNode.spec.tsx b/static/app/views/performance/newTraceDetails/traceModels/missingInstrumentationNode.spec.tsx deleted file mode 100644 index 59a1df91f7e0c8..00000000000000 --- a/static/app/views/performance/newTraceDetails/traceModels/missingInstrumentationNode.spec.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; -import { - makeNodeMetadata, - makeSpan, -} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeTestUtils'; - -import {MissingInstrumentationNode} from './missingInstrumentationNode'; - -describe('MissingInstrumentationNode', () => { - it('stores references to previous and next spans', () => { - const previous = new TraceTreeNode(null, makeSpan(), makeNodeMetadata()); - const current = new TraceTreeNode(null, makeSpan(), makeNodeMetadata()); - - const node = new MissingInstrumentationNode( - new TraceTreeNode(null, makeSpan(), makeNodeMetadata()), - { - type: 'missing_instrumentation', - start_timestamp: previous.value.timestamp, - timestamp: current.value.start_timestamp, - }, - makeNodeMetadata(), - previous, - current - ); - - expect(node.previous).toBe(previous); - expect(node.next).toBe(current); - }); -}); diff --git a/static/app/views/performance/newTraceDetails/traceModels/missingInstrumentationNode.tsx b/static/app/views/performance/newTraceDetails/traceModels/missingInstrumentationNode.tsx deleted file mode 100644 index 2272c6ec82f450..00000000000000 --- a/static/app/views/performance/newTraceDetails/traceModels/missingInstrumentationNode.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import {isSpanNode} from 'sentry/views/performance/newTraceDetails/traceGuards'; - -import type {TraceTree} from './traceTree'; -import {TraceTreeNode} from './traceTreeNode'; - -export class MissingInstrumentationNode extends TraceTreeNode { - next: TraceTreeNode | TraceTreeNode; - previous: TraceTreeNode | TraceTreeNode; - - constructor( - parent: TraceTreeNode, - node: TraceTree.MissingInstrumentationSpan, - metadata: TraceTree.Metadata, - previous: TraceTreeNode | TraceTreeNode, - next: TraceTreeNode | TraceTreeNode - ) { - super(parent, node, metadata); - - this.next = next; - this.previous = previous; - - // The space of a missing instrumentation node is gap between previous end and next start - const previousEndTimestamp = isSpanNode(previous) - ? previous.value.timestamp - : previous.value.end_timestamp; - this.space = [ - previousEndTimestamp * 1e3, - (next.value.start_timestamp - previousEndTimestamp) * 1e3, - ]; - } -} diff --git a/static/app/views/performance/newTraceDetails/traceModels/parentAutogroupNode.spec.tsx b/static/app/views/performance/newTraceDetails/traceModels/parentAutogroupNode.spec.tsx deleted file mode 100644 index 950bdcc004906e..00000000000000 --- a/static/app/views/performance/newTraceDetails/traceModels/parentAutogroupNode.spec.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { - makeNodeMetadata, - makeParentAutogroup, - makeSpan, -} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeTestUtils'; - -import {ParentAutogroupNode} from './parentAutogroupNode'; -import {TraceTreeNode} from './traceTreeNode'; - -const start = new Date('2024-02-29T00:00:00Z').getTime() / 1e3; - -describe('ParentAutogroupNode', () => { - it('not expanded by default', () => { - const node = new ParentAutogroupNode( - null, - makeParentAutogroup({autogrouped_by: {op: 'op'}}), - makeNodeMetadata(), - new TraceTreeNode(null, makeSpan(), makeNodeMetadata()), - new TraceTreeNode(null, makeSpan(), makeNodeMetadata()) - ); - expect(node.expanded).toBe(false); - }); - - it('segments', () => { - const head = new TraceTreeNode( - null, - makeSpan({start_timestamp: start, timestamp: start + 1}), - makeNodeMetadata() - ); - const tail = new TraceTreeNode( - head, - makeSpan({start_timestamp: start + 1, timestamp: start + 2}), - makeNodeMetadata() - ); - - head.children.push(tail); - - const node = new ParentAutogroupNode( - null, - makeParentAutogroup({autogrouped_by: {op: 'op'}}), - makeNodeMetadata(), - head, - tail - ); - - expect(node.autogroupedSegments).toEqual([[start * 1e3, 2000]]); - }); - - it('segments with gap', () => { - const head = new TraceTreeNode( - null, - makeSpan({start_timestamp: start, timestamp: start + 1}), - makeNodeMetadata() - ); - const tail = new TraceTreeNode( - head, - makeSpan({start_timestamp: start + 2, timestamp: start + 3}), - makeNodeMetadata() - ); - - head.children.push(tail); - - const node = new ParentAutogroupNode( - null, - makeParentAutogroup({autogrouped_by: {op: 'op'}}), - makeNodeMetadata(), - head, - tail - ); - - expect(node.autogroupedSegments).toEqual([ - [start * 1e3, 1000], - [start * 1e3 + 2000, 1000], - ]); - }); -}); diff --git a/static/app/views/performance/newTraceDetails/traceModels/parentAutogroupNode.tsx b/static/app/views/performance/newTraceDetails/traceModels/parentAutogroupNode.tsx deleted file mode 100644 index 31649e52a11452..00000000000000 --- a/static/app/views/performance/newTraceDetails/traceModels/parentAutogroupNode.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import type {TraceTree} from './traceTree'; -import {TraceTreeNode} from './traceTreeNode'; - -export class ParentAutogroupNode extends TraceTreeNode { - head: TraceTreeNode | TraceTreeNode; - tail: TraceTreeNode | TraceTreeNode; - groupCount = 0; - profiles: TraceTree.Profile[] = []; - - private _autogroupedSegments: Array<[number, number]> | undefined; - - constructor( - parent: TraceTreeNode | null, - node: TraceTree.ChildrenAutogroup, - metadata: TraceTree.Metadata, - head: TraceTreeNode | TraceTreeNode, - tail: TraceTreeNode | TraceTreeNode - ) { - super(parent, node, metadata); - - this.expanded = false; - this.head = head; - this.tail = tail; - } - - get autogroupedSegments(): Array<[number, number]> { - if (this._autogroupedSegments) { - return this._autogroupedSegments; - } - - const children: Array> = []; - let start: TraceTreeNode | undefined = this.head; - - while (start && start !== this.tail) { - children.push(start); - start = start.children[0]; - } - - children.push(this.tail); - - this._autogroupedSegments = computeCollapsedBarSpace(children); - return this._autogroupedSegments; - } -} - -// Returns a list of segments from a grouping sequence that can be used to render a span bar chart -// It looks for gaps between spans and creates a segment for each gap. If there are no gaps, it -// merges the n and n+1 segments. -export function computeCollapsedBarSpace( - nodes: Array> -): Array<[number, number]> { - if (nodes.length === 0) { - return []; - } - - const first = nodes[0]!; - - const segments: Array<[number, number]> = []; - - let start = first.space[0]; - let end = first.space[0] + first.space[1]; - let i = 1; - - while (i < nodes.length) { - const next = nodes[i]!; - - if (next.space[0] > end) { - segments.push([start, end - start]); - start = next.space[0]; - end = next.space[0] + next.space[1]; - i++; - } else { - end = next.space[0] + next.space[1]; - i++; - } - } - - segments.push([start, end - start]); - - return segments; -} diff --git a/static/app/views/performance/newTraceDetails/traceModels/siblingAutogroupNode.spec.tsx b/static/app/views/performance/newTraceDetails/traceModels/siblingAutogroupNode.spec.tsx deleted file mode 100644 index 8a46901310a3e7..00000000000000 --- a/static/app/views/performance/newTraceDetails/traceModels/siblingAutogroupNode.spec.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { - makeNodeMetadata, - makeSiblingAutogroup, - makeSpan, -} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeTestUtils'; - -import {SiblingAutogroupNode} from './siblingAutogroupNode'; -import {TraceTreeNode} from './traceTreeNode'; - -const start = new Date('2024-02-29T00:00:00Z').getTime() / 1e3; - -describe('SiblingAutogroupNode', () => { - it('not expanded by default', () => { - const node = new SiblingAutogroupNode( - null, - makeSiblingAutogroup({autogrouped_by: {op: 'op', description: 'description'}}), - makeNodeMetadata() - ); - expect(node.expanded).toBe(false); - }); - - it('segments', () => { - const node = new SiblingAutogroupNode( - null, - makeSiblingAutogroup({autogrouped_by: {op: 'op', description: 'description'}}), - makeNodeMetadata() - ); - - for (let i = 0; i < 5; i++) { - node.children.push( - new TraceTreeNode( - node, - makeSpan({ - description: 'span', - op: 'db', - start_timestamp: start + i, - timestamp: start + i + 1, - span_id: `span-${i}`, - parent_span_id: node.value.span_id, - }), - makeNodeMetadata() - ) - ); - } - expect(node.autogroupedSegments).toEqual([[start * 1e3, 5000]]); - }); - - it('segments with gap', () => { - const node = new SiblingAutogroupNode( - null, - makeSiblingAutogroup({autogrouped_by: {op: 'op', description: 'description'}}), - makeNodeMetadata() - ); - - node.children.push( - new TraceTreeNode( - node, - makeSpan({ - start_timestamp: start, - timestamp: start + 1, - }), - makeNodeMetadata() - ) - ); - - node.children.push( - new TraceTreeNode( - node, - makeSpan({ - start_timestamp: start + 2, - timestamp: start + 3, - }), - makeNodeMetadata() - ) - ); - expect(node.autogroupedSegments).toEqual([ - [start * 1e3, 1000], - [start * 1e3 + 2000, 1000], - ]); - }); -}); diff --git a/static/app/views/performance/newTraceDetails/traceModels/siblingAutogroupNode.tsx b/static/app/views/performance/newTraceDetails/traceModels/siblingAutogroupNode.tsx deleted file mode 100644 index d0650c204db2f1..00000000000000 --- a/static/app/views/performance/newTraceDetails/traceModels/siblingAutogroupNode.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import {computeCollapsedBarSpace} from './parentAutogroupNode'; -import type {TraceTree} from './traceTree'; -import {TraceTreeNode} from './traceTreeNode'; - -export class SiblingAutogroupNode extends TraceTreeNode { - groupCount = 0; - profiles: TraceTree.Profile[] = []; - - private _autogroupedSegments: Array<[number, number]> | undefined; - - constructor( - parent: TraceTreeNode | null, - node: TraceTree.SiblingAutogroup, - metadata: TraceTree.Metadata - ) { - super(parent, node, metadata); - this.expanded = false; - } - - get autogroupedSegments(): Array<[number, number]> { - if (this._autogroupedSegments) { - return this._autogroupedSegments; - } - - this._autogroupedSegments = computeCollapsedBarSpace(this.children); - return this._autogroupedSegments; - } -} diff --git a/static/app/views/performance/newTraceDetails/traceModels/traceCollapsedNode.tsx b/static/app/views/performance/newTraceDetails/traceModels/traceCollapsedNode.tsx deleted file mode 100644 index 197c092e3a0e97..00000000000000 --- a/static/app/views/performance/newTraceDetails/traceModels/traceCollapsedNode.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type {TraceTree} from './traceTree'; -import {TraceTreeNode} from './traceTreeNode'; - -export class CollapsedNode extends TraceTreeNode { - constructor( - parent: TraceTreeNode, - node: TraceTree.CollapsedNode, - metadata: TraceTree.Metadata - ) { - super(parent, node, metadata); - this.expanded = false; - } -} diff --git a/static/app/views/performance/newTraceDetails/traceModels/traceTree.spec.tsx b/static/app/views/performance/newTraceDetails/traceModels/traceTree.spec.tsx index e3af443e11f4c1..b7d312cee1d1b3 100644 --- a/static/app/views/performance/newTraceDetails/traceModels/traceTree.spec.tsx +++ b/static/app/views/performance/newTraceDetails/traceModels/traceTree.spec.tsx @@ -950,7 +950,7 @@ describe('TraceTree', () => { const parentAutogroupNode = tree.root.findChild(n => isParentAutogroupedNode(n) - )! as ParentAutogroupNode; + ) as ParentAutogroupNode; // Expand the chain and collapse an intermediary child parentAutogroupNode.expand(true, tree); diff --git a/static/app/views/performance/newTraceDetails/traceModels/traceTree.ssr.spec.tsx b/static/app/views/performance/newTraceDetails/traceModels/traceTree.ssr.spec.tsx index be4d97de9e8aab..32f58601de0c25 100644 --- a/static/app/views/performance/newTraceDetails/traceModels/traceTree.ssr.spec.tsx +++ b/static/app/views/performance/newTraceDetails/traceModels/traceTree.ssr.spec.tsx @@ -3,11 +3,12 @@ import {OrganizationFixture} from 'sentry-fixture/organization'; import { makeEAPSpan, makeEAPTrace, - makeEventTransaction, makeSpan, makeTrace, makeTransaction, + mockSpansResponse, } from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeTestUtils'; +import {DEFAULT_TRACE_VIEW_PREFERENCES} from 'sentry/views/performance/newTraceDetails/traceState/tracePreferences'; import {TraceTree} from './traceTree'; @@ -22,12 +23,16 @@ const ssrTrace = makeTrace({ start_timestamp: start, timestamp: start + 2, ['transaction.op']: 'http.server', + event_id: '000', + project_slug: 'project-0', children: [ makeTransaction({ start_timestamp: start, timestamp: start + 2, ['transaction.op']: 'pageload', children: [], + event_id: '001', + project_slug: 'project-1', }), ], }), @@ -93,14 +98,17 @@ describe('server side rendering', () => { expect(tree.build().serialize()).toMatchSnapshot(); }); - it('reparents server handler under browser request span', () => { + it('reparents server handler under browser request span', async () => { const tree = TraceTree.FromTrace(ssrTrace, traceMetadata); - TraceTree.FromSpans( - tree.root.children[0]!.children[0]!, - ssrSpans, - makeEventTransaction() - ); + mockSpansResponse([], 'project-0', '000'); + mockSpansResponse(ssrSpans, 'project-1', '001'); + await tree.fetchNodeSubTree(true, tree.root.children[0]!.children[0]!, { + api: new MockApiClient(), + organization, + preferences: DEFAULT_TRACE_VIEW_PREFERENCES, + }); + expect(tree.build().serialize()).toMatchSnapshot(); }); @@ -130,7 +138,7 @@ describe('server side rendering', () => { expect(tree.build().serialize()).toMatchSnapshot(); }); - it('does not reparent if server handler does not have SSR reparent reason', () => { + it('does not reparent if server handler does not have SSR reparent reason', async () => { const tree = TraceTree.FromTrace(ssrTrace, traceMetadata); // The automatic pageload reparenting should have happened @@ -149,7 +157,13 @@ describe('server side rendering', () => { serverHandler.reparent_reason = null; // This is where it would re-parent again - TraceTree.FromSpans(pageload, ssrSpans, makeEventTransaction()); + mockSpansResponse(ssrSpans, 'project-1', '001'); + mockSpansResponse([], 'project-0', '000'); + await tree.fetchNodeSubTree(true, pageload, { + api: new MockApiClient(), + organization, + preferences: DEFAULT_TRACE_VIEW_PREFERENCES, + }); const browserRequestSpan = tree.root.children[0]!.children[0]!.children.find( span => span.value && 'op' in span.value && span.value.op === 'browser.request' diff --git a/static/app/views/performance/newTraceDetails/traceModels/traceTree.tsx b/static/app/views/performance/newTraceDetails/traceModels/traceTree.tsx index 2dc8c4470451f1..04344a44938643 100644 --- a/static/app/views/performance/newTraceDetails/traceModels/traceTree.tsx +++ b/static/app/views/performance/newTraceDetails/traceModels/traceTree.tsx @@ -15,6 +15,7 @@ import type { import {getTraceQueryParams} from 'sentry/views/performance/newTraceDetails/traceApi/useTrace'; import type {TraceMetaQueryResults} from 'sentry/views/performance/newTraceDetails/traceApi/useTraceMeta'; import { + isAutogroupedNode, isEAPError, isEAPSpan, isJavascriptSDKEvent, @@ -459,6 +460,11 @@ export class TraceTree extends TraceTreeEventDispatcher { }); tree.eap_spans_count++; + + // We only want to add transactions as profiled events. + if ((node as EapSpanNode).value.is_transaction && node.profiles.size > 0) { + tree.profiled_events.add(node); + } } else if (isUptimeCheck(value)) { node = new UptimeCheckNode(parent, value, { organization: options.organization, @@ -475,6 +481,11 @@ export class TraceTree extends TraceTreeEventDispatcher { replayTraceSlug: options.replayTraceSlug, meta: options.meta, }); + + // We only want to add transactions as profiled events. + if (node.profiles.size > 0) { + tree.profiled_events.add(node); + } } if (node.canFetchChildren || !node.expanded) { @@ -627,6 +638,8 @@ export class TraceTree extends TraceTreeEventDispatcher { TraceTree.ApplyPreferences(node, options); } } + + this.build(); } appendTree(tree: TraceTree) { @@ -842,7 +855,9 @@ export class TraceTree extends TraceTreeEventDispatcher { throw new Error('Parent node is missing, this should be unreachable code'); } - const children = node.parent.directVisibleChildren; + const children = isParentAutogroupedNode(node.parent) + ? node.parent.tail.children + : node.parent.children; const index = children.indexOf(node); if (index === -1) { throw new Error('Node is not a child of its parent'); @@ -912,7 +927,7 @@ export class TraceTree extends TraceTreeEventDispatcher { queue.push(...node.getNextTraversalNodes()); - if (node.children.length < 5) { + if (node.children.length < 5 || isAutogroupedNode(node)) { continue; } @@ -990,8 +1005,10 @@ export class TraceTree extends TraceTreeEventDispatcher { for (const error of child.errors) { autoGroupedNode.errors.add(error); } + } - for (const occurrence of child.occurrences) { + if (node.children[j]!.occurrences.size > 0) { + for (const occurrence of node.children[j]!.occurrences) { autoGroupedNode.occurrences.add(occurrence); } } @@ -1116,7 +1133,7 @@ export class TraceTree extends TraceTreeEventDispatcher { const transactionNodes = tree.root.findAllChildren( node => node.canFetchChildren && transactionIds.has(node.id ?? '') - ) as TransactionNode[]; + ); const promises = transactionNodes.map(node => tree.fetchNodeSubTree(true, node, { diff --git a/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode.spec.tsx b/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode.spec.tsx deleted file mode 100644 index eb2fa8ca747ae9..00000000000000 --- a/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode.spec.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import { - makeSpan, - makeTraceError, - makeTracePerformanceIssue, - makeTransaction, -} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeTestUtils'; - -import {TraceTreeNode} from './traceTreeNode'; - -const metadata: TraceTree.Metadata = { - event_id: '1', - project_slug: 'node', -}; - -describe('TraceTreeNode', () => { - it('infers space from timestamp and start_timestamp', () => { - const node = new TraceTreeNode(null, makeTraceError({timestamp: 1}), metadata); - expect(node.space).toEqual([1 * 1e3, 0]); - }); - - it('infers start_timestamp and timestamp when not provided', () => { - const node = new TraceTreeNode( - null, - makeSpan({start_timestamp: 1, timestamp: 3}), - metadata - ); - expect(node.space).toEqual([1 * 1e3, 2 * 1e3]); - }); - - it('stores performance issue on node', () => { - const issue = makeTracePerformanceIssue({issue_short_id: 'issue1'}); - const node = new TraceTreeNode( - null, - makeTransaction({ - start_timestamp: 1, - timestamp: 3, - performance_issues: [issue], - }), - metadata - ); - expect(node.occurrences.has(issue)).toBe(true); - }); - - it('stores error on node', () => { - const issue = makeTraceError(); - - const node = new TraceTreeNode( - null, - makeTransaction({ - start_timestamp: 1, - timestamp: 3, - errors: [issue], - }), - metadata - ); - - expect(node.errors.has(issue)).toBe(true); - }); - - it('a trace error is stored on node', () => { - const node = new TraceTreeNode(null, makeTraceError(), metadata); - expect(node.errors.size).toBe(1); - }); - - it('stores profile on node', () => { - const node = new TraceTreeNode( - null, - makeTransaction({ - start_timestamp: 1, - timestamp: 3, - profile_id: 'profile', - }), - metadata - ); - - const profile = node.profiles[0] as {profile_id: string}; - expect(profile.profile_id).toBe('profile'); - }); - - it('stores profiler_id on node', () => { - const node = new TraceTreeNode( - null, - makeTransaction({ - start_timestamp: 1, - timestamp: 3, - profiler_id: 'profile', - }), - metadata - ); - - const profile = node.profiles[0] as {profiler_id: string}; - expect(profile.profiler_id).toBe('profile'); - }); - - it('stores parent reference', () => { - const parent = new TraceTreeNode(null, makeTransaction(), metadata); - const child = new TraceTreeNode(parent, makeTransaction(), metadata); - - expect(child.parent).toBe(parent); - }); - - describe('maxSeverity', () => { - it('fatal > info', () => { - const node = new TraceTreeNode( - null, - makeTransaction({ - errors: [makeTraceError({level: 'fatal'})], - performance_issues: [makeTracePerformanceIssue({level: 'info'})], - }), - metadata - ); - expect(node.maxIssueSeverity).toBe('fatal'); - }); - }); - - describe('expanding and collapsing', () => { - it('default is expanded', () => { - const node = new TraceTreeNode(null, makeTransaction(), metadata); - expect(node.expanded).toBe(true); - }); - - it('android tcp connections are not expanded by default', () => { - const node = new TraceTreeNode( - null, - makeSpan({op: 'http.client', origin: 'auto.http.okhttp'}), - metadata - ); - - expect(node.expanded).toBe(false); - }); - }); -}); diff --git a/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode.tsx b/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode.tsx deleted file mode 100644 index 4d5c02a5c828d4..00000000000000 --- a/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import type {Theme} from '@emotion/react'; - -import type {EventTransaction} from 'sentry/types/event'; - -import type {TraceTree} from './traceTree'; - -function isTraceTransaction(value: TraceTree.NodeValue): value is TraceTree.Transaction { - return !!(value && 'transaction' in value); -} - -function isTraceError(value: TraceTree.NodeValue): value is TraceTree.TraceError { - return !!(value && 'level' in value); -} - -function isTraceSpan(value: TraceTree.NodeValue): value is TraceTree.Span { - return !!( - value && - 'span_id' in value && - !isTraceAutogroup(value) && - !isTraceTransaction(value) - ); -} - -function isEAPSpan(value: TraceTree.NodeValue): value is TraceTree.EAPSpan { - return !!(value && 'is_transaction' in value); -} - -function isTraceAutogroup( - value: TraceTree.NodeValue -): value is TraceTree.ChildrenAutogroup | TraceTree.SiblingAutogroup { - return !!(value && 'autogrouped_by' in value); -} - -function shouldCollapseNodeByDefault(node: TraceTreeNode) { - // Only collapse EAP spans if they are a segments/transactions - if (isEAPSpan(node.value)) { - return node.value.is_transaction; - } - - if (isTraceSpan(node.value)) { - // Android creates TCP connection spans which are noisy and not useful in most cases. - // Unless the span has a child txn which would indicate a continuaton of the trace, we collapse it. - if (node.value.op === 'http.client' && node.value.origin === 'auto.http.okhttp') { - return true; - } - } - - return false; -} - -export class TraceTreeNode { - parent: TraceTreeNode | null = null; - reparent_reason: 'pageload server handler' | null = null; - - fetchStatus: 'resolved' | 'error' | 'idle' | 'loading' = 'idle'; - value: T; - - canFetch = false; - expanded = true; - zoomedIn = false; - - metadata: TraceTree.Metadata = { - project_slug: undefined, - event_id: undefined, - spans: undefined, - }; - - eapSpanOpsBreakdown: TraceTree.OpsBreakdown = []; - - event: EventTransaction | null = null; - - // Events associated with the node, these are inferred from the node value. - errors = new Set(); - occurrences = new Set(); - profiles: TraceTree.Profile[] = []; - - space: [number, number] = [0, 0]; - children: TraceTreeNode[] = []; - - depth: number | undefined; - connectors: number[] | undefined; - - constructor(parent: TraceTreeNode | null, value: T, metadata: TraceTree.Metadata) { - this.parent = parent ?? null; - this.value = value; - this.metadata = metadata; - - // The node can fetch its children if it has more than one span, or if we failed to fetch the span count. - this.canFetch = - typeof metadata.spans === 'number' - ? metadata.spans > 1 - : isTraceTransaction(this.value); - - // If a node has both a start and end timestamp, then we can infer a duration, - // otherwise we can only infer a timestamp. - if ( - value && - (('end_timestamp' in value && typeof value.end_timestamp === 'number') || - ('timestamp' in value && typeof value.timestamp === 'number')) && - 'start_timestamp' in value && - typeof value.start_timestamp === 'number' - ) { - const end_timestamp = - 'end_timestamp' in value ? value.end_timestamp : value.timestamp; - this.space = [ - value.start_timestamp * 1e3, - (end_timestamp - value.start_timestamp) * 1e3, - ]; - } else if (value && 'timestamp' in value && typeof value.timestamp === 'number') { - this.space = [value.timestamp * 1e3, 0]; - } else if ( - value && - 'start_timestamp' in value && - typeof value.start_timestamp === 'number' - ) { - this.space = [value.start_timestamp * 1e3, 0]; - } - - if (value) { - if ('errors' in value && Array.isArray(value.errors)) { - value.errors.forEach(error => this.errors.add(error)); - } - - if ('performance_issues' in value && Array.isArray(value.performance_issues)) { - value.performance_issues.forEach(issue => this.occurrences.add(issue)); - } - - // EAP spans can have occurences - if ('occurrences' in value && Array.isArray(value.occurrences)) { - value.occurrences.forEach(occurence => this.occurrences.add(occurence)); - } - - const isNonTransactionEAPSpan = isEAPSpan(value) && !value.is_transaction; - - if (!isNonTransactionEAPSpan) { - if ( - 'profile_id' in value && - typeof value.profile_id === 'string' && - value.profile_id.trim() !== '' - ) { - this.profiles.push({profile_id: value.profile_id}); - } - if ( - 'profiler_id' in value && - typeof value.profiler_id === 'string' && - value.profiler_id.trim() !== '' - ) { - this.profiles.push({profiler_id: value.profiler_id}); - } - } - } - - // For error nodes, its value is the only associated issue. - if (isTraceError(this.value)) { - this.errors.add(this.value); - } - - // Android http spans generate sub spans for things like dns resolution in http requests, - // which creates a lot of noise and is not useful to display. - if (shouldCollapseNodeByDefault(this)) { - this.expanded = false; - } - } - - get hasErrors(): boolean { - return this.errors.size > 0 || this.occurrences.size > 0; - } - - private _max_severity: keyof Theme['level'] | undefined; - get maxIssueSeverity(): keyof Theme['level'] { - if (this._max_severity) { - return this._max_severity; - } - - for (const error of this.errors) { - if (error.level === 'error' || error.level === 'fatal') { - this._max_severity = error.level; - return this.maxIssueSeverity; - } - } - - return 'default'; - } - - invalidate() { - this.connectors = undefined; - this.depth = undefined; - } - - static Root() { - return new TraceTreeNode(null, null, { - event_id: undefined, - project_slug: undefined, - }); - } -} diff --git a/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode.spec.tsx b/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode.spec.tsx index 2200be78a57e6e..908ad5e6d62d7f 100644 --- a/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode.spec.tsx +++ b/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode/spanNode.spec.tsx @@ -177,14 +177,7 @@ describe('SpanNode', () => { const transaction = makeTransaction({ event_id: 'txn-abc-123', }); - const mockFn = jest.fn(); - const transactionNode = new TransactionNode( - null, - transaction, - createMockExtra(), - mockFn, - mockFn - ); + const transactionNode = new TransactionNode(null, transaction, createMockExtra()); const span = makeSpan({ span_id: 'span-def-456', diff --git a/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode.tsx b/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode.tsx index f4639865fd1c53..96ecd1ddb0fdd3 100644 --- a/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode.tsx +++ b/static/app/views/performance/newTraceDetails/traceModels/traceTreeNode/transactionNode.tsx @@ -233,7 +233,7 @@ export class TransactionNode extends BaseNode { * Returns the bounds of the added subtree as [start, end] timestamps. */ appendSpans(spans: TraceTree.Span[], event: EventTransaction | null): [number, number] { - const txnChildren = this.findAllChildren(c => isTransactionNode(c)); + const txnChildren = this.findAllChildren(c => isTransactionNode(c)); // Clear children of root node as we are recreating the sub tree this.children = []; @@ -285,7 +285,7 @@ export class TransactionNode extends BaseNode { // Reparent transactions under children spans for (const transaction of txnChildren) { - const parent = spanIdToNode.get(transaction.value.parent_span_id); + const parent = spanIdToNode.get(transaction.value?.parent_span_id ?? ''); // If the parent span does not exist in the span tree, the transaction will remain under the current node if (!parent) { if (transaction.parent?.children.indexOf(transaction) === -1) { diff --git a/static/app/views/performance/newTraceDetails/traceModels/traceTreeTestUtils.tsx b/static/app/views/performance/newTraceDetails/traceModels/traceTreeTestUtils.tsx index 23ee51bbee9508..fdaba0f1117db2 100644 --- a/static/app/views/performance/newTraceDetails/traceModels/traceTreeTestUtils.tsx +++ b/static/app/views/performance/newTraceDetails/traceModels/traceTreeTestUtils.tsx @@ -261,16 +261,6 @@ export function makeUptimeCheckTiming( } as TraceTree.UptimeCheckTiming; } -export function makeNodeMetadata( - overrides: Partial = {} -): TraceTree.Metadata { - return { - event_id: undefined, - project_slug: undefined, - ...overrides, - }; -} - export function mockSpansResponse( spans: TraceTree.Span[], project_slug: string, diff --git a/static/app/views/performance/newTraceDetails/traceRenderers/traceVirtualizedList.tsx b/static/app/views/performance/newTraceDetails/traceRenderers/traceVirtualizedList.tsx index 3cff7d64aed63e..9869f77a1a6621 100644 --- a/static/app/views/performance/newTraceDetails/traceRenderers/traceVirtualizedList.tsx +++ b/static/app/views/performance/newTraceDetails/traceRenderers/traceVirtualizedList.tsx @@ -1,8 +1,7 @@ import {useLayoutEffect, useRef, useState} from 'react'; import {requestAnimationTimeout} from 'sentry/utils/profiling/hooks/useVirtualizedTree/virtualizedTreeUtils'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; import type {TraceScheduler} from 'sentry/views/performance/newTraceDetails/traceRenderers/traceScheduler'; import { VirtualizedList, @@ -11,13 +10,13 @@ import { export interface VirtualizedRow { index: number; - item: TraceTreeNode; + item: BaseNode; key: number; style: React.CSSProperties; } interface UseVirtualizedListProps { container: HTMLElement | null; - items: ReadonlyArray>; + items: readonly BaseNode[]; manager: VirtualizedViewManager; render: (item: VirtualizedRow) => React.ReactNode; scheduler: TraceScheduler; @@ -61,7 +60,7 @@ export const useVirtualizedList = ( const renderRef = useRef<(item: VirtualizedRow) => React.ReactNode>(props.render); renderRef.current = props.render; - const itemsRef = useRef>>(props.items); + const itemsRef = useRef(props.items); itemsRef.current = props.items; const managerRef = useRef(props.manager); managerRef.current = props.manager; @@ -268,7 +267,7 @@ function findRenderedItems({ render, manager, }: { - items: ReadonlyArray>; + items: readonly BaseNode[]; manager: VirtualizedViewManager; overscroll: number; render: (arg: VirtualizedRow) => React.ReactNode; @@ -352,7 +351,7 @@ function findOptimisticStartIndex({ scrollTop, viewport, }: { - items: ReadonlyArray>; + items: readonly BaseNode[]; overscroll: number; rowHeight: number; scrollTop: number; diff --git a/static/app/views/performance/newTraceDetails/traceRenderers/virtualizedViewManager.tsx b/static/app/views/performance/newTraceDetails/traceRenderers/virtualizedViewManager.tsx index 12e1e65e201246..8b52ed03c8081d 100644 --- a/static/app/views/performance/newTraceDetails/traceRenderers/virtualizedViewManager.tsx +++ b/static/app/views/performance/newTraceDetails/traceRenderers/virtualizedViewManager.tsx @@ -14,7 +14,7 @@ import { isMissingInstrumentationNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; import {TraceRowWidthMeasurer} from 'sentry/views/performance/newTraceDetails/traceRenderers/traceRowWidthMeasurer'; import {TraceTextMeasurer} from 'sentry/views/performance/newTraceDetails/traceRenderers/traceTextMeasurer'; import type {TraceView} from 'sentry/views/performance/newTraceDetails/traceRenderers/traceView'; @@ -36,7 +36,7 @@ function getHorizontalDelta(x: number, y: number): number { } type ViewColumn = { - column_nodes: Array>; + column_nodes: BaseNode[]; column_refs: Array; translate: [number, number]; width: number; @@ -54,8 +54,7 @@ type VerticalIndicator = { export type ViewManagerScrollAnchor = 'top' | 'center if outside' | 'center'; export class VirtualizedViewManager { - row_measurer: TraceRowWidthMeasurer> = - new TraceRowWidthMeasurer(); + row_measurer: TraceRowWidthMeasurer = new TraceRowWidthMeasurer(); indicator_label_measurer: TraceRowWidthMeasurer = new TraceRowWidthMeasurer(); text_measurer: TraceTextMeasurer; @@ -370,7 +369,7 @@ export class VirtualizedViewManager { column: string, ref: HTMLElement | null, index: number, - node: TraceTreeNode + node: BaseNode ) { if (column === 'list' && ref) { const scrollableElement = ref.children[0] as HTMLElement | undefined; @@ -949,7 +948,7 @@ export class VirtualizedViewManager { const translation = this.columns.list.translate[0]; let min = Number.POSITIVE_INFINITY; let max = Number.NEGATIVE_INFINITY; - let innerMostNode: TraceTreeNode | undefined; + let innerMostNode: BaseNode | undefined; for (let i = 5; i < this.columns.span_list.column_refs.length - 5; i++) { const width = this.row_measurer.cache.get(this.columns.list.column_nodes[i]!); @@ -980,7 +979,7 @@ export class VirtualizedViewManager { } } - isOutsideOfView(node: TraceTreeNode): boolean { + isOutsideOfView(node: BaseNode): boolean { const width = this.row_measurer.cache.get(node); if (width === undefined) { @@ -997,7 +996,7 @@ export class VirtualizedViewManager { } scrollRowIntoViewHorizontally( - node: TraceTreeNode, + node: BaseNode, duration = 600, offset_px = 0, position: 'exact' | 'measured' = 'measured' @@ -1129,7 +1128,7 @@ export class VirtualizedViewManager { } computeSpanTextPlacement( - node: TraceTreeNode, + node: BaseNode, span_space: [number, number], text: string ): [number, number] { @@ -1449,7 +1448,7 @@ export class VirtualizedViewManager { ); } - drawSpanText(span_text: this['span_text'][0], node: TraceTreeNode | undefined) { + drawSpanText(span_text: this['span_text'][0], node: BaseNode | undefined) { if (!span_text) { return; } @@ -1689,7 +1688,7 @@ export class VirtualizedViewManager { // of the span to include the icon. We need this because when the icon is close to the edge // it can extend it and cause overlaps with duration labels function getIconTimestamps( - node: TraceTreeNode, + node: BaseNode, span_space: [number, number], icon_width: number ) { diff --git a/static/app/views/performance/newTraceDetails/traceRow/traceAutogroupedRow.tsx b/static/app/views/performance/newTraceDetails/traceRow/traceAutogroupedRow.tsx index 7fa05fc932a8f4..3db4f4040bf1db 100644 --- a/static/app/views/performance/newTraceDetails/traceRow/traceAutogroupedRow.tsx +++ b/static/app/views/performance/newTraceDetails/traceRow/traceAutogroupedRow.tsx @@ -1,7 +1,7 @@ import {t} from 'sentry/locale'; import {TraceIcons} from 'sentry/views/performance/newTraceDetails/traceIcons'; -import type {ParentAutogroupNode} from 'sentry/views/performance/newTraceDetails/traceModels/parentAutogroupNode'; -import type {SiblingAutogroupNode} from 'sentry/views/performance/newTraceDetails/traceModels/siblingAutogroupNode'; +import type {ParentAutogroupNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/parentAutogroupNode'; +import type {SiblingAutogroupNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/siblingAutogroupNode'; import { AutogroupedTraceBar, makeTraceNodeBarColor, diff --git a/static/app/views/performance/newTraceDetails/traceRow/traceBackgroundPatterns.tsx b/static/app/views/performance/newTraceDetails/traceRow/traceBackgroundPatterns.tsx index 168b061d9139b5..b19da7dcddc04a 100644 --- a/static/app/views/performance/newTraceDetails/traceRow/traceBackgroundPatterns.tsx +++ b/static/app/views/performance/newTraceDetails/traceRow/traceBackgroundPatterns.tsx @@ -2,7 +2,7 @@ import {Fragment, useMemo} from 'react'; import clamp from 'lodash/clamp'; import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; import type {VirtualizedViewManager} from 'sentry/views/performance/newTraceDetails/traceRenderers/virtualizedViewManager'; function getMaxErrorSeverity(errors: TraceTree.TraceErrorIssue[]) { @@ -21,10 +21,10 @@ function getMaxErrorSeverity(errors: TraceTree.TraceErrorIssue[]) { } interface BackgroundPatternsProps { - errors: TraceTreeNode['errors']; + errors: BaseNode['errors']; manager: VirtualizedViewManager; node_space: [number, number] | null; - occurrences: TraceTreeNode['occurrences']; + occurrences: BaseNode['occurrences']; } export function TraceBackgroundPatterns(props: BackgroundPatternsProps) { diff --git a/static/app/views/performance/newTraceDetails/traceRow/traceBar.tsx b/static/app/views/performance/newTraceDetails/traceRow/traceBar.tsx index 6d101749008a3a..1cbfd0625e59e8 100644 --- a/static/app/views/performance/newTraceDetails/traceRow/traceBar.tsx +++ b/static/app/views/performance/newTraceDetails/traceRow/traceBar.tsx @@ -13,8 +13,7 @@ import { isTraceErrorNode, isTransactionNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; import type {VirtualizedViewManager} from 'sentry/views/performance/newTraceDetails/traceRenderers/virtualizedViewManager'; import {TraceBackgroundPatterns} from 'sentry/views/performance/newTraceDetails/traceRow/traceBackgroundPatterns'; import { @@ -22,10 +21,7 @@ import { TraceOccurenceIcons, } from 'sentry/views/performance/newTraceDetails/traceRow/traceIcons'; -export function makeTraceNodeBarColor( - theme: Theme, - node: TraceTreeNode -): string { +export function makeTraceNodeBarColor(theme: Theme, node: BaseNode): string { if (isTransactionNode(node)) { return pickBarColor( getStylingSliceName(node.value.project_slug, node.value.sdk_name) ?? @@ -152,12 +148,12 @@ export function MissingInstrumentationTraceBar( interface TraceBarProps { color: string; - errors: TraceTreeNode['errors']; + errors: BaseNode['errors']; manager: VirtualizedViewManager; - node: TraceTreeNode; + node: BaseNode; node_space: [number, number] | null; - occurrences: TraceTreeNode['occurrences']; - profiles: TraceTreeNode['profiles']; + occurrences: BaseNode['occurrences']; + profiles: BaseNode['profiles']; virtualized_index: number; } @@ -221,7 +217,7 @@ export function TraceBar(props: TraceBarProps) { ) : null} {props.occurrences.size > 0 || props.errors.size > 0 || - props.profiles.length > 0 ? ( + props.profiles.size > 0 ? ( ['errors']; + errors: BaseNode['errors']; manager: VirtualizedViewManager; - node: TraceTreeNode; + node: BaseNode; node_spaces: Array<[number, number]>; - occurrences: TraceTreeNode['occurrences']; - profiles: TraceTreeNode['profiles']; + occurrences: BaseNode['occurrences']; + profiles: BaseNode['profiles']; virtualized_index: number; } diff --git a/static/app/views/performance/newTraceDetails/traceRow/traceCollapsedRow.tsx b/static/app/views/performance/newTraceDetails/traceRow/traceCollapsedRow.tsx index f50d913b68706b..cd2bbfbd4d59ed 100644 --- a/static/app/views/performance/newTraceDetails/traceRow/traceCollapsedRow.tsx +++ b/static/app/views/performance/newTraceDetails/traceRow/traceCollapsedRow.tsx @@ -5,18 +5,17 @@ import { isCollapsedNode, isTraceErrorNode, } from 'sentry/views/performance/newTraceDetails/traceGuards'; -import type {CollapsedNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceCollapsedNode'; -import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; +import type {CollapsedNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/collapsedNode'; import type {TraceRowProps} from 'sentry/views/performance/newTraceDetails/traceRow/traceRow'; export function TraceCollapsedRow(props: TraceRowProps) { const stats = useMemo(() => { const childStatistics = {issues: 0, events: 0}; - const seen = new Set>(); + const seen = new Set(); - TraceTree.ForEachChild(props.node, c => { + props.node.forEachChild(c => { // Dont count collapsed nodes and track what we've seen because // the collapsed nodes may contain duplicate children due to vertical // collapsing. diff --git a/static/app/views/performance/newTraceDetails/traceRow/traceErrorRow.tsx b/static/app/views/performance/newTraceDetails/traceRow/traceErrorRow.tsx index 09765b93d6586a..384b9161ed2904 100644 --- a/static/app/views/performance/newTraceDetails/traceRow/traceErrorRow.tsx +++ b/static/app/views/performance/newTraceDetails/traceRow/traceErrorRow.tsx @@ -2,10 +2,8 @@ import type {Theme} from '@emotion/react'; import {PlatformIcon} from 'platformicons'; import {t} from 'sentry/locale'; -import {isEAPErrorNode} from 'sentry/views/performance/newTraceDetails/traceGuards'; import {TraceIcons} from 'sentry/views/performance/newTraceDetails/traceIcons'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {ErrorNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/errorNode'; import {InvisibleTraceBar} from 'sentry/views/performance/newTraceDetails/traceRow/traceBar'; import { maybeFocusTraceRow, @@ -26,17 +24,10 @@ const ERROR_LEVEL_LABELS: Record = { unknown: t('Unknown'), }; -export function TraceErrorRow( - props: TraceRowProps< - TraceTreeNode | TraceTreeNode - > -) { - const description = isEAPErrorNode(props.node) - ? props.node.value.description - : (props.node.value.title ?? props.node.value.message); - const timestamp = isEAPErrorNode(props.node) - ? props.node.value.start_timestamp - : props.node.value.timestamp; +export function TraceErrorRow(props: TraceRowProps) { + const description = props.node.description; + const timestamp = props.node.space[0]; + return (
['errors']; + errors: BaseNode['errors']; manager: VirtualizedViewManager; node_space: [number, number] | null; } @@ -53,7 +52,7 @@ export function TraceErrorIcons(props: ErrorIconsProps) { interface TraceOccurenceIconsProps { manager: VirtualizedViewManager; node_space: [number, number] | null; - occurrences: TraceTreeNode['occurrences']; + occurrences: BaseNode['occurrences']; } export function TraceOccurenceIcons(props: TraceOccurenceIconsProps) { diff --git a/static/app/views/performance/newTraceDetails/traceRow/traceLoadingRow.tsx b/static/app/views/performance/newTraceDetails/traceRow/traceLoadingRow.tsx index da97f7e3c51e53..a803eec06fcaa3 100644 --- a/static/app/views/performance/newTraceDetails/traceRow/traceLoadingRow.tsx +++ b/static/app/views/performance/newTraceDetails/traceRow/traceLoadingRow.tsx @@ -3,7 +3,7 @@ import type {Theme} from '@emotion/react'; import Placeholder from 'sentry/components/placeholder'; import {isTraceNode} from 'sentry/views/performance/newTraceDetails/traceGuards'; import {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {BaseNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/baseNode'; import type {VirtualizedViewManager} from 'sentry/views/performance/newTraceDetails/traceRenderers/virtualizedViewManager'; import { TRACE_COUNT_FORMATTER, @@ -20,7 +20,7 @@ function randomBetween(min: number, max: number) { export function TraceLoadingRow(props: { index: number; manager: VirtualizedViewManager; - node: TraceTreeNode; + node: BaseNode; style: React.CSSProperties; theme: Theme; }) { @@ -50,11 +50,11 @@ export function TraceLoadingRow(props: { className={`TraceChildrenCountWrapper ${isTraceNode(props.node) ? 'Root' : ''}`} > - {props.node.children.length > 0 || props.node.canFetch ? ( + {props.node.children.length > 0 || props.node.canFetchChildren ? ( void 0} onDoubleClick={() => void 0} > diff --git a/static/app/views/performance/newTraceDetails/traceRow/traceMissingInstrumentationRow.tsx b/static/app/views/performance/newTraceDetails/traceRow/traceMissingInstrumentationRow.tsx index 5d32f0f86e857d..78d66004b3e14b 100644 --- a/static/app/views/performance/newTraceDetails/traceRow/traceMissingInstrumentationRow.tsx +++ b/static/app/views/performance/newTraceDetails/traceRow/traceMissingInstrumentationRow.tsx @@ -1,7 +1,6 @@ import {t} from 'sentry/locale'; import {TraceIcons} from 'sentry/views/performance/newTraceDetails/traceIcons'; -import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; -import type {TraceTreeNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode'; +import type {NoInstrumentationNode} from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeNode/noInstrumentationNode'; import { makeTraceNodeBarColor, MissingInstrumentationTraceBar, @@ -13,7 +12,7 @@ import { } from 'sentry/views/performance/newTraceDetails/traceRow/traceRow'; export function TraceMissingInstrumentationRow( - props: TraceRowProps> + props: TraceRowProps ) { return (
>) { +export function TraceRootRow(props: TraceRowProps) { if (!isTraceNode(props.node) && !isEAPTraceNode(props.node)) { throw new Error('Trace row rendered called on row that is not root'); } @@ -43,7 +42,7 @@ export function TraceRootRow(props: TraceRowProps {' '}
- {props.node.children.length > 0 || props.node.canFetch ? ( + {props.node.children.length > 0 || props.node.canFetchChildren ? ( { onRowKeyDown: (e: React.KeyboardEvent) => void; onSpanArrowClick: (e: React.MouseEvent) => void; onZoomIn: (e: React.MouseEvent) => void; - previouslyFocusedNodeRef: React.MutableRefObject | null>; + previouslyFocusedNodeRef: React.MutableRefObject; projects: Record; registerListColumnRef: (e: HTMLDivElement | null) => void; registerSpanArrowRef: (e: HTMLButtonElement | null) => void; @@ -49,8 +49,8 @@ export interface TraceRowProps { export function maybeFocusTraceRow( ref: HTMLDivElement | null, - node: TraceTreeNode, - previouslyFocusedNodeRef: React.MutableRefObject | null> + node: BaseNode, + previouslyFocusedNodeRef: React.MutableRefObject ) { if (!ref) { return; @@ -65,9 +65,9 @@ export function maybeFocusTraceRow( export function TraceRowConnectors(props: { manager: VirtualizedViewManager; - node: TraceTreeNode; + node: BaseNode; }) { - const hasChildren = TraceTree.HasVisibleChildren(props.node); + const hasChildren = props.node.hasVisibleChildren(); const nodeDepth = TraceTree.Depth(props.node); return ( @@ -86,7 +86,7 @@ export function TraceRowConnectors(props: { ); })} {hasChildren ? : null} - {TraceTree.IsLastChild(props.node) ? ( + {props.node.isLastChild() ? ( ) : null} @@ -99,7 +99,7 @@ export function TraceChildrenButton(props: { icon: React.ReactNode; onClick: (e: React.MouseEvent) => void; onDoubleClick: (e: React.MouseEvent) => void; - status: TraceTreeNode['fetchStatus'] | undefined; + status: BaseNode['fetchStatus'] | undefined; }) { return (