From 00fe8e48316e40f074b64dbb47497806701ef1c1 Mon Sep 17 00:00:00 2001 From: Armen Zambrano G <44410+armenzg@users.noreply.github.com> Date: Thu, 9 May 2024 12:50:10 -0400 Subject: [PATCH 1/2] feat(related_issues): Create separate sections for each relation type In #70504 I added support for the `type` parameter which allows getting separately the data for same-root issues vs trace-connected issues. This UI change allows each related issues section to fetch and render the data independently, thus, making the page load faster. These changes are required in order to support just fetching trace-connected issues from the Issues Details page. --- .../groupRelatedIssues/index.spec.tsx | 79 +++---- .../issueDetails/groupRelatedIssues/index.tsx | 199 +++++++++--------- 2 files changed, 134 insertions(+), 144 deletions(-) diff --git a/static/app/views/issueDetails/groupRelatedIssues/index.spec.tsx b/static/app/views/issueDetails/groupRelatedIssues/index.spec.tsx index a78b8edac8a61a..058062f583ef01 100644 --- a/static/app/views/issueDetails/groupRelatedIssues/index.spec.tsx +++ b/static/app/views/issueDetails/groupRelatedIssues/index.spec.tsx @@ -6,7 +6,8 @@ import {render, screen} from 'sentry-test/reactTestingLibrary'; import {GroupRelatedIssues} from 'sentry/views/issueDetails/groupRelatedIssues'; describe('Related Issues View', function () { - let relatedIssuesMock: jest.Mock; + let sameRootIssuesMock: jest.Mock; + let traceIssuesMock: jest.Mock; let issuesMock: jest.Mock; const router = RouterFixture(); @@ -17,48 +18,25 @@ describe('Related Issues View', function () { const group2 = '20'; // query=issue.id:[15,20] -> query=issue.id%3A%5B15%2C20%5D const orgIssuesEndpoint = `/organizations/${orgSlug}/issues/?query=issue.id%3A%5B${group1}%2C${group2}%5D`; + // const orgIssuesEndpoint = `/organizations/${orgSlug}/issues/?query=issue.id:[${group1},${group2}]`; const params = {groupId: groupId}; const errorType = 'RuntimeError'; const noData = { - data: [ - { - type: 'same_root_cause', - data: [], - }, - { - type: 'trace_connected', - data: [], - }, - ], + type: 'irrelevant', + data: [], }; const onlySameRootData = { - data: [ - { - type: 'same_root_cause', - data: [group1, group2], - }, - { - type: 'trace_connected', - data: [], - }, - ], + type: 'same_root_cause', + data: [group1, group2], }; const onlyTraceConnectedData = { - data: [ - { - type: 'same_root_cause', - data: [], - }, - { - type: 'trace_connected', - data: [group1, group2], - meta: { - event_id: 'abcd', - trace_id: '1234', - }, - }, - ], + type: 'trace_connected', + data: [group1, group2], + meta: { + event_id: 'abcd', + trace_id: '1234', + }, }; const issuesData = [ { @@ -99,8 +77,12 @@ describe('Related Issues View', function () { }); it('renders with no data', async function () { - relatedIssuesMock = MockApiClient.addMockResponse({ - url: `/issues/${groupId}/related-issues/`, + sameRootIssuesMock = MockApiClient.addMockResponse({ + url: `/issues/${groupId}/related-issues/?type=same_root_cause`, + body: noData, + }); + traceIssuesMock = MockApiClient.addMockResponse({ + url: `/issues/${groupId}/related-issues/?type=trace_connected`, body: noData, }); render( @@ -121,14 +103,19 @@ describe('Related Issues View', function () { await screen.findByText('No trace-connected related issues were found.') ).toBeInTheDocument(); - expect(relatedIssuesMock).toHaveBeenCalled(); + expect(sameRootIssuesMock).toHaveBeenCalled(); + expect(traceIssuesMock).toHaveBeenCalled(); }); it('renders with same root issues', async function () { - relatedIssuesMock = MockApiClient.addMockResponse({ - url: `/issues/${groupId}/related-issues/`, + sameRootIssuesMock = MockApiClient.addMockResponse({ + url: `/issues/${groupId}/related-issues/?type=same_root_cause`, body: onlySameRootData, }); + MockApiClient.addMockResponse({ + url: `/issues/${groupId}/related-issues/?type=trace_connected`, + body: [], + }); issuesMock = MockApiClient.addMockResponse({ url: orgIssuesEndpoint, body: issuesData, @@ -149,7 +136,7 @@ describe('Related Issues View', function () { expect(await screen.findByText(`EARTH-${group1}`)).toBeInTheDocument(); expect(await screen.findByText(`EARTH-${group2}`)).toBeInTheDocument(); - expect(relatedIssuesMock).toHaveBeenCalled(); + expect(sameRootIssuesMock).toHaveBeenCalled(); expect(issuesMock).toHaveBeenCalled(); expect( await screen.findByText('No trace-connected related issues were found.') @@ -163,8 +150,12 @@ describe('Related Issues View', function () { }); it('renders with trace connected issues', async function () { - relatedIssuesMock = MockApiClient.addMockResponse({ - url: `/issues/${groupId}/related-issues/`, + MockApiClient.addMockResponse({ + url: `/issues/${groupId}/related-issues/?type=same_root_cause`, + body: [], + }); + traceIssuesMock = MockApiClient.addMockResponse({ + url: `/issues/${groupId}/related-issues/?type=trace_connected`, body: onlyTraceConnectedData, }); issuesMock = MockApiClient.addMockResponse({ @@ -186,7 +177,7 @@ describe('Related Issues View', function () { expect(await screen.findByText(`EARTH-${group1}`)).toBeInTheDocument(); expect(await screen.findByText(`EARTH-${group2}`)).toBeInTheDocument(); - expect(relatedIssuesMock).toHaveBeenCalled(); + expect(traceIssuesMock).toHaveBeenCalled(); expect(issuesMock).toHaveBeenCalled(); expect( await screen.findByText('No same-root-cause related issues were found.') diff --git a/static/app/views/issueDetails/groupRelatedIssues/index.tsx b/static/app/views/issueDetails/groupRelatedIssues/index.tsx index 0b94bf16281a9c..d4bc5ce9cec06d 100644 --- a/static/app/views/issueDetails/groupRelatedIssues/index.tsx +++ b/static/app/views/issueDetails/groupRelatedIssues/index.tsx @@ -19,59 +19,109 @@ type RouteParams = { type Props = RouteComponentProps; type RelatedIssuesResponse = { - data: [ - { - data: number[]; - meta: { - event_id: string; - trace_id: string; - }; - type: string; - }, - ]; + data: number[]; + meta: { + event_id: string; + trace_id: string; + }; + type: string; }; +interface RelatedIssuesSectionProps { + groupId: string; + orgSlug: string; + relationType: string; +} + function GroupRelatedIssues({params}: Props) { const {groupId} = params; const organization = useOrganization(); const orgSlug = organization.slug; + return ( + + + + + ); +} + +function RelatedIssuesSection({ + groupId, + orgSlug, + relationType, +}: RelatedIssuesSectionProps) { // Fetch the list of related issues const { isLoading, isError, data: relatedIssues, refetch, - } = useApiQuery([`/issues/${groupId}/related-issues/`], { - staleTime: 0, - }); - - let traceMeta = { - trace_id: '', - event_id: '', - }; - const { - same_root_cause: sameRootCauseIssues = [], - trace_connected: traceConnectedIssues = [], - } = (relatedIssues?.data ?? []).reduce( - (mapping, item) => { - if (item.type === 'trace_connected') { - traceMeta = {...item.meta}; - } - const issuesList = item.data; - mapping[item.type] = issuesList; - return mapping; - }, - {same_root_cause: [], trace_connected: []} + } = useApiQuery( + [`/issues/${groupId}/related-issues/?type=${relationType}`], + { + staleTime: 0, + } ); + const traceMeta = relationType === 'trace_connected' ? relatedIssues?.meta : undefined; + const issues = relatedIssues?.data ?? []; + const query = `issue.id:[${issues}]`; // project=-1 allows ensuring that the query will show issues from any projects for the org // This is important for traces since issues can be for any project in the org const baseUrl = `/organizations/${orgSlug}/issues/?project=-1`; + let linkToTrace; + let openIssuesButton; + let title; + if (relationType === 'trace_connected' && traceMeta) { + title = t('Issues in the same trace'); + linkToTrace = ( + + {t('These issues were all found within ')} + + {t('this trace')} + + . + + ); + openIssuesButton = ( + + {t('Open in Issues')} + + ); + } else { + title = t('Issues caused by the same root cause'); + openIssuesButton = ( + + {t('Open in Issues')} + + ); + } return ( - + + {title} {isLoading ? ( ) : isError ? ( @@ -79,77 +129,26 @@ function GroupRelatedIssues({params}: Props) { message={t('Unable to load related issues, please try again later')} onRetry={refetch} /> - ) : ( + ) : issues.length > 0 ? ( -
- - {t('Issues caused by the same root cause')} - {sameRootCauseIssues.length > 0 ? ( -
- -
- - {t('Open in Issues')} - - - -
- ) : ( - {t('No same-root-cause related issues were found.')} - )} - -
-
- - {t('Issues in the same trace')} - {traceConnectedIssues.length > 0 ? ( -
- - - {t('These issues were all found within ')} - - {t('this trace')} - - . - - - {t('Open in Issues')} - - - -
- ) : ( - {t('No trace-connected related issues were found.')} - )} -
-
+ + {linkToTrace ?? null} + {openIssuesButton ?? null} + + + ) : relationType === 'trace_connected' ? ( + {t('No trace-connected related issues were found.')} + ) : ( + {t('No same-root-cause related issues were found.')} )} - +
); } From bbcb88d0db45bfa4463ce24a03d29da5d119b306 Mon Sep 17 00:00:00 2001 From: Armen Zambrano G <44410+armenzg@users.noreply.github.com> Date: Fri, 10 May 2024 10:06:39 -0400 Subject: [PATCH 2/2] Minor touches --- static/app/views/issueDetails/groupRelatedIssues/index.spec.tsx | 1 - static/app/views/issueDetails/groupRelatedIssues/index.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/static/app/views/issueDetails/groupRelatedIssues/index.spec.tsx b/static/app/views/issueDetails/groupRelatedIssues/index.spec.tsx index 058062f583ef01..f77f77d0b6cdea 100644 --- a/static/app/views/issueDetails/groupRelatedIssues/index.spec.tsx +++ b/static/app/views/issueDetails/groupRelatedIssues/index.spec.tsx @@ -18,7 +18,6 @@ describe('Related Issues View', function () { const group2 = '20'; // query=issue.id:[15,20] -> query=issue.id%3A%5B15%2C20%5D const orgIssuesEndpoint = `/organizations/${orgSlug}/issues/?query=issue.id%3A%5B${group1}%2C${group2}%5D`; - // const orgIssuesEndpoint = `/organizations/${orgSlug}/issues/?query=issue.id:[${group1},${group2}]`; const params = {groupId: groupId}; const errorType = 'RuntimeError'; diff --git a/static/app/views/issueDetails/groupRelatedIssues/index.tsx b/static/app/views/issueDetails/groupRelatedIssues/index.tsx index d4bc5ce9cec06d..5364e4b740e119 100644 --- a/static/app/views/issueDetails/groupRelatedIssues/index.tsx +++ b/static/app/views/issueDetails/groupRelatedIssues/index.tsx @@ -79,9 +79,9 @@ function RelatedIssuesSection({ // project=-1 allows ensuring that the query will show issues from any projects for the org // This is important for traces since issues can be for any project in the org const baseUrl = `/organizations/${orgSlug}/issues/?project=-1`; + let title; let linkToTrace; let openIssuesButton; - let title; if (relationType === 'trace_connected' && traceMeta) { title = t('Issues in the same trace'); linkToTrace = (