import {Client} from 'sentry/api'; import {IconGraph} from 'sentry/icons'; import {t} from 'sentry/locale'; import {Organization, Project} from 'sentry/types'; import {uniqueId} from 'sentry/utils/guid'; import {AlertType} from '../../wizard/options'; import { ActionType, AlertRuleComparisonType, AlertRuleThresholdType, AlertRuleTriggerType, Dataset, EventTypes, TargetType, Trigger, } from './types'; export type PresetContext = { aggregate: string; comparisonType: AlertRuleComparisonType; dataset: Dataset; eventTypes: EventTypes[]; name: string; thresholdType: AlertRuleThresholdType; triggers: Trigger[]; comparisonDelta?: number; query?: string; timeWindow?: number; }; export type Preset = { Icon: typeof IconGraph; // Will be shown on the corresponding alert type in the wizard. alertType: AlertType; description: string; id: string; makeContext( client: Client, project: Project, organization: Organization ): Promise; makeUnqueriedContext(project: Project, organization: Organization): PresetContext; title: string; }; async function getHighestVolumeTransaction( client: Client, organizationSlug: string, projectId: string ): Promise<[string, number] | null> { const result = await client.requestPromise( `/organizations/${organizationSlug}/events/`, { method: 'GET', data: { statsPeriod: '7d', project: projectId, field: ['count()', 'transaction'], sort: '-count', referrer: 'alert.presets.highest-volume', query: 'event.type:transaction', per_page: 1, }, } ); const transaction = result.data[0]; if (transaction) { return [transaction.transaction, transaction['count()']]; } return null; } function makeTeamCriticalAlert(project: Project, threshold: number = 200) { return { label: AlertRuleTriggerType.CRITICAL, alertThreshold: threshold, actions: project.teams.slice(0, 4).map(team => ({ type: ActionType.EMAIL, targetType: TargetType.TEAM, targetIdentifier: team.id, unsavedDateCreated: new Date().toISOString(), inputChannelId: null, options: null, unsavedId: uniqueId(), })), }; } function makeTeamWarningAlert(threshold: number = 100) { return { label: AlertRuleTriggerType.WARNING, alertThreshold: threshold, actions: [], }; } export const PRESET_AGGREGATES: readonly Preset[] = [ { id: 'p95-highest-volume', title: t('Slow transactions'), description: 'Get notified when important transactions are slower on average', Icon: IconGraph, alertType: 'trans_duration', makeUnqueriedContext(project, _) { return { name: t('p95 Alert for %s', [project.slug]), aggregate: 'p95(transaction.duration)', dataset: Dataset.TRANSACTIONS, eventTypes: [EventTypes.TRANSACTION], timeWindow: 60, comparisonDelta: 1440, comparisonType: AlertRuleComparisonType.CHANGE, thresholdType: AlertRuleThresholdType.ABOVE, triggers: [makeTeamCriticalAlert(project), makeTeamWarningAlert()], }; }, async makeContext(client, project, organization) { const transaction = ( await getHighestVolumeTransaction(client, organization.slug, project.id) )?.[0]; return { ...this.makeUnqueriedContext(project, organization), query: 'transaction:' + transaction, }; }, }, { id: 'throughput-highest-volume', title: t('Throttled throughput'), description: 'Send an alert when transaction throughput drops significantly', Icon: IconGraph, alertType: 'throughput', makeUnqueriedContext(project, _) { return { name: t('Throughput Alert for %s', [project.slug]), aggregate: 'count()', dataset: Dataset.TRANSACTIONS, eventTypes: [EventTypes.TRANSACTION], timeWindow: 30, comparisonDelta: 24 * 60 * 7, comparisonType: AlertRuleComparisonType.CHANGE, thresholdType: AlertRuleThresholdType.BELOW, triggers: [makeTeamCriticalAlert(project, 500), makeTeamWarningAlert(300)], }; }, async makeContext(client, project, organization) { const transaction = ( await getHighestVolumeTransaction(client, organization.slug, project.id) )?.[0]; return { ...this.makeUnqueriedContext(project, organization), query: 'transaction:' + transaction, }; }, }, { id: 'apdex-highest-volume', title: t('Apdex Score'), description: 'Learn when the ratio of satisfactory, tolerable, and frustrated requests drop', Icon: IconGraph, alertType: 'apdex', makeUnqueriedContext(project, _) { return { name: t('Apdex regression for %s', [project.slug]), aggregate: 'apdex(300)', dataset: Dataset.TRANSACTIONS, eventTypes: [EventTypes.TRANSACTION], timeWindow: 30, comparisonDelta: 24 * 60 * 7, comparisonType: AlertRuleComparisonType.CHANGE, thresholdType: AlertRuleThresholdType.BELOW, triggers: [makeTeamCriticalAlert(project), makeTeamWarningAlert()], }; }, async makeContext(client, project, organization) { const transaction = ( await getHighestVolumeTransaction(client, organization.slug, project.id) )?.[0]; return { ...this.makeUnqueriedContext(project, organization), query: 'transaction:' + transaction, }; }, }, ] as const;