mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Make event tracking idempotent using unique messageId (#16267)
This commit is contained in:
parent
6f27c9065c
commit
6c0930899d
@ -32,6 +32,22 @@ 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 = '';
|
||||
if (args.uniqueIdentifier) {
|
||||
messageId += `${args.uniqueIdentifier}-`;
|
||||
}
|
||||
if (args.actionId) {
|
||||
messageId += args.actionId;
|
||||
}
|
||||
if (messageId.length) {
|
||||
return messageId;
|
||||
}
|
||||
return generateRandomId();
|
||||
};
|
||||
|
||||
const exceptionsToFilter = {
|
||||
[`You must pass either an "anonymousId" or a "userId".`]: true,
|
||||
};
|
||||
@ -231,14 +247,6 @@ export default class MetaMetricsController {
|
||||
);
|
||||
}
|
||||
|
||||
const existingFragment = this.getExistingEventFragment(
|
||||
options.actionId,
|
||||
options.uniqueIdentifier,
|
||||
);
|
||||
if (existingFragment) {
|
||||
return existingFragment;
|
||||
}
|
||||
|
||||
const { fragments } = this.store.getState();
|
||||
|
||||
const id = options.uniqueIdentifier ?? uuidv4();
|
||||
@ -266,6 +274,8 @@ export default class MetaMetricsController {
|
||||
value: fragment.value,
|
||||
currency: fragment.currency,
|
||||
environmentType: fragment.environmentType,
|
||||
actionId: options.actionId,
|
||||
uniqueIdentifier: options.uniqueIdentifier,
|
||||
});
|
||||
}
|
||||
|
||||
@ -287,26 +297,6 @@ export default class MetaMetricsController {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fragment stored in memory with provided id or undefined if it
|
||||
* does not exist.
|
||||
*
|
||||
* @param {string} actionId - actionId passed from UI
|
||||
* @param {string} uniqueIdentifier - uniqueIdentifier of the event
|
||||
* @returns {[MetaMetricsEventFragment]}
|
||||
*/
|
||||
getExistingEventFragment(actionId, uniqueIdentifier) {
|
||||
const { fragments } = this.store.getState();
|
||||
|
||||
const existingFragment = Object.values(fragments).find(
|
||||
(fragment) =>
|
||||
fragment.actionId === actionId &&
|
||||
fragment.uniqueIdentifier === uniqueIdentifier,
|
||||
);
|
||||
|
||||
return existingFragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an event fragment in state
|
||||
*
|
||||
@ -367,6 +357,8 @@ export default class MetaMetricsController {
|
||||
value: fragment.value,
|
||||
currency: fragment.currency,
|
||||
environmentType: fragment.environmentType,
|
||||
actionId: fragment.actionId,
|
||||
uniqueIdentifier: fragment.uniqueIdentifier,
|
||||
});
|
||||
const { fragments } = this.store.getState();
|
||||
delete fragments[id];
|
||||
@ -453,7 +445,10 @@ export default class MetaMetricsController {
|
||||
* @param {MetaMetricsPageOptions} [options] - options for handling the page
|
||||
* view
|
||||
*/
|
||||
trackPage({ name, params, environmentType, page, referrer }, options) {
|
||||
trackPage(
|
||||
{ name, params, environmentType, page, referrer, actionId },
|
||||
options,
|
||||
) {
|
||||
try {
|
||||
if (this.state.participateInMetaMetrics === false) {
|
||||
return;
|
||||
@ -469,6 +464,7 @@ export default class MetaMetricsController {
|
||||
const idTrait = metaMetricsId ? 'userId' : 'anonymousId';
|
||||
const idValue = metaMetricsId ?? METAMETRICS_ANONYMOUS_ID;
|
||||
this._submitSegmentAPICall('page', {
|
||||
messageId: buildUniqueMessageId({ actionId }),
|
||||
[idTrait]: idValue,
|
||||
name,
|
||||
properties: {
|
||||
@ -659,6 +655,7 @@ export default class MetaMetricsController {
|
||||
} = rawPayload;
|
||||
return {
|
||||
event,
|
||||
messageId: buildUniqueMessageId(rawPayload),
|
||||
properties: {
|
||||
// These values are omitted from properties because they have special meaning
|
||||
// in segment. https://segment.com/docs/connections/spec/track/#properties.
|
||||
|
@ -20,6 +20,7 @@ const NETWORK = 'Mainnet';
|
||||
const FAKE_CHAIN_ID = '0x1338';
|
||||
const LOCALE = 'en_US';
|
||||
const TEST_META_METRICS_ID = '0xabc';
|
||||
const DUMMY_ACTION_ID = 'DUMMY_ACTION_ID';
|
||||
|
||||
const MOCK_TRAITS = {
|
||||
test_boolean: true,
|
||||
@ -634,6 +635,50 @@ describe('MetaMetricsController', function () {
|
||||
);
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
it('multiple trackPage call with same actionId should result in same messageId being sent to segment', function () {
|
||||
const mock = sinon.mock(segment);
|
||||
const metaMetricsController = getMetaMetricsController({
|
||||
preferencesStore: getMockPreferencesStore({
|
||||
participateInMetaMetrics: null,
|
||||
}),
|
||||
});
|
||||
mock
|
||||
.expects('page')
|
||||
.twice()
|
||||
.withArgs({
|
||||
name: 'home',
|
||||
userId: TEST_META_METRICS_ID,
|
||||
context: DEFAULT_TEST_CONTEXT,
|
||||
properties: {
|
||||
params: null,
|
||||
...DEFAULT_PAGE_PROPERTIES,
|
||||
},
|
||||
messageId: DUMMY_ACTION_ID,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
metaMetricsController.trackPage(
|
||||
{
|
||||
name: 'home',
|
||||
params: null,
|
||||
actionId: DUMMY_ACTION_ID,
|
||||
environmentType: ENVIRONMENT_TYPE_BACKGROUND,
|
||||
page: METAMETRICS_BACKGROUND_PAGE_OBJECT,
|
||||
},
|
||||
{ isOptInPath: true },
|
||||
);
|
||||
metaMetricsController.trackPage(
|
||||
{
|
||||
name: 'home',
|
||||
params: null,
|
||||
actionId: DUMMY_ACTION_ID,
|
||||
environmentType: ENVIRONMENT_TYPE_BACKGROUND,
|
||||
page: METAMETRICS_BACKGROUND_PAGE_OBJECT,
|
||||
},
|
||||
{ isOptInPath: true },
|
||||
);
|
||||
mock.verify();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_buildUserTraitsObject', function () {
|
||||
|
@ -2041,7 +2041,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async addCustomNetwork(customRpc) {
|
||||
async addCustomNetwork(customRpc, actionId) {
|
||||
const { chainId, chainName, rpcUrl, ticker, blockExplorerUrl } = customRpc;
|
||||
|
||||
await this.preferencesController.addToFrequentRpcList(
|
||||
@ -2077,6 +2077,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
sensitiveProperties: {
|
||||
rpc_url: rpcUrlOrigin,
|
||||
},
|
||||
actionId,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3516,7 +3516,10 @@ export async function closeNotificationPopup() {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export function trackMetaMetricsEvent(payload, options) {
|
||||
return submitRequestToBackground('trackMetaMetricsEvent', [payload, options]);
|
||||
return submitRequestToBackground('trackMetaMetricsEvent', [
|
||||
{ ...payload, actionId: generateActionId() },
|
||||
options,
|
||||
]);
|
||||
}
|
||||
|
||||
export function createEventFragment(options) {
|
||||
@ -3548,7 +3551,10 @@ export function finalizeEventFragment(id, options) {
|
||||
* @param {MetaMetricsPageOptions} options - options for handling the page view
|
||||
*/
|
||||
export function trackMetaMetricsPage(payload, options) {
|
||||
return submitRequestToBackground('trackMetaMetricsPage', [payload, options]);
|
||||
return submitRequestToBackground('trackMetaMetricsPage', [
|
||||
{ ...payload, actionId: generateActionId() },
|
||||
options,
|
||||
]);
|
||||
}
|
||||
|
||||
export function updateViewedNotifications(notificationIdViewedStatusMap) {
|
||||
@ -3578,6 +3584,7 @@ export async function setSmartTransactionsOptInStatus(
|
||||
prevOptInState,
|
||||
) {
|
||||
trackMetaMetricsEvent({
|
||||
actionId: generateActionId(),
|
||||
event: 'STX OptIn',
|
||||
category: EVENT.CATEGORIES.SWAPS,
|
||||
sensitiveProperties: {
|
||||
@ -3841,7 +3848,10 @@ export function addCustomNetwork(customRpc) {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
dispatch(setNewCustomNetworkAdded(customRpc));
|
||||
await submitRequestToBackground('addCustomNetwork', [customRpc]);
|
||||
await submitRequestToBackground('addCustomNetwork', [
|
||||
customRpc,
|
||||
generateActionId(),
|
||||
]);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
dispatch(displayWarning('Had a problem changing networks!'));
|
||||
|
Loading…
Reference in New Issue
Block a user