mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
MetaMetrics: add segment.identify (#14195)
* MetaMetrics: add segment.identify * MetaMetrics: rm non-existent typedef * MetaMetrics: WIP segment.identify pt. 2 * Revert "MetaMetrics: WIP segment.identify pt. 2" This reverts commit 8126de4dff9312aab2275cba81d0432bfcdb097b. * MetaMetrics: add identify tests * MetaMetricsController: update identify w/ fix * MetaMetricsController: fix identify tests * MetaMetricsController#identify: warn instead of throw * MetaMetrics: transform date into ISO string * MetaMetricsController: alphabetize test * MetaMetricsController: restore asset in test * MetaMetrics: rn traits -> userTraits * MetaMetrics: restore trackEvent location * update to follow analytics-node api * MetaMetricsController: update tests + @link * MetaMetrics: destimation -> destination Co-authored-by: brad-decker <bhdecker84@gmail.com>
This commit is contained in:
parent
d2c5962463
commit
f088db99a3
@ -281,6 +281,30 @@ export default class MetaMetricsController {
|
||||
this.store.updateState({ fragments });
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls this._identify with validated metaMetricsId and user traits if user is participating
|
||||
* in the MetaMetrics analytics program
|
||||
*
|
||||
* @param {Object} userTraits
|
||||
*/
|
||||
identify(userTraits) {
|
||||
const { metaMetricsId, participateInMetaMetrics } = this.state;
|
||||
|
||||
if (!participateInMetaMetrics || !metaMetricsId || !userTraits) {
|
||||
return;
|
||||
}
|
||||
if (typeof userTraits !== 'object') {
|
||||
console.warn(
|
||||
`MetaMetricsController#identify: userTraits parameter must be an object. Received type: ${typeof userTraits}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const allValidTraits = this._buildValidTraits(userTraits);
|
||||
|
||||
this._identify(allValidTraits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the `participateInMetaMetrics` property
|
||||
*
|
||||
@ -434,7 +458,7 @@ export default class MetaMetricsController {
|
||||
handleMetaMaskStateUpdate(newState) {
|
||||
const userTraits = this._buildUserTraitsObject(newState);
|
||||
if (userTraits) {
|
||||
// this.identify(userTraits);
|
||||
this.identify(userTraits);
|
||||
}
|
||||
}
|
||||
|
||||
@ -535,6 +559,103 @@ export default class MetaMetricsController {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object of all valid user traits. For dates, we transform them into ISO-8601 timestamp strings.
|
||||
*
|
||||
* @see {@link https://segment.com/docs/connections/spec/common/#timestamps}
|
||||
* @param {Object} userTraits
|
||||
* @returns {Object}
|
||||
*/
|
||||
_buildValidTraits(userTraits) {
|
||||
return Object.entries(userTraits).reduce((validTraits, [key, value]) => {
|
||||
if (this._isValidTraitDate(value)) {
|
||||
validTraits[key] = value.toISOString();
|
||||
} else if (this._isValidTrait(value)) {
|
||||
validTraits[key] = value;
|
||||
} else {
|
||||
console.warn(
|
||||
`MetaMetricsController: "${key}" value is not a valid trait type`,
|
||||
);
|
||||
}
|
||||
return validTraits;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls segment.identify with given user traits
|
||||
*
|
||||
* @see {@link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#identify}
|
||||
* @private
|
||||
* @param {Object} userTraits
|
||||
*/
|
||||
_identify(userTraits) {
|
||||
const { metaMetricsId } = this.state;
|
||||
|
||||
if (!userTraits || Object.keys(userTraits).length === 0) {
|
||||
console.warn('MetaMetricsController#_identify: No userTraits found');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.segment.identify({
|
||||
userId: metaMetricsId,
|
||||
traits: userTraits,
|
||||
});
|
||||
} catch (err) {
|
||||
this._captureException(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the trait value. Segment accepts any data type. We are adding validation here to
|
||||
* support data types for our Segment destination(s) e.g. MixPanel
|
||||
*
|
||||
* @param {*} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isValidTrait(value) {
|
||||
const type = typeof value;
|
||||
|
||||
return (
|
||||
type === 'string' ||
|
||||
type === 'boolean' ||
|
||||
type === 'number' ||
|
||||
this._isValidTraitArray(value) ||
|
||||
this._isValidTraitDate(value)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Segment accepts any data type value. We have special logic to validate arrays.
|
||||
*
|
||||
* @param {*} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isValidTraitArray = (value) => {
|
||||
return (
|
||||
Array.isArray(value) &&
|
||||
(value.every((element) => {
|
||||
return typeof element === 'string';
|
||||
}) ||
|
||||
value.every((element) => {
|
||||
return typeof element === 'boolean';
|
||||
}) ||
|
||||
value.every((element) => {
|
||||
return typeof element === 'number';
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the value is an accepted date type
|
||||
*
|
||||
* @param {*} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isValidTraitDate = (value) => {
|
||||
return Object.prototype.toString.call(value) === '[object Date]';
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform validation on the payload and update the id type to use before
|
||||
* sending to Segment. Also examines the options to route and handle the
|
||||
|
@ -23,6 +23,20 @@ const FAKE_CHAIN_ID = '0x1338';
|
||||
const LOCALE = 'en_US';
|
||||
const TEST_META_METRICS_ID = '0xabc';
|
||||
|
||||
const MOCK_TRAITS = {
|
||||
test_boolean: true,
|
||||
test_string: 'abc',
|
||||
test_number: 123,
|
||||
test_bool_array: [true, true, false],
|
||||
test_string_array: ['test', 'test', 'test'],
|
||||
test_boolean_array: [1, 2, 3],
|
||||
};
|
||||
|
||||
const MOCK_INVALID_TRAITS = {
|
||||
test_null: null,
|
||||
test_array_multi_types: [true, 'a', 1],
|
||||
};
|
||||
|
||||
const DEFAULT_TEST_CONTEXT = {
|
||||
app: { name: 'MetaMask Extension', version: VERSION },
|
||||
page: METAMETRICS_BACKGROUND_PAGE_OBJECT,
|
||||
@ -216,6 +230,78 @@ describe('MetaMetricsController', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('identify', function () {
|
||||
it('should call segment.identify for valid traits if user is participating in metametrics', async function () {
|
||||
const metaMetricsController = getMetaMetricsController({
|
||||
participateInMetaMetrics: true,
|
||||
metaMetricsId: TEST_META_METRICS_ID,
|
||||
});
|
||||
const mock = sinon.mock(segment);
|
||||
|
||||
mock
|
||||
.expects('identify')
|
||||
.once()
|
||||
.withArgs({ userId: TEST_META_METRICS_ID, traits: MOCK_TRAITS });
|
||||
|
||||
metaMetricsController.identify({
|
||||
...MOCK_TRAITS,
|
||||
...MOCK_INVALID_TRAITS,
|
||||
});
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
it('should transform date type traits into ISO-8601 timestamp strings', async function () {
|
||||
const metaMetricsController = getMetaMetricsController({
|
||||
participateInMetaMetrics: true,
|
||||
metaMetricsId: TEST_META_METRICS_ID,
|
||||
});
|
||||
const mock = sinon.mock(segment);
|
||||
|
||||
const mockDate = new Date();
|
||||
const mockDateISOString = mockDate.toISOString();
|
||||
|
||||
mock
|
||||
.expects('identify')
|
||||
.once()
|
||||
.withArgs({
|
||||
userId: TEST_META_METRICS_ID,
|
||||
traits: {
|
||||
test_date: mockDateISOString,
|
||||
},
|
||||
});
|
||||
|
||||
metaMetricsController.identify({
|
||||
test_date: mockDate,
|
||||
});
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
it('should not call segment.identify if user is not participating in metametrics', function () {
|
||||
const metaMetricsController = getMetaMetricsController({
|
||||
participateInMetaMetrics: false,
|
||||
});
|
||||
const mock = sinon.mock(segment);
|
||||
|
||||
mock.expects('identify').never();
|
||||
|
||||
metaMetricsController.identify(MOCK_TRAITS);
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
it('should not call segment.identify if there are no valid traits to identify', async function () {
|
||||
const metaMetricsController = getMetaMetricsController({
|
||||
participateInMetaMetrics: true,
|
||||
metaMetricsId: TEST_META_METRICS_ID,
|
||||
});
|
||||
const mock = sinon.mock(segment);
|
||||
|
||||
mock.expects('identify').never();
|
||||
|
||||
metaMetricsController.identify(MOCK_INVALID_TRAITS);
|
||||
mock.verify();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setParticipateInMetaMetrics', function () {
|
||||
it('should update the value of participateInMetaMetrics', function () {
|
||||
const metaMetricsController = getMetaMetricsController({
|
||||
|
Loading…
Reference in New Issue
Block a user