Browse Source

feat(perf-detector-threshold-configuration) Added link from span evidence to settings. (#52449)

- Added a button beside span evidence to go to detector threshold
settings:
<img width="932" alt="Screenshot 2023-07-07 at 12 47 08 PM"
src="https://github.com/getsentry/sentry/assets/60121741/d77bf2ea-652d-4405-ac1a-d2fe32de1829">

- Should scroll down to section.

---------

Co-authored-by: Abdullah Khan <abdullahkhan@PG9Y57YDXQ.local>
Abdkhan14 1 year ago
parent
commit
79aeae8e6c

+ 63 - 0
static/app/components/events/interfaces/performance/spanEvidence.spec.tsx

@@ -6,6 +6,9 @@ import {
 } from 'sentry-test/performance/utils';
 import {render, screen} from 'sentry-test/reactTestingLibrary';
 
+import {EntryType} from 'sentry/types';
+import {projectDetectorSettingsId} from 'sentry/views/settings/projectPerformance/projectPerformance';
+
 import {SpanEvidenceSection} from './spanEvidence';
 
 const {organization, project} = initializeData();
@@ -83,4 +86,64 @@ describe('spanEvidence', () => {
     expect(affectedSpan).toBeInTheDocument();
     expect(affectedSpan).toHaveTextContent('db — connect');
   });
+
+  it('renders settings button for issue with configurable thresholds', () => {
+    const event = TestStubs.Event({
+      occurrence: {
+        type: 1001,
+      },
+      entries: [
+        {
+          data: [],
+          type: EntryType.SPANS,
+        },
+      ],
+    });
+
+    render(
+      <SpanEvidenceSection
+        event={event}
+        organization={organization}
+        projectSlug={project.slug}
+      />,
+      {organization}
+    );
+
+    expect(screen.getByText('Span Evidence')).toBeInTheDocument();
+
+    const settingsBtn = screen.getByTestId('span-evidence-settings-btn');
+    expect(settingsBtn).toBeInTheDocument();
+    expect(settingsBtn).toHaveAttribute(
+      'href',
+      `/settings/projects/project-slug/performance/#${projectDetectorSettingsId}`
+    );
+  });
+
+  it('does not render settings button for issue without configurable thresholds', () => {
+    const event = TestStubs.Event({
+      occurrence: {
+        type: 2003, // profile_json_decode_main_thread
+      },
+      entries: [
+        {
+          data: [],
+          type: EntryType.SPANS,
+        },
+      ],
+    });
+
+    render(
+      <SpanEvidenceSection
+        event={event}
+        organization={organization}
+        projectSlug={project.slug}
+      />,
+      {organization}
+    );
+
+    expect(screen.getByText('Span Evidence')).toBeInTheDocument();
+
+    const settingsBtn = screen.queryByTestId('span-evidence-settings-btn');
+    expect(settingsBtn).not.toBeInTheDocument();
+  });
 });

+ 34 - 1
static/app/components/events/interfaces/performance/spanEvidence.tsx

@@ -1,11 +1,20 @@
 import styled from '@emotion/styled';
 
+import {LinkButton} from 'sentry/components/button';
 import {EventDataSection} from 'sentry/components/events/eventDataSection';
 import {getProblemSpansForSpanTree} from 'sentry/components/events/interfaces/performance/utils';
+import {IconSettings} from 'sentry/icons';
 import {t} from 'sentry/locale';
-import {EventTransaction, Organization} from 'sentry/types';
+import {space} from 'sentry/styles/space';
+import {
+  EventTransaction,
+  getIssueTypeFromOccurenceType,
+  IssueType,
+  Organization,
+} from 'sentry/types';
 import {ProfileGroupProvider} from 'sentry/views/profiling/profileGroupProvider';
 import {ProfileContext, ProfilesProvider} from 'sentry/views/profiling/profilesProvider';
+import {projectDetectorSettingsId} from 'sentry/views/settings/projectPerformance/projectPerformance';
 
 import TraceView from '../spans/traceView';
 import {TraceContextType} from '../spans/types';
@@ -34,6 +43,14 @@ export function SpanEvidenceSection({event, organization, projectSlug}: Props) {
 
   const hasProfilingFeature = organization.features.includes('profiling');
 
+  const issueType = getIssueTypeFromOccurenceType(event.occurrence?.type);
+  const hasConfigurableThresholds =
+    issueType &&
+    ![
+      IssueType.PERFORMANCE_N_PLUS_ONE_API_CALLS, // TODO Abdullah Khan: Remove check when thresholds for these two issues are configurable.
+      IssueType.PERFORMANCE_CONSECUTIVE_HTTP,
+    ].includes(issueType);
+
   return (
     <EventDataSection
       title={t('Span Evidence')}
@@ -41,6 +58,18 @@ export function SpanEvidenceSection({event, organization, projectSlug}: Props) {
       help={t(
         'Span Evidence identifies the root cause of this issue, found in other similar events within the same issue.'
       )}
+      actions={
+        hasConfigurableThresholds && (
+          <LinkButton
+            data-test-id="span-evidence-settings-btn"
+            to={`/settings/projects/${projectSlug}/performance/#${projectDetectorSettingsId}`}
+            size="xs"
+          >
+            <StyledSettingsIcon size="xs" />
+            Settings
+          </LinkButton>
+        )
+      }
     >
       <SpanEvidenceKeyValueList event={event} projectSlug={projectSlug} />
       {hasProfilingFeature ? (
@@ -96,3 +125,7 @@ const TraceViewWrapper = styled('div')`
   border: 1px solid ${p => p.theme.innerBorder};
   border-radius: ${p => p.theme.borderRadius};
 `;
+
+const StyledSettingsIcon = styled(IconSettings)`
+  margin-right: ${space(0.5)};
+`;

+ 3 - 1
static/app/views/settings/projectPerformance/projectPerformance.tsx

@@ -49,6 +49,8 @@ export const allowedSizeValues: number[] = [
   2000000, 3000000, 4000000, 5000000, 6000000, 7000000, 8000000, 9000000, 10000000,
 ]; // 50kb to 10MB in bytes
 
+export const projectDetectorSettingsId = 'detector-threshold-settings';
+
 type ProjectPerformanceSettings = {[key: string]: number | boolean};
 
 enum DetectorConfigAdmin {
@@ -807,7 +809,7 @@ class ProjectPerformance extends AsyncView<Props, State> {
             >
               <Access access={requiredScopes} project={project}>
                 {({hasAccess}) => (
-                  <div>
+                  <div id={projectDetectorSettingsId}>
                     <StyledPanelHeader>
                       {t('Performance Issues - Detector Threshold Settings')}
                     </StyledPanelHeader>