Browse Source

feat(insights): Updates the Insights to create Alert flow to propagate the environment, timerange, and chart name whenever possible (#77507)

When creating an alert off an Insights chart, we now also forward the
environment, timerange, and chart name whenever possible.

This change also adds a check to prevent users from creating an alert if
more than 1 environment is selected (no env filter is also permissible)
since the Alert rule page does not support this.

Only 24h, 3d, 7d, and 14d timeranges are forwarded since those are the
ranges that Alert rules support.
edwardgou-sentry 5 months ago
parent
commit
2fd57011f7

+ 13 - 3
static/app/views/alerts/rules/metric/create.tsx

@@ -3,6 +3,7 @@ import type {Organization} from 'sentry/types/organization';
 import type {Project} from 'sentry/types/project';
 import {metric} from 'sentry/utils/analytics';
 import type EventView from 'sentry/utils/discover/eventView';
+import {decodeScalar} from 'sentry/utils/queryString';
 import normalizeUrl from 'sentry/utils/url/normalizeUrl';
 import {
   createDefaultRule,
@@ -49,8 +50,15 @@ function MetricRulesCreate(props: Props) {
     router.push(normalizeUrl(target));
   }
 
-  const {project, eventView, wizardTemplate, sessionId, userTeamIds, ...otherProps} =
-    props;
+  const {
+    project,
+    eventView,
+    wizardTemplate,
+    sessionId,
+    userTeamIds,
+    location,
+    ...otherProps
+  } = props;
   const defaultRule = eventView
     ? createRuleFromEventView(eventView)
     : wizardTemplate
@@ -60,15 +68,17 @@ function MetricRulesCreate(props: Props) {
   const projectTeamIds = new Set(project.teams.map(({id}) => id));
   const defaultOwnerId = userTeamIds.find(id => projectTeamIds.has(id)) ?? null;
   defaultRule.owner = defaultOwnerId && `team:${defaultOwnerId}`;
+  const environment = decodeScalar(location?.query?.environment) ?? null;
 
   return (
     <RuleForm
       onSubmitSuccess={handleSubmitSuccess}
-      rule={{...defaultRule, projects: [project.slug]}}
+      rule={{...defaultRule, environment, projects: [project.slug]}}
       sessionId={sessionId}
       project={project}
       userTeamIds={userTeamIds}
       eventView={eventView}
+      location={location}
       {...otherProps}
     />
   );

+ 21 - 9
static/app/views/insights/common/components/chartPanel.tsx

@@ -53,24 +53,36 @@ export default function ChartPanel({
   const moduleName = useModuleNameFromUrl();
   const alertsUrls =
     alertConfigs?.map(alertConfig => {
-      // Alerts only support single project selection
+      // Alerts only support single project selection and one or all environment
       const singleProject = selection.projects.length === 1 && project;
+      const singleEnvironment = selection.environments.length <= 1;
       const alertsUrl =
-        alertConfig && singleProject
-          ? getAlertsUrl({project, orgSlug: organization.slug, ...alertConfig})
+        alertConfig && singleProject && singleEnvironment
+          ? getAlertsUrl({
+              project,
+              orgSlug: organization.slug,
+              pageFilters: selection,
+              name: typeof title === 'string' ? title : undefined,
+              ...alertConfig,
+            })
           : undefined;
       const name = alertConfig.name ?? 'Create Alert';
-      const disabled = !singleProject;
+      const disabled = !singleProject || !singleEnvironment;
+      const tooltip = !singleProject
+        ? t(
+            'Alerts are only available for single project selection. Update your project filter to create an alert.'
+          )
+        : !singleEnvironment
+          ? t(
+              'Alerts are only available with at most one environment selection. Update your environment filter to create an alert.'
+            )
+          : undefined;
       return {
         key: name,
         label: name,
         to: alertsUrl,
         disabled,
-        tooltip: disabled
-          ? t(
-              'Alerts are only available for single project selection. Update your project filter to create an alert.'
-            )
-          : undefined,
+        tooltip,
         onClick: () => {
           trackAnalytics('insight.general.create_alert', {
             organization,

+ 11 - 2
static/app/views/insights/common/utils/getAlertsUrl.spec.tsx

@@ -1,16 +1,25 @@
+import {PageFiltersFixture} from 'sentry-fixture/pageFilters';
+
 import {initializeOrg} from 'sentry-test/initializeOrg';
 
 import {getAlertsUrl} from 'sentry/views/insights/common/utils/getAlertsUrl';
 
 describe('getAlertsUrl', function () {
   const {project} = initializeOrg();
+  const pageFilters = PageFiltersFixture();
   it('should return a url to the alert rule page prepopulated with DB params', function () {
     const aggregate = 'avg(d:spans/duration@millisecond)';
     const query = 'span.module:db';
     const orgSlug = 'orgSlug';
-    const url = getAlertsUrl({project, aggregate, query, orgSlug});
+    const url = getAlertsUrl({
+      project,
+      aggregate,
+      query,
+      orgSlug,
+      pageFilters,
+    });
     expect(url).toEqual(
-      '/organizations/orgSlug/alerts/new/metric/?aggregate=avg%28d%3Aspans%2Fduration%40millisecond%29&dataset=generic_metrics&eventTypes=transaction&project=project-slug&query=span.module%3Adb'
+      '/organizations/orgSlug/alerts/new/metric/?aggregate=avg%28d%3Aspans%2Fduration%40millisecond%29&dataset=generic_metrics&eventTypes=transaction&project=project-slug&query=span.module%3Adb&statsPeriod=7d'
     );
   });
 });

+ 24 - 0
static/app/views/insights/common/utils/getAlertsUrl.tsx

@@ -1,5 +1,6 @@
 import * as qs from 'query-string';
 
+import type {PageFilters} from 'sentry/types/core';
 import type {Project} from 'sentry/types/project';
 import normalizeUrl from 'sentry/utils/url/normalizeUrl';
 import {Dataset} from 'sentry/views/alerts/rules/metric/types';
@@ -9,20 +10,43 @@ export function getAlertsUrl({
   query,
   aggregate,
   orgSlug,
+  pageFilters,
+  name,
 }: {
   aggregate: string;
   orgSlug: string;
+  pageFilters: PageFilters;
   project: Project;
+  name?: string;
   query?: string;
 }) {
+  const statsPeriod = getStatsPeriod(pageFilters);
+  const environment = pageFilters.environments;
   const queryParams = {
     aggregate: aggregate,
     dataset: Dataset.GENERIC_METRICS,
     project: project.slug,
     eventTypes: 'transaction',
     query,
+    statsPeriod,
+    environment,
+    name,
   };
   return normalizeUrl(
     `/organizations/${orgSlug}/alerts/new/metric/?${qs.stringify(queryParams)}`
   );
 }
+
+// Alert rules only support 24h, 3d, 7d, 14d periods
+function getStatsPeriod(pageFilters: PageFilters) {
+  const {period} = pageFilters.datetime;
+  switch (period) {
+    case '24h':
+    case '3d':
+    case '7d':
+    case '14d':
+      return period;
+    default:
+      return '7d';
+  }
+}