mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Pass signed user_hash to Intercom via Rudderstack (#63921)
* move analytics identifiers to backend
* implement hash function
* grab secret from env
* expose and retrieve intercom secret from config
* concat email with appUrl to ensure uniqueness
* revert to just using email
* Revert "revert to just using email"
This reverts commit 8f10f9b1bc.
* add docstring
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { extend } from 'lodash';
|
||||
|
||||
import { OrgRole, rangeUtil, WithAccessControlMetadata } from '@grafana/data';
|
||||
import { AnalyticsSettings, OrgRole, rangeUtil, WithAccessControlMetadata } from '@grafana/data';
|
||||
import { featureEnabled, getBackendSrv } from '@grafana/runtime';
|
||||
import { AccessControlAction, UserPermission } from 'app/types';
|
||||
import { CurrentUserInternal } from 'app/types/config';
|
||||
@@ -28,6 +28,7 @@ export class User implements Omit<CurrentUserInternal, 'lightTheme'> {
|
||||
helpFlags1: number;
|
||||
hasEditPermissionInFolders: boolean;
|
||||
permissions?: UserPermission;
|
||||
analytics: AnalyticsSettings;
|
||||
fiscalYearStartMonth: number;
|
||||
|
||||
constructor() {
|
||||
@@ -51,6 +52,9 @@ export class User implements Omit<CurrentUserInternal, 'lightTheme'> {
|
||||
this.language = '';
|
||||
this.weekStart = '';
|
||||
this.gravatarUrl = '';
|
||||
this.analytics = {
|
||||
identifier: '',
|
||||
};
|
||||
|
||||
if (config.bootData.user) {
|
||||
extend(this, config.bootData.user);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CurrentUserDTO } from '@grafana/data';
|
||||
import { EchoBackend, EchoEventType, PageviewEchoEvent } from '@grafana/runtime';
|
||||
|
||||
import { getUserIdentifier, loadScript } from '../../utils';
|
||||
import { loadScript } from '../../utils';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -34,7 +34,7 @@ export class GA4EchoBackend implements EchoBackend<PageviewEchoEvent, GA4EchoBac
|
||||
};
|
||||
|
||||
if (options.user) {
|
||||
configOptions.user_id = getUserIdentifier(options.user);
|
||||
configOptions.user_id = options.user.analytics.identifier;
|
||||
}
|
||||
|
||||
this.googleAnalytics4SendManualPageViews = options.googleAnalytics4SendManualPageViews;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { identify, load, page, track } from 'rudder-sdk-js'; // SDK is loaded dynamically from config, so we only import types from the SDK package
|
||||
import type { apiOptions, identify, load, page, track } from 'rudder-sdk-js'; // SDK is loaded dynamically from config, so we only import types from the SDK package
|
||||
|
||||
import { CurrentUserDTO } from '@grafana/data';
|
||||
import {
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
PageviewEchoEvent,
|
||||
} from '@grafana/runtime';
|
||||
|
||||
import { getUserIdentifier, loadScript } from '../../utils';
|
||||
import { loadScript } from '../../utils';
|
||||
|
||||
interface Rudderstack {
|
||||
identify: typeof identify;
|
||||
@@ -70,13 +70,24 @@ export class RudderstackBackend implements EchoBackend<PageviewEchoEvent, Rudder
|
||||
window.rudderanalytics?.load?.(options.writeKey, options.dataPlaneUrl, { configUrl: options.configUrl });
|
||||
|
||||
if (options.user) {
|
||||
const identifier = getUserIdentifier(options.user);
|
||||
const { identifier, intercomIdentifier } = options.user.analytics;
|
||||
const apiOptions: apiOptions = {};
|
||||
|
||||
window.rudderanalytics?.identify?.(identifier, {
|
||||
email: options.user.email,
|
||||
orgId: options.user.orgId,
|
||||
language: options.user.language,
|
||||
});
|
||||
if (intercomIdentifier) {
|
||||
apiOptions.Intercom = {
|
||||
user_hash: intercomIdentifier,
|
||||
};
|
||||
}
|
||||
|
||||
window.rudderanalytics?.identify?.(
|
||||
identifier,
|
||||
{
|
||||
email: options.user.email,
|
||||
orgId: options.user.orgId,
|
||||
language: options.user.language,
|
||||
},
|
||||
apiOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { CurrentUserDTO, OrgRole } from '@grafana/data';
|
||||
|
||||
import { getUserIdentifier } from './utils';
|
||||
|
||||
const baseUser: CurrentUserDTO = {
|
||||
isSignedIn: true,
|
||||
id: 3,
|
||||
login: 'myUsername',
|
||||
email: 'email@example.com',
|
||||
name: 'My Name',
|
||||
theme: 'dark',
|
||||
lightTheme: false, // deprecated
|
||||
orgCount: 1,
|
||||
orgId: 1,
|
||||
orgName: 'Main Org.',
|
||||
orgRole: OrgRole.Admin,
|
||||
isGrafanaAdmin: false,
|
||||
gravatarUrl: '/avatar/abc-123',
|
||||
timezone: 'browser',
|
||||
weekStart: 'browser',
|
||||
locale: 'en-AU',
|
||||
language: 'en-US',
|
||||
externalUserId: '',
|
||||
};
|
||||
|
||||
const gcomUser: CurrentUserDTO = {
|
||||
...baseUser,
|
||||
externalUserId: 'abc-123',
|
||||
};
|
||||
|
||||
describe('echo getUserIdentifier', () => {
|
||||
it('should return the external user ID (gcom ID) if available', () => {
|
||||
const id = getUserIdentifier(gcomUser);
|
||||
expect(id).toBe('abc-123');
|
||||
});
|
||||
|
||||
it('should fall back to the email address', () => {
|
||||
const id = getUserIdentifier(baseUser);
|
||||
expect(id).toBe('email@example.com');
|
||||
});
|
||||
});
|
||||
@@ -1,19 +1,5 @@
|
||||
import { CurrentUserDTO } from '@grafana/data';
|
||||
import { attachDebugger, createLogger } from '@grafana/ui';
|
||||
|
||||
/**
|
||||
* Returns an opaque identifier for a user, for reporting purposes.
|
||||
* Because this is for use when reporting across multiple Grafana installations
|
||||
* It cannot simply be user.id because that's not unique across two installations.
|
||||
*/
|
||||
export function getUserIdentifier(user: CurrentUserDTO) {
|
||||
if (user.externalUserId.length) {
|
||||
return user.externalUserId;
|
||||
}
|
||||
|
||||
return user.email;
|
||||
}
|
||||
|
||||
export function loadScript(url: string, async = false) {
|
||||
return new Promise((resolve) => {
|
||||
const script = document.createElement('script');
|
||||
|
||||
Reference in New Issue
Block a user