Browse Source

feat(issue-details): Add analytics events for issue tracking (#46706)

Closes https://github.com/getsentry/sentry/issues/46422
Malachi Willey 1 year ago
parent
commit
0765604aa2

+ 16 - 2
static/app/components/group/externalIssueActions.tsx

@@ -8,7 +8,10 @@ import IssueSyncListElement from 'sentry/components/issueSyncListElement';
 import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {Group, GroupIntegration} from 'sentry/types';
+import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
+import {getAnalyticsDataForGroup} from 'sentry/utils/events';
 import useApi from 'sentry/utils/useApi';
+import useOrganization from 'sentry/utils/useOrganization';
 import IntegrationItem from 'sentry/views/settings/organizationIntegrations/integrationItem';
 
 import ExternalIssueForm from './externalIssueForm';
@@ -25,6 +28,7 @@ type LinkedIssues = {
 };
 
 const ExternalIssueActions = ({configurations, group, onChange}: Props) => {
+  const organization = useOrganization();
   const api = useApi();
   const {linked, unlinked} = configurations
     .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))
@@ -62,11 +66,21 @@ const ExternalIssueActions = ({configurations, group, onChange}: Props) => {
     });
   };
 
-  const doOpenModal = (integration: GroupIntegration) =>
+  const doOpenModal = (integration: GroupIntegration) => {
+    trackAdvancedAnalyticsEvent('issue_details.external_issue_modal_opened', {
+      organization,
+      ...getAnalyticsDataForGroup(group),
+      external_issue_provider: integration.provider.key,
+      external_issue_type: 'first_party',
+    });
+
     openModal(
-      deps => <ExternalIssueForm {...deps} {...{group, onChange, integration}} />,
+      deps => (
+        <ExternalIssueForm {...deps} {...{group, onChange, integration, organization}} />
+      ),
       {closeEvents: 'escape-key'}
     );
+  };
 
   return (
     <Fragment>

+ 12 - 1
static/app/components/group/externalIssueForm.tsx

@@ -8,7 +8,9 @@ import AbstractExternalIssueForm, {
 import {FormProps} from 'sentry/components/forms/form';
 import NavTabs from 'sentry/components/navTabs';
 import {t, tct} from 'sentry/locale';
-import {Group, Integration, IntegrationExternalIssue} from 'sentry/types';
+import {Group, Integration, IntegrationExternalIssue, Organization} from 'sentry/types';
+import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
+import {getAnalyticsDataForGroup} from 'sentry/utils/events';
 
 const MESSAGES_BY_ACTION = {
   link: t('Successfully linked issue.'),
@@ -24,6 +26,7 @@ type Props = {
   group: Group;
   integration: Integration;
   onChange: (onSuccess?: () => void, onError?: () => void) => void;
+  organization: Organization;
 } & AbstractExternalIssueForm['props'];
 
 type State = AbstractExternalIssueForm['state'];
@@ -69,6 +72,14 @@ export default class ExternalIssueForm extends AbstractExternalIssueForm<Props,
   onSubmitSuccess = (_data: IntegrationExternalIssue): void => {
     const {onChange, closeModal} = this.props;
     const {action} = this.state;
+
+    trackAdvancedAnalyticsEvent('issue_details.external_issue_created', {
+      organization: this.props.organization,
+      ...getAnalyticsDataForGroup(this.props.group),
+      external_issue_provider: this.props.integration.provider.key,
+      external_issue_type: 'first_party',
+    });
+
     onChange(() => addSuccessMessage(MESSAGES_BY_ACTION[action]));
     closeModal();
 

+ 2 - 1
static/app/components/group/externalIssuesList.tsx

@@ -180,7 +180,7 @@ class ExternalIssueList extends AsyncComponent<Props, State> {
 
   renderSentryAppIssues(): ExternalIssueComponent[] {
     const {externalIssues, sentryAppInstallations} = this.state;
-    const {components, group} = this.props;
+    const {components, group, organization} = this.props;
 
     return components
       .map<ExternalIssueComponent | null>(component => {
@@ -203,6 +203,7 @@ class ExternalIssueList extends AsyncComponent<Props, State> {
             <ErrorBoundary key={sentryApp.slug} mini>
               <SentryAppExternalIssueActions
                 group={group}
+                organization={organization}
                 event={this.props.event}
                 sentryAppComponent={component}
                 sentryAppInstallation={installation}

+ 9 - 0
static/app/components/group/pluginActions.tsx

@@ -8,6 +8,8 @@ import NavTabs from 'sentry/components/navTabs';
 import {t, tct} from 'sentry/locale';
 import plugins from 'sentry/plugins';
 import {Group, Organization, Plugin, Project} from 'sentry/types';
+import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
+import {getAnalyticsDataForGroup} from 'sentry/utils/events';
 import withApi from 'sentry/utils/withApi';
 import withOrganization from 'sentry/utils/withOrganization';
 
@@ -98,6 +100,13 @@ class PluginActions extends Component<Props, State> {
     const {project, group, organization} = this.props;
     const plugin = {...this.props.plugin, issue};
 
+    trackAdvancedAnalyticsEvent('issue_details.external_issue_modal_opened', {
+      organization,
+      ...getAnalyticsDataForGroup(group),
+      external_issue_provider: plugin.slug,
+      external_issue_type: 'plugin',
+    });
+
     openModal(
       deps => (
         <PluginActionsModal

+ 19 - 1
static/app/components/group/sentryAppExternalIssueActions.tsx

@@ -13,11 +13,14 @@ import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {
   Group,
+  Organization,
   PlatformExternalIssue,
   SentryAppComponent,
   SentryAppInstallation,
 } from 'sentry/types';
 import {Event} from 'sentry/types/event';
+import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
+import {getAnalyticsDataForGroup} from 'sentry/utils/events';
 import {recordInteraction} from 'sentry/utils/recordSentryAppInteraction';
 import withApi from 'sentry/utils/withApi';
 
@@ -27,6 +30,7 @@ type Props = {
   api: Client;
   event: Event;
   group: Group;
+  organization: Organization;
   sentryAppComponent: SentryAppComponent;
   sentryAppInstallation: SentryAppInstallation;
   disabled?: boolean;
@@ -60,8 +64,15 @@ class SentryAppExternalIssueActions extends Component<Props, State> {
       return;
     }
 
-    const {group, event, sentryAppComponent, sentryAppInstallation} = this.props;
+    const {group, event, organization, sentryAppComponent, sentryAppInstallation} =
+      this.props;
 
+    trackAdvancedAnalyticsEvent('issue_details.external_issue_modal_opened', {
+      organization,
+      ...getAnalyticsDataForGroup(group),
+      external_issue_provider: sentryAppComponent.sentryApp.slug,
+      external_issue_type: 'sentry_app',
+    });
     recordInteraction(
       sentryAppComponent.sentryApp.slug,
       'sentry_app_component_interacted',
@@ -109,6 +120,13 @@ class SentryAppExternalIssueActions extends Component<Props, State> {
   };
 
   onSubmitSuccess = (externalIssue: PlatformExternalIssue) => {
+    const {organization, group, sentryAppComponent} = this.props;
+    trackAdvancedAnalyticsEvent('issue_details.external_issue_modal_opened', {
+      organization,
+      ...getAnalyticsDataForGroup(group),
+      external_issue_provider: sentryAppComponent.sentryApp.slug,
+      external_issue_type: 'sentry_app',
+    });
     this.setState({externalIssue});
   };
 

+ 9 - 0
static/app/plugins/components/issueActions.tsx

@@ -6,6 +6,8 @@ import {t} from 'sentry/locale';
 import PluginComponentBase from 'sentry/plugins/pluginComponentBase';
 import GroupStore from 'sentry/stores/groupStore';
 import {Group, Organization, Plugin, Project} from 'sentry/types';
+import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
+import {getAnalyticsDataForGroup} from 'sentry/utils/events';
 
 type Field = {
   depends?: string[];
@@ -306,6 +308,13 @@ class IssueActions extends PluginComponentBase<Props, State> {
     // a refetch in GroupDetails
     type StaleGroup = Group & {stale?: boolean};
 
+    trackAdvancedAnalyticsEvent('issue_details.external_issue_created', {
+      organization: this.props.organization,
+      ...getAnalyticsDataForGroup(this.props.group),
+      external_issue_provider: this.props.plugin.slug,
+      external_issue_type: 'plugin',
+    });
+
     GroupStore.onUpdateSuccess('', [this.getGroup().id], {stale: true} as StaleGroup);
     this.props.onSuccess && this.props.onSuccess(data);
   }

+ 11 - 0
static/app/utils/analytics/issueAnalyticsEvents.tsx

@@ -1,4 +1,5 @@
 import type {SourceMapProcessingIssueType} from 'sentry/components/events/interfaces/crashContent/exception/useSourceMapDebug';
+import {IntegrationType} from 'sentry/types';
 import type {BaseEventAnalyticsParams} from 'sentry/utils/analytics/workflowAnalyticsEvents';
 import {CommonGroupAnalyticsData} from 'sentry/utils/events';
 
@@ -15,6 +16,11 @@ type SourceMapDebugParam = {
 
 interface GroupEventParams extends CommonGroupAnalyticsData, BaseEventAnalyticsParams {}
 
+interface ExternalIssueParams extends CommonGroupAnalyticsData {
+  external_issue_provider: string;
+  external_issue_type: IntegrationType;
+}
+
 export type IssueEventParameters = {
   'event_cause.dismissed': {};
   'event_cause.docs_clicked': {};
@@ -30,6 +36,8 @@ export type IssueEventParameters = {
   'issue.shared_publicly': {};
   'issue_details.copy_event_link_clicked': GroupEventParams;
   'issue_details.event_details_clicked': GroupEventParams;
+  'issue_details.external_issue_created': ExternalIssueParams;
+  'issue_details.external_issue_modal_opened': ExternalIssueParams;
   'issue_details.header_view_replay_clicked': GroupEventParams;
   'issue_details.performance.autogrouped_siblings_toggle': {};
   'issue_details.performance.hidden_spans_expanded': {};
@@ -232,4 +240,7 @@ export const issueEventMap: Record<IssueEventKey, string | null> = {
   'issue_details.copy_event_link_clicked': 'Issue Details: Copy Event Link Clicked',
   'issue_details.event_details_clicked': 'Issue Details: Full Event Details Clicked',
   'issue_details.header_view_replay_clicked': 'Issue Details: Header View Replay Clicked',
+  'issue_details.external_issue_modal_opened':
+    'Issue Details: External Issue Modal Opened',
+  'issue_details.external_issue_created': 'Issue Details: External Issue Created',
 };