From 88a9b33846c22bc35ea44ec73a1096fd8670cc5d Mon Sep 17 00:00:00 2001 From: Robert Ing Date: Wed, 8 Nov 2023 17:38:11 -0500 Subject: [PATCH] Do not mutate endpoints, rename to baseUrl --- src/apiClient.ts | 6 +- src/constants.ts | 2 +- src/mockBatchCreator.ts | 1 - src/sdkRuntimeModels.ts | 2 +- src/store.ts | 133 ++++++++++++++++++++++--------------- test/src/tests-core-sdk.js | 9 +-- test/src/tests-store.ts | 6 +- 7 files changed, 89 insertions(+), 70 deletions(-) diff --git a/src/apiClient.ts b/src/apiClient.ts index d11f6973..9acc2c1e 100644 --- a/src/apiClient.ts +++ b/src/apiClient.ts @@ -42,10 +42,10 @@ export default function APIClient( const self = this; this.queueEventForBatchUpload = function(event: SDKEvent) { if (!this.uploader) { - const millis: string = mpInstance._Helpers.getFeatureFlag( + const millis: number = parseNumber(mpInstance._Helpers.getFeatureFlag( Constants.FeatureFlags.EventBatchingIntervalMillis - ); - this.uploader = new BatchUploader(mpInstance, parseNumber(millis)); + )); + this.uploader = new BatchUploader(mpInstance, millis); } this.uploader.queueEvent(event); diff --git a/src/constants.ts b/src/constants.ts index f18f3e1f..8a0ed83b 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -126,7 +126,7 @@ const Constants = { integrationDelayTimeout: 5000, // Milliseconds for forcing the integration delay to un-suspend event queueing due to integration partner errors maxCookieSize: 3000, // Number of bytes for cookie size to not exceed aliasMaxWindow: 90, // Max age of Alias request startTime, in days - uploadInterval: '0', // Maximum milliseconds in between batch uploads, below 500 will mean immediate upload + uploadInterval: '0', // Maximum milliseconds in between batch uploads, below 500 will mean immediate upload. This is a string because that is how the server returns it }, DefaultUrls: { v1SecureServiceUrl: 'jssdks.mparticle.com/v1/JS/', diff --git a/src/mockBatchCreator.ts b/src/mockBatchCreator.ts index 6ac8fd0a..f07bd2d4 100644 --- a/src/mockBatchCreator.ts +++ b/src/mockBatchCreator.ts @@ -27,7 +27,6 @@ export default class _BatchValidator { createServiceUrl: mockFunction, parseNumber: mockFunction, isObject: mockFunction, - getFeatureFlag: mockFunction, returnConvertedBoolean: mockFunction, Validators: null, }, diff --git a/src/sdkRuntimeModels.ts b/src/sdkRuntimeModels.ts index 671bf3bd..083afb20 100644 --- a/src/sdkRuntimeModels.ts +++ b/src/sdkRuntimeModels.ts @@ -246,7 +246,7 @@ export interface SDKHelpersApi { parseNumber?(value: string | number): number; generateUniqueId(); generateHash?(value: string): string; - getFeatureFlag(feature: string); // TODO: Feature Constants should be converted to enum + getFeatureFlag?(feature: string); // TODO: Feature Constants should be converted to enum isDelayedByIntegration?( delayedIntegrations: Dictionary, timeoutStart: number, diff --git a/src/store.ts b/src/store.ts index 1ffbfb95..2e79fb16 100644 --- a/src/store.ts +++ b/src/store.ts @@ -111,6 +111,13 @@ interface WrapperSDKInfo { isInfoSet: boolean; } +export interface IFeatureFlags { + reportBatching?: string; + eventBatchingIntervalMillis?: string; + offlineStorage?: string; + directURLRouting?: boolean; +} + // Temporary Interface until Store can be refactored as a class export interface IStore { isEnabled: boolean; @@ -161,7 +168,7 @@ export default function Store( this: IStore, config: SDKInitConfig, mpInstance: MParticleWebSDK, - apiKey: string, + apiKey: string ) { const defaultStore: Partial = { isEnabled: true, @@ -242,7 +249,7 @@ export default function Store( this.SDKConfig.flags, apiKey ); - console.log(endpoints); + for (const endpointKey in endpoints) { this.SDKConfig[endpointKey] = endpoints[endpointKey]; } @@ -255,7 +262,8 @@ export default function Store( this.SDKConfig.kits = config.kits || {}; - this.SDKConfig.sideloadedKits = config.sideloadedKits || []; + this.SDKConfig.sideloadedKits = + config.sideloadedKits || []; if (config.hasOwnProperty('isIOS')) { this.SDKConfig.isIOS = config.isIOS; @@ -267,7 +275,8 @@ export default function Store( } if (config.hasOwnProperty('useCookieStorage')) { - this.SDKConfig.useCookieStorage = config.useCookieStorage; + this.SDKConfig.useCookieStorage = + config.useCookieStorage; } else { this.SDKConfig.useCookieStorage = false; } @@ -275,7 +284,8 @@ export default function Store( if (config.hasOwnProperty('maxProducts')) { this.SDKConfig.maxProducts = config.maxProducts; } else { - this.SDKConfig.maxProducts = Constants.DefaultConfig.maxProducts; + this.SDKConfig.maxProducts = + Constants.DefaultConfig.maxProducts; } if (config.hasOwnProperty('maxCookieSize')) { @@ -302,13 +312,19 @@ export default function Store( } if (config.hasOwnProperty('identifyRequest')) { - this.SDKConfig.identifyRequest = config.identifyRequest; + this.SDKConfig.identifyRequest = + config.identifyRequest; } if (config.hasOwnProperty('identityCallback')) { var callback = config.identityCallback; - if (mpInstance._Helpers.Validators.isFunction(callback)) { - this.SDKConfig.identityCallback = config.identityCallback; + if ( + mpInstance._Helpers.Validators.isFunction( + callback + ) + ) { + this.SDKConfig.identityCallback = + config.identityCallback; } else { mpInstance.Logger.warning( 'The optional callback must be a function. You tried entering a(n) ' + @@ -327,7 +343,8 @@ export default function Store( } if (config.hasOwnProperty('sessionTimeout')) { - this.SDKConfig.sessionTimeout = config.sessionTimeout; + this.SDKConfig.sessionTimeout = + config.sessionTimeout; } if (config.hasOwnProperty('dataPlan')) { @@ -339,7 +356,8 @@ export default function Store( const dataPlan = config.dataPlan as DataPlanConfig; if (dataPlan.planId) { if (isDataPlanSlug(dataPlan.planId)) { - this.SDKConfig.dataPlan.PlanId = dataPlan.planId; + this.SDKConfig.dataPlan.PlanId = + dataPlan.planId; } else { mpInstance.Logger.error( 'Your data plan id must be a string and match the data plan slug format (i.e. under_case_slug)' @@ -349,7 +367,8 @@ export default function Store( if (dataPlan.planVersion) { if (isNumber(dataPlan.planVersion)) { - this.SDKConfig.dataPlan.PlanVersion = dataPlan.planVersion; + this.SDKConfig.dataPlan.PlanVersion = + dataPlan.planVersion; } else { mpInstance.Logger.error( 'Your data plan version must be a number' @@ -377,7 +396,8 @@ export default function Store( } if (config.hasOwnProperty('aliasMaxWindow')) { - this.SDKConfig.aliasMaxWindow = config.aliasMaxWindow; + this.SDKConfig.aliasMaxWindow = + config.aliasMaxWindow; } else { this.SDKConfig.aliasMaxWindow = Constants.DefaultConfig.aliasMaxWindow; @@ -386,11 +406,19 @@ export default function Store( if (config.hasOwnProperty('dataPlanOptions')) { const dataPlanOptions = config.dataPlanOptions; if ( - !dataPlanOptions.hasOwnProperty('dataPlanVersion') || - !dataPlanOptions.hasOwnProperty('blockUserAttributes') || - !dataPlanOptions.hasOwnProperty('blockEventAttributes') || + !dataPlanOptions.hasOwnProperty( + 'dataPlanVersion' + ) || + !dataPlanOptions.hasOwnProperty( + 'blockUserAttributes' + ) || + !dataPlanOptions.hasOwnProperty( + 'blockEventAttributes' + ) || !dataPlanOptions.hasOwnProperty('blockEvents') || - !dataPlanOptions.hasOwnProperty('blockUserIdentities') + !dataPlanOptions.hasOwnProperty( + 'blockUserIdentities' + ) ) { mpInstance.Logger.error( 'Ensure your config.dataPlanOptions object has the following keys: a "dataPlanVersion" object, and "blockUserAttributes", "blockEventAttributes", "blockEvents", "blockUserIdentities" booleans' @@ -400,7 +428,8 @@ export default function Store( if (config.hasOwnProperty('onCreateBatch')) { if (typeof config.onCreateBatch === 'function') { - this.SDKConfig.onCreateBatch = config.onCreateBatch; + this.SDKConfig.onCreateBatch = + config.onCreateBatch; } else { mpInstance.Logger.error( 'config.onCreateBatch must be a function' @@ -412,13 +441,6 @@ export default function Store( } } -export interface IFeatureFlags { - reportBatching?: string; - eventBatchingIntervalMillis?: string; - offlineStorage?: string; - directURLRouting?: boolean; -} - export function processFlags( config: SDKInitConfig, SDKConfig: SDKConfig @@ -449,7 +471,7 @@ export function processFlags( export function processEndpoints( config: SDKInitConfig, flags: IFeatureFlags, - apiKey: string + apiKey?: string ): Dictionary { // an API key is not present in a webview only mode. In this case, no endpoints are needed if (!apiKey) { @@ -457,61 +479,62 @@ export function processEndpoints( } // Set default endpoints - let endpoints: Dictionary = JSON.parse( - JSON.stringify(Constants.DefaultUrls) - ); + let endpoints: Dictionary; // When direct URL routing is false, update endpoints based custom urls // passed to the config - if (!flags.directURLRouting) { - return processNonDirectEndpoints(endpoints, config); + if (flags.directURLRouting) { + return processDirectUrlEndpoints(config, apiKey); } else { - return processDirectUrlEndpoints(endpoints, config, apiKey); + return processNonDirectEndpoints(config); } } -function processNonDirectEndpoints( - endpoints: Dictionary, - config: SDKInitConfig -): Dictionary { - for (let endpoint in endpoints) { - if (config.hasOwnProperty(endpoint)) { - endpoints[endpoint] = config[endpoint]; - } +function processNonDirectEndpoints(config: SDKInitConfig): Dictionary { + let defaultBaseUrls: Dictionary = Constants.DefaultUrls; + let newBaseUrls: Dictionary = {}; + + for (let baseUrlKey in defaultBaseUrls) { + newBaseUrls[baseUrlKey] = config[baseUrlKey] || defaultBaseUrls[baseUrlKey]; } - return endpoints; + + return newBaseUrls; } function processDirectUrlEndpoints( - endpoints: Dictionary, config: SDKInitConfig, apiKey: string ): Dictionary { + let defaultBaseUrls = Constants.DefaultUrls; + let directBaseUrls: Dictionary = {}; // When Direct URL Routing is true, we create a new set of endpoints that // include the silo in the urls. mParticle API keys are prefixed with the // silo and a hyphen (ex. "us1-", "us2-", "eu1-"). us1 was the first silo, // and before other silos existed, there were no prefixes and all apiKeys // were us1. As such, if we split on a '-' and the resulting array length // is 1, then it is an older APIkey that should route to us1. - const splitKey: Array = apiKey.split('-'); - let routingPrefix: string; - - // when splitKey.length is greater than 1, then splitKey[0] will be + // When splitKey.length is greater than 1, then splitKey[0] will be // us1, us2, eu1, au1, or st1, etc as new silos are added - routingPrefix = splitKey.length <= 1 ? 'us1' : splitKey[0]; + const DEFAULT_SILO = 'us1'; + const splitKey: Array = apiKey.split('-'); + const routingPrefix: string = + splitKey.length <= 1 ? DEFAULT_SILO : splitKey[0]; - for (let endpointKey in endpoints) { + for (let baseUrlKey in defaultBaseUrls) { // Any custom endpoints passed to config will take priority over direct // mapping to the silo - if (config.hasOwnProperty(endpointKey)) { - endpoints[endpointKey] = config[endpointKey]; + if (baseUrlKey === 'configUrl') { + directBaseUrls[baseUrlKey] = + config[baseUrlKey] || defaultBaseUrls[baseUrlKey]; + continue; + } + + if (config.hasOwnProperty(baseUrlKey)) { + directBaseUrls[baseUrlKey] = config[baseUrlKey]; } else { - if (endpointKey === 'configUrl') { - continue; - } - const urlparts = endpoints[endpointKey].split('.'); + const urlparts = defaultBaseUrls[baseUrlKey].split('.'); - endpoints[endpointKey] = [ + directBaseUrls[baseUrlKey] = [ urlparts[0], routingPrefix, ...urlparts.slice(1), @@ -519,5 +542,5 @@ function processDirectUrlEndpoints( } } - return endpoints; + return directBaseUrls; } \ No newline at end of file diff --git a/test/src/tests-core-sdk.js b/test/src/tests-core-sdk.js index 224fc8b8..63a29634 100644 --- a/test/src/tests-core-sdk.js +++ b/test/src/tests-core-sdk.js @@ -876,7 +876,6 @@ describe('core SDK', function() { mockServer.requests = []; mParticle.init(apiKey, window.mParticle.config); - window.mParticle.logEvent('Test Event'); fetchMock.lastOptions().body.should.be.ok() @@ -1136,7 +1135,7 @@ describe('core SDK', function() { }); describe('pod feature flag', function() { - const endpoints = JSON.parse(JSON.stringify(Constants.DefaultUrls)); + const endpoints = Constants.DefaultUrls; // set up URLs object for each silo let URLs = { us1: {}, @@ -1149,11 +1148,13 @@ describe('core SDK', function() { // The below function builds out the above URLs object to have silo-specific urls, ie: // URLs.us1.aliasUrl = 'jssdks.us1.mparticle.com/v1/identity/'; // URLs.us2.aliasUrl = 'jssdks.us2.mparticle.com/v1/identity/'; - // URLs.us1.configUrl = 'jssdkcdns.us1.mparticle.com/JS/v2/'; - // URLs.us2.configUrl = 'jssdkcdns.us2.mparticle.com/JS/v2/'; // etc, etc for each silo, and each endpoint Object.keys(URLs).forEach((key) => { for (let endpointKey in endpoints) { + if (endpointKey === 'configUrl') { + // Do not route config url to silo, use the default instead + URLs[key][endpointKey] = endpoints[endpointKey]; + } const endpointParts = endpoints[endpointKey].split('.'); URLs[key][endpointKey] = [endpointParts[0], key, ...endpointParts.slice(1)].join('.') } diff --git a/test/src/tests-store.ts b/test/src/tests-store.ts index cb9de58a..2bac9a52 100644 --- a/test/src/tests-store.ts +++ b/test/src/tests-store.ts @@ -235,10 +235,7 @@ describe('Store', () => { const featureFlags = { directURLRouting: false }; it('should return default endpoints if no endpoints are passed', () => { - const endpoints: Dictionary = JSON.parse( - JSON.stringify(Constants.DefaultUrls) - ); - + const endpoints: Dictionary = Constants.DefaultUrls; const result = processEndpoints( {} as unknown as SDKInitConfig, featureFlags as unknown as IFeatureFlags, @@ -270,7 +267,6 @@ describe('Store', () => { v1SecureServiceUrl: "jssdks.mparticle.com/v1/JS/", v2SecureServiceUrl: "jssdks.mparticle.com/v2/JS/", } - debugger expect(result).to.deep.equal(expectedResult) });