Browse Source

ref(analytics): Move integration analytics into submodules (#35222)

This PR organizes the integration analytics into a directory. They were getting extremely long, and with the linting rule to sort dictionary keys, it was hard to parse which ones are related to one another.

This PR introduces some organization changes to the integration analytics. The current file is pretty long and the new linting rule which sorts keys makes it hard to group analytics that map to similar payloads or features. As a prototype, I've created submodules for StacktraceLink events and Codeowners events to show what I think is a better pattern.

The added benefit of this new pattern is that when using the analytics function, you don't have to rewrite (and possibly typo) the analytics key. With it inside an enum, it forces the user to use the exact correct key. However, to be backwards compatible (and avoid changing a bunch of files in this PR, I added a literal type so that using a string literal is still valid. I changed one example in stacktraceLink.tsx to show what I'd like our analytics to look like in the future.

No functional changes or new event types in this PR.
Leander Rodrigues 2 years ago
parent
commit
7a3d1ca20e

+ 2 - 1
static/app/components/events/interfaces/frame/stacktraceLink.tsx

@@ -19,6 +19,7 @@ import {
   RepositoryProjectPathConfigWithIntegration,
 } from 'sentry/types';
 import {Event} from 'sentry/types/event';
+import {StacktraceLinkEvents} from 'sentry/utils/analytics/integrations/stacktraceLinkAnalyticsEvents';
 import handleXhrErrorResponse from 'sentry/utils/handleXhrErrorResponse';
 import {
   getIntegrationIcon,
@@ -165,7 +166,7 @@ class StacktraceLink extends AsyncComponent<Props, State> {
     const provider = this.config?.provider;
     if (provider) {
       trackIntegrationAnalytics(
-        'integrations.stacktrace_link_clicked',
+        StacktraceLinkEvents.OPEN_LINK,
         {
           view: 'stacktrace_issue_details',
           provider: provider.key,

+ 1 - 1
static/app/plugins/components/settings.tsx

@@ -7,7 +7,7 @@ import LoadingIndicator from 'sentry/components/loadingIndicator';
 import {t, tct} from 'sentry/locale';
 import {Organization, Plugin, Project} from 'sentry/types';
 import {parseRepo} from 'sentry/utils';
-import {IntegrationAnalyticsKey} from 'sentry/utils/analytics/integrationAnalyticsEvents';
+import {IntegrationAnalyticsKey} from 'sentry/utils/analytics/integrations';
 import {trackIntegrationAnalytics} from 'sentry/utils/integrationUtil';
 
 type Props = {

+ 21 - 0
static/app/utils/analytics/integrations/codeownersAnalyticsEvents.ts

@@ -0,0 +1,21 @@
+import {IntegrationView} from './index';
+
+export enum CodeownersEvents {
+  SETUP_CTA = 'integrations.code_owners_cta_setup_clicked',
+  DOCS_CTA = 'integrations.code_owners_cta_docs_clicked',
+  SHOW_PROMPT = 'integrations.show_code_owners_prompt',
+  DISMISS_PROMPT = 'integrations.dismissed_code_owners_prompt',
+}
+// This type allows analytics functions to use the string literal or enum.KEY
+type CodeownersEventsLiterals = `${CodeownersEvents}`;
+
+export type CodeownersEventParameters = {
+  [key in CodeownersEventsLiterals]: {project_id: string} & IntegrationView;
+};
+
+export const codeownersEventMap: Record<CodeownersEventsLiterals, string> = {
+  [CodeownersEvents.SETUP_CTA]: 'Integrations: Code Owners CTA Setup Clicked',
+  [CodeownersEvents.DOCS_CTA]: 'Integrations: Code Owners CTA Docs Clicked',
+  [CodeownersEvents.SHOW_PROMPT]: 'Integrations: Show Code Owners Prompt',
+  [CodeownersEvents.DISMISS_PROMPT]: 'Integrations: Dismissed Code Owners Prompt',
+};

+ 18 - 53
static/app/utils/analytics/integrationAnalyticsEvents.tsx → static/app/utils/analytics/integrations/index.ts

@@ -1,8 +1,12 @@
-import {StacktraceErrorMessage} from 'sentry/components/events/interfaces/frame/stacktraceLink';
-import {IntegrationType, PlatformType, SentryAppStatus} from 'sentry/types';
+import {IntegrationType, SentryAppStatus} from 'sentry/types';
 
-// define the various event payloads
-type View = {
+import {codeownersEventMap, CodeownersEventParameters} from './codeownersAnalyticsEvents';
+import {
+  stacktraceLinkEventMap,
+  StacktraceLinkEventParameters,
+} from './stacktraceLinkAnalyticsEvents';
+
+export type IntegrationView = {
   view?:
     | 'external_install'
     | 'legacy_integrations'
@@ -23,27 +27,20 @@ type SingleIntegrationEventParams = {
   integration_status?: SentryAppStatus;
   integration_tab?: 'configurations' | 'overview';
   plan?: string;
-} & View;
+} & IntegrationView;
 
 type MultipleIntegrationsEventParams = {
   integrations_installed: number;
-} & View;
+} & IntegrationView;
 
 type IntegrationSearchEventParams = {
   num_results: number;
   search_term: string;
-} & View;
+} & IntegrationView;
 
 type IntegrationCategorySelectEventParams = {
   category: string;
-} & View;
-
-type IntegrationStacktraceLinkEventParams = {
-  error_reason?: StacktraceErrorMessage;
-  platform?: PlatformType;
-  provider?: string;
-  setup_type?: 'automatic' | 'manual';
-} & View;
+} & IntegrationView;
 
 type IntegrationServerlessFunctionsViewedParams = {
   num_functions: number;
@@ -57,23 +54,16 @@ type IntegrationInstallationInputValueChangeEventParams = {
   field_name: string;
 } & SingleIntegrationEventParams;
 
-type IntegrationCodeOwnersEventParams = {
-  project_id: string;
-} & View;
-// define the event key to payload mappings
+// Event key to payload mappings
 export type IntegrationEventParameters = {
   'integrations.cloudformation_link_clicked': SingleIntegrationEventParams;
   'integrations.code_mappings_viewed': SingleIntegrationEventParams;
-  'integrations.code_owners_cta_docs_clicked': IntegrationCodeOwnersEventParams;
-  'integrations.code_owners_cta_setup_clicked': IntegrationCodeOwnersEventParams;
   'integrations.config_saved': SingleIntegrationEventParams;
   'integrations.details_viewed': SingleIntegrationEventParams;
   'integrations.directory_category_selected': IntegrationCategorySelectEventParams;
   'integrations.directory_item_searched': IntegrationSearchEventParams;
   'integrations.disabled': SingleIntegrationEventParams;
-  'integrations.dismissed_code_owners_prompt': IntegrationCodeOwnersEventParams;
   'integrations.enabled': SingleIntegrationEventParams;
-  // for an individual configuration
   'integrations.index_viewed': MultipleIntegrationsEventParams;
   'integrations.install_modal_opened': SingleIntegrationEventParams;
   'integrations.installation_complete': SingleIntegrationEventParams;
@@ -82,28 +72,20 @@ export type IntegrationEventParameters = {
   'integrations.integration_tab_clicked': SingleIntegrationEventParams;
   'integrations.integration_viewed': SingleIntegrationEventParams;
   'integrations.plugin_add_to_project_clicked': SingleIntegrationEventParams;
-  'integrations.reconfigure_stacktrace_setup': IntegrationStacktraceLinkEventParams;
   'integrations.request_install': SingleIntegrationEventParams;
   'integrations.resolve_now_clicked': SingleIntegrationEventParams;
   'integrations.serverless_function_action': IntegrationServerlessFunctionActionParams;
   'integrations.serverless_functions_viewed': IntegrationServerlessFunctionsViewedParams;
-  'integrations.show_code_owners_prompt': IntegrationCodeOwnersEventParams;
-  'integrations.stacktrace_complete_setup': IntegrationStacktraceLinkEventParams;
-  'integrations.stacktrace_docs_clicked': IntegrationStacktraceLinkEventParams;
-  'integrations.stacktrace_link_clicked': IntegrationStacktraceLinkEventParams;
-  'integrations.stacktrace_link_cta_dismissed': IntegrationStacktraceLinkEventParams;
-  'integrations.stacktrace_manual_option_clicked': IntegrationStacktraceLinkEventParams;
-  'integrations.stacktrace_start_setup': IntegrationStacktraceLinkEventParams;
-  'integrations.stacktrace_submit_config': IntegrationStacktraceLinkEventParams;
   'integrations.switch_manual_sdk_setup': SingleIntegrationEventParams;
   'integrations.uninstall_clicked': SingleIntegrationEventParams;
   'integrations.uninstall_completed': SingleIntegrationEventParams;
   'integrations.upgrade_plan_modal_opened': SingleIntegrationEventParams;
-};
+} & CodeownersEventParameters &
+  StacktraceLinkEventParameters;
 
 export type IntegrationAnalyticsKey = keyof IntegrationEventParameters;
 
-// define the event key to event name mappings
+// Event key to name mappings
 export const integrationEventMap: Record<IntegrationAnalyticsKey, string> = {
   'integrations.upgrade_plan_modal_opened': 'Integrations: Upgrade Plan Modal Opened',
   'integrations.install_modal_opened': 'Integrations: Install Modal Opened',
@@ -125,29 +107,12 @@ export const integrationEventMap: Record<IntegrationAnalyticsKey, string> = {
   'integrations.index_viewed': 'Integrations: Index Page Viewed',
   'integrations.directory_item_searched': 'Integrations: Directory Item Searched',
   'integrations.directory_category_selected': 'Integrations: Directory Category Selected',
-  'integrations.stacktrace_link_cta_dismissed':
-    'Integrations: Stacktrace Link CTA Dismissed',
-  'integrations.stacktrace_start_setup': 'Integrations: Stacktrace Start Setup',
-  'integrations.stacktrace_submit_config': 'Integrations: Stacktrace Submit Config',
-  'integrations.stacktrace_complete_setup': 'Integrations: Stacktrace Complete Setup',
-  'integrations.stacktrace_manual_option_clicked':
-    'Integrations: Stacktrace Manual Option Clicked',
-  'integrations.stacktrace_link_clicked': 'Integrations: Stacktrace Link Clicked',
-  'integrations.reconfigure_stacktrace_setup':
-    'Integrations: Reconfigure Stacktrace Setup',
-  'integrations.stacktrace_docs_clicked': 'Integrations: Stacktrace Docs Clicked',
-
   'integrations.serverless_functions_viewed': 'Integrations: Serverless Functions Viewed',
   'integrations.installation_input_value_changed':
     'Integrations: Installation Input Value Changed',
   'integrations.serverless_function_action': 'Integrations: Serverless Function Action',
   'integrations.cloudformation_link_clicked': 'Integrations: CloudFormation Link Clicked',
   'integrations.switch_manual_sdk_setup': 'Integrations: Switch Manual SDK Setup',
-  'integrations.code_owners_cta_setup_clicked':
-    'Integrations: Code Owners CTA Setup Clicked',
-  'integrations.code_owners_cta_docs_clicked':
-    'Integrations: Code Owners CTA Setup Clicked',
-  'integrations.show_code_owners_prompt': 'Integrations: Show Code Owners Prompt',
-  'integrations.dismissed_code_owners_prompt':
-    'Integrations: Dismissed Code Owners Prompt',
+  ...codeownersEventMap,
+  ...stacktraceLinkEventMap,
 };

+ 38 - 0
static/app/utils/analytics/integrations/stacktraceLinkAnalyticsEvents.ts

@@ -0,0 +1,38 @@
+import {StacktraceErrorMessage} from 'sentry/components/events/interfaces/frame/stacktraceLink';
+import {PlatformType} from 'sentry/types';
+
+import {IntegrationView} from './index';
+
+export enum StacktraceLinkEvents {
+  RECONFIGURE_SETUP = 'integrations.reconfigure_stacktrace_setup',
+  COMPLETE_SETUP = 'integrations.stacktrace_complete_setup',
+  OPEN_DOCS = 'integrations.stacktrace_docs_clicked',
+  OPEN_LINK = 'integrations.stacktrace_link_clicked',
+  DISMISS_CTA = 'integrations.stacktrace_link_cta_dismissed',
+  MANUAL_OPTION = 'integrations.stacktrace_manual_option_clicked',
+  START_SETUP = 'integrations.stacktrace_start_setup',
+  SUBMIT = 'integrations.stacktrace_submit_config',
+}
+
+// This type allows analytics functions to use the string literal or enum.KEY
+type StacktraceLinkEventsLiterals = `${StacktraceLinkEvents}`;
+
+export type StacktraceLinkEventParameters = {
+  [key in StacktraceLinkEventsLiterals]: {
+    error_reason?: StacktraceErrorMessage;
+    platform?: PlatformType;
+    provider?: string;
+    setup_type?: 'automatic' | 'manual';
+  } & IntegrationView;
+};
+
+export const stacktraceLinkEventMap: Record<StacktraceLinkEventsLiterals, string> = {
+  [StacktraceLinkEvents.RECONFIGURE_SETUP]: 'Integrations: Reconfigure Stacktrace Setup',
+  [StacktraceLinkEvents.COMPLETE_SETUP]: 'Integrations: Stacktrace Complete Setup',
+  [StacktraceLinkEvents.OPEN_DOCS]: 'Integrations: Stacktrace Docs Clicked',
+  [StacktraceLinkEvents.OPEN_LINK]: 'Integrations: Stacktrace Link Clicked',
+  [StacktraceLinkEvents.DISMISS_CTA]: 'Integrations: Stacktrace Link CTA Dismissed',
+  [StacktraceLinkEvents.MANUAL_OPTION]: 'Integrations: Stacktrace Manual Option Clicked',
+  [StacktraceLinkEvents.START_SETUP]: 'Integrations: Stacktrace Start Setup',
+  [StacktraceLinkEvents.SUBMIT]: 'Integrations: Stacktrace Submit Config',
+};

+ 1 - 1
static/app/utils/integrationUtil.tsx

@@ -30,7 +30,7 @@ import {Hooks} from 'sentry/types/hooks';
 import {
   integrationEventMap,
   IntegrationEventParameters,
-} from 'sentry/utils/analytics/integrationAnalyticsEvents';
+} from 'sentry/utils/analytics/integrations';
 import makeAnalyticsFunction from 'sentry/utils/analytics/makeAnalyticsFunction';
 
 const mapIntegrationParams = analyticsParams => {

+ 1 - 1
static/app/views/integrationOrganizationLink.tsx

@@ -14,7 +14,7 @@ import LoadingIndicator from 'sentry/components/loadingIndicator';
 import NarrowLayout from 'sentry/components/narrowLayout';
 import {t, tct} from 'sentry/locale';
 import {Integration, IntegrationProvider, Organization} from 'sentry/types';
-import {IntegrationAnalyticsKey} from 'sentry/utils/analytics/integrationAnalyticsEvents';
+import {IntegrationAnalyticsKey} from 'sentry/utils/analytics/integrations';
 import {
   getIntegrationFeatureGate,
   trackIntegrationAnalytics,

+ 1 - 1
static/app/views/organizationIntegrations/abstractIntegrationDetailedView.tsx

@@ -23,7 +23,7 @@ import {
 import {
   IntegrationAnalyticsKey,
   IntegrationEventParameters,
-} from 'sentry/utils/analytics/integrationAnalyticsEvents';
+} from 'sentry/utils/analytics/integrations';
 import {
   getCategories,
   getIntegrationFeatureGate,

+ 1 - 1
static/app/views/organizationIntegrations/installedIntegration.tsx

@@ -12,7 +12,7 @@ import {IconDelete, IconSettings, IconWarning} from 'sentry/icons';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
 import {Integration, IntegrationProvider, ObjectStatus, Organization} from 'sentry/types';
-import {IntegrationAnalyticsKey} from 'sentry/utils/analytics/integrationAnalyticsEvents';
+import {IntegrationAnalyticsKey} from 'sentry/utils/analytics/integrations';
 
 import AddIntegrationButton from './addIntegrationButton';
 import IntegrationItem from './integrationItem';

+ 1 - 1
static/app/views/organizationIntegrations/installedPlugin.tsx

@@ -22,7 +22,7 @@ import {
   PluginNoProject,
   PluginProjectItem,
 } from 'sentry/types';
-import {IntegrationAnalyticsKey} from 'sentry/utils/analytics/integrationAnalyticsEvents';
+import {IntegrationAnalyticsKey} from 'sentry/utils/analytics/integrations';
 import withApi from 'sentry/utils/withApi';
 
 type Props = {