advancedAnalytics.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import * as qs from 'query-string';
  2. import {Organization} from 'app/types';
  3. import {trackAnalyticsEvent} from 'app/utils/analytics';
  4. import {growthEventMap, GrowthEventParameters} from 'app/utils/growthAnalyticsEvents';
  5. import {uniqueId} from 'app/utils/guid';
  6. import {
  7. integrationEventMap,
  8. IntegrationEventParameters,
  9. } from 'app/utils/integrationEvents';
  10. const ANALYTICS_SESSION = 'ANALYTICS_SESSION';
  11. export const startAnalyticsSession = () => {
  12. const sessionId = uniqueId();
  13. window.sessionStorage.setItem(ANALYTICS_SESSION, sessionId);
  14. return sessionId;
  15. };
  16. export const clearAnalyticsSession = () => {
  17. window.sessionStorage.removeItem(ANALYTICS_SESSION);
  18. };
  19. export const getAnalyticsSessionId = () =>
  20. window.sessionStorage.getItem(ANALYTICS_SESSION);
  21. const hasAnalyticsDebug = () => window.localStorage.getItem('DEBUG_ANALYTICS') === '1';
  22. export type EventParameters = IntegrationEventParameters & GrowthEventParameters;
  23. const allEventMap = {...integrationEventMap, ...growthEventMap};
  24. type AnalyticsKey = keyof EventParameters;
  25. /**
  26. * Tracks an event for analytics.
  27. * Must be tied to an organization.
  28. * Uses the current session ID or generates a new one if startSession == true.
  29. * An analytics session corresponds to a single action funnel such as installation.
  30. * Tracking by session allows us to track individual funnel attempts for a single user.
  31. */
  32. export function trackAdvancedAnalyticsEvent<T extends AnalyticsKey>(
  33. eventKey: T,
  34. analyticsParams: EventParameters[T],
  35. org: Organization | null, // if org is undefined, event won't be recorded
  36. options?: {startSession: boolean},
  37. mapValuesFn?: (params: object) => object
  38. ) {
  39. try {
  40. const {startSession} = options || {};
  41. let sessionId = startSession ? startAnalyticsSession() : getAnalyticsSessionId();
  42. const eventName = allEventMap[eventKey];
  43. //we should always have a session id but if we don't, we should generate one
  44. if (hasAnalyticsDebug() && !sessionId) {
  45. // eslint-disable-next-line no-console
  46. console.warn(`analytics_session_id absent from event ${eventKey}`);
  47. sessionId = startAnalyticsSession();
  48. }
  49. let custom_referrer: string | undefined;
  50. try {
  51. //pull the referrer from the query parameter of the page
  52. const {referrer} = qs.parse(window.location.search) || {};
  53. if (typeof referrer === 'string') {
  54. // Amplitude has its own referrer which inteferes with our custom referrer
  55. custom_referrer = referrer;
  56. }
  57. } catch {
  58. // ignore if this fails to parse
  59. // this can happen if we have an invalid query string
  60. // e.g. unencoded "%"
  61. }
  62. //if org is null, we want organization_id to be null
  63. const organization_id = org ? org.id : org;
  64. let params = {
  65. eventKey,
  66. eventName,
  67. analytics_session_id: sessionId,
  68. organization_id,
  69. role: org?.role,
  70. custom_referrer,
  71. ...analyticsParams,
  72. };
  73. if (mapValuesFn) {
  74. params = mapValuesFn(params) as any;
  75. }
  76. //could put this into a debug method or for the main trackAnalyticsEvent event
  77. if (hasAnalyticsDebug()) {
  78. // eslint-disable-next-line no-console
  79. console.log('trackAdvancedAnalytics', params);
  80. }
  81. trackAnalyticsEvent(params);
  82. } catch (e) {
  83. // eslint-disable-next-line no-console
  84. console.error('Error tracking analytics event', e);
  85. }
  86. }