packages/fxa-content-server/server/lib/amplitude.js (877 lines of code) (raw):
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This module contains mappings from content server event names to
// amplitude event definitions. A module in fxa-shared is responsible
// for performing the actual transformations.
//
// You can see the event taxonomy here:
//
// https://docs.google.com/spreadsheets/d/1G_8OJGOxeWXdGJ1Ugmykk33Zsl-qAQL05CONSeD4Uz4
/* eslint-disable camelcase */
'use strict';
const pick = require('lodash/pick');
const {
GROUPS,
initialize,
mapBrowser,
mapFormFactor,
mapLocation,
mapOs,
validate,
} = require('fxa-shared/metrics/amplitude').amplitude;
const logger = require('./logging/log')();
const ua = require('fxa-shared/lib/user-agent').default;
const config = require('./configuration');
const { version: VERSION } = require('../../package.json');
const SERVICES = config.get('oauth_client_id_map');
const amplitude = config.get('amplitude');
const Sentry = require('@sentry/node');
const statsd = require('./statsd');
// Maps view name to email type
const EMAIL_TYPES = {
'complete-reset-password': 'reset_password',
'complete-signin': 'login',
'verify-email': 'registration',
};
const EVENTS = {
'flow.reset-password.submit': {
group: GROUPS.login,
event: 'forgot_submit',
},
'flow.choose-what-to-sync.back': {
group: GROUPS.registration,
event: 'cwts_back',
},
'flow.choose-what-to-sync.engage': {
group: GROUPS.registration,
event: 'cwts_engage',
},
'flow.choose-what-to-sync.submit': {
group: GROUPS.registration,
event: 'cwts_submit',
},
'flow.choose-what-to-sync.cwts_do_not_sync': {
group: GROUPS.registration,
event: 'cwts_do_not_sync',
},
'flow.would-you-like-to-sync.cwts_do_not_sync': {
group: GROUPS.login,
event: 'cwts_do_not_sync',
},
'flow.update-firefox.engage': {
group: GROUPS.notify,
event: 'update_firefox_engage',
},
'flow.update-firefox.view': {
group: GROUPS.notify,
event: 'update_firefox_view',
},
'screen.choose-what-to-sync': {
group: GROUPS.registration,
event: 'cwts_view',
},
'cached.signin.success': {
group: GROUPS.login,
event: 'complete',
},
'pairing.signin.success': {
group: GROUPS.login,
event: 'complete_pairing',
},
'screen.signin-confirmed': {
group: GROUPS.signin,
event: 'signin_confirmed_view',
},
'flow.signin-confirmed.continue': {
group: GROUPS.signin,
event: 'signin_confirmed_continue',
},
'flow.signin-confirmed.start-browsing': {
group: GROUPS.signin,
event: 'signin_confirmed_start_browsing',
},
'screen.signin-bounced': {
group: GROUPS.signin,
},
'flow.signin-bounced.link.support': {
group: GROUPS.signin,
event: 'signin_bounced_view',
},
'flow.signin-bounced.link.create-account': {
group: GROUPS.signin,
event: 'signin_bounced_create_account',
},
'flow.signin-bounced.link.back': {
group: GROUPS.signin,
event: 'signin_bounced_back',
},
// Signup code based metrics
'screen.confirm-signup-code': {
group: GROUPS.registration,
event: 'signup_code_view',
},
'flow.confirm-signup-code.engage': {
group: GROUPS.registration,
event: 'signup_code_engage',
},
'flow.confirm-signup-code.submit': {
group: GROUPS.registration,
event: 'signup_code_submit',
},
// Signup confirmed metrics
'screen.signup-confirmed': {
group: GROUPS.registration,
event: 'signup_confirmed_view',
},
'flow.signup-confirmed.continue': {
group: GROUPS.registration,
event: 'signup_confirmed_view_continue',
},
'flow.signup-confirmed.start-browsing': {
group: GROUPS.registration,
event: 'signup_confirmed_start_browsing',
},
// Reset password confirmation
'screen.reset-password-confirmed': {
group: GROUPS.login,
event: 'reset_password_confirmed_view',
},
'flow.reset-password-confirmed.continue': {
group: GROUPS.login,
event: 'reset_password_confirmed_continue',
},
'flow.reset-password-confirmed.start-browsing': {
group: GROUPS.login,
event: 'reset_password_confirmed_start_browsing',
},
// Reset password with recovery key verified
'screen.reset-password-with-recovery-key-verified': {
group: GROUPS.login,
event: 'reset_password_with_recovery_key_verified_view',
},
'flow.reset-password-with-recovery-key-verified.generate-new-key': {
group: GROUPS.login,
event: 'reset_password_with_recovery_key_verified_generate_new_key',
},
'flow.reset-password-with-recovery-key-verified.continue-to-account': {
group: GROUPS.login,
event: 'reset_password_with_recovery_key_verified_continue_to_account',
},
'flow.reset-password-with-recovery-key-verified.continue': {
group: GROUPS.login,
event: 'reset_password_with_recovery_key_verified_continue',
},
'flow.reset-password-with-recovery-key-verified.start-browsing': {
group: GROUPS.login,
event: 'reset_password_with_recovery_key_verified_start_browsing',
},
// Confirm account recovery key
'screen.confirm-recovery-key': {
group: GROUPS.activity,
event: 'confirm_recovery_key_view',
},
'flow.confirm-recovery-key.engage': {
group: GROUPS.activity,
event: 'confirm_recovery_key_engage',
},
'flow.confirm-recovery-key.submit': {
group: GROUPS.activity,
event: 'confirm_recovery_key_submit',
},
'flow.confirm-recovery-key.success': {
group: GROUPS.activity,
event: 'confirm_recovery_key_success',
},
// Verified account recovery key
'screen.post-verify.account-recovery.verified-recovery-key': {
group: GROUPS.activity,
event: 'verified_recovery_key_view',
},
'flow.post-verify.account-recovery.verified-recovery-key.submit': {
group: GROUPS.activity,
event: 'verified_recovery_key_submit',
},
// Primary email verified - Primary email confirmed
'screen.primary-email-verified': {
group: GROUPS.signup,
event: 'primary_email_verified_view',
},
'flow.primary-email-verified.continue': {
group: GROUPS.signup,
event: 'primary_email_verified_view_continue',
},
'flow.primary-email-verified.start-browsing': {
group: GROUPS.signup,
event: 'primary_email_verified_view_start_browsing',
},
'screen.account-recovery-confirm-key': {
group: GROUPS.login,
event: 'forgot_password_confirm_recovery_key_view',
},
'flow.account-recovery-confirm-key.engage': {
group: GROUPS.login,
event: 'forgot_password_confirm_recovery_key_engage',
},
'flow.account-recovery-confirm-key.submit': {
group: GROUPS.login,
event: 'forgot_password_confirm_recovery_key_submit',
},
// Password reset with account recovery key metrics
'screen.account-recovery-reset-password': {
group: GROUPS.login,
event: 'forgot_password_recovery_key_view',
},
'flow.account-recovery-reset-password.engage': {
group: GROUPS.login,
event: 'forgot_password_recovery_key_engage',
},
'flow.account-recovery-reset-password.submit': {
group: GROUPS.login,
event: 'forgot_password_recovery_key_submit',
},
'flow.account-recovery-reset-password.recovery-key-consume.success': {
group: GROUPS.login,
event: 'forgot_password_recovery_key_success',
},
'flow.add-newsletters.subscribe': {
group: GROUPS.newsletters,
event: 'subscribe',
},
'flow.add-newsletters.link.maybe-later': {
group: GROUPS.newsletters,
event: 'later',
},
'screen.connect-another-device': {
group: GROUPS.connectDevice,
event: 'view',
},
'flow.rp.engage': {
group: GROUPS.rp,
event: 'engage',
},
'flow.email-domain-validation.triggered': {
group: GROUPS.registration,
event: 'domain_validation_triggered',
},
'flow.email-domain-validation.skipped': {
group: GROUPS.registration,
event: 'domain_validation_skipped',
},
'flow.email-domain-validation.ignored': {
group: GROUPS.registration,
event: 'domain_validation_ignored',
},
'flow.scan-code.device-connected': {
group: GROUPS.qrConnectDevice,
event: 'scan_code_device_connected',
},
'flow.connected.link.done': {
group: GROUPS.qrConnectDevice,
event: 'connected_done',
},
'enter-email.thirdPartyAuth': {
group: GROUPS.thirdPartyAuth,
event: 'view',
},
'screen.cannot-create-account': {
group: GROUPS.registration,
event: 'cannot_create_account_view',
},
// cookies_disabled
'screen.cookies-disabled': {
group: GROUPS.activity,
event: 'cookies_disabled_view',
},
'flow.cookies-disabled.try-again-submit': {
group: GROUPS.activity,
event: 'cookies_disabled_try_again_submit',
},
'flow.cookies-disabled.try-again-success': {
group: GROUPS.activity,
event: 'cookies_disabled_try_again_success',
},
'flow.cookies-disabled.try-again-fail': {
group: GROUPS.activity,
event: 'cookies_disabled_try_again_fail',
},
// legal
'screen.legal': {
group: GROUPS.activity,
event: 'legal_view',
},
'screen.legal-terms': {
group: GROUPS.activity,
event: 'legal_terms_view',
},
'screen.legal-privacy': {
group: GROUPS.activity,
event: 'legal_privacy_view',
},
'flow.legal-terms.back': {
group: GROUPS.activity,
event: 'legal_terms_back',
},
'flow.legal-privacy.back': {
group: GROUPS.activity,
event: 'legal_privacy_back',
},
/* Everything under this point should be Settings events, aka 'fxa_pref' group */
// Account recovery key
'screen.settings.account-recovery': {
group: GROUPS.settings,
event: 'account_recovery_view',
},
// Revoke
'flow.settings.account-recovery.confirm-revoke.submit': {
group: GROUPS.settings,
event: 'account_recovery_confirm_revoke_submit',
},
'flow.settings.account-recovery.confirm-revoke.success': {
group: GROUPS.settings,
event: 'account_recovery_confirm_revoke_success',
},
'flow.settings.account-recovery.confirm-revoke.fail': {
group: GROUPS.settings,
event: 'account_recovery_confirm_revoke_fail',
},
// Create/Change Key
// Info page
// The first step of the "recovery key" wizard loads for a user who is creating a key
'flow.settings.account-recovery.create-key.info': {
group: GROUPS.settings,
event: 'account_recovery_create_key_info',
},
// The first step of the "recovery key" wizard loads for a user who is changing an existing key
'flow.settings.account-recovery.change-key.info': {
group: GROUPS.settings,
event: 'account_recovery_change_key_info',
},
// A user on the first step of the "recovery key" wizard, who is trying to create a new key, clicks the "Start creating your account recovery key" button to proceed to the next step
'flow.settings.account-recovery.create-key.start': {
group: GROUPS.settings,
event: 'account_recovery_create_key_start',
},
// A user on the first step of the "recovery key" wizard, who is trying to change an existing key, clicks the button to proceed to the next step
'flow.settings.account-recovery.change-key.start': {
group: GROUPS.settings,
event: 'account_recovery_change_key_start',
},
// A user in the recovery key flow to create a key exits the flow
'flow.settings.account-recovery.create-key.cancel': {
group: GROUPS.settings,
event: 'account_recovery_create_key_cancel',
},
// A user in the recovery key flow to change an existing key exits the flow
'flow.settings.account-recovery.change-key.cancel': {
group: GROUPS.settings,
event: 'account_recovery_change_key_cancel',
},
// Confirm password page
'flow.settings.account-recovery.confirm-password.submit': {
group: GROUPS.settings,
event: 'account_recovery_confirm_password_submit',
},
'flow.settings.account-recovery.confirm-password.success': {
group: GROUPS.settings,
event: 'account_recovery_confirm_password_success',
},
'flow.settings.account-recovery.confirm-password.fail': {
group: GROUPS.settings,
event: 'account_recovery_confirm_password_fail',
},
// Key download page
// A user on the "download" step of the account recovery flow downloads their key
'flow.settings.account-recovery.recovery-key.download-option': {
group: GROUPS.settings,
event: 'account_recovery_option_download',
},
// Recovery key download was successful
'flow.settings.account-recovery.recovery-key.download-success': {
group: GROUPS.settings,
event: 'account_recovery_download_success',
},
// An error occured while download the recovery key
'flow.settings.account-recovery.recovery-key.download-failed': {
group: GROUPS.settings,
event: 'account_recovery_download_failed',
},
// A user on the "download" step of the account recovery flow copies their key to their clipboard
'flow.settings.account-recovery.recovery-key.copy-option': {
group: GROUPS.settings,
event: 'account_recovery_option_copy',
},
// A user on the "download" step of the account recovery flow moves to the next step (or exits the flow) without downloading
'flow.settings.account-recovery.recovery-key.skip-download': {
group: GROUPS.settings,
event: 'account_recovery_skip_download',
},
// Save hint page
'flow.settings.account-recovery.create-hint.view': {
group: GROUPS.settings,
event: 'account_recovery_create_hint_view',
},
// A user on the "hint" step of the account recovery flow clicks the submit button to save the hint they entered into the text input
'flow.settings.account-recovery.create-hint.submit': {
group: GROUPS.settings,
event: 'account_recovery_create_hint_submit',
},
'flow.settings.account-recovery.create-hint.success': {
group: GROUPS.settings,
event: 'account_recovery_create_hint_success',
},
'flow.settings.account-recovery.create-hint.fail': {
group: GROUPS.settings,
event: 'account_recovery_create_hint_fail',
},
// A user exits hint step of the account recovery key flow without saving a hint, either by exiting the flow entirely or by skipping to the next step
'flow.settings.account-recovery.create-hint.skip': {
group: GROUPS.settings,
event: 'account_recovery_create_hint_skip',
},
// Avatar
'screen.settings.avatar.change': {
group: GROUPS.settings,
event: 'avatar_change_view',
},
'avatar.crop.submit.change': {
group: GROUPS.settings,
event: 'avatar_crop_submit_change',
},
// Change password
'screen.settings.change-password': {
group: GROUPS.settings,
event: 'change_password_view',
},
'settings.change-password.success': {
group: GROUPS.settings,
event: 'password',
},
// Create password
'screen.settings.create-password': {
group: GROUPS.settings,
event: 'create_password_view',
},
'settings.create-password.engage': {
group: GROUPS.settings,
event: 'create_password_engage',
},
'settings.create-password.submit': {
group: GROUPS.settings,
event: 'create_password_submit',
},
'settings.create-password.success': {
group: GROUPS.settings,
event: 'create_password_success',
},
'settings.create-password.fail': {
group: GROUPS.settings,
event: 'create_password_fail',
},
// Delete account
'screen.settings.delete-account': {
group: GROUPS.settings,
event: 'delete_account_view',
},
'flow.settings.account-delete.terms-checked.success': {
group: GROUPS.settings,
event: 'delete_account_terms_checked_success',
},
'flow.settings.account-delete.confirm-password.success': {
group: GROUPS.settings,
event: 'delete_account_confirm_password_success',
},
'flow.settings.account-delete.confirm-password.fail': {
group: GROUPS.settings,
event: 'delete_account_confirm_password_fail',
},
// Secondary email
'screen.settings.emails': {
group: GROUPS.settings,
event: 'add_secondary_email_view',
},
'settings.emails.submit': {
group: GROUPS.settings,
event: 'add_secondary_email_submit',
},
'verify-secondary-email.verification.clicked': {
group: GROUPS.settings,
event: 'verify_secondary_email_clicked',
},
'verify-secondary-email.verification.success': {
group: GROUPS.settings,
event: 'verify_secondary_email_success',
},
'verify-secondary-email.verification.fail': {
group: GROUPS.settings,
event: 'verify_secondary_email_fail',
},
// Two factor auth
'screen.settings.two-step-authentication.recovery-codes': {
group: GROUPS.settings,
event: 'two_step_authentication_recovery_codes_view',
},
'flow.settings.two-step-authentication.submit': {
group: GROUPS.settings,
event: 'two_step_authentication_submit',
},
'flow.settings.two-step-authentication.download-option': {
group: GROUPS.settings,
event: 'two_step_authentication_recovery_codes_download',
},
'flow.settings.two-step-authentication.copy-option': {
group: GROUPS.settings,
event: 'two_step_authentication_recovery_codes_copy',
},
'flow.settings.two-step-authentication.print-option': {
group: GROUPS.settings,
event: 'two_step_authentication_recovery_codes_print',
},
// Misc
'settings.signout.success': {
group: GROUPS.settings,
event: 'logout',
},
// pairing page during sign up
'screen.signup.pair': {
group: GROUPS.connectDevice,
event: 'pair_view',
},
'screen.pair': {
group: GROUPS.connectDevice,
event: 'pair_view',
},
'signup.pair.submit': {
group: GROUPS.connectDevice,
event: 'pair_submit',
},
'screen.pair.supp': {
group: GROUPS.connectDevice,
event: 'pair_supp_view',
},
'screen.pair.auth.totp': {
group: GROUPS.connectDevice,
event: 'view',
},
'pair.submit': {
group: GROUPS.connectDevice,
event: 'pair_submit',
},
// link to download mobile Firefox
'screen.pair.downloadlink.engage': {
group: GROUPS.connectDevice,
event: 'download_engage',
},
// link to skip pairing and go to Settings
'screen.pair.notnow.engage': {
group: GROUPS.connectDevice,
event: 'pair_notnow_engage',
},
// pairing process
'screen.pair.auth.allow': {
group: GROUPS.qrConnectDevice,
event: 'view',
},
// submit event from pairing authority
'flow.pair.auth.allow.submit': {
group: GROUPS.qrConnectDevice,
event: 'submit',
},
// the screen displayed if the pairing authority approved first
'screen.pair.auth.wait-for-supp': {
group: GROUPS.qrConnectDevice,
event: 'wait_for_supp',
},
'screen.pair.supp.allow': {
group: GROUPS.qrConnectDevice,
event: 'supp_allow_view',
},
// the screen displayed if the pairing supplicant approved first
'screen.pair.supp.wait-for-auth': {
group: GROUPS.qrConnectDevice,
event: 'wait_for_auth',
},
'screen.pair.auth.complete': {
group: GROUPS.qrConnectDevice,
event: 'complete',
},
'screen.pair.auth.fx-view': {
group: GROUPS.qrConnectDevice,
event: 'fx_view_engage',
},
'cad.notnow.engage': {
group: GROUPS.connectDevice,
event: 'cad_notnow_engage',
},
};
const VIEW_ENGAGE_SUBMIT_EVENT_GROUPS = {
'add-newsletters': GROUPS.newsletters,
'enter-email': GROUPS.emailFirst,
'force-auth': GROUPS.login,
pair: GROUPS.connectDevice,
'rp-button': GROUPS.button,
settings: GROUPS.settings,
signin: GROUPS.login,
signup: GROUPS.registration,
subscribe: GROUPS.sub,
support: GROUPS.subSupport,
};
// In the following regular expressions, the first match group is
// exposed as `eventCategory` and the second as `eventTarget`.
const FUZZY_EVENTS = new Map([
[
/^screen(?:\.signin|\.signup)?\.(get-started|ready-to-scan|scan-code|connected)$/,
{
group: GROUPS.qrConnectDevice,
event: (eventCategory, eventTarget) =>
`${eventCategory.replace(/-/g, '_')}_view`,
},
],
[
/^flow(?:\.signin|\.signup)?\.(get-started|ready-to-scan|scan-code|connected).submit$/,
{
group: GROUPS.qrConnectDevice,
event: (eventCategory, eventTarget) =>
`${eventCategory.replace(/-/g, '_')}_submit`,
},
],
[
/^flow(?:\.signin|\.signup)?\.(get-started|ready-to-scan).link.maybe-later$/,
{
group: GROUPS.qrConnectDevice,
event: (eventCategory, eventTarget) =>
`${eventCategory.replace(/-/g, '_')}_later`,
},
],
[
/^flow(?:\.signin|\.signup)?\.(ready-to-scan|scan-code|connected).link.use-sms$/,
{
group: GROUPS.qrConnectDevice,
event: (eventCategory, eventTarget) =>
`${eventCategory.replace(/-/g, '_')}_use_sms`,
},
],
[
/^experiment\.(?:control|designF|designG)\.passwordStrength\.([\w]+)$/,
{
group: GROUPS.registration,
event: (eventCategory, eventTarget) => `password_${eventCategory}`,
},
],
[
/^flow\.signin-totp-code\.submit$/,
{
group: GROUPS.login,
event: 'totp_code_submit',
},
],
[
/^screen\.signin-totp-code$/,
{
group: GROUPS.login,
event: 'totp_code_view',
},
],
[
/^flow\.signin-totp-code\.engage$/,
{
group: GROUPS.login,
event: 'totp_code_engage',
},
],
[
/^screen\.settings\.two-step-authentication$/,
{
group: GROUPS.settings,
event: 'two_step_authentication_view',
},
],
[
/^flow\.([\w-]+)\.engage$/,
{
group: (eventCategory) => VIEW_ENGAGE_SUBMIT_EVENT_GROUPS[eventCategory],
event: 'engage',
},
],
[
/^flow\.[\w-]+\.forgot-password$/,
{
group: GROUPS.login,
event: 'forgot_pwd',
},
],
[
/^flow\.[\w-]+\.have-account$/,
{
group: GROUPS.registration,
event: 'have_account',
},
],
[
/^flow\.((?:install|signin)_from)\.\w+$/,
{
group: GROUPS.connectDevice,
event: 'view',
},
],
[
/^screen\.(?:signup|signin)\.(sms)$/,
{
group: GROUPS.connectDevice,
event: 'view',
},
],
[
/^flow\.connect-another-device\.link\.(app-store)\.([\w-]+)$/,
{
group: GROUPS.connectDevice,
event: 'engage',
},
],
[
/^flow\.(signup|signin)\.connect-another-device\.link\.([\w-]+)$/,
{
group: GROUPS.connectDevice,
event: 'engage',
},
],
[
/^flow\.([\w-]+)\.submit$/,
{
group: (eventCategory) => VIEW_ENGAGE_SUBMIT_EVENT_GROUPS[eventCategory],
event: 'submit',
},
],
[
/^screen\.(?:oauth\.)?([\w-]+)$/,
{
group: (eventCategory) => VIEW_ENGAGE_SUBMIT_EVENT_GROUPS[eventCategory],
event: 'view',
},
],
[
/^settings\.communication-preferences\.(optIn|optOut)\.success$/,
{
group: GROUPS.settings,
event: 'newsletter',
},
],
[
/^settings\.clients\.disconnect\.submit\.([a-z]+)$/,
{
group: GROUPS.settings,
event: 'disconnect_device',
},
],
[
/^([\w-]+).verification.clicked$/,
{
group: (eventCategory) =>
eventCategory in EMAIL_TYPES ? GROUPS.email : null,
event: 'click',
},
],
[
/^flow\.signin-totp-code\.success$/,
{
group: GROUPS.login,
event: 'totp_code_success',
},
],
[
/^flow\.(support)\.success$/,
{
group: (eventCategory) => VIEW_ENGAGE_SUBMIT_EVENT_GROUPS[eventCategory],
event: 'success',
},
],
[
/^flow\.(support)\.view$/,
{
group: (eventCategory) => VIEW_ENGAGE_SUBMIT_EVENT_GROUPS[eventCategory],
event: 'view',
},
],
[
/^flow\.(support)\.fail$/,
{
group: (eventCategory) => VIEW_ENGAGE_SUBMIT_EVENT_GROUPS[eventCategory],
event: 'fail',
},
],
[
/^flow\.email-domain-validation\.(block|warn|success)$/,
{
group: GROUPS.registration,
event: 'domain_validation_result',
},
],
[
/^flow\.?(apple|google)?\.(deeplink|oauth-start|oauth-complete|signin-complete)$/,
{
group: GROUPS.thirdPartyAuth,
event: (eventCategory, eventTarget) => {
return `${eventCategory.replace(/-/g, '_')}_${eventTarget.replace(
/-/g,
'_'
)}`;
},
},
],
[
/^flow\.?([\w-]+)?\.(brand-messaging-prelaunch-view|brand-messaging-prelaunch-learn-more|brand-messaging-prelaunch-banner-close|brand-messaging-postlaunch-view|brand-messaging-postlaunch-learn-more|brand-messaging-postlaunch-banner-close)$/,
{
group: GROUPS.branding,
event: (eventCategory, eventTarget) => {
return `${eventCategory.replace(/-/g, '_')}_${eventTarget.replace(
/-/g,
'_'
)}`;
},
},
],
[
/^screen\.post-verify\.third-party-auth\.set-password$/,
{
group: GROUPS.thirdPartyAuth,
event: 'set_password_view',
},
],
[
/^flow\.post-verify\.third-party-auth\.set-password\.(engage|submit|success)$/,
{
group: GROUPS.thirdPartyAuth,
event: (eventCategory, eventTarget) => `set_password_${eventCategory}`,
},
],
]);
const transform = initialize(SERVICES, EVENTS, FUZZY_EVENTS, logger, statsd);
module.exports = receiveEvent;
function receiveEvent(event, request, data) {
if (amplitude.disabled || !event || !request || !data) {
return;
}
if (amplitude.rawEvents) {
const rawEvent = {
event,
context: {
eventSource: 'content',
version: VERSION,
emailTypes: EMAIL_TYPES,
userAgent: request.headers && request.headers['user-agent'],
...pick(data, [
'deviceId',
'devices',
'emailDomain',
'entrypoint_experiment',
'entrypoint_variation',
'entrypoint',
'experiments',
'flowBeginTime',
'flowId',
'lang',
'location',
'newsletters',
'planId',
'productId',
'service',
'syncEngines',
'templateVersion',
'uid',
'userPreferences',
'utm_campaign',
'utm_content',
'utm_medium',
'utm_source',
'utm_term',
]),
},
};
logger.info('rawAmplitudeData', rawEvent);
statsd.increment('amplitude.event.raw');
}
const userAgent = ua.parse(request.headers && request.headers['user-agent']);
const amplitudeEvent = transform(
event,
Object.assign(
{ emailTypes: EMAIL_TYPES, version: VERSION },
pruneUnsetValues(data),
mapBrowser(userAgent),
mapOs(userAgent),
mapFormFactor(userAgent),
mapLocation(data.location)
)
);
statsd.increment('amplitude.event');
if (amplitudeEvent) {
if (amplitude.schemaValidation) {
try {
validate(amplitudeEvent);
} catch (err) {
logger.error('amplitude.validationError', { err, amplitudeEvent });
// Since we are adding a schema retroactively, let's be conservative:
// temporarily capture any validation "errors" with Sentry to ensure
// that the schema is not too strict against existing events. We'll
// update the schema accordingly. And allow the events in the
// meantime.
Sentry.withScope((scope) => {
scope.setContext('amplitude.validationError', {
event_type: amplitudeEvent.event_type,
flow_id: amplitudeEvent.user_properties.flow_id,
error: err.message,
});
Sentry.captureMessage('Amplitude event failed validation', 'error');
});
}
}
logger.info('amplitudeEvent', amplitudeEvent);
} else {
statsd.increment('amplitude.event.dropped');
}
}
function pruneUnsetValues(data) {
const result = {};
Object.keys(data).forEach((key) => {
const value = data[key];
if (value && value !== 'none') {
result[key] = value;
}
});
return result;
}