From f586f142be41096d6f25369876f6d68330732819 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Thu, 5 Jan 2023 09:49:55 -0500 Subject: [PATCH] add an extra identifier on anonymized duplicate events (#17080) --- app/scripts/controllers/metametrics.js | 14 +- app/scripts/controllers/metametrics.test.js | 192 ++++++++++++++++++++ 2 files changed, 201 insertions(+), 5 deletions(-) diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index a7fb97829..3edffbec1 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -35,15 +35,18 @@ const defaultCaptureException = (err) => { // The function is used to build a unique messageId for segment messages // It uses actionId and uniqueIdentifier from event if present const buildUniqueMessageId = (args) => { - let messageId = ''; + const messageIdParts = []; if (args.uniqueIdentifier) { - messageId += `${args.uniqueIdentifier}-`; + messageIdParts.push(args.uniqueIdentifier); } if (args.actionId) { - messageId += args.actionId; + messageIdParts.push(args.actionId); } - if (messageId.length) { - return messageId; + if (messageIdParts.length && args.isDuplicateAnonymizedEvent) { + messageIdParts.push('0x000'); + } + if (messageIdParts.length) { + return messageIdParts.join('-'); } return generateRandomId(); }; @@ -530,6 +533,7 @@ export default class MetaMetricsController { this._buildEventPayload({ ...payload, properties: combinedProperties, + isDuplicateAnonymizedEvent: true, }), { ...options, excludeMetaMetricsId: true }, ), diff --git a/app/scripts/controllers/metametrics.test.js b/app/scripts/controllers/metametrics.test.js index be9aed29d..fc5ce0f6d 100644 --- a/app/scripts/controllers/metametrics.test.js +++ b/app/scripts/controllers/metametrics.test.js @@ -159,6 +159,7 @@ describe('MetaMetricsController', function () { clock = sinon.useFakeTimers(now.getTime()); sinon.stub(Utils, 'generateRandomId').returns('DUMMY_RANDOM_ID'); }); + describe('constructor', function () { it('should properly initialize', function () { const mock = sinon.mock(segment); @@ -677,6 +678,197 @@ describe('MetaMetricsController', function () { }); }); + describe('deterministic messageId', function () { + it('should use the actionId as messageId when provided', function () { + const metaMetricsController = getMetaMetricsController(); + const spy = sinon.spy(segment, 'track'); + metaMetricsController.submitEvent({ + event: 'Fake Event', + category: 'Unit Test', + properties: { foo: 'bar' }, + actionId: '0x001', + }); + assert.ok(spy.calledOnce); + assert.ok( + spy.calledWith({ + event: 'Fake Event', + userId: TEST_META_METRICS_ID, + context: DEFAULT_TEST_CONTEXT, + properties: { + foo: 'bar', + ...DEFAULT_EVENT_PROPERTIES, + }, + messageId: '0x001', + timestamp: new Date(), + }), + ); + }); + + it('should append 0x000 to the actionId of anonymized event when tracking sensitiveProperties', function () { + const metaMetricsController = getMetaMetricsController(); + const spy = sinon.spy(segment, 'track'); + metaMetricsController.submitEvent({ + event: 'Fake Event', + category: 'Unit Test', + sensitiveProperties: { foo: 'bar' }, + actionId: '0x001', + }); + assert.ok(spy.calledTwice); + + assert.ok( + spy.calledWith({ + event: 'Fake Event', + anonymousId: METAMETRICS_ANONYMOUS_ID, + context: DEFAULT_TEST_CONTEXT, + properties: { + foo: 'bar', + ...DEFAULT_EVENT_PROPERTIES, + }, + messageId: '0x001-0x000', + timestamp: new Date(), + }), + ); + assert.ok( + spy.calledWith({ + event: 'Fake Event', + userId: TEST_META_METRICS_ID, + context: DEFAULT_TEST_CONTEXT, + properties: { + ...DEFAULT_EVENT_PROPERTIES, + }, + messageId: '0x001', + timestamp: new Date(), + }), + ); + }); + + it('should use the uniqueIdentifier as messageId when provided', function () { + const metaMetricsController = getMetaMetricsController(); + const spy = sinon.spy(segment, 'track'); + metaMetricsController.submitEvent({ + event: 'Fake Event', + category: 'Unit Test', + properties: { foo: 'bar' }, + uniqueIdentifier: 'transaction-submitted-0000', + }); + assert.ok(spy.calledOnce); + assert.ok( + spy.calledWith({ + event: 'Fake Event', + userId: TEST_META_METRICS_ID, + context: DEFAULT_TEST_CONTEXT, + properties: { + foo: 'bar', + ...DEFAULT_EVENT_PROPERTIES, + }, + messageId: 'transaction-submitted-0000', + timestamp: new Date(), + }), + ); + }); + + it('should append 0x000 to the uniqueIdentifier of anonymized event when tracking sensitiveProperties', function () { + const metaMetricsController = getMetaMetricsController(); + const spy = sinon.spy(segment, 'track'); + metaMetricsController.submitEvent({ + event: 'Fake Event', + category: 'Unit Test', + sensitiveProperties: { foo: 'bar' }, + uniqueIdentifier: 'transaction-submitted-0000', + }); + assert.ok(spy.calledTwice); + assert.ok( + spy.calledWith({ + event: 'Fake Event', + anonymousId: METAMETRICS_ANONYMOUS_ID, + context: DEFAULT_TEST_CONTEXT, + properties: { + foo: 'bar', + ...DEFAULT_EVENT_PROPERTIES, + }, + messageId: 'transaction-submitted-0000-0x000', + timestamp: new Date(), + }), + ); + assert.ok( + spy.calledWith({ + event: 'Fake Event', + userId: TEST_META_METRICS_ID, + context: DEFAULT_TEST_CONTEXT, + properties: { + ...DEFAULT_EVENT_PROPERTIES, + }, + messageId: 'transaction-submitted-0000', + timestamp: new Date(), + }), + ); + }); + + it('should combine the uniqueIdentifier and actionId as messageId when both provided', function () { + const metaMetricsController = getMetaMetricsController(); + const spy = sinon.spy(segment, 'track'); + metaMetricsController.submitEvent({ + event: 'Fake Event', + category: 'Unit Test', + properties: { foo: 'bar' }, + actionId: '0x001', + uniqueIdentifier: 'transaction-submitted-0000', + }); + assert.ok(spy.calledOnce); + assert.ok( + spy.calledWith({ + event: 'Fake Event', + userId: TEST_META_METRICS_ID, + context: DEFAULT_TEST_CONTEXT, + properties: { + foo: 'bar', + ...DEFAULT_EVENT_PROPERTIES, + }, + messageId: 'transaction-submitted-0000-0x001', + timestamp: new Date(), + }), + ); + }); + + it('should append 0x000 to the combined uniqueIdentifier and actionId of anonymized event when tracking sensitiveProperties', function () { + const metaMetricsController = getMetaMetricsController(); + const spy = sinon.spy(segment, 'track'); + metaMetricsController.submitEvent({ + event: 'Fake Event', + category: 'Unit Test', + sensitiveProperties: { foo: 'bar' }, + actionId: '0x001', + uniqueIdentifier: 'transaction-submitted-0000', + }); + assert.ok(spy.calledTwice); + assert.ok( + spy.calledWith({ + event: 'Fake Event', + anonymousId: METAMETRICS_ANONYMOUS_ID, + context: DEFAULT_TEST_CONTEXT, + properties: { + foo: 'bar', + ...DEFAULT_EVENT_PROPERTIES, + }, + messageId: 'transaction-submitted-0000-0x001-0x000', + timestamp: new Date(), + }), + ); + assert.ok( + spy.calledWith({ + event: 'Fake Event', + userId: TEST_META_METRICS_ID, + context: DEFAULT_TEST_CONTEXT, + properties: { + ...DEFAULT_EVENT_PROPERTIES, + }, + messageId: 'transaction-submitted-0000-0x001', + timestamp: new Date(), + }), + ); + }); + }); + describe('_buildUserTraitsObject', function () { it('should return full user traits object on first call', function () { const MOCK_ALL_TOKENS = {