From 23b6faaab643388d303cf1402cb70c5ca00b210d Mon Sep 17 00:00:00 2001 From: Yu Long Date: Fri, 15 Nov 2024 15:13:55 +0100 Subject: [PATCH 1/4] refactor(analytics): group some constants together --- .../components/Card/Card.Analytics.test.tsx | 19 +++++++------- .../components/GooglePay/GooglePay.test.ts | 4 +-- .../RedirectShopper/RedirectShopper.tsx | 6 ++++- .../ThreeDS2/ThreeDS2Challenge.test.tsx | 6 ++--- .../components/ThreeDS2/ThreeDS2Challenge.tsx | 4 +-- .../ThreeDS2DeviceFingerprint.test.tsx | 6 ++--- .../ThreeDS2/ThreeDS2DeviceFingerprint.tsx | 4 +-- .../ThreeDS2/callSubmit3DS2Fingerprint.ts | 8 +++--- .../Challenge/PrepareChallenge3DS2.test.tsx | 8 +++--- .../Challenge/PrepareChallenge3DS2.tsx | 12 ++++----- .../PrepareFingerprint3DS2.test.tsx | 6 ++--- .../PrepareFingerprint3DS2.tsx | 4 +-- .../lib/src/core/Analytics/Analytics.test.ts | 4 +-- packages/lib/src/core/Analytics/Analytics.ts | 19 +++++--------- .../Analytics/analyticsPreProcessor.test.ts | 14 +++++------ .../core/Analytics/analyticsPreProcessor.ts | 24 ++++++++---------- packages/lib/src/core/Analytics/constants.ts | 25 +++++++++++++------ packages/lib/src/core/Analytics/types.ts | 7 +++--- 18 files changed, 92 insertions(+), 88 deletions(-) diff --git a/packages/lib/src/components/Card/Card.Analytics.test.tsx b/packages/lib/src/components/Card/Card.Analytics.test.tsx index a4b34b106b..c8ad3d7b25 100644 --- a/packages/lib/src/components/Card/Card.Analytics.test.tsx +++ b/packages/lib/src/components/Card/Card.Analytics.test.tsx @@ -8,8 +8,7 @@ let analyticsEventObject; import { ANALYTICS_CONFIGURED_STR, - ANALYTICS_EVENT_INFO, - ANALYTICS_EVENT_LOG, + ANALYTICS_EVENT, ANALYTICS_FOCUS_STR, ANALYTICS_RENDERED_STR, ANALYTICS_SUBMIT_STR, @@ -47,7 +46,7 @@ describe('Card: calls that generate "info" analytics should produce objects with // With configData removed inspect what's left expect(analyticsEventObject).toEqual({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: card.constructor['type'], type: ANALYTICS_RENDERED_STR } }); }); @@ -65,7 +64,7 @@ describe('Card: calls that generate "info" analytics should produce objects with // With configData removed inspect what's left expect(analyticsEventObject).toEqual({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: card.constructor['type'], type: ANALYTICS_RENDERED_STR, isStoredPaymentMethod: true, brand: 'mc' } }); }); @@ -76,7 +75,7 @@ describe('Card: calls that generate "info" analytics should produce objects with }); expect(analyticsModule.createAnalyticsEvent).toHaveBeenCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: card.constructor['type'], type: ANALYTICS_CONFIGURED_STR } }); }); @@ -89,7 +88,7 @@ describe('Card: calls that generate "info" analytics should produce objects with }); expect(analyticsModule.createAnalyticsEvent).toHaveBeenCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: card.constructor['type'], type: ANALYTICS_CONFIGURED_STR, isStoredPaymentMethod: true, brand: 'mc' } }); }); @@ -109,7 +108,7 @@ describe('Card: calls that generate "info" analytics should produce objects with }); expect(analyticsModule.createAnalyticsEvent).toHaveBeenCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: card.constructor['type'], type: ANALYTICS_FOCUS_STR, target: 'card_number' } }); }); @@ -129,7 +128,7 @@ describe('Card: calls that generate "info" analytics should produce objects with }); expect(analyticsModule.createAnalyticsEvent).toHaveBeenCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: card.constructor['type'], type: ANALYTICS_UNFOCUS_STR, target: 'card_number' } }); }); @@ -141,7 +140,7 @@ describe('Card: calls that generate "info" analytics should produce objects with }); expect(analyticsModule.createAnalyticsEvent).toHaveBeenCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: card.constructor['type'], type: ANALYTICS_VALIDATION_ERROR_STR, @@ -170,7 +169,7 @@ describe('Card: calls that generate "log" analytics should produce objects with card.submitAnalytics({ type: ANALYTICS_SUBMIT_STR }); expect(analyticsModule.createAnalyticsEvent).toHaveBeenCalledWith({ - event: ANALYTICS_EVENT_LOG, + event: ANALYTICS_EVENT.log, data: { component: card.constructor['type'], type: ANALYTICS_SUBMIT_STR, diff --git a/packages/lib/src/components/GooglePay/GooglePay.test.ts b/packages/lib/src/components/GooglePay/GooglePay.test.ts index 9700b65e07..b835e5850b 100644 --- a/packages/lib/src/components/GooglePay/GooglePay.test.ts +++ b/packages/lib/src/components/GooglePay/GooglePay.test.ts @@ -2,7 +2,7 @@ import GooglePay from './GooglePay'; import GooglePayService from './GooglePayService'; import Analytics from '../../core/Analytics'; -import { ANALYTICS_EVENT_INFO, ANALYTICS_SELECTED_STR, NO_CHECKOUT_ATTEMPT_ID } from '../../core/Analytics/constants'; +import { ANALYTICS_EVENT, ANALYTICS_SELECTED_STR, NO_CHECKOUT_ATTEMPT_ID } from '../../core/Analytics/constants'; const analyticsModule = Analytics({ analytics: {}, loadingContext: '', locale: '', clientKey: '', bundleType: 'umd' }); @@ -458,7 +458,7 @@ describe('GooglePay', () => { gpay.submit(); expect(analyticsModule.createAnalyticsEvent).toHaveBeenCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: gpay.props.type, type: ANALYTICS_SELECTED_STR, diff --git a/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx b/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx index b01693e8ce..0702319c97 100644 --- a/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx +++ b/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx @@ -10,12 +10,14 @@ interface RedirectShopperProps { redirectFromTopWhenInIframe?: boolean; paymentMethodType?: string; onActionHandled?: (rtnObj: ActionHandledReturnObject) => void; + onRedirectError?: () => void; } class RedirectShopper extends Component { private postForm; public static defaultProps = { beforeRedirect: resolve => resolve(), + onRedirectError: () => {}, method: 'GET' }; @@ -49,7 +51,9 @@ class RedirectShopper extends Component { }) ); - dispatchEvent.then(doRedirect).catch(() => {}); + dispatchEvent.then(doRedirect).catch(() => { + this.props.onRedirectError(); + }); } render({ url, method, data = {} }) { diff --git a/packages/lib/src/components/ThreeDS2/ThreeDS2Challenge.test.tsx b/packages/lib/src/components/ThreeDS2/ThreeDS2Challenge.test.tsx index 1a015c5f0c..61045730fa 100644 --- a/packages/lib/src/components/ThreeDS2/ThreeDS2Challenge.test.tsx +++ b/packages/lib/src/components/ThreeDS2/ThreeDS2Challenge.test.tsx @@ -1,6 +1,6 @@ import { ThreeDS2Challenge } from './index'; import Analytics from '../../core/Analytics'; -import { ANALYTICS_API_ERROR, Analytics3DS2Errors, ANALYTICS_EVENT_ERROR, ANALYTICS_RENDERED_STR } from '../../core/Analytics/constants'; +import { ANALYTICS_ERROR_TYPE, Analytics3DS2Errors, ANALYTICS_RENDERED_STR, ANALYTICS_EVENT } from '../../core/Analytics/constants'; import { THREEDS2_CHALLENGE_ERROR, THREEDS2_ERROR } from './constants'; const analyticsModule = Analytics({ analytics: {}, loadingContext: '', locale: '', clientKey: '', bundleType: 'umd' }); @@ -31,11 +31,11 @@ describe('ThreeDS2Challenge: calls that generate analytics should produce object const view = challenge.render(); expect(analyticsModule.createAnalyticsEvent).toHaveBeenCalledWith({ - event: ANALYTICS_EVENT_ERROR, + event: ANALYTICS_EVENT.error, data: { component: challenge.constructor['type'], type: THREEDS2_ERROR, - errorType: ANALYTICS_API_ERROR, + errorType: ANALYTICS_ERROR_TYPE.apiError, message: `${THREEDS2_CHALLENGE_ERROR}: Missing 'paymentData' property from threeDS2 action`, code: Analytics3DS2Errors.ACTION_IS_MISSING_PAYMENT_DATA } diff --git a/packages/lib/src/components/ThreeDS2/ThreeDS2Challenge.tsx b/packages/lib/src/components/ThreeDS2/ThreeDS2Challenge.tsx index c336c299c3..7bfbf96a60 100644 --- a/packages/lib/src/components/ThreeDS2/ThreeDS2Challenge.tsx +++ b/packages/lib/src/components/ThreeDS2/ThreeDS2Challenge.tsx @@ -7,7 +7,7 @@ import { hasOwnProperty } from '../../utils/hasOwnProperty'; import { TxVariants } from '../tx-variants'; import { ThreeDS2ChallengeConfiguration } from './types'; import AdyenCheckoutError, { API_ERROR } from '../../core/Errors/AdyenCheckoutError'; -import { ANALYTICS_API_ERROR, Analytics3DS2Errors, ANALYTICS_RENDERED_STR, Analytics3DS2Events } from '../../core/Analytics/constants'; +import { ANALYTICS_ERROR_TYPE, Analytics3DS2Errors, ANALYTICS_RENDERED_STR, Analytics3DS2Events } from '../../core/Analytics/constants'; import { SendAnalyticsObject } from '../../core/Analytics/types'; import { CoreProvider } from '../../core/Context/CoreProvider'; import { ActionHandledReturnObject } from '../../types/global-types'; @@ -61,7 +61,7 @@ class ThreeDS2Challenge extends UIElement { this.submitAnalytics({ type: THREEDS2_ERROR, code: Analytics3DS2Errors.ACTION_IS_MISSING_PAYMENT_DATA, - errorType: ANALYTICS_API_ERROR, + errorType: ANALYTICS_ERROR_TYPE.apiError, message: `${THREEDS2_CHALLENGE_ERROR}: Missing 'paymentData' property from threeDS2 action` }); diff --git a/packages/lib/src/components/ThreeDS2/ThreeDS2DeviceFingerprint.test.tsx b/packages/lib/src/components/ThreeDS2/ThreeDS2DeviceFingerprint.test.tsx index b7abd7abbb..0920a35f9e 100644 --- a/packages/lib/src/components/ThreeDS2/ThreeDS2DeviceFingerprint.test.tsx +++ b/packages/lib/src/components/ThreeDS2/ThreeDS2DeviceFingerprint.test.tsx @@ -1,6 +1,6 @@ import { ThreeDS2DeviceFingerprint } from './index'; import Analytics from '../../core/Analytics'; -import { ANALYTICS_API_ERROR, Analytics3DS2Errors, ANALYTICS_EVENT_ERROR, ANALYTICS_RENDERED_STR } from '../../core/Analytics/constants'; +import { Analytics3DS2Errors, ANALYTICS_RENDERED_STR, ANALYTICS_EVENT, ANALYTICS_ERROR_TYPE } from '../../core/Analytics/constants'; import { THREEDS2_ERROR, THREEDS2_FINGERPRINT_ERROR } from './constants'; const analyticsModule = Analytics({ analytics: {}, loadingContext: '', locale: '', clientKey: '', bundleType: 'umd' }); @@ -32,11 +32,11 @@ describe('ThreeDS2DeviceFingerprint: calls that generate analytics should produc const view = fingerprint.render(); expect(analyticsModule.createAnalyticsEvent).toHaveBeenCalledWith({ - event: ANALYTICS_EVENT_ERROR, + event: ANALYTICS_EVENT.error, data: { component: fingerprint.constructor['type'], type: THREEDS2_ERROR, - errorType: ANALYTICS_API_ERROR, + errorType: ANALYTICS_ERROR_TYPE.apiError, message: `${THREEDS2_FINGERPRINT_ERROR}: Missing 'paymentData' property from threeDS2 action`, code: Analytics3DS2Errors.ACTION_IS_MISSING_PAYMENT_DATA } diff --git a/packages/lib/src/components/ThreeDS2/ThreeDS2DeviceFingerprint.tsx b/packages/lib/src/components/ThreeDS2/ThreeDS2DeviceFingerprint.tsx index cc42630db7..6e547e9785 100644 --- a/packages/lib/src/components/ThreeDS2/ThreeDS2DeviceFingerprint.tsx +++ b/packages/lib/src/components/ThreeDS2/ThreeDS2DeviceFingerprint.tsx @@ -6,7 +6,7 @@ import { existy } from '../../utils/commonUtils'; import { TxVariants } from '../tx-variants'; import { ThreeDS2DeviceFingerprintConfiguration } from './types'; import AdyenCheckoutError, { API_ERROR } from '../../core/Errors/AdyenCheckoutError'; -import { ANALYTICS_API_ERROR, Analytics3DS2Errors, ANALYTICS_RENDERED_STR, Analytics3DS2Events } from '../../core/Analytics/constants'; +import { ANALYTICS_ERROR_TYPE, Analytics3DS2Errors, ANALYTICS_RENDERED_STR, Analytics3DS2Events } from '../../core/Analytics/constants'; import { SendAnalyticsObject } from '../../core/Analytics/types'; import { THREEDS2_ERROR, THREEDS2_FINGERPRINT, THREEDS2_FINGERPRINT_ERROR, THREEDS2_FULL } from './constants'; import { ActionHandledReturnObject } from '../../types/global-types'; @@ -53,7 +53,7 @@ class ThreeDS2DeviceFingerprint extends UIElement { @@ -91,7 +91,7 @@ class PrepareChallenge3DS2 extends Component {}; const baseAnalyticsError = { type: THREEDS2_ERROR, - errorType: ANALYTICS_API_ERROR + errorType: ANALYTICS_ERROR_TYPE.apiError }; let completeFunction: any; @@ -147,7 +147,7 @@ describe('ThreeDS2DeviceFingerprint - flow completes with errors that are consid type: THREEDS2_ERROR, message: 'threeDS2Fingerprint: timeout', code: Analytics3DS2Errors.THREEDS2_TIMEOUT, - errorType: ANALYTICS_NETWORK_ERROR + errorType: ANALYTICS_ERROR_TYPE.network }); // analytics to say process is complete diff --git a/packages/lib/src/components/ThreeDS2/components/DeviceFingerprint/PrepareFingerprint3DS2.tsx b/packages/lib/src/components/ThreeDS2/components/DeviceFingerprint/PrepareFingerprint3DS2.tsx index d0e98322ad..77afc36314 100644 --- a/packages/lib/src/components/ThreeDS2/components/DeviceFingerprint/PrepareFingerprint3DS2.tsx +++ b/packages/lib/src/components/ThreeDS2/components/DeviceFingerprint/PrepareFingerprint3DS2.tsx @@ -15,7 +15,7 @@ import { THREEDS2_ERROR, TIMEOUT } from '../../constants'; -import { ANALYTICS_API_ERROR, ANALYTICS_NETWORK_ERROR, Analytics3DS2Errors, Analytics3DS2Events } from '../../../../core/Analytics/constants'; +import { ANALYTICS_ERROR_TYPE, Analytics3DS2Errors, Analytics3DS2Events } from '../../../../core/Analytics/constants'; class PrepareFingerprint3DS2 extends Component { public static type = 'scheme'; @@ -163,7 +163,7 @@ class PrepareFingerprint3DS2 extends Component { - const arrayName = type === ANALYTICS_EVENT_INFO ? type : `${type}s`; + const addAnalyticsEvent = (type: AnalyticsEvent, obj: AnalyticsObject) => { + const arrayName = type === ANALYTICS_EVENT.info ? type : `${type}s`; eventsQueue.add(`${arrayName}`, obj); /** @@ -45,7 +38,7 @@ const Analytics = ({ locale, clientKey, analytics, amount, analyticsContext, bun * - info events are stored until a log or error comes along, * but, if after a set time, no other analytics event (log or error) has come along then we send the info events anyway */ - if (type === ANALYTICS_EVENT_INFO) { + if (type === ANALYTICS_EVENT.info) { clearTimeout(sendEventsTimerId); sendEventsTimerId = setTimeout(() => void sendAnalyticsEvents(), ANALYTICS_INFO_TIMER_INTERVAL); } @@ -56,7 +49,7 @@ const Analytics = ({ locale, clientKey, analytics, amount, analyticsContext, bun * ...but... tests with the 3DS2 process show that many logs can happen almost at the same time (or you can have an error followed immediately by a log), * so instead of making several sequential api calls we see if we can "batch" them using debounce */ - if (type === ANALYTICS_EVENT_LOG || type === ANALYTICS_EVENT_ERROR) { + if (type === ANALYTICS_EVENT.log || type === ANALYTICS_EVENT.error) { clearTimeout(sendEventsTimerId); // clear any timer that might be about to dispatch the info events array debounce(sendAnalyticsEvents)(); diff --git a/packages/lib/src/core/Analytics/analyticsPreProcessor.test.ts b/packages/lib/src/core/Analytics/analyticsPreProcessor.test.ts index 0ff6b64c4e..02772164fd 100644 --- a/packages/lib/src/core/Analytics/analyticsPreProcessor.test.ts +++ b/packages/lib/src/core/Analytics/analyticsPreProcessor.test.ts @@ -1,7 +1,7 @@ import Analytics from './Analytics'; import { PaymentAmount } from '../../types'; import { analyticsPreProcessor } from './analyticsPreProcessor'; -import { ANALYTICS_EVENT_INFO, ANALYTICS_RENDERED_STR } from './constants'; +import { ANALYTICS_EVENT, ANALYTICS_RENDERED_STR } from './constants'; let analytics; let sendAnalytics; @@ -20,7 +20,7 @@ describe('Testing AnalyticsPreProcessor: process and output', () => { sendAnalytics('paywithgoogle', { type: ANALYTICS_RENDERED_STR }, { isExpress: true, expressPage: 'cart' }); expect(analytics.createAnalyticsEvent).toBeCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: 'paywithgoogle', type: 'rendered', @@ -34,7 +34,7 @@ describe('Testing AnalyticsPreProcessor: process and output', () => { sendAnalytics('paywithgoogle', { type: ANALYTICS_RENDERED_STR }, { isExpress: null, expressPage: 'cart' }); expect(analytics.createAnalyticsEvent).toBeCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: 'paywithgoogle', type: 'rendered' @@ -46,7 +46,7 @@ describe('Testing AnalyticsPreProcessor: process and output', () => { sendAnalytics('paywithgoogle', { type: ANALYTICS_RENDERED_STR }, { isExpress: undefined, expressPage: 'cart' }); expect(analytics.createAnalyticsEvent).toBeCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: 'paywithgoogle', type: 'rendered' @@ -58,7 +58,7 @@ describe('Testing AnalyticsPreProcessor: process and output', () => { sendAnalytics('paywithgoogle', { type: ANALYTICS_RENDERED_STR }, { isExpress: 'true', expressPage: 'cart' }); expect(analytics.createAnalyticsEvent).toBeCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: 'paywithgoogle', type: 'rendered' @@ -70,7 +70,7 @@ describe('Testing AnalyticsPreProcessor: process and output', () => { sendAnalytics('paywithgoogle', { type: ANALYTICS_RENDERED_STR }, { isExpress: false, expressPage: 'cart' }); expect(analytics.createAnalyticsEvent).toBeCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: 'paywithgoogle', type: 'rendered', @@ -83,7 +83,7 @@ describe('Testing AnalyticsPreProcessor: process and output', () => { sendAnalytics('paywithgoogle', { type: ANALYTICS_RENDERED_STR }, { isExpress: true, expressPage: 'foobar' }); expect(analytics.createAnalyticsEvent).toBeCalledWith({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component: 'paywithgoogle', type: 'rendered', diff --git a/packages/lib/src/core/Analytics/analyticsPreProcessor.ts b/packages/lib/src/core/Analytics/analyticsPreProcessor.ts index 4105b550f1..9707a333e7 100644 --- a/packages/lib/src/core/Analytics/analyticsPreProcessor.ts +++ b/packages/lib/src/core/Analytics/analyticsPreProcessor.ts @@ -5,9 +5,6 @@ import { ANALYTICS_CONFIGURED_STR, ANALYTICS_DISPLAYED_STR, ANALYTICS_DOWNLOAD_STR, - ANALYTICS_EVENT_ERROR, - ANALYTICS_EVENT_INFO, - ANALYTICS_EVENT_LOG, ANALYTICS_FOCUS_STR, ANALYTICS_INPUT_STR, ANALYTICS_RENDERED_STR, @@ -15,7 +12,8 @@ import { ANALYTICS_SUBMIT_STR, ANALYTICS_UNFOCUS_STR, ANALYTICS_VALIDATION_ERROR_STR, - ANALYTICS_EXPRESS_PAGES_ARRAY + ANALYTICS_EXPRESS_PAGES_ARRAY, + ANALYTICS_EVENT } from './constants'; import { THREEDS2_ERROR, THREEDS2_FULL } from '../../components/ThreeDS2/constants'; import AdyenCheckoutError, { SDK_ERROR } from '../Errors/AdyenCheckoutError'; @@ -65,7 +63,7 @@ export const analyticsPreProcessor = (analyticsModule: AnalyticsModule) => { }; analyticsModule.createAnalyticsEvent({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data }); @@ -77,7 +75,7 @@ export const analyticsPreProcessor = (analyticsModule: AnalyticsModule) => { const data = { component, type, isStoredPaymentMethod, brand }; analyticsModule.createAnalyticsEvent({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data }); break; @@ -89,7 +87,7 @@ export const analyticsPreProcessor = (analyticsModule: AnalyticsModule) => { case ANALYTICS_INPUT_STR: // issuerList case ANALYTICS_DOWNLOAD_STR: // QR codes analyticsModule.createAnalyticsEvent({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component, type, target } }); break; @@ -99,7 +97,7 @@ export const analyticsPreProcessor = (analyticsModule: AnalyticsModule) => { case ANALYTICS_SELECTED_STR: { const { issuer } = analyticsObj; analyticsModule.createAnalyticsEvent({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component, type, target, issuer } }); break; @@ -108,7 +106,7 @@ export const analyticsPreProcessor = (analyticsModule: AnalyticsModule) => { case ANALYTICS_VALIDATION_ERROR_STR: { const { validationErrorCode, validationErrorMessage } = analyticsObj; analyticsModule.createAnalyticsEvent({ - event: ANALYTICS_EVENT_INFO, + event: ANALYTICS_EVENT.info, data: { component, type, target, validationErrorCode, validationErrorMessage } }); break; @@ -119,7 +117,7 @@ export const analyticsPreProcessor = (analyticsModule: AnalyticsModule) => { */ case ANALYTICS_SUBMIT_STR: analyticsModule.createAnalyticsEvent({ - event: ANALYTICS_EVENT_LOG, + event: ANALYTICS_EVENT.log, data: { component, type, message: 'Shopper clicked pay' } }); break; @@ -127,7 +125,7 @@ export const analyticsPreProcessor = (analyticsModule: AnalyticsModule) => { case ANALYTICS_ACTION_STR: { const { subtype, message } = analyticsObj; analyticsModule.createAnalyticsEvent({ - event: ANALYTICS_EVENT_LOG, + event: ANALYTICS_EVENT.log, data: { component, type, subtype, message } }); break; @@ -138,7 +136,7 @@ export const analyticsPreProcessor = (analyticsModule: AnalyticsModule) => { const { message, metadata, subtype, result } = analyticsObj; analyticsModule.createAnalyticsEvent({ - event: ANALYTICS_EVENT_LOG, + event: ANALYTICS_EVENT.log, data: { component, type, message, metadata, subtype, result } }); break; @@ -150,7 +148,7 @@ export const analyticsPreProcessor = (analyticsModule: AnalyticsModule) => { case THREEDS2_ERROR: { const { message, code, errorType } = analyticsObj; analyticsModule.createAnalyticsEvent({ - event: ANALYTICS_EVENT_ERROR, + event: ANALYTICS_EVENT.error, data: { component, type, message, code, errorType } }); break; diff --git a/packages/lib/src/core/Analytics/constants.ts b/packages/lib/src/core/Analytics/constants.ts index 16e0483c52..eb1623753a 100644 --- a/packages/lib/src/core/Analytics/constants.ts +++ b/packages/lib/src/core/Analytics/constants.ts @@ -12,9 +12,23 @@ export const ANALYTICS_INFO_TIMER_INTERVAL = process.env.NODE_ENV === 'developme export const ANALYTICS_SEARCH_DEBOUNCE_TIME = 3000; -export const ANALYTICS_EVENT_LOG = 'log'; -export const ANALYTICS_EVENT_ERROR = 'error'; -export const ANALYTICS_EVENT_INFO = 'info'; +export const ANALYTICS_EVENT = { + log: 'log', + error: 'error', + info: 'info' +}; + +export const ANALYTICS_ERROR_TYPE = { + network: 'Network', + implementation: 'Implementation', + internal: 'Internal', + apiError: 'ApiError', + sdkError: 'SdkError', + thirdParty: 'ThirdParty', + generic: 'Generic', + redirect: 'Redirect', + threeDS2: 'ThreeDS2' +}; export const ANALYTICS_ACTION_STR = 'action'; export const ANALYTICS_SUBMIT_STR = 'submit'; @@ -39,11 +53,6 @@ export const ANALYTICS_FEATURED_ISSUER = 'featured_issuer'; export const ANALYTICS_LIST = 'list'; export const ANALYTICS_LIST_SEARCH = 'list_search'; -export const ANALYTICS_IMPLEMENTATION_ERROR = 'ImplementationError'; -export const ANALYTICS_API_ERROR = 'ApiError'; -export const ANALYTICS_SDK_ERROR = 'SdkError'; -export const ANALYTICS_NETWORK_ERROR = 'Network'; - export enum Analytics3DS2Errors { ACTION_IS_MISSING_PAYMENT_DATA = '700', // Missing 'paymentData' property from threeDS2 action ACTION_IS_MISSING_TOKEN = '701', // Missing 'token' property from threeDS2 action diff --git a/packages/lib/src/core/Analytics/types.ts b/packages/lib/src/core/Analytics/types.ts index 32e5408169..c318174232 100644 --- a/packages/lib/src/core/Analytics/types.ts +++ b/packages/lib/src/core/Analytics/types.ts @@ -1,6 +1,7 @@ import { PaymentAmount } from '../../types'; import { CoreConfiguration } from '../types'; import { SocialSecurityMode } from '../../components/Card/types'; +import { ANALYTICS_EVENT } from './constants'; export interface Experiment { controlGroup: boolean; @@ -85,9 +86,9 @@ export interface AnalyticsObject { configData?: Record; } -export type ANALYTICS_EVENT = 'log' | 'error' | 'info'; +export type AnalyticsEvent = (typeof ANALYTICS_EVENT)[keyof typeof ANALYTICS_EVENT]; -export type CreateAnalyticsObject = Omit & { event: ANALYTICS_EVENT }; +export type CreateAnalyticsObject = Omit & { event: AnalyticsEvent }; export type AnalyticsInitialEvent = { containerWidth: number; @@ -108,7 +109,7 @@ export type AnalyticsConfig = { export type CreateAnalyticsEventData = Omit; export type CreateAnalyticsEventObject = { - event: ANALYTICS_EVENT; + event: AnalyticsEvent; data: CreateAnalyticsEventData; }; From 82c15fe55d6770a5bc896282e6795e4d5a1bc734 Mon Sep 17 00:00:00 2001 From: Yu Long Date: Mon, 18 Nov 2024 17:49:00 +0100 Subject: [PATCH 2/4] refactor(analytics): send redirect error event --- .../lib/src/components/Redirect/Redirect.tsx | 19 ++++++- .../internal/UIElement/UIElement.tsx | 20 +++++--- .../lib/src/core/Analytics/EventsQueue.ts | 10 ++-- .../core/Analytics/analyticsPreProcessor.ts | 3 +- packages/lib/src/core/Analytics/constants.ts | 4 ++ packages/lib/src/core/Analytics/types.ts | 4 +- packages/lib/src/core/Analytics/utils.ts | 51 ++++++++++--------- 7 files changed, 71 insertions(+), 40 deletions(-) diff --git a/packages/lib/src/components/Redirect/Redirect.tsx b/packages/lib/src/components/Redirect/Redirect.tsx index 8abe8425b6..33f8f0955f 100644 --- a/packages/lib/src/components/Redirect/Redirect.tsx +++ b/packages/lib/src/components/Redirect/Redirect.tsx @@ -6,6 +6,7 @@ import RedirectButton from '../internal/RedirectButton'; import { TxVariants } from '../tx-variants'; import { RedirectConfiguration } from './types'; import collectBrowserInfo from '../../utils/browserInfo'; +import { ANALYTICS_ERROR_CODE, ANALYTICS_ERROR_TYPE, ANALYTICS_EVENT } from '../../core/Analytics/constants'; class RedirectElement extends UIElement { public static type = TxVariants.redirect; @@ -23,6 +24,15 @@ class RedirectElement extends UIElement { }; } + private handleRedirectError = () => { + super.submitAnalytics({ + component: this.props.paymentMethodType, + type: ANALYTICS_EVENT.error, + errorType: ANALYTICS_ERROR_TYPE.redirect, + code: ANALYTICS_ERROR_CODE.redirect + }); + }; + get isValid() { return true; } @@ -33,7 +43,14 @@ class RedirectElement extends UIElement { render() { if (this.props.url && this.props.method) { - return ; + return ( + + ); } if (this.props.showPayButton) { diff --git a/packages/lib/src/components/internal/UIElement/UIElement.tsx b/packages/lib/src/components/internal/UIElement/UIElement.tsx index 7fbdeee913..945de0637b 100644 --- a/packages/lib/src/components/internal/UIElement/UIElement.tsx +++ b/packages/lib/src/components/internal/UIElement/UIElement.tsx @@ -161,17 +161,25 @@ export abstract class UIElement

exten * - otherwise, distinguish cards from non-cards: cards will use their static type property, everything else will use props.type */ try { - let component = this.constructor['analyticsType']; - if (!component) { - component = this.constructor['type'] === 'scheme' || this.constructor['type'] === 'bcmc' ? this.constructor['type'] : this.props.type; - } - - this.props.modules.analytics.sendAnalytics(component, analyticsObj, uiElementProps); + this.props.modules.analytics.sendAnalytics(this.getComponent(analyticsObj), analyticsObj, uiElementProps); } catch (error) { console.warn('Failed to submit the analytics event. Error:', error); } } + private getComponent({ component }: SendAnalyticsObject): string { + if (component) { + return component; + } + if (this.constructor['analyticsType']) { + return this.constructor['analyticsType']; + } + if (this.constructor['type'] === 'scheme' || this.constructor['type'] === 'bcmc') { + return this.constructor['type']; + } + return this.props.type; + } + public submit(): void { if (!this.isValid) { this.showValidation(); diff --git a/packages/lib/src/core/Analytics/EventsQueue.ts b/packages/lib/src/core/Analytics/EventsQueue.ts index 71deae0e9e..f48fadb8b4 100644 --- a/packages/lib/src/core/Analytics/EventsQueue.ts +++ b/packages/lib/src/core/Analytics/EventsQueue.ts @@ -24,7 +24,7 @@ const EventsQueue = ({ analyticsContext, clientKey, analyticsPath }: EventQueueP logs: [] }; - const runQueue = (checkoutAttemptId: string): Promise => { + const runQueue = async (checkoutAttemptId: string): Promise => { if (!caActions.info.length && !caActions.logs.length && !caActions.errors.length) { return Promise.resolve(null); } @@ -35,7 +35,7 @@ const EventsQueue = ({ analyticsContext, clientKey, analyticsPath }: EventQueueP path: `${analyticsPath}/${checkoutAttemptId}?clientKey=${clientKey}` }; - const promise = httpPost(options, caActions) + return httpPost(options, caActions) .then(() => { // Succeed, silently return undefined; @@ -44,11 +44,9 @@ const EventsQueue = ({ analyticsContext, clientKey, analyticsPath }: EventQueueP // Caught, silently, at http level. We do not expect this catch block to ever fire, but... just in case... console.debug('### EventsQueue:::: send has failed'); }); - - return promise; }; - const eqModule: EventsQueueModule = { + return { add: (type, actionObj) => { caActions[type].push(actionObj); }, @@ -66,8 +64,6 @@ const EventsQueue = ({ analyticsContext, clientKey, analyticsPath }: EventQueueP // Expose getter for testing purposes getQueue: () => caActions }; - - return eqModule; }; export default EventsQueue; diff --git a/packages/lib/src/core/Analytics/analyticsPreProcessor.ts b/packages/lib/src/core/Analytics/analyticsPreProcessor.ts index 9707a333e7..7030da0a74 100644 --- a/packages/lib/src/core/Analytics/analyticsPreProcessor.ts +++ b/packages/lib/src/core/Analytics/analyticsPreProcessor.ts @@ -145,7 +145,8 @@ export const analyticsPreProcessor = (analyticsModule: AnalyticsModule) => { /** * ERRORS */ - case THREEDS2_ERROR: { + case THREEDS2_ERROR: + case ANALYTICS_EVENT.error: { const { message, code, errorType } = analyticsObj; analyticsModule.createAnalyticsEvent({ event: ANALYTICS_EVENT.error, diff --git a/packages/lib/src/core/Analytics/constants.ts b/packages/lib/src/core/Analytics/constants.ts index eb1623753a..52de8182f5 100644 --- a/packages/lib/src/core/Analytics/constants.ts +++ b/packages/lib/src/core/Analytics/constants.ts @@ -30,6 +30,10 @@ export const ANALYTICS_ERROR_TYPE = { threeDS2: 'ThreeDS2' }; +export const ANALYTICS_ERROR_CODE = { + redirect: '600' +}; + export const ANALYTICS_ACTION_STR = 'action'; export const ANALYTICS_SUBMIT_STR = 'submit'; export const ANALYTICS_SELECTED_STR = 'selected'; diff --git a/packages/lib/src/core/Analytics/types.ts b/packages/lib/src/core/Analytics/types.ts index c318174232..8a0599d891 100644 --- a/packages/lib/src/core/Analytics/types.ts +++ b/packages/lib/src/core/Analytics/types.ts @@ -115,7 +115,9 @@ export type CreateAnalyticsEventObject = { export type EventQueueProps = Pick & { analyticsPath: string }; -export type SendAnalyticsObject = Omit; +export interface SendAnalyticsObject extends Omit { + component?: string; +} export type FieldErrorAnalyticsObject = { fieldType: string; diff --git a/packages/lib/src/core/Analytics/utils.ts b/packages/lib/src/core/Analytics/utils.ts index ee6ac4a96f..89bd77f985 100644 --- a/packages/lib/src/core/Analytics/utils.ts +++ b/packages/lib/src/core/Analytics/utils.ts @@ -30,30 +30,33 @@ export const getUTCTimestamp = () => Date.now(); * * All objects can also have a "metadata" object of key-value pairs */ -export const createAnalyticsObject = (aObj: CreateAnalyticsObject): AnalyticsObject => ({ - timestamp: String(getUTCTimestamp()), - component: aObj.component, - id: uuid(), - /** ERROR */ - ...(aObj.event === 'error' && { code: aObj.code, errorType: aObj.errorType, message: aObj.message }), // error event - /** LOG */ - ...(aObj.event === 'log' && { type: aObj.type, message: aObj.message }), // log event - ...(aObj.event === 'log' && (aObj.type === ANALYTICS_ACTION_STR || aObj.type === THREEDS2_FULL) && { subType: aObj.subtype }), // only added if we have a log event of Action type or ThreeDS2 - ...(aObj.event === 'log' && aObj.type === THREEDS2_FULL && { result: aObj.result }), // only added if we have a log event of ThreeDS2 type - /** INFO */ - ...(aObj.event === 'info' && { type: aObj.type, target: aObj.target }), // info event - ...(aObj.event === 'info' && aObj.issuer && { issuer: aObj.issuer }), // relates to issuerLists - ...(aObj.event === 'info' && { isExpress: aObj.isExpress, expressPage: aObj.expressPage }), // relates to Plugins & detecting Express PMs - ...(aObj.event === 'info' && aObj.isStoredPaymentMethod && { isStoredPaymentMethod: aObj.isStoredPaymentMethod, brand: aObj.brand }), // only added if we have an info event about a storedPM - ...(aObj.event === 'info' && - aObj.type === ANALYTICS_VALIDATION_ERROR_STR && { - validationErrorCode: mapErrorCodesForAnalytics(aObj.validationErrorCode, aObj.target), - validationErrorMessage: aObj.validationErrorMessage - }), // only added if we have an info event describing a validation error - ...(aObj.configData && { configData: aObj.configData }), - /** All */ - ...(aObj.metadata && { metadata: aObj.metadata }) -}); + +export const createAnalyticsObject = (aObj: CreateAnalyticsObject): AnalyticsObject => { + return { + timestamp: String(getUTCTimestamp()), + component: aObj.component, + id: uuid(), + /** ERROR */ + ...(aObj.event === 'error' && { code: aObj.code, errorType: aObj.errorType, message: aObj.message }), // error event + /** LOG */ + ...(aObj.event === 'log' && { type: aObj.type, message: aObj.message }), // log event + ...(aObj.event === 'log' && (aObj.type === ANALYTICS_ACTION_STR || aObj.type === THREEDS2_FULL) && { subType: aObj.subtype }), // only added if we have a log event of Action type or ThreeDS2 + ...(aObj.event === 'log' && aObj.type === THREEDS2_FULL && { result: aObj.result }), // only added if we have a log event of ThreeDS2 type + /** INFO */ + ...(aObj.event === 'info' && { type: aObj.type, target: aObj.target }), // info event + ...(aObj.event === 'info' && aObj.issuer && { issuer: aObj.issuer }), // relates to issuerLists + ...(aObj.event === 'info' && { isExpress: aObj.isExpress, expressPage: aObj.expressPage }), // relates to Plugins & detecting Express PMs + ...(aObj.event === 'info' && aObj.isStoredPaymentMethod && { isStoredPaymentMethod: aObj.isStoredPaymentMethod, brand: aObj.brand }), // only added if we have an info event about a storedPM + ...(aObj.event === 'info' && + aObj.type === ANALYTICS_VALIDATION_ERROR_STR && { + validationErrorCode: mapErrorCodesForAnalytics(aObj.validationErrorCode, aObj.target), + validationErrorMessage: aObj.validationErrorMessage + }), // only added if we have an info event describing a validation error + ...(aObj.configData && { configData: aObj.configData }), + /** All */ + ...(aObj.metadata && { metadata: aObj.metadata }) + }; +}; const mapErrorCodesForAnalytics = (errorCode: string, target: string) => { // Some of the more generic error codes required combination with target to retrieve a specific code From 6b7c6ae3309296e341f194a001c0cf37f60d819d Mon Sep 17 00:00:00 2001 From: Yu Long Date: Wed, 20 Nov 2024 11:43:59 +0100 Subject: [PATCH 3/4] test: added test --- .changeset/new-bags-crash.md | 5 ++ .../src/components/Redirect/Redirect.test.tsx | 81 ++++++++++++++++++- .../RedirectShopper/RedirectShopper.tsx | 1 + packages/lib/src/core/Analytics/constants.ts | 2 +- packages/lib/src/core/Analytics/types.ts | 4 +- packages/lib/src/core/Analytics/utils.ts | 51 ++++++------ 6 files changed, 111 insertions(+), 33 deletions(-) create mode 100644 .changeset/new-bags-crash.md diff --git a/.changeset/new-bags-crash.md b/.changeset/new-bags-crash.md new file mode 100644 index 0000000000..c3063c7407 --- /dev/null +++ b/.changeset/new-bags-crash.md @@ -0,0 +1,5 @@ +--- +'@adyen/adyen-web': patch +--- + +Send redirection error events to analytics. diff --git a/packages/lib/src/components/Redirect/Redirect.test.tsx b/packages/lib/src/components/Redirect/Redirect.test.tsx index 0c3cf8a8d4..984e2ab2fe 100644 --- a/packages/lib/src/components/Redirect/Redirect.test.tsx +++ b/packages/lib/src/components/Redirect/Redirect.test.tsx @@ -1,8 +1,10 @@ import { mount } from 'enzyme'; +import { render, waitFor, screen } from '@testing-library/preact'; import { h } from 'preact'; -import Redirect from './Redirect'; import RedirectShopper from './components/RedirectShopper'; import RedirectElement from './Redirect'; +import Analytics from '../../core/Analytics'; +import { RedirectConfiguration } from './types'; jest.mock('../../utils/detectInIframeInSameOrigin', () => { return jest.fn().mockImplementation(() => { @@ -13,7 +15,7 @@ jest.mock('../../utils/detectInIframeInSameOrigin', () => { describe('Redirect', () => { describe('isValid', () => { test('Is always valid', () => { - const redirect = new Redirect(global.core, { type: 'redirect' }); + const redirect = new RedirectElement(global.core, { type: 'redirect' }); expect(redirect.isValid).toBe(true); }); }); @@ -57,3 +59,78 @@ describe('Redirect', () => { }); }); }); + +describe('Redirect error', () => { + const oldWindowLocation = window.location; + + beforeAll(() => { + delete window.location; + // @ts-ignore test only + window.location = Object.defineProperties( + {}, + { + ...Object.getOwnPropertyDescriptors(oldWindowLocation), + assign: { + configurable: true, + value: jest.fn() + } + } + ); + }); + + afterAll(() => { + window.location = oldWindowLocation; + }); + + test('should send an error event to the analytics module if beforeRedirect rejects', async () => { + const analytics = Analytics({ analytics: {}, loadingContext: '', locale: '', clientKey: '', bundleType: '' }); + analytics.sendAnalytics = jest.fn(() => {}); + const props: RedirectConfiguration = { + url: 'test', + method: 'POST', + paymentMethodType: 'ideal', + modules: { analytics }, + beforeRedirect: (_, reject) => { + return reject(); + } + }; + + const redirectElement = new RedirectElement(global.core, props); + render(redirectElement.render()); + await waitFor(() => { + expect(screen.getByTestId('redirect-shopper-form')).toBeInTheDocument(); + }); + + expect(analytics.sendAnalytics).toHaveBeenCalledWith( + 'ideal', + { code: '600', component: 'ideal', errorType: 'Redirect', type: 'error' }, + undefined + ); + }); + + test('should send an error event to the analytics module if the redirection failed', async () => { + (window.location.assign as jest.Mock).mockImplementation(() => { + throw new Error('Mock error'); + }); + + const analytics = Analytics({ analytics: {}, loadingContext: '', locale: '', clientKey: '', bundleType: '' }); + analytics.sendAnalytics = jest.fn(() => {}); + const props: RedirectConfiguration = { + url: 'test', + method: 'GET', + paymentMethodType: 'ideal', + modules: { analytics } + }; + + const redirectElement = new RedirectElement(global.core, props); + render(redirectElement.render()); + + await waitFor(() => { + expect(analytics.sendAnalytics).toHaveBeenCalledWith( + 'ideal', + { code: '600', component: 'ideal', errorType: 'Redirect', type: 'error' }, + undefined + ); + }); + }); +}); diff --git a/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx b/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx index 0702319c97..3210632dbe 100644 --- a/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx +++ b/packages/lib/src/components/Redirect/components/RedirectShopper/RedirectShopper.tsx @@ -61,6 +61,7 @@ class RedirectShopper extends Component { return (

{ diff --git a/packages/lib/src/core/Analytics/constants.ts b/packages/lib/src/core/Analytics/constants.ts index 52de8182f5..94ac1472bb 100644 --- a/packages/lib/src/core/Analytics/constants.ts +++ b/packages/lib/src/core/Analytics/constants.ts @@ -20,7 +20,7 @@ export const ANALYTICS_EVENT = { export const ANALYTICS_ERROR_TYPE = { network: 'Network', - implementation: 'Implementation', + implementation: 'ImplementationError', internal: 'Internal', apiError: 'ApiError', sdkError: 'SdkError', diff --git a/packages/lib/src/core/Analytics/types.ts b/packages/lib/src/core/Analytics/types.ts index 8a0599d891..a1fef53355 100644 --- a/packages/lib/src/core/Analytics/types.ts +++ b/packages/lib/src/core/Analytics/types.ts @@ -115,9 +115,7 @@ export type CreateAnalyticsEventObject = { export type EventQueueProps = Pick & { analyticsPath: string }; -export interface SendAnalyticsObject extends Omit { - component?: string; -} +export type SendAnalyticsObject = Omit & { component?: string }; export type FieldErrorAnalyticsObject = { fieldType: string; diff --git a/packages/lib/src/core/Analytics/utils.ts b/packages/lib/src/core/Analytics/utils.ts index 89bd77f985..ee6ac4a96f 100644 --- a/packages/lib/src/core/Analytics/utils.ts +++ b/packages/lib/src/core/Analytics/utils.ts @@ -30,33 +30,30 @@ export const getUTCTimestamp = () => Date.now(); * * All objects can also have a "metadata" object of key-value pairs */ - -export const createAnalyticsObject = (aObj: CreateAnalyticsObject): AnalyticsObject => { - return { - timestamp: String(getUTCTimestamp()), - component: aObj.component, - id: uuid(), - /** ERROR */ - ...(aObj.event === 'error' && { code: aObj.code, errorType: aObj.errorType, message: aObj.message }), // error event - /** LOG */ - ...(aObj.event === 'log' && { type: aObj.type, message: aObj.message }), // log event - ...(aObj.event === 'log' && (aObj.type === ANALYTICS_ACTION_STR || aObj.type === THREEDS2_FULL) && { subType: aObj.subtype }), // only added if we have a log event of Action type or ThreeDS2 - ...(aObj.event === 'log' && aObj.type === THREEDS2_FULL && { result: aObj.result }), // only added if we have a log event of ThreeDS2 type - /** INFO */ - ...(aObj.event === 'info' && { type: aObj.type, target: aObj.target }), // info event - ...(aObj.event === 'info' && aObj.issuer && { issuer: aObj.issuer }), // relates to issuerLists - ...(aObj.event === 'info' && { isExpress: aObj.isExpress, expressPage: aObj.expressPage }), // relates to Plugins & detecting Express PMs - ...(aObj.event === 'info' && aObj.isStoredPaymentMethod && { isStoredPaymentMethod: aObj.isStoredPaymentMethod, brand: aObj.brand }), // only added if we have an info event about a storedPM - ...(aObj.event === 'info' && - aObj.type === ANALYTICS_VALIDATION_ERROR_STR && { - validationErrorCode: mapErrorCodesForAnalytics(aObj.validationErrorCode, aObj.target), - validationErrorMessage: aObj.validationErrorMessage - }), // only added if we have an info event describing a validation error - ...(aObj.configData && { configData: aObj.configData }), - /** All */ - ...(aObj.metadata && { metadata: aObj.metadata }) - }; -}; +export const createAnalyticsObject = (aObj: CreateAnalyticsObject): AnalyticsObject => ({ + timestamp: String(getUTCTimestamp()), + component: aObj.component, + id: uuid(), + /** ERROR */ + ...(aObj.event === 'error' && { code: aObj.code, errorType: aObj.errorType, message: aObj.message }), // error event + /** LOG */ + ...(aObj.event === 'log' && { type: aObj.type, message: aObj.message }), // log event + ...(aObj.event === 'log' && (aObj.type === ANALYTICS_ACTION_STR || aObj.type === THREEDS2_FULL) && { subType: aObj.subtype }), // only added if we have a log event of Action type or ThreeDS2 + ...(aObj.event === 'log' && aObj.type === THREEDS2_FULL && { result: aObj.result }), // only added if we have a log event of ThreeDS2 type + /** INFO */ + ...(aObj.event === 'info' && { type: aObj.type, target: aObj.target }), // info event + ...(aObj.event === 'info' && aObj.issuer && { issuer: aObj.issuer }), // relates to issuerLists + ...(aObj.event === 'info' && { isExpress: aObj.isExpress, expressPage: aObj.expressPage }), // relates to Plugins & detecting Express PMs + ...(aObj.event === 'info' && aObj.isStoredPaymentMethod && { isStoredPaymentMethod: aObj.isStoredPaymentMethod, brand: aObj.brand }), // only added if we have an info event about a storedPM + ...(aObj.event === 'info' && + aObj.type === ANALYTICS_VALIDATION_ERROR_STR && { + validationErrorCode: mapErrorCodesForAnalytics(aObj.validationErrorCode, aObj.target), + validationErrorMessage: aObj.validationErrorMessage + }), // only added if we have an info event describing a validation error + ...(aObj.configData && { configData: aObj.configData }), + /** All */ + ...(aObj.metadata && { metadata: aObj.metadata }) +}); const mapErrorCodesForAnalytics = (errorCode: string, target: string) => { // Some of the more generic error codes required combination with target to retrieve a specific code From 66856c9cf2c15a326eb03bce3f72786c87e32d07 Mon Sep 17 00:00:00 2001 From: Yu Long Date: Thu, 5 Dec 2024 15:29:36 +0100 Subject: [PATCH 4/4] refactor: remove `async` for `runQueue` --- packages/lib/src/core/Analytics/EventsQueue.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/src/core/Analytics/EventsQueue.ts b/packages/lib/src/core/Analytics/EventsQueue.ts index f48fadb8b4..f6c5ed9b16 100644 --- a/packages/lib/src/core/Analytics/EventsQueue.ts +++ b/packages/lib/src/core/Analytics/EventsQueue.ts @@ -24,7 +24,7 @@ const EventsQueue = ({ analyticsContext, clientKey, analyticsPath }: EventQueueP logs: [] }; - const runQueue = async (checkoutAttemptId: string): Promise => { + const runQueue = (checkoutAttemptId: string): Promise => { if (!caActions.info.length && !caActions.logs.length && !caActions.errors.length) { return Promise.resolve(null); }