diff --git a/plugins/analytics-module-newrelic-browser/README.md b/plugins/analytics-module-newrelic-browser/README.md index e31496505d..a94a60575e 100644 --- a/plugins/analytics-module-newrelic-browser/README.md +++ b/plugins/analytics-module-newrelic-browser/README.md @@ -74,6 +74,32 @@ app: This plugin supports sending user context to New Relic Browser by providing a User ID. This requires instantiating the `NewRelicBrowser` instance with an `identityApi` instance passed to it, but this is optional. If omitted the plugin will not send user context to New Relic Browser. +By default the user ID is calculated as a SHA-256 hash of the current user's `userEntityRef` as returned by the `identityApi`. To set a +different value, provide a `userIdTransform` function alongside `identityApi` when you instantiate `NewRelicBrowser`. This function will be passed the `userEntityRef` as an argument and should resolve to the value you wish to set as the user ID. For example: + +```typescript +import { + analyticsApiRef, + configApiRef, + identityApiRef, +} from '@backstage/core-plugin-api'; +import { GoogleAnalytics } from '@backstage/plugin-analytics-module-newrelic-browser'; + +export const apis: AnyApiFactory[] = [ + createApiFactory({ + api: analyticsApiRef, + deps: { configApi: configApiRef, identityApi: identityApiRef }, + factory: ({ configApi, identityApi }) => + NewRelicBrowser.fromConfig(configApi, { + identityApi, + userIdTransform: async (userEntityRef: string): Promise => { + return customHashingFunction(userEntityRef); + }, + }), + }), +]; +``` + ## Development If you would like to contribute improvements to this plugin, the easiest way to diff --git a/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts b/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts index 291d5eba68..293c0a9b45 100644 --- a/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts +++ b/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts @@ -43,6 +43,7 @@ export class NewRelicBrowser implements AnalyticsApi { private constructor( options: NewRelicBrowserOptions, identityApi?: IdentityApi, + userIdTransform?: 'sha-256' | ((userEntityRef: string) => Promise), ) { // Configure the New Relic Browser agent const agentOptions = { @@ -76,14 +77,31 @@ export class NewRelicBrowser implements AnalyticsApi { // Initialize the agent this.agent = new BrowserAgent(agentOptions) as unknown as NewRelicAPI; + // Check if identity has been provided if (identityApi) { identityApi.getBackstageIdentity().then(identity => { - this.agent.setUserId(identity.userEntityRef); + if (typeof userIdTransform === 'function') { + userIdTransform(identity.userEntityRef).then(userId => { + this.agent.setUserId(userId); + }); + } else { + this.hash(identity.userEntityRef).then(userId => { + this.agent.setUserId(userId); + }); + } }); } } - static fromConfig(config: Config, options: { identityApi?: IdentityApi }) { + static fromConfig( + config: Config, + options: { + identityApi?: IdentityApi; + userIdTransform?: + | 'sha-256' + | ((userEntityRef: string) => Promise); + }, + ) { const browserOptions: NewRelicBrowserOptions = { endpoint: config.getString('app.analytics.nr.endpoint'), accountId: config.getString('app.analytics.nr.accountId'), @@ -96,7 +114,11 @@ export class NewRelicBrowser implements AnalyticsApi { cookiesEnabled: config.getOptionalBoolean('app.analytics.nr.cookiesEnabled') ?? false, }; - return new NewRelicBrowser(browserOptions, options.identityApi); + return new NewRelicBrowser( + browserOptions, + options.identityApi, + options.userIdTransform, + ); } captureEvent(event: AnalyticsEvent) { @@ -138,4 +160,17 @@ export class NewRelicBrowser implements AnalyticsApi { this.agent.addPageAction(action, customAttributes); } } + + /** + * Simple hash function; relies on web cryptography + the sha-256 algorithm. + * @param value value to be hashed + */ + private async hash(value: string): Promise { + const digest = await window.crypto.subtle.digest( + 'sha-256', + new TextEncoder().encode(value), + ); + const hashArray = Array.from(new Uint8Array(digest)); + return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + } }