Less magical custom hash mechanism.

Signed-off-by: Eric Peterson <i.am@eric.pe>
This commit is contained in:
Eric Peterson
2022-01-30 18:51:18 +01:00
parent 35d1d675b5
commit 919a77845d
5 changed files with 45 additions and 35 deletions
@@ -270,10 +270,8 @@ describe('GoogleAnalytics', () => {
});
});
it('sets pre-hashed userId when PrivateUser entity ref is provided', async () => {
(identityApi.getBackstageIdentity as jest.Mock).mockResolvedValueOnce({
userEntityRef: 'PrivateUser:hashed/s0m3hash3dvalu3',
});
it('set custom-hashed userId when userIdTransform is provided', async () => {
const userIdTransform = jest.fn().mockResolvedValue('s0m3hash3dvalu3');
const optionalConfig = new ConfigReader({
app: {
analytics: {
@@ -281,7 +279,10 @@ describe('GoogleAnalytics', () => {
},
},
});
const api = GoogleAnalytics.fromConfig(optionalConfig, { identityApi });
const api = GoogleAnalytics.fromConfig(optionalConfig, {
identityApi,
userIdTransform,
});
api.captureEvent({
action: 'navigate',
subject: '/',
@@ -297,6 +298,7 @@ describe('GoogleAnalytics', () => {
expect(setData).toMatchObject({
userId: 's0m3hash3dvalu3',
});
expect(userIdTransform).toHaveBeenCalledWith('User:default/someone');
});
it('does not set userId when identityApi is provided and ga.identity is explicitly disabled', async () => {
@@ -15,7 +15,6 @@
*/
import ReactGA from 'react-ga';
import { parseEntityRef } from '@backstage/catalog-model';
import {
AnalyticsApi,
AnalyticsContextValue,
@@ -39,6 +38,7 @@ type CustomDimensionOrMetricConfig = {
*/
export class GoogleAnalytics implements AnalyticsApi {
private readonly cdmConfig: CustomDimensionOrMetricConfig[];
private customUserIdTransform?: (userEntityRef: string) => Promise<string>;
private readonly capture: DeferredCapture;
/**
@@ -46,6 +46,7 @@ export class GoogleAnalytics implements AnalyticsApi {
*/
private constructor(options: {
identityApi?: IdentityApi;
userIdTransform?: 'sha-256' | ((userEntityRef: string) => Promise<string>);
cdmConfig: CustomDimensionOrMetricConfig[];
identity: string;
trackingId: string;
@@ -58,6 +59,7 @@ export class GoogleAnalytics implements AnalyticsApi {
identity,
trackingId,
identityApi,
userIdTransform = 'sha-256',
scriptSrc,
testMode,
debug,
@@ -76,6 +78,10 @@ export class GoogleAnalytics implements AnalyticsApi {
// If identity is required, defer event capture until identity is known.
this.capture = new DeferredCapture({ defer: identity === 'required' });
// Allow custom userId transformation.
this.customUserIdTransform =
typeof userIdTransform === 'function' ? userIdTransform : undefined;
// Capture user only when explicitly enabled and provided.
if (identity !== 'disabled' && identityApi) {
this.setUserFrom(identityApi);
@@ -87,7 +93,12 @@ export class GoogleAnalytics implements AnalyticsApi {
*/
static fromConfig(
config: Config,
options: { identityApi?: IdentityApi } = {},
options: {
identityApi?: IdentityApi;
userIdTransform?:
| 'sha-256'
| ((userEntityRef: string) => Promise<string>);
} = {},
) {
// Get all necessary configuration.
const trackingId = config.getString('app.analytics.ga.trackingId');
@@ -220,11 +231,9 @@ export class GoogleAnalytics implements AnalyticsApi {
* Returns a PII-free user ID for use in Google Analytics.
*/
private getPrivateUserId(userEntityRef: string): Promise<string> {
const entity = parseEntityRef(userEntityRef);
// Mechanism allowing integrators to provide their own hashed values.
if (entity.kind === 'PrivateUser') {
return Promise.resolve(entity.name);
// Allow integrators to provide their own hashing transformer.
if (this.customUserIdTransform) {
return this.customUserIdTransform(userEntityRef);
}
return this.hash(userEntityRef);