|
17 | 17 | from sentry.analytics.events.alert_rule_ui_component_webhook_sent import ( |
18 | 18 | AlertRuleUiComponentWebhookSentEvent, |
19 | 19 | ) |
| 20 | +from sentry.analytics.events.alert_sent import AlertSentEvent |
20 | 21 | from sentry.analytics.events.comment_webhooks import ( |
21 | 22 | CommentCreatedEvent, |
22 | 23 | CommentDeletedEvent, |
|
36 | 37 | from sentry.db.models.base import Model |
37 | 38 | from sentry.exceptions import RestrictedIPAddress |
38 | 39 | from sentry.hybridcloud.rpc.caching import cell_caching_service |
| 40 | +from sentry.incidents.models.incident import INCIDENT_STATUS, IncidentStatus |
39 | 41 | from sentry.issues.issue_occurrence import IssueOccurrence |
40 | 42 | from sentry.models.activity import Activity |
41 | 43 | from sentry.models.group import Group |
|
63 | 65 | ) |
64 | 66 | from sentry.sentry_apps.services.hook.service import hook_service |
65 | 67 | from sentry.sentry_apps.utils.errors import SentryAppSentryError |
66 | | -from sentry.sentry_apps.utils.webhooks import IssueAlertActionType, SentryAppResourceType |
| 68 | +from sentry.sentry_apps.utils.webhooks import ( |
| 69 | + IssueAlertActionType, |
| 70 | + MetricAlertActionType, |
| 71 | + SentryAppResourceType, |
| 72 | + find_alert_rule_action_ui_component, |
| 73 | +) |
67 | 74 | from sentry.services.eventstore.models import BaseEvent, Event, GroupEvent |
68 | 75 | from sentry.shared_integrations.exceptions import ApiHostError, ApiTimeoutError, ClientError |
69 | 76 | from sentry.silo.base import SiloMode |
|
72 | 79 | from sentry.types.rules import RuleFuture |
73 | 80 | from sentry.users.services.user.model import RpcUser |
74 | 81 | from sentry.users.services.user.service import user_service |
75 | | -from sentry.utils import metrics |
| 82 | +from sentry.utils import json, metrics |
76 | 83 | from sentry.utils.function_cache import cache_func_for_models |
77 | 84 | from sentry.utils.http import absolute_uri |
78 | 85 | from sentry.utils.sentry_apps import send_and_save_webhook_request |
@@ -908,6 +915,113 @@ def regenerate_service_hooks_for_installation( |
908 | 915 | ) |
909 | 916 |
|
910 | 917 |
|
| 918 | +def _record_metric_alert_sent_analytics( |
| 919 | + organization_id: int, |
| 920 | + project_id: int, |
| 921 | + alert_id: int, |
| 922 | + sentry_app_id: int, |
| 923 | + notification_uuid: str | None, |
| 924 | +) -> None: |
| 925 | + try: |
| 926 | + analytics.record( |
| 927 | + AlertSentEvent( |
| 928 | + organization_id=organization_id, |
| 929 | + project_id=project_id, |
| 930 | + alert_id=alert_id, |
| 931 | + alert_type="metric_alert", |
| 932 | + provider="sentry_app", |
| 933 | + external_id=str(sentry_app_id), |
| 934 | + notification_uuid=notification_uuid, |
| 935 | + ) |
| 936 | + ) |
| 937 | + except Exception as e: |
| 938 | + sentry_sdk.capture_exception(e) |
| 939 | + |
| 940 | + |
| 941 | +def _record_metric_alert_ui_component_analytics( |
| 942 | + organization_id: int, |
| 943 | + sentry_app_id: int, |
| 944 | + app_platform_event: AppPlatformEvent, |
| 945 | +) -> None: |
| 946 | + if not find_alert_rule_action_ui_component(app_platform_event): |
| 947 | + return |
| 948 | + try: |
| 949 | + analytics.record( |
| 950 | + AlertRuleUiComponentWebhookSentEvent( |
| 951 | + organization_id=organization_id, |
| 952 | + sentry_app_id=sentry_app_id, |
| 953 | + event=f"{app_platform_event.resource}.{app_platform_event.action}", |
| 954 | + ) |
| 955 | + ) |
| 956 | + except Exception as e: |
| 957 | + sentry_sdk.capture_exception(e) |
| 958 | + |
| 959 | + |
| 960 | +@instrumented_task( |
| 961 | + name="sentry.sentry_apps.tasks.sentry_apps.send_metric_alert_webhook", |
| 962 | + namespace=sentryapp_tasks, |
| 963 | + retry=Retry(times=3, delay=60 * 5), |
| 964 | + silo_mode=SiloMode.CELL, |
| 965 | +) |
| 966 | +@retry_decorator |
| 967 | +def send_metric_alert_webhook( |
| 968 | + sentry_app_id: int, |
| 969 | + new_status: int, |
| 970 | + incident_attachment_json: str, |
| 971 | + organization_id: int, |
| 972 | + project_id: int, |
| 973 | + alert_id: int, |
| 974 | + notification_uuid: str | None = None, |
| 975 | + **kwargs: Any, |
| 976 | +) -> None: |
| 977 | + try: |
| 978 | + new_status_str = INCIDENT_STATUS[IncidentStatus(new_status)].lower() |
| 979 | + event = SentryAppEventType( |
| 980 | + f"{SentryAppResourceType.METRIC_ALERT}.{MetricAlertActionType(new_status_str)}" |
| 981 | + ) |
| 982 | + except ValueError as e: |
| 983 | + sentry_sdk.capture_exception(e) |
| 984 | + return |
| 985 | + |
| 986 | + with SentryAppInteractionEvent( |
| 987 | + operation_type=SentryAppInteractionType.PREPARE_WEBHOOK, |
| 988 | + event_type=event, |
| 989 | + ).capture() as lifecycle: |
| 990 | + sentry_app = app_service.get_sentry_app_by_id(id=sentry_app_id) |
| 991 | + if sentry_app is None: |
| 992 | + lifecycle.record_failure(SentryAppWebhookFailureReason.MISSING_SENTRY_APP) |
| 993 | + return |
| 994 | + |
| 995 | + installations = app_service.get_many( |
| 996 | + filter=dict( |
| 997 | + organization_id=organization_id, |
| 998 | + app_ids=[sentry_app.id], |
| 999 | + status=SentryAppInstallationStatus.INSTALLED, |
| 1000 | + ) |
| 1001 | + ) |
| 1002 | + if not installations: |
| 1003 | + lifecycle.record_failure(SentryAppWebhookFailureReason.MISSING_INSTALLATION) |
| 1004 | + return |
| 1005 | + |
| 1006 | + if len(installations) > 1: |
| 1007 | + lifecycle.record_failure(SentryAppWebhookFailureReason.MULTIPLE_INSTALLATIONS) |
| 1008 | + return |
| 1009 | + |
| 1010 | + app_platform_event = AppPlatformEvent( |
| 1011 | + resource=SentryAppResourceType.METRIC_ALERT, |
| 1012 | + action=MetricAlertActionType(new_status_str), |
| 1013 | + install=installations[0], |
| 1014 | + data=json.loads(incident_attachment_json), |
| 1015 | + ) |
| 1016 | + |
| 1017 | + send_and_save_webhook_request(sentry_app, app_platform_event) |
| 1018 | + |
| 1019 | + _record_metric_alert_sent_analytics( |
| 1020 | + organization_id, project_id, alert_id, sentry_app.id, notification_uuid |
| 1021 | + ) |
| 1022 | + _record_metric_alert_ui_component_analytics(organization_id, sentry_app.id, app_platform_event) |
| 1023 | + |
| 1024 | + |
911 | 1025 | @instrumented_task( |
912 | 1026 | name="sentry.sentry_apps.tasks.sentry_apps.broadcast_webhooks_for_organization", |
913 | 1027 | namespace=sentryapp_tasks, |
|
0 commit comments