Browse Source

feat(alerts): spike protection notification setting frontend (#40793)

Cathy Teng 2 years ago
parent
commit
c62172c376

+ 16 - 7
static/app/views/settings/account/accountNotificationFineTuning.tsx

@@ -19,6 +19,7 @@ import {
 } from 'sentry/views/settings/account/notifications/fields';
 } from 'sentry/views/settings/account/notifications/fields';
 import NotificationSettingsByType from 'sentry/views/settings/account/notifications/notificationSettingsByType';
 import NotificationSettingsByType from 'sentry/views/settings/account/notifications/notificationSettingsByType';
 import {
 import {
+  getNotificationTypeFromPathname,
   groupByOrganization,
   groupByOrganization,
   isGroupedByProject,
   isGroupedByProject,
 } from 'sentry/views/settings/account/notifications/utils';
 } from 'sentry/views/settings/account/notifications/utils';
@@ -32,6 +33,16 @@ const PanelBodyLineItem = styled(PanelBody)`
   }
   }
 `;
 `;
 
 
+const accountNotifications = [
+  'alerts',
+  'deploy',
+  'workflow',
+  'activeRelease',
+  'approval',
+  'quota',
+  'spikeProtection',
+];
+
 type ANBPProps = {
 type ANBPProps = {
   field: FineTuneField;
   field: FineTuneField;
   projects: Project[];
   projects: Project[];
@@ -128,7 +139,8 @@ type State = AsyncView['state'] & {
 
 
 class AccountNotificationFineTuning extends AsyncView<Props, State> {
 class AccountNotificationFineTuning extends AsyncView<Props, State> {
   getEndpoints(): ReturnType<AsyncView['getEndpoints']> {
   getEndpoints(): ReturnType<AsyncView['getEndpoints']> {
-    const {fineTuneType} = this.props.params;
+    const {fineTuneType: pathnameType} = this.props.params;
+    const fineTuneType = getNotificationTypeFromPathname(pathnameType);
     const endpoints = [
     const endpoints = [
       ['notifications', '/users/me/notifications/'],
       ['notifications', '/users/me/notifications/'],
       ['fineTuneData', `/users/me/notifications/${fineTuneType}/`],
       ['fineTuneData', `/users/me/notifications/${fineTuneType}/`],
@@ -167,13 +179,10 @@ class AccountNotificationFineTuning extends AsyncView<Props, State> {
 
 
   renderBody() {
   renderBody() {
     const {params} = this.props;
     const {params} = this.props;
-    const {fineTuneType} = params;
+    const {fineTuneType: pathnameType} = params;
+    const fineTuneType = getNotificationTypeFromPathname(pathnameType);
 
 
-    if (
-      ['alerts', 'deploy', 'workflow', 'activeRelease', 'approval', 'quota'].includes(
-        fineTuneType
-      )
-    ) {
+    if (accountNotifications.includes(fineTuneType)) {
       return <NotificationSettingsByType notificationType={fineTuneType} />;
       return <NotificationSettingsByType notificationType={fineTuneType} />;
     }
     }
 
 

+ 17 - 1
static/app/views/settings/account/notifications/constants.tsx

@@ -36,13 +36,29 @@ export const NOTIFICATION_SETTINGS_TYPES = [
   'quota',
   'quota',
   'reports',
   'reports',
   'email',
   'email',
-];
+  'spikeProtection',
+] as const;
 
 
 export const SELF_NOTIFICATION_SETTINGS_TYPES = [
 export const SELF_NOTIFICATION_SETTINGS_TYPES = [
   'personalActivityNotifications',
   'personalActivityNotifications',
   'selfAssignOnResolve',
   'selfAssignOnResolve',
 ];
 ];
 
 
+// 'alerts' | 'activeRelease' | 'workflow' ...
+export type NotificationSettingsType = typeof NOTIFICATION_SETTINGS_TYPES[number];
+
+export const NOTIFICATION_SETTINGS_PATHNAMES: Record<NotificationSettingsType, string> = {
+  alerts: 'alerts',
+  activeRelease: 'activeRelease',
+  workflow: 'workflow',
+  deploy: 'deploy',
+  approval: 'approval',
+  quota: 'quota',
+  reports: 'reports',
+  email: 'email',
+  spikeProtection: 'spike-protection',
+};
+
 export const CONFIRMATION_MESSAGE = (
 export const CONFIRMATION_MESSAGE = (
   <div>
   <div>
     <p style={{marginBottom: '20px'}}>
     <p style={{marginBottom: '20px'}}>

+ 13 - 0
static/app/views/settings/account/notifications/fields.tsx

@@ -97,6 +97,19 @@ export const ACCOUNT_NOTIFICATION_FIELDS: Record<string, FineTuneField> = {
     // No choices here because it's going to have dynamic content
     // No choices here because it's going to have dynamic content
     // Component will create choices,
     // Component will create choices,
   },
   },
+  spikeProtection: {
+    title: t('Spike Protection Notifications'),
+    description: t(
+      'Notifications about spikes on projects that you have enabled spike protection for.'
+    ),
+    type: 'select',
+    defaultValue: '1',
+    options: [
+      {value: '1', label: t('On')},
+      {value: '0', label: t('Off')},
+    ],
+    defaultFieldName: 'spikeProtection',
+  },
   email: {
   email: {
     title: t('Email Routing'),
     title: t('Email Routing'),
     description: t(
     description: t(

+ 10 - 0
static/app/views/settings/account/notifications/fields2.tsx

@@ -104,6 +104,16 @@ export const NOTIFICATION_SETTING_FIELDS: Record<string, Field> = {
     label: t('Email Routing'),
     label: t('Email Routing'),
     help: t('Change the email address that receives notifications.'),
     help: t('Change the email address that receives notifications.'),
   },
   },
+  spikeProtection: {
+    name: 'spikeProtection',
+    type: 'select',
+    label: t('Spike Protection'),
+    choices: [
+      ['always', t('On')],
+      ['never', t('Off')],
+    ],
+    help: t('Notifications about spikes on a per project basis.'),
+  },
   personalActivityNotifications: {
   personalActivityNotifications: {
     name: 'personalActivityNotifications',
     name: 'personalActivityNotifications',
     type: 'select',
     type: 'select',

+ 2 - 1
static/app/views/settings/account/notifications/notificationSettings.tsx

@@ -14,6 +14,7 @@ import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAna
 import withOrganizations from 'sentry/utils/withOrganizations';
 import withOrganizations from 'sentry/utils/withOrganizations';
 import {
 import {
   CONFIRMATION_MESSAGE,
   CONFIRMATION_MESSAGE,
+  NOTIFICATION_SETTINGS_PATHNAMES,
   NOTIFICATION_SETTINGS_TYPES,
   NOTIFICATION_SETTINGS_TYPES,
   NotificationSettingsObject,
   NotificationSettingsObject,
   SELF_NOTIFICATION_SETTINGS_TYPES,
   SELF_NOTIFICATION_SETTINGS_TYPES,
@@ -147,7 +148,7 @@ class NotificationSettings extends AsyncComponent<Props, State> {
               &nbsp;
               &nbsp;
               <Link
               <Link
                 data-test-id="fine-tuning"
                 data-test-id="fine-tuning"
-                to={`/settings/account/notifications/${notificationType}`}
+                to={`/settings/account/notifications/${NOTIFICATION_SETTINGS_PATHNAMES[notificationType]}`}
               >
               >
                 Fine tune
                 Fine tune
               </Link>
               </Link>

+ 20 - 1
static/app/views/settings/account/notifications/utils.tsx

@@ -6,6 +6,7 @@ import {OrganizationSummary, Project} from 'sentry/types';
 import {
 import {
   ALL_PROVIDERS,
   ALL_PROVIDERS,
   MIN_PROJECTS_FOR_CONFIRMATION,
   MIN_PROJECTS_FOR_CONFIRMATION,
+  NOTIFICATION_SETTINGS_PATHNAMES,
   NotificationSettingsByProviderObject,
   NotificationSettingsByProviderObject,
   NotificationSettingsObject,
   NotificationSettingsObject,
   VALUE_MAPPING,
   VALUE_MAPPING,
@@ -16,8 +17,16 @@ import ParentLabel from 'sentry/views/settings/account/notifications/parentLabel
 /**
 /**
  * Which fine-tuning parts are grouped by project
  * Which fine-tuning parts are grouped by project
  */
  */
+const notificationsByProject = [
+  'alerts',
+  'email',
+  'workflow',
+  'activeRelease',
+  'spikeProtection',
+];
+
 export const isGroupedByProject = (notificationType: string): boolean =>
 export const isGroupedByProject = (notificationType: string): boolean =>
-  ['alerts', 'email', 'workflow', 'activeRelease'].includes(notificationType);
+  notificationsByProject.includes(notificationType);
 
 
 export const getParentKey = (notificationType: string): string => {
 export const getParentKey = (notificationType: string): string => {
   return isGroupedByProject(notificationType) ? 'project' : 'organization';
   return isGroupedByProject(notificationType) ? 'project' : 'organization';
@@ -436,3 +445,13 @@ export function getDocsLinkForEventType(event: 'error' | 'transaction' | 'attach
       return 'https://docs.sentry.io/product/accounts/quotas/manage-event-stream-guide/#common-workflows-for-managing-your-event-stream';
       return 'https://docs.sentry.io/product/accounts/quotas/manage-event-stream-guide/#common-workflows-for-managing-your-event-stream';
   }
   }
 }
 }
+
+/**
+ * Returns the corresponding notification type name from the router path name
+ */
+export function getNotificationTypeFromPathname(routerPathname: string) {
+  const result = Object.entries(NOTIFICATION_SETTINGS_PATHNAMES).find(
+    ([_, pathname]) => pathname === routerPathname
+  ) ?? [routerPathname];
+  return result[0];
+}