diff --git a/src/sentry/analytics/events/alert_rule_ui_component_webhook_sent.py b/src/sentry/analytics/events/alert_rule_ui_component_webhook_sent.py index cb5aab4edbc078..d9bc0ee6f377d8 100644 --- a/src/sentry/analytics/events/alert_rule_ui_component_webhook_sent.py +++ b/src/sentry/analytics/events/alert_rule_ui_component_webhook_sent.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("alert_rule_ui_component_webhook.sent") class AlertRuleUiComponentWebhookSentEvent(analytics.Event): - type = "alert_rule_ui_component_webhook.sent" - - attributes = ( - # organization_id refers to the organization that installed the sentryapp - analytics.Attribute("organization_id"), - analytics.Attribute("sentry_app_id"), - analytics.Attribute("event"), - ) + # organization_id refers to the organization that installed the sentryapp + organization_id: str + sentry_app_id: str + event: str analytics.register(AlertRuleUiComponentWebhookSentEvent) diff --git a/src/sentry/analytics/events/internal_integration_created.py b/src/sentry/analytics/events/internal_integration_created.py index 62cb7916b6cfc8..474c553c0b6139 100644 --- a/src/sentry/analytics/events/internal_integration_created.py +++ b/src/sentry/analytics/events/internal_integration_created.py @@ -1,14 +1,11 @@ from sentry import analytics +@analytics.eventclass("internal_integration.created") class InternalIntegrationCreatedEvent(analytics.Event): - type = "internal_integration.created" - - attributes = ( - analytics.Attribute("user_id"), - analytics.Attribute("organization_id"), - analytics.Attribute("sentry_app"), - ) + user_id: str + organization_id: str + sentry_app: str analytics.register(InternalIntegrationCreatedEvent) diff --git a/src/sentry/analytics/events/sentry_app_created.py b/src/sentry/analytics/events/sentry_app_created.py index b01651e2bf6ef1..48643d2e6b6adc 100644 --- a/src/sentry/analytics/events/sentry_app_created.py +++ b/src/sentry/analytics/events/sentry_app_created.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("sentry_app.created") class SentryAppCreatedEvent(analytics.Event): - type = "sentry_app.created" - - attributes = ( - analytics.Attribute("user_id"), - analytics.Attribute("organization_id"), - analytics.Attribute("sentry_app"), - analytics.Attribute("created_alert_rule_ui_component", type=bool, required=False), - ) + user_id: str + organization_id: str + sentry_app: str + created_alert_rule_ui_component: bool | None = None analytics.register(SentryAppCreatedEvent) diff --git a/src/sentry/analytics/events/sentry_app_deleted.py b/src/sentry/analytics/events/sentry_app_deleted.py index 2b705e49ac8b1b..0bfd172ad1d376 100644 --- a/src/sentry/analytics/events/sentry_app_deleted.py +++ b/src/sentry/analytics/events/sentry_app_deleted.py @@ -1,14 +1,11 @@ from sentry import analytics +@analytics.eventclass("sentry_app.deleted") class SentryAppDeletedEvent(analytics.Event): - type = "sentry_app.deleted" - - attributes = ( - analytics.Attribute("user_id"), - analytics.Attribute("organization_id"), - analytics.Attribute("sentry_app"), - ) + user_id: str + organization_id: str + sentry_app: str analytics.register(SentryAppDeletedEvent) diff --git a/src/sentry/analytics/events/sentry_app_installation_token_created.py b/src/sentry/analytics/events/sentry_app_installation_token_created.py index f78733cb223bca..623ef83322e37f 100644 --- a/src/sentry/analytics/events/sentry_app_installation_token_created.py +++ b/src/sentry/analytics/events/sentry_app_installation_token_created.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("sentry_app_installation_token.created") class SentryAppInstallationTokenCreated(analytics.Event): - type = "sentry_app_installation_token.created" - - attributes = ( - analytics.Attribute("user_id"), - analytics.Attribute("organization_id"), - analytics.Attribute("sentry_app_installation_id"), - analytics.Attribute("sentry_app"), - ) + user_id: str + organization_id: str + sentry_app_installation_id: str + sentry_app: str analytics.register(SentryAppInstallationTokenCreated) diff --git a/src/sentry/analytics/events/sentry_app_installation_token_deleted.py b/src/sentry/analytics/events/sentry_app_installation_token_deleted.py index 1177fd04df1fff..f66a0e33fa4698 100644 --- a/src/sentry/analytics/events/sentry_app_installation_token_deleted.py +++ b/src/sentry/analytics/events/sentry_app_installation_token_deleted.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("sentry_app_installation_token.deleted") class SentryAppInstallationTokenDeleted(analytics.Event): - type = "sentry_app_installation_token.deleted" - - attributes = ( - analytics.Attribute("user_id"), - analytics.Attribute("organization_id"), - analytics.Attribute("sentry_app_installation_id"), - analytics.Attribute("sentry_app"), - ) + user_id: str + organization_id: str + sentry_app_installation_id: str + sentry_app: str analytics.register(SentryAppInstallationTokenDeleted) diff --git a/src/sentry/analytics/events/sentry_app_installation_updated.py b/src/sentry/analytics/events/sentry_app_installation_updated.py index a53d1d3a18e22c..93c06580d92480 100644 --- a/src/sentry/analytics/events/sentry_app_installation_updated.py +++ b/src/sentry/analytics/events/sentry_app_installation_updated.py @@ -1,14 +1,11 @@ from sentry import analytics +@analytics.eventclass("sentry_app_installation.updated") class SentryAppInstallationUpdatedEvent(analytics.Event): - type = "sentry_app_installation.updated" - - attributes = ( - analytics.Attribute("sentry_app_installation_id"), - analytics.Attribute("sentry_app_id"), - analytics.Attribute("organization_id"), - ) + sentry_app_installation_id: str + sentry_app_id: str + organization_id: str analytics.register(SentryAppInstallationUpdatedEvent) diff --git a/src/sentry/analytics/events/sentry_app_installed.py b/src/sentry/analytics/events/sentry_app_installed.py index 9144b97a6f6bc7..5bb4e90f8e4cc6 100644 --- a/src/sentry/analytics/events/sentry_app_installed.py +++ b/src/sentry/analytics/events/sentry_app_installed.py @@ -1,14 +1,11 @@ from sentry import analytics +@analytics.eventclass("sentry_app.installed") class SentryAppInstalledEvent(analytics.Event): - type = "sentry_app.installed" - - attributes = ( - analytics.Attribute("user_id"), - analytics.Attribute("organization_id"), - analytics.Attribute("sentry_app"), - ) + user_id: str + organization_id: str + sentry_app: str analytics.register(SentryAppInstalledEvent) diff --git a/src/sentry/analytics/events/sentry_app_schema_validation_error.py b/src/sentry/analytics/events/sentry_app_schema_validation_error.py index 965513831df51a..d782b1bff57814 100644 --- a/src/sentry/analytics/events/sentry_app_schema_validation_error.py +++ b/src/sentry/analytics/events/sentry_app_schema_validation_error.py @@ -1,17 +1,14 @@ from sentry import analytics +@analytics.eventclass("sentry_app.schema_validation_error") class SentryAppSchemaValidationError(analytics.Event): - type = "sentry_app.schema_validation_error" - - attributes = ( - analytics.Attribute("schema"), - analytics.Attribute("user_id"), - analytics.Attribute("sentry_app_id", required=False), - analytics.Attribute("sentry_app_name"), - analytics.Attribute("organization_id"), - analytics.Attribute("error_message"), - ) + schema: str + user_id: str + sentry_app_id: str | None = None + sentry_app_name: str + organization_id: str + error_message: str analytics.register(SentryAppSchemaValidationError) diff --git a/src/sentry/analytics/events/sentry_app_token_exchanged.py b/src/sentry/analytics/events/sentry_app_token_exchanged.py index ffa44fb2d2070c..df5eac1ab6f6ae 100644 --- a/src/sentry/analytics/events/sentry_app_token_exchanged.py +++ b/src/sentry/analytics/events/sentry_app_token_exchanged.py @@ -1,13 +1,10 @@ from sentry import analytics +@analytics.eventclass("sentry_app.token_exchanged") class SentryAppTokenExchangedEvent(analytics.Event): - type = "sentry_app.token_exchanged" - - attributes = ( - analytics.Attribute("sentry_app_installation_id"), - analytics.Attribute("exchange_type"), - ) + sentry_app_installation_id: str + exchange_type: str analytics.register(SentryAppTokenExchangedEvent) diff --git a/src/sentry/analytics/events/sentry_app_uninstalled.py b/src/sentry/analytics/events/sentry_app_uninstalled.py index 993b1c3803abb8..39e849ca40339e 100644 --- a/src/sentry/analytics/events/sentry_app_uninstalled.py +++ b/src/sentry/analytics/events/sentry_app_uninstalled.py @@ -1,14 +1,11 @@ from sentry import analytics +@analytics.eventclass("sentry_app.uninstalled") class SentryAppUninstalledEvent(analytics.Event): - type = "sentry_app.uninstalled" - - attributes = ( - analytics.Attribute("user_id"), - analytics.Attribute("organization_id"), - analytics.Attribute("sentry_app"), - ) + user_id: str + organization_id: str + sentry_app: str analytics.register(SentryAppUninstalledEvent) diff --git a/src/sentry/analytics/events/sentry_app_updated.py b/src/sentry/analytics/events/sentry_app_updated.py index 285d3ebbdce45c..bd76f403d14075 100644 --- a/src/sentry/analytics/events/sentry_app_updated.py +++ b/src/sentry/analytics/events/sentry_app_updated.py @@ -1,15 +1,12 @@ from sentry import analytics +@analytics.eventclass("sentry_app.updated") class SentryAppUpdatedEvent(analytics.Event): - type = "sentry_app.updated" - - attributes = ( - analytics.Attribute("user_id"), - analytics.Attribute("organization_id"), - analytics.Attribute("sentry_app"), - analytics.Attribute("created_alert_rule_ui_component", type=bool, required=False), - ) + user_id: str + organization_id: str + sentry_app: str + created_alert_rule_ui_component: bool | None = None analytics.register(SentryAppUpdatedEvent) diff --git a/src/sentry/integrations/services/integration/impl.py b/src/sentry/integrations/services/integration/impl.py index e42622ac6aec02..80472c4f78c517 100644 --- a/src/sentry/integrations/services/integration/impl.py +++ b/src/sentry/integrations/services/integration/impl.py @@ -8,6 +8,9 @@ from django.utils import timezone from sentry import analytics +from sentry.analytics.events.alert_rule_ui_component_webhook_sent import ( + AlertRuleUiComponentWebhookSentEvent, +) from sentry.api.paginator import OffsetPaginator from sentry.constants import SentryAppInstallationStatus from sentry.hybridcloud.rpc.pagination import RpcPaginationArgs, RpcPaginationResult @@ -422,10 +425,11 @@ def send_incident_alert_notification( if alert_rule_action_ui_component: analytics.record( - "alert_rule_ui_component_webhook.sent", - organization_id=organization_id, - sentry_app_id=sentry_app.id, - event=f"{app_platform_event.resource}.{app_platform_event.action}", + AlertRuleUiComponentWebhookSentEvent( + organization_id=organization_id, + sentry_app_id=sentry_app.id, + event=f"{app_platform_event.resource}.{app_platform_event.action}", + ) ) return alert_rule_action_ui_component diff --git a/src/sentry/sentry_apps/api/endpoints/installation_details.py b/src/sentry/sentry_apps/api/endpoints/installation_details.py index 7e7fb50bce6f6d..95571a35a1697e 100644 --- a/src/sentry/sentry_apps/api/endpoints/installation_details.py +++ b/src/sentry/sentry_apps/api/endpoints/installation_details.py @@ -5,6 +5,7 @@ from rest_framework.response import Response from sentry import analytics, audit_log, deletions +from sentry.analytics.events.sentry_app_uninstalled import SentryAppUninstalledEvent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint @@ -64,10 +65,11 @@ def delete(self, request: Request, installation) -> Response: data={"sentry_app": sentry_app_installation.sentry_app.name}, ) analytics.record( - "sentry_app.uninstalled", - user_id=request.user.id, - organization_id=sentry_app_installation.organization_id, - sentry_app=sentry_app_installation.sentry_app.slug, + SentryAppUninstalledEvent( + user_id=request.user.id, + organization_id=sentry_app_installation.organization_id, + sentry_app=sentry_app_installation.sentry_app.slug, + ) ) return Response(status=204) diff --git a/src/sentry/sentry_apps/api/endpoints/sentry_app_details.py b/src/sentry/sentry_apps/api/endpoints/sentry_app_details.py index 4873b0f784c29a..23db96e9af9ee4 100644 --- a/src/sentry/sentry_apps/api/endpoints/sentry_app_details.py +++ b/src/sentry/sentry_apps/api/endpoints/sentry_app_details.py @@ -9,6 +9,7 @@ from rest_framework.response import Response from sentry import analytics, audit_log, deletions, features +from sentry.analytics.events.sentry_app_deleted import SentryAppDeletedEvent from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint @@ -232,10 +233,11 @@ def delete(self, request: Request, sentry_app) -> Response: data={"sentry_app": sentry_app.name}, ) analytics.record( - "sentry_app.deleted", - user_id=request.user.id, - organization_id=sentry_app.owner_id, - sentry_app=sentry_app.slug, + SentryAppDeletedEvent( + user_id=request.user.id, + organization_id=sentry_app.owner_id, + sentry_app=sentry_app.slug, + ) ) return Response(status=204) diff --git a/src/sentry/sentry_apps/api/endpoints/sentry_internal_app_token_details.py b/src/sentry/sentry_apps/api/endpoints/sentry_internal_app_token_details.py index 9af2f110bcfb22..836c2ef904f2c2 100644 --- a/src/sentry/sentry_apps/api/endpoints/sentry_internal_app_token_details.py +++ b/src/sentry/sentry_apps/api/endpoints/sentry_internal_app_token_details.py @@ -5,6 +5,9 @@ from rest_framework.response import Response from sentry import analytics, deletions +from sentry.analytics.events.sentry_app_installation_token_deleted import ( + SentryAppInstallationTokenDeleted, +) from sentry.api.api_owners import ApiOwner from sentry.api.api_publish_status import ApiPublishStatus from sentry.api.base import control_silo_endpoint @@ -63,11 +66,12 @@ def delete(self, request: Request, sentry_app, api_token) -> Response: deletions.exec_sync(install_token) analytics.record( - "sentry_app_installation_token.deleted", - user_id=request.user.id, - organization_id=sentry_app_installation.organization_id, - sentry_app_installation_id=sentry_app_installation.id, - sentry_app=sentry_app.slug, + SentryAppInstallationTokenDeleted( + user_id=request.user.id, + organization_id=sentry_app_installation.organization_id, + sentry_app_installation_id=sentry_app_installation.id, + sentry_app=sentry_app.slug, + ) ) return Response(status=204) diff --git a/src/sentry/sentry_apps/installations.py b/src/sentry/sentry_apps/installations.py index cc641bb3233e59..520ac26c2f6111 100644 --- a/src/sentry/sentry_apps/installations.py +++ b/src/sentry/sentry_apps/installations.py @@ -8,6 +8,13 @@ from django.http.request import HttpRequest from sentry import analytics, audit_log +from sentry.analytics.events.sentry_app_installation_token_created import ( + SentryAppInstallationTokenCreated, +) +from sentry.analytics.events.sentry_app_installation_updated import ( + SentryAppInstallationUpdatedEvent, +) +from sentry.analytics.events.sentry_app_installed import SentryAppInstalledEvent from sentry.api.serializers import serialize from sentry.constants import INTERNAL_INTEGRATION_TOKEN_COUNT_MAX, SentryAppInstallationStatus from sentry.exceptions import ApiTokenLimitError @@ -92,11 +99,12 @@ def record_analytics(self, user: User | RpcUser) -> None: from sentry import analytics analytics.record( - "sentry_app_installation_token.created", - user_id=user.id, - organization_id=self.organization_id, - sentry_app_installation_id=self.sentry_app_installation.id, - sentry_app=self.sentry_app.slug, + SentryAppInstallationTokenCreated( + user_id=user.id, + organization_id=self.organization_id, + sentry_app_installation_id=self.sentry_app_installation.id, + sentry_app=self.sentry_app.slug, + ) ) @cached_property @@ -178,10 +186,11 @@ def audit(self, request: HttpRequest | None) -> None: def record_analytics(self, user: User | RpcUser) -> None: analytics.record( - "sentry_app.installed", - user_id=user.id, - organization_id=self.organization_id, - sentry_app=self.slug, + SentryAppInstalledEvent( + user_id=user.id, + organization_id=self.organization_id, + sentry_app=self.slug, + ) ) metrics.incr("sentry_apps.installation.success") @@ -249,8 +258,9 @@ def _update_status(self): def record_analytics(self): analytics.record( - "sentry_app_installation.updated", - sentry_app_installation_id=self.sentry_app_installation.id, - sentry_app_id=self.sentry_app_installation.sentry_app.id, - organization_id=self.sentry_app_installation.organization_id, + SentryAppInstallationUpdatedEvent( + sentry_app_installation_id=self.sentry_app_installation.id, + sentry_app_id=self.sentry_app_installation.sentry_app.id, + organization_id=self.sentry_app_installation.organization_id, + ) ) diff --git a/src/sentry/sentry_apps/logic.py b/src/sentry/sentry_apps/logic.py index 663b65edb92e83..c10d6ff5689896 100644 --- a/src/sentry/sentry_apps/logic.py +++ b/src/sentry/sentry_apps/logic.py @@ -14,6 +14,9 @@ from sentry_sdk.api import isolation_scope from sentry import analytics, audit_log +from sentry.analytics.events.internal_integration_created import InternalIntegrationCreatedEvent +from sentry.analytics.events.sentry_app_created import SentryAppCreatedEvent +from sentry.analytics.events.sentry_app_updated import SentryAppUpdatedEvent from sentry.api.helpers.slugs import sentry_slugify from sentry.auth.staff import has_staff_option from sentry.constants import SentryAppStatus @@ -283,11 +286,13 @@ def _create_ui_components(self) -> None: def record_analytics(self, user: User | RpcUser, new_schema_elements: set[str] | None) -> None: analytics.record( - "sentry_app.updated", - user_id=user.id, - organization_id=self.sentry_app.owner_id, - sentry_app=self.sentry_app.slug, - created_alert_rule_ui_component="alert-rule-action" in (new_schema_elements or set()), + SentryAppUpdatedEvent( + user_id=user.id, + organization_id=self.sentry_app.owner_id, + sentry_app=self.sentry_app.slug, + created_alert_rule_ui_component="alert-rule-action" + in (new_schema_elements or set()), + ) ) @@ -470,17 +475,20 @@ def audit(self, request: HttpRequest | None, sentry_app: SentryApp) -> None: def record_analytics(self, user: User | RpcUser, sentry_app: SentryApp) -> None: analytics.record( - "sentry_app.created", - user_id=user.id, - organization_id=self.organization_id, - sentry_app=sentry_app.slug, - created_alert_rule_ui_component="alert-rule-action" in _get_schema_types(self.schema), + SentryAppCreatedEvent( + user_id=user.id, + organization_id=self.organization_id, + sentry_app=sentry_app.slug, + created_alert_rule_ui_component="alert-rule-action" + in _get_schema_types(self.schema), + ) ) if self.is_internal: analytics.record( - "internal_integration.created", - user_id=user.id, - organization_id=self.organization_id, - sentry_app=sentry_app.slug, + InternalIntegrationCreatedEvent( + user_id=user.id, + organization_id=self.organization_id, + sentry_app=sentry_app.slug, + ) ) diff --git a/src/sentry/sentry_apps/tasks/sentry_apps.py b/src/sentry/sentry_apps/tasks/sentry_apps.py index 1ab3a6fd96d3d9..6512f8c2cb692a 100644 --- a/src/sentry/sentry_apps/tasks/sentry_apps.py +++ b/src/sentry/sentry_apps/tasks/sentry_apps.py @@ -10,6 +10,9 @@ from requests.exceptions import RequestException from sentry import analytics, features, nodestore +from sentry.analytics.events.alert_rule_ui_component_webhook_sent import ( + AlertRuleUiComponentWebhookSentEvent, +) from sentry.api.serializers import serialize from sentry.api.serializers.models.group import BaseGroupSerializerResponse from sentry.constants import SentryAppInstallationStatus @@ -242,10 +245,11 @@ def send_alert_webhook_v2( # On success, record analytic event for Alert Rule UI Component if request_data.data.get("issue_alert"): analytics.record( - "alert_rule_ui_component_webhook.sent", - organization_id=organization.id, - sentry_app_id=sentry_app_id, - event=SentryAppEventType.EVENT_ALERT_TRIGGERED, + AlertRuleUiComponentWebhookSentEvent( + organization_id=organization.id, + sentry_app_id=sentry_app_id, + event=SentryAppEventType.EVENT_ALERT_TRIGGERED, + ) ) diff --git a/src/sentry/sentry_apps/token_exchange/grant_exchanger.py b/src/sentry/sentry_apps/token_exchange/grant_exchanger.py index 5b78ed675b9709..2ac8d2c9538233 100644 --- a/src/sentry/sentry_apps/token_exchange/grant_exchanger.py +++ b/src/sentry/sentry_apps/token_exchange/grant_exchanger.py @@ -6,6 +6,7 @@ from django.utils.functional import cached_property from sentry import analytics +from sentry.analytics.events.sentry_app_token_exchanged import SentryAppTokenExchangedEvent from sentry.locks import locks from sentry.models.apiapplication import ApiApplication from sentry.models.apigrant import ApiGrant @@ -80,9 +81,10 @@ def run(self): def record_analytics(self) -> None: analytics.record( - "sentry_app.token_exchanged", - sentry_app_installation_id=self.install.id, - exchange_type="authorization", + SentryAppTokenExchangedEvent( + sentry_app_installation_id=self.install.id, + exchange_type="authorization", + ) ) def _validate(self) -> None: diff --git a/src/sentry/sentry_apps/token_exchange/refresher.py b/src/sentry/sentry_apps/token_exchange/refresher.py index d585059aab33a6..1b4e6f18c52431 100644 --- a/src/sentry/sentry_apps/token_exchange/refresher.py +++ b/src/sentry/sentry_apps/token_exchange/refresher.py @@ -5,6 +5,7 @@ from django.utils.functional import cached_property from sentry import analytics +from sentry.analytics.events.sentry_app_token_exchanged import SentryAppTokenExchangedEvent from sentry.hybridcloud.models.outbox import OutboxDatabaseError from sentry.models.apiapplication import ApiApplication from sentry.models.apitoken import ApiToken @@ -75,9 +76,10 @@ def run(self) -> ApiToken: def _record_analytics(self) -> None: analytics.record( - "sentry_app.token_exchanged", - sentry_app_installation_id=self.install.id, - exchange_type="refresh", + SentryAppTokenExchangedEvent( + sentry_app_installation_id=self.install.id, + exchange_type="refresh", + ) ) def _validate(self) -> None: