Browse Source

feat(uptime): Add "Allow Tracing" option to uptime form (#81081)

Looks like this

<img width="790" alt="image"
src="https://github.com/user-attachments/assets/98fa3d1a-473f-4bcd-aace-bb675faba8c2">
Evan Purkhiser 15 hours ago
parent
commit
5b42c1e6a5

+ 9 - 0
static/app/views/alerts/rules/uptime/httpSnippet.spec.tsx

@@ -1,3 +1,5 @@
+import {generateSentryTraceHeader} from '@sentry/utils';
+
 import {render} from 'sentry-test/reactTestingLibrary';
 
 import {HTTPSnippet} from './httpSnippet';
@@ -15,9 +17,16 @@ describe('HTTPSnippet', function () {
         method="POST"
         body={'{"key": "value"}'}
         headers={[['X-Something', 'Header Value']]}
+        traceSampling={false}
       />
     );
 
+    expect(jest.mocked(generateSentryTraceHeader)).toHaveBeenCalledWith(
+      undefined,
+      undefined,
+      false
+    );
+
     const expected = [
       'POST /test?query=value HTTP/1.1',
       'Host: example.com',

+ 7 - 4
static/app/views/alerts/rules/uptime/httpSnippet.tsx

@@ -1,4 +1,4 @@
-import {useState} from 'react';
+import {useMemo} from 'react';
 import styled from '@emotion/styled';
 import {generateSentryTraceHeader} from '@sentry/utils';
 
@@ -10,12 +10,15 @@ interface Props {
   body: string | null;
   headers: Array<[key: string, value: string]>;
   method: string;
+  traceSampling: boolean;
   url: string;
 }
 
-export function HTTPSnippet({body, headers, method, url}: Props) {
-  const [exampleTrace] = useState(() =>
-    generateSentryTraceHeader(undefined, undefined, true)
+export function HTTPSnippet({body, headers, method, url, traceSampling}: Props) {
+  const exampleTrace = useMemo(
+    () =>
+      generateSentryTraceHeader(undefined, undefined, traceSampling ? undefined : false),
+    [traceSampling]
   );
 
   const urlObject = safeURL(url);

+ 1 - 0
static/app/views/alerts/rules/uptime/types.tsx

@@ -24,5 +24,6 @@ export interface UptimeRule {
   projectSlug: string;
   status: UptimeMonitorStatus;
   timeoutMs: number;
+  traceSampling: boolean;
   url: string;
 }

+ 9 - 0
static/app/views/alerts/rules/uptime/uptimeAlertForm.spec.tsx

@@ -54,6 +54,8 @@ describe('Uptime Alert Form', function () {
     await userEvent.type(input('Name of header 1'), 'X-Something');
     await userEvent.type(input('Value of X-Something'), 'Header Value');
 
+    await userEvent.click(screen.getByRole('checkbox', {name: 'Allow Tracing'}));
+
     const name = input('Uptime rule name');
     await userEvent.clear(name);
     await userEvent.type(name, 'New Uptime Rule');
@@ -78,6 +80,7 @@ describe('Uptime Alert Form', function () {
           method: 'POST',
           headers: [['X-Something', 'Header Value']],
           body: '{"key": "value"}',
+          traceSampling: true,
           intervalSeconds: 60,
         }),
       })
@@ -96,6 +99,7 @@ describe('Uptime Alert Form', function () {
         ['X-Test2', 'value 2'],
       ],
       body: '{"key": "value"}',
+      traceSampling: true,
       owner: ActorFixture(),
     });
     render(
@@ -115,6 +119,7 @@ describe('Uptime Alert Form', function () {
     expect(screen.getByRole('menuitemradio', {name: 'POST'})).toBeChecked();
     await selectEvent.openMenu(input('Environment'));
     expect(screen.getByRole('menuitemradio', {name: 'prod'})).toBeChecked();
+    expect(screen.getByRole('checkbox', {name: 'Allow Tracing'})).toBeChecked();
   });
 
   it('handles simple edits', async function () {
@@ -163,6 +168,7 @@ describe('Uptime Alert Form', function () {
       projectSlug: project.slug,
       url: 'https://existing-url.com',
       owner: ActorFixture(),
+      traceSampling: false,
     });
     render(
       <UptimeAlertForm organization={organization} project={project} rule={rule} />,
@@ -188,6 +194,8 @@ describe('Uptime Alert Form', function () {
     await userEvent.type(input('Name of header 2'), 'X-Another');
     await userEvent.type(input('Value of X-Another'), 'Second Value');
 
+    await userEvent.click(screen.getByRole('checkbox', {name: 'Allow Tracing'}));
+
     const name = input('Uptime rule name');
     await userEvent.clear(name);
     await userEvent.type(name, 'Updated name');
@@ -216,6 +224,7 @@ describe('Uptime Alert Form', function () {
           ],
           body: '{"different": "value"}',
           intervalSeconds: 60 * 10,
+          traceSampling: true,
         }),
       })
     );

+ 12 - 0
static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx

@@ -6,6 +6,7 @@ import {Observer} from 'mobx-react';
 import {Button} from 'sentry/components/button';
 import Confirm from 'sentry/components/confirm';
 import FieldWrapper from 'sentry/components/forms/fieldGroup/fieldWrapper';
+import BooleanField from 'sentry/components/forms/fields/booleanField';
 import HiddenField from 'sentry/components/forms/fields/hiddenField';
 import SelectField from 'sentry/components/forms/fields/selectField';
 import SentryMemberTeamSelectorField from 'sentry/components/forms/fields/sentryMemberTeamSelectorField';
@@ -62,6 +63,7 @@ function getFormDataFromRule(rule: UptimeRule) {
     body: rule.body,
     headers: rule.headers,
     intervalSeconds: rule.intervalSeconds,
+    traceSampling: rule.traceSampling,
     owner: rule.owner ? `${rule.owner.type}:${rule.owner.id}` : null,
   };
 }
@@ -216,6 +218,15 @@ export function UptimeAlertForm({project, handleDelete, rule}: Props) {
               label={t('Headers')}
               flexibleControlStateSize
             />
+            <BooleanField
+              name="traceSampling"
+              label={t('Allow Tracing')}
+              showHelpInTooltip
+              help={t(
+                'Allows uptime checks to trigger traces if the checked service is configured with a Sentry SDK.'
+              )}
+              flexibleControlStateSize
+            />
             <TextareaField
               name="body"
               label={t('Body')}
@@ -235,6 +246,7 @@ export function UptimeAlertForm({project, handleDelete, rule}: Props) {
                 method={formModel.getValue('method')}
                 headers={formModel.getValue('headers')}
                 body={formModel.getValue('body')}
+                traceSampling={formModel.getValue('traceSampling')}
               />
             )}
           </Observer>

+ 1 - 0
tests/js/fixtures/uptimeRule.ts

@@ -16,6 +16,7 @@ export function UptimeRuleFixture(params: Partial<UptimeRule> = {}): UptimeRule
     headers: [],
     method: 'GET',
     body: null,
+    traceSampling: false,
     ...params,
   }
 }