@mparticle/web-sdk 2.19.2 → 2.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "2.19.2",
3
+ "version": "2.20.0",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -28,13 +28,14 @@
28
28
  "build:esm": "cross-env ENVIRONMENT=prod BUILD=esm rollup --config rollup.config.js",
29
29
  "build:stub": "cross-env ENVIRONMENT=prod BUILD=stub rollup --config rollup.config.js",
30
30
  "build:snippet": "uglifyjs snippet.js -nm -o snippet.min.js",
31
+ "build:test-bundle": "cross-env TESTTYPE=main ENVIRONMENT=prod rollup --config rollup.test.config.js",
31
32
  "build:browserify:cjs": "browserify test/integrations/cjs/browserify/index.js -o test/integrations/cjs/dist/browserify-output.js && npm run test:karma:browserify:cjs",
32
33
  "build:rollup:cjs": "rollup --config test/integrations/cjs/rollup/rollup.config.js && npm run test:karma:rollup:cjs",
33
34
  "build:webpack:cjs": "webpack --config test/integrations/cjs/webpack/webpack.config.js && npm run test:karma:webpack:cjs",
34
35
  "build:rollup:module": "rollup --config test/integrations/module/rollup/rollup.config.js && npm run test:karma:rollup:module",
35
36
  "build:webpack:module": "webpack --config test/integrations/module/webpack/webpack.config.js && npm run test:karma:webpack:module",
36
37
  "build:ts": "tsc -p .",
37
- "test": "npm run build && cross-env TESTTYPE=main ENVIRONMENT=prod rollup --config rollup.test.config.js && cross-env DEBUG=false karma start test/karma.config.js",
38
+ "test": "npm run build && npm run build:test-bundle && cross-env DEBUG=false karma start test/karma.config.js",
38
39
  "test:debug": "cross-env DEBUG=true karma start test/karma.config.js",
39
40
  "test:stub": "cross-env TESTTYPE=stub ENVIRONMENT=prod rollup --config rollup.test.config.js && karma start test/stub/karma.stub.config.js",
40
41
  "test:integrations": "npm run test:requirejs && npm run test:integrations:cjs && npm run test:integrations:module",
@@ -9,17 +9,35 @@ import { convertEvents } from './sdkToEventsApiConverter';
9
9
  import Types from './types';
10
10
  import { isEmpty } from './utils';
11
11
 
12
+ /**
13
+ * BatchUploader contains all the logic to upload batches to mParticle.
14
+ * It queues events as they come in and at set intervals turns them into batches.
15
+ * It then attempts to upload them to mParticle.
16
+ *
17
+ * These uploads happen on an interval basis using window.fetch or XHR
18
+ * requests, depending on what is available in the browser.
19
+ *
20
+ * Uploads can also be triggered on browser visibility/focus changes via an
21
+ * event listener, which then uploads to mPartice via the browser's Beacon API.
22
+ */
23
+
12
24
  export class BatchUploader {
13
- //we upload JSON, but this content type is required to avoid a CORS preflight request
25
+ // We upload JSON, but this content type is required to avoid a CORS preflight request
14
26
  static readonly CONTENT_TYPE: string = 'text/plain;charset=UTF-8';
15
27
  static readonly MINIMUM_INTERVAL_MILLIS: number = 500;
16
28
  uploadIntervalMillis: number;
17
- pendingEvents: SDKEvent[];
18
- pendingUploads: Batch[];
29
+ eventsQueuedForProcessing: SDKEvent[];
30
+ batchesQueuedForProcessing: Batch[];
19
31
  mpInstance: MParticleWebSDK;
20
32
  uploadUrl: string;
21
33
  batchingEnabled: boolean;
34
+ private uploader: AsyncUploader;
22
35
 
36
+ /**
37
+ * Creates an instance of a BatchUploader
38
+ * @param {MParticleWebSDK} mpInstance - the mParticle SDK instance
39
+ * @param {number} uploadInterval - the desired upload interval in milliseconds
40
+ */
23
41
  constructor(mpInstance: MParticleWebSDK, uploadInterval: number) {
24
42
  this.mpInstance = mpInstance;
25
43
  this.uploadIntervalMillis = uploadInterval;
@@ -28,8 +46,8 @@ export class BatchUploader {
28
46
  if (this.uploadIntervalMillis < BatchUploader.MINIMUM_INTERVAL_MILLIS) {
29
47
  this.uploadIntervalMillis = BatchUploader.MINIMUM_INTERVAL_MILLIS;
30
48
  }
31
- this.pendingEvents = [];
32
- this.pendingUploads = [];
49
+ this.eventsQueuedForProcessing = [];
50
+ this.batchesQueuedForProcessing = [];
33
51
 
34
52
  const { SDKConfig, devToken } = this.mpInstance._Store;
35
53
  const baseUrl = this.mpInstance._Helpers.createServiceUrl(
@@ -38,15 +56,20 @@ export class BatchUploader {
38
56
  );
39
57
  this.uploadUrl = `${baseUrl}/events`;
40
58
 
41
- setTimeout(() => {
42
- this.prepareAndUpload(true, false);
43
- }, this.uploadIntervalMillis);
59
+ this.uploader = window.fetch
60
+ ? new FetchUploader(this.uploadUrl)
61
+ : new XHRUploader(this.uploadUrl);
62
+
63
+ this.triggerUploadInterval(true, false);
44
64
  this.addEventListeners();
45
65
  }
46
66
 
67
+ // Adds listeners to be used trigger Navigator.sendBeacon if the browser
68
+ // loses focus for any reason, such as closing browser tab or minimizing window
47
69
  private addEventListeners() {
48
70
  const _this = this;
49
71
 
72
+ // visibility change is a document property, not window
50
73
  document.addEventListener('visibilitychange', () => {
51
74
  _this.prepareAndUpload(false, _this.isBeaconAvailable());
52
75
  });
@@ -65,16 +88,32 @@ export class BatchUploader {
65
88
  return false;
66
89
  }
67
90
 
91
+ // Triggers a setTimeout for prepareAndUpload
92
+ private triggerUploadInterval(
93
+ triggerFuture: boolean = false,
94
+ useBeacon: boolean = false
95
+ ): void {
96
+ setTimeout(() => {
97
+ this.prepareAndUpload(triggerFuture, useBeacon);
98
+ }, this.uploadIntervalMillis);
99
+ }
100
+
101
+ /**
102
+ * This method will queue a single Event which will eventually be processed into a Batch
103
+ * @param event event that should be queued
104
+ */
68
105
  queueEvent(event: SDKEvent): void {
69
106
  if (!isEmpty(event)) {
70
- this.pendingEvents.push(event);
107
+ this.eventsQueuedForProcessing.push(event);
71
108
  this.mpInstance.Logger.verbose(
72
109
  `Queuing event: ${JSON.stringify(event)}`
73
110
  );
74
111
  this.mpInstance.Logger.verbose(
75
- `Queued event count: ${this.pendingEvents.length}`
112
+ `Queued event count: ${this.eventsQueuedForProcessing.length}`
76
113
  );
77
114
 
115
+ // TODO: Remove this check once the v2 code path is removed
116
+ // https://go.mparticle.com/work/SQDSDKS-3720
78
117
  if (
79
118
  !this.batchingEnabled ||
80
119
  Types.TriggerUploadType[event.EventDataType]
@@ -93,7 +132,7 @@ export class BatchUploader {
93
132
  * @param sdkEvents current pending events
94
133
  * @param defaultUser the user to reference for events that are missing data
95
134
  */
96
- private static createNewUploads(
135
+ private static createNewBatches(
97
136
  sdkEvents: SDKEvent[],
98
137
  defaultUser: MParticleUser,
99
138
  mpInstance: MParticleWebSDK
@@ -141,9 +180,8 @@ export class BatchUploader {
141
180
  mpInstance._Store.SDKConfig.onCreateBatch;
142
181
 
143
182
  if (onCreateBatchCallback) {
144
- uploadBatchObject = onCreateBatchCallback(
145
- uploadBatchObject
146
- );
183
+ uploadBatchObject =
184
+ onCreateBatchCallback(uploadBatchObject);
147
185
  if (uploadBatchObject) {
148
186
  uploadBatchObject.modified = true;
149
187
  } else {
@@ -169,47 +207,58 @@ export class BatchUploader {
169
207
  * @param triggerFuture whether to trigger the loop again - for manual/forced uploads this should be false
170
208
  * @param useBeacon whether to use the beacon API - used when the page is being unloaded
171
209
  */
172
- private async prepareAndUpload(triggerFuture: boolean, useBeacon: boolean) {
210
+ private async prepareAndUpload(
211
+ triggerFuture: boolean,
212
+ useBeacon: boolean
213
+ ): Promise<void> {
214
+ // Fetch current user so that events can be grouped by MPID
173
215
  const currentUser = this.mpInstance.Identity.getCurrentUser();
174
216
 
175
- const currentEvents = this.pendingEvents;
176
- this.pendingEvents = [];
177
- const newUploads = BatchUploader.createNewUploads(
217
+ const currentEvents = this.eventsQueuedForProcessing;
218
+ this.eventsQueuedForProcessing = [];
219
+
220
+ const newBatches = BatchUploader.createNewBatches(
178
221
  currentEvents,
179
222
  currentUser,
180
223
  this.mpInstance
181
224
  );
182
- if (newUploads && newUploads.length) {
183
- this.pendingUploads.push(...newUploads);
225
+
226
+ if (!isEmpty(newBatches)) {
227
+ this.batchesQueuedForProcessing.push(...newBatches);
184
228
  }
185
229
 
186
- const currentUploads = this.pendingUploads;
187
- this.pendingUploads = [];
188
- const remainingUploads: Batch[] = await this.upload(
230
+ // Clear out pending batches to avoid any potential duplication
231
+ const batchesToUpload = this.batchesQueuedForProcessing;
232
+ this.batchesQueuedForProcessing = [];
233
+
234
+ const batchesThatDidNotUpload = await this.uploadBatches(
189
235
  this.mpInstance.Logger,
190
- currentUploads,
236
+ batchesToUpload,
191
237
  useBeacon
192
238
  );
193
- if (remainingUploads && remainingUploads.length) {
194
- this.pendingUploads.unshift(...remainingUploads);
239
+
240
+ // Batches that do not successfully upload are added back to the process queue
241
+ // in the order they were created so that we can attempt re-transmission in
242
+ // the same sequence. This is to prevent any potential data corruption.
243
+ if (!isEmpty(batchesThatDidNotUpload)) {
244
+ // TODO: https://go.mparticle.com/work/SQDSDKS-5165
245
+ this.batchesQueuedForProcessing.unshift(...batchesThatDidNotUpload);
195
246
  }
196
247
 
197
248
  if (triggerFuture) {
198
- setTimeout(() => {
199
- this.prepareAndUpload(true, false);
200
- }, this.uploadIntervalMillis);
249
+ this.triggerUploadInterval(triggerFuture, false);
201
250
  }
202
251
  }
203
252
 
204
- private async upload(
253
+ // TODO: Refactor to use logger as a class method
254
+ // https://go.mparticle.com/work/SQDSDKS-5167
255
+ private async uploadBatches(
205
256
  logger: SDKLoggerApi,
206
- _uploads: Batch[],
257
+ batches: Batch[],
207
258
  useBeacon: boolean
208
- ): Promise<Batch[]> {
209
- let uploader;
210
-
259
+ ): Promise<Batch[] | null> {
211
260
  // Filter out any batches that don't have events
212
- const uploads = _uploads.filter(upload => !isEmpty(upload.events));
261
+ const uploads = batches.filter((batch) => !isEmpty(batch.events));
213
262
 
214
263
  if (isEmpty(uploads)) {
215
264
  return null;
@@ -235,19 +284,8 @@ export class BatchUploader {
235
284
  });
236
285
  navigator.sendBeacon(this.uploadUrl, blob);
237
286
  } else {
238
- if (!uploader) {
239
- if (window.fetch) {
240
- uploader = new FetchUploader(this.uploadUrl, logger);
241
- } else {
242
- uploader = new XHRUploader(this.uploadUrl, logger);
243
- }
244
- }
245
287
  try {
246
- const response = await uploader.upload(
247
- fetchPayload,
248
- uploads,
249
- i
250
- );
288
+ const response = await this.uploader.upload(fetchPayload);
251
289
 
252
290
  if (response.status >= 200 && response.status < 300) {
253
291
  logger.verbose(
@@ -260,7 +298,7 @@ export class BatchUploader {
260
298
  logger.error(
261
299
  `HTTP error status ${response.status} received`
262
300
  );
263
- //server error, add back current events and try again later
301
+ // Server error, add back current batches and try again later
264
302
  return uploads.slice(i, uploads.length);
265
303
  } else if (response.status >= 401) {
266
304
  logger.error(
@@ -268,6 +306,16 @@ export class BatchUploader {
268
306
  );
269
307
  //if we're getting a 401, assume we'll keep getting a 401 and clear the uploads.
270
308
  return null;
309
+ } else {
310
+ // In case there is an HTTP error we did not anticipate.
311
+ console.error(
312
+ `HTTP error status ${response.status} while uploading events.`,
313
+ response
314
+ );
315
+
316
+ throw new Error(
317
+ `Uncaught HTTP Error ${response.status}. Batch upload will be re-attempted.`
318
+ );
271
319
  }
272
320
  } catch (e) {
273
321
  logger.error(
@@ -283,34 +331,24 @@ export class BatchUploader {
283
331
 
284
332
  abstract class AsyncUploader {
285
333
  url: string;
286
- logger: SDKLoggerApi;
334
+ public abstract upload(fetchPayload: fetchPayload): Promise<XHRResponse>;
287
335
 
288
- constructor(url: string, logger: SDKLoggerApi) {
336
+ constructor(url: string) {
289
337
  this.url = url;
290
- this.logger = logger;
291
338
  }
292
339
  }
293
340
 
294
341
  class FetchUploader extends AsyncUploader {
295
- private async upload(
296
- fetchPayload: fetchPayload,
297
- uploads: Batch[],
298
- i: number
299
- ) {
342
+ public async upload(fetchPayload: fetchPayload): Promise<XHRResponse> {
300
343
  const response: XHRResponse = await fetch(this.url, fetchPayload);
301
344
  return response;
302
345
  }
303
346
  }
304
347
 
305
348
  class XHRUploader extends AsyncUploader {
306
- private async upload(
307
- fetchPayload: fetchPayload,
308
- uploads: Batch[],
309
- i: number
310
- ) {
349
+ public async upload(fetchPayload: fetchPayload): Promise<XHRResponse> {
311
350
  const response: XHRResponse = await this.makeRequest(
312
351
  this.url,
313
- this.logger,
314
352
  fetchPayload.body
315
353
  );
316
354
  return response;
@@ -318,7 +356,6 @@ class XHRUploader extends AsyncUploader {
318
356
 
319
357
  private async makeRequest(
320
358
  url: string,
321
- logger: SDKLoggerApi,
322
359
  data: string
323
360
  ): Promise<XMLHttpRequest> {
324
361
  const xhr: XMLHttpRequest = new XMLHttpRequest();
package/src/forwarders.js CHANGED
@@ -1,8 +1,15 @@
1
1
  import Types from './types';
2
2
  import filteredMparticleUser from './filteredMparticleUser';
3
+ import { isEmpty } from './utils';
3
4
 
4
5
  export default function Forwarders(mpInstance, kitBlocker) {
5
6
  var self = this;
7
+
8
+ const UserAttributeActionTypes = {
9
+ setUserAttribute: 'setUserAttribute',
10
+ removeUserAttribute: 'removeUserAttribute',
11
+ };
12
+
6
13
  this.initForwarders = function(userIdentities, forwardingStatsCallback) {
7
14
  var user = mpInstance.Identity.getCurrentUser();
8
15
  if (
@@ -53,7 +60,6 @@ export default function Forwarders(mpInstance, kitBlocker) {
53
60
  user ? user.getAllUserAttributes() : {},
54
61
  forwarder.userAttributeFilters
55
62
  );
56
-
57
63
  if (!forwarder.initialized) {
58
64
  forwarder.logger = mpInstance.Logger;
59
65
  forwarder.init(
@@ -387,33 +393,47 @@ export default function Forwarders(mpInstance, kitBlocker) {
387
393
  }
388
394
  };
389
395
 
390
- this.callSetUserAttributeOnForwarders = function(key, value) {
391
- if (kitBlocker && kitBlocker.isAttributeKeyBlocked(key)) {
396
+ this.handleForwarderUserAttributes = function(functionNameKey, key, value) {
397
+ if (
398
+ (kitBlocker && kitBlocker.isAttributeKeyBlocked(key)) ||
399
+ !mpInstance._Store.activeForwarders.length
400
+ ) {
392
401
  return;
393
402
  }
394
403
 
395
- if (mpInstance._Store.activeForwarders.length) {
396
- mpInstance._Store.activeForwarders.forEach(function(forwarder) {
404
+ mpInstance._Store.activeForwarders.forEach(function(forwarder) {
405
+ const forwarderFunction = forwarder[functionNameKey];
406
+ if (
407
+ !forwarderFunction ||
408
+ mpInstance._Helpers.isFilteredUserAttribute(
409
+ key,
410
+ forwarder.userAttributeFilters
411
+ )
412
+ ) {
413
+ return;
414
+ }
415
+ try {
416
+ let result;
417
+
397
418
  if (
398
- forwarder.setUserAttribute &&
399
- forwarder.userAttributeFilters &&
400
- !mpInstance._Helpers.inArray(
401
- forwarder.userAttributeFilters,
402
- mpInstance._Helpers.generateHash(key)
403
- )
419
+ functionNameKey ===
420
+ UserAttributeActionTypes.setUserAttribute
404
421
  ) {
405
- try {
406
- var result = forwarder.setUserAttribute(key, value);
422
+ result = forwarder.setUserAttribute(key, value);
423
+ } else if (
424
+ functionNameKey ===
425
+ UserAttributeActionTypes.removeUserAttribute
426
+ ) {
427
+ result = forwarder.removeUserAttribute(key);
428
+ }
407
429
 
408
- if (result) {
409
- mpInstance.Logger.verbose(result);
410
- }
411
- } catch (e) {
412
- mpInstance.Logger.error(e);
413
- }
430
+ if (result) {
431
+ mpInstance.Logger.verbose(result);
414
432
  }
415
- });
416
- }
433
+ } catch (e) {
434
+ mpInstance.Logger.error(e);
435
+ }
436
+ });
417
437
  };
418
438
 
419
439
  this.setForwarderUserIdentities = function(userIdentities) {
@@ -504,19 +524,59 @@ export default function Forwarders(mpInstance, kitBlocker) {
504
524
  mpInstance._Persistence.forwardingStatsBatches.forwardingStatsEventQueue = queue;
505
525
  };
506
526
 
507
- this.configureForwarder = function(configuration) {
527
+ // Processing forwarders is a 2 step process:
528
+ // 1. Configure the kit
529
+ // 2. Initialize the kit
530
+ // There are 2 types of kits:
531
+ // 1. UI-enabled kits
532
+ // 2. Sideloaded kits.
533
+ this.processForwarders = function(config, forwardingStatsCallback) {
534
+ if (!config) {
535
+ mpInstance.Logger.warning(
536
+ 'No config was passed. Cannot process forwarders'
537
+ );
538
+ } else {
539
+ this.processUIEnabledKits(config);
540
+ this.processSideloadedKits(config);
541
+
542
+ self.initForwarders(
543
+ mpInstance._Store.SDKConfig.identifyRequest.userIdentities,
544
+ forwardingStatsCallback
545
+ );
546
+ }
547
+ };
548
+
549
+ // These are kits that are enabled via the mParticle UI.
550
+ // A kit that is UI-enabled will have a kit configuration that returns from
551
+ // the server, or in rare cases, is passed in by the developer.
552
+ // The kit configuration will be compared with the kit constructors to determine
553
+ // if there is a match before being initialized.
554
+ // Only kits that are configured properly can be active and used for kit forwarding.
555
+ this.processUIEnabledKits = function(config) {
556
+ try {
557
+ if (Array.isArray(config.kitConfigs) && config.kitConfigs.length) {
558
+ config.kitConfigs.forEach(function(kitConfig) {
559
+ self.configureUIEnabledKit(kitConfig);
560
+ });
561
+ }
562
+ } catch (e) {
563
+ mpInstance.Logger.error(
564
+ 'MP Kits not configured propertly. Kits may not be initialized. ' +
565
+ e
566
+ );
567
+ }
568
+ };
569
+
570
+ this.configureUIEnabledKit = function(configuration) {
508
571
  var newForwarder = null,
509
572
  config = configuration,
510
573
  forwarders = {};
511
574
 
512
- // if there are kits inside of mpInstance._Store.SDKConfig.kits, then mParticle is self hosted
513
- if (
514
- mpInstance._Helpers.isObject(mpInstance._Store.SDKConfig.kits) &&
515
- Object.keys(mpInstance._Store.SDKConfig.kits).length > 0
516
- ) {
575
+ // If there are kits inside of mpInstance._Store.SDKConfig.kits, then mParticle is self hosted
576
+ if (!isEmpty(mpInstance._Store.SDKConfig.kits)) {
517
577
  forwarders = mpInstance._Store.SDKConfig.kits;
518
578
  // otherwise mParticle is loaded via script tag
519
- } else if (mpInstance._preInit.forwarderConstructors.length > 0) {
579
+ } else if (!isEmpty(mpInstance._preInit.forwarderConstructors)) {
520
580
  mpInstance._preInit.forwarderConstructors.forEach(function(
521
581
  forwarder
522
582
  ) {
@@ -532,38 +592,10 @@ export default function Forwarders(mpInstance, kitBlocker) {
532
592
  config.isSandbox ===
533
593
  mpInstance._Store.SDKConfig.isDevelopmentMode
534
594
  ) {
535
- newForwarder = new forwarders[name].constructor();
536
-
537
- newForwarder.id = config.moduleId;
538
- newForwarder.isSandbox = config.isDebug || config.isSandbox;
539
- newForwarder.hasSandbox = config.hasDebugString === 'true';
540
- newForwarder.isVisible = config.isVisible;
541
- newForwarder.settings = config.settings;
542
-
543
- newForwarder.eventNameFilters = config.eventNameFilters;
544
- newForwarder.eventTypeFilters = config.eventTypeFilters;
545
- newForwarder.attributeFilters = config.attributeFilters;
546
-
547
- newForwarder.screenNameFilters = config.screenNameFilters;
548
- newForwarder.screenNameFilters = config.screenNameFilters;
549
- newForwarder.screenAttributeFilters =
550
- config.screenAttributeFilters;
551
-
552
- newForwarder.userIdentityFilters =
553
- config.userIdentityFilters;
554
- newForwarder.userAttributeFilters =
555
- config.userAttributeFilters;
556
-
557
- newForwarder.filteringEventAttributeValue =
558
- config.filteringEventAttributeValue;
559
- newForwarder.filteringUserAttributeValue =
560
- config.filteringUserAttributeValue;
561
- newForwarder.eventSubscriptionId =
562
- config.eventSubscriptionId;
563
- newForwarder.filteringConsentRuleValues =
564
- config.filteringConsentRuleValues;
565
- newForwarder.excludeAnonymousUser =
566
- config.excludeAnonymousUser;
595
+ newForwarder = this.returnConfiguredKit(
596
+ forwarders[name],
597
+ config
598
+ );
567
599
 
568
600
  mpInstance._Store.configuredForwarders.push(newForwarder);
569
601
  break;
@@ -572,6 +604,84 @@ export default function Forwarders(mpInstance, kitBlocker) {
572
604
  }
573
605
  };
574
606
 
607
+ // Sideloaded kits are not configured in the UI and do not have kit configurations
608
+ // They are automatically added to active forwarders.
609
+
610
+ // TODO: Sideloading kits currently requires the use of a register method
611
+ // which requires an object on which to be registered.
612
+ // In the future, when all kits are moved to the config rather than
613
+ // there being a separate process for MP configured kits and
614
+ // sideloaded kits, this will need to be refactored.
615
+ this.processSideloadedKits = function(config) {
616
+ try {
617
+ if (Array.isArray(config.sideloadedKits)) {
618
+ const sideloadedKits = { kits: {} };
619
+ // First register each kit's constructor onto sideloadedKits,
620
+ // which is typed { kits: Dictionary<constructor> }.
621
+ // The constructors are keyed by the name of the kit.
622
+ config.sideloadedKits.forEach(function(sideloadedKit) {
623
+ sideloadedKit.register(sideloadedKits);
624
+ });
625
+
626
+ // Then configure each kit
627
+ for (const registeredKitKey in sideloadedKits.kits) {
628
+ const kitConstructor =
629
+ sideloadedKits.kits[registeredKitKey];
630
+ self.configureSideloadedKit(kitConstructor);
631
+ }
632
+ }
633
+ } catch (e) {
634
+ mpInstance.Logger.error(
635
+ 'Sideloaded Kits not configured propertly. Kits may not be initialized. ' +
636
+ e
637
+ );
638
+ }
639
+ };
640
+
641
+ // kits can be included via mParticle UI, or via sideloaded kit config API
642
+ this.configureSideloadedKit = function(kitConstructor) {
643
+ mpInstance._Store.configuredForwarders.push(
644
+ this.returnConfiguredKit(kitConstructor)
645
+ );
646
+ };
647
+
648
+ this.returnConfiguredKit = function(forwarder, config = {}) {
649
+ const newForwarder = new forwarder.constructor();
650
+ newForwarder.id = config.moduleId;
651
+
652
+ // TODO: isSandbox, hasSandbox is never used in any kit or in core SDK.
653
+ // isVisibleInvestigate is only used in 1 place. It is always true if
654
+ // it is sent to JS. Investigate further to determine if these can be removed.
655
+ // https://go.mparticle.com/work/SQDSDKS-5156
656
+ newForwarder.isSandbox = config.isDebug || config.isSandbox;
657
+ newForwarder.hasSandbox = config.hasDebugString === 'true';
658
+ newForwarder.isVisible = config.isVisible || true;
659
+ newForwarder.settings = config.settings || {};
660
+
661
+ newForwarder.eventNameFilters = config.eventNameFilters || [];
662
+ newForwarder.eventTypeFilters = config.eventTypeFilters || [];
663
+ newForwarder.attributeFilters = config.attributeFilters || [];
664
+
665
+ newForwarder.screenNameFilters = config.screenNameFilters || [];
666
+ newForwarder.screenAttributeFilters =
667
+ config.screenAttributeFilters || [];
668
+
669
+ newForwarder.userIdentityFilters = config.userIdentityFilters || [];
670
+ newForwarder.userAttributeFilters = config.userAttributeFilters || [];
671
+
672
+ newForwarder.filteringEventAttributeValue =
673
+ config.filteringEventAttributeValue || {};
674
+ newForwarder.filteringUserAttributeValue =
675
+ config.filteringUserAttributeValue || {};
676
+ newForwarder.eventSubscriptionId = config.eventSubscriptionId || null;
677
+ newForwarder.filteringConsentRuleValues =
678
+ config.filteringConsentRuleValues || {};
679
+ newForwarder.excludeAnonymousUser =
680
+ config.excludeAnonymousUser || false;
681
+
682
+ return newForwarder;
683
+ };
684
+
575
685
  this.configurePixel = function(settings) {
576
686
  if (
577
687
  settings.isDebug ===
@@ -583,39 +693,18 @@ export default function Forwarders(mpInstance, kitBlocker) {
583
693
  }
584
694
  };
585
695
 
586
- this.processForwarders = function(config, forwardingStatsCallback) {
587
- if (!config) {
588
- mpInstance.Logger.warning(
589
- 'No config was passed. Cannot process forwarders'
590
- );
591
- } else {
592
- try {
593
- if (
594
- Array.isArray(config.kitConfigs) &&
595
- config.kitConfigs.length
596
- ) {
597
- config.kitConfigs.forEach(function(kitConfig) {
598
- self.configureForwarder(kitConfig);
599
- });
600
- }
601
-
602
- if (
603
- Array.isArray(config.pixelConfigs) &&
604
- config.pixelConfigs.length
605
- ) {
606
- config.pixelConfigs.forEach(function(pixelConfig) {
607
- self.configurePixel(pixelConfig);
608
- });
609
- }
610
- self.initForwarders(
611
- mpInstance._Store.SDKConfig.identifyRequest.userIdentities,
612
- forwardingStatsCallback
613
- );
614
- } catch (e) {
615
- mpInstance.Logger.error(
616
- 'Config was not parsed propertly. Forwarders may not be initialized.'
617
- );
696
+ this.processPixelConfigs = function(config) {
697
+ try {
698
+ if (!isEmpty(config.pixelConfigs)) {
699
+ config.pixelConfigs.forEach(function(pixelConfig) {
700
+ self.configurePixel(pixelConfig);
701
+ });
618
702
  }
703
+ } catch (e) {
704
+ mpInstance.Logger.error(
705
+ 'Cookie Sync configs not configured propertly. Cookie Sync may not be initialized. ' +
706
+ e
707
+ );
619
708
  }
620
709
  };
621
710
  }
package/src/helpers.js CHANGED
@@ -387,6 +387,11 @@ export default function Helpers(mpInstance) {
387
387
  return filteredUserAttributes;
388
388
  };
389
389
 
390
+ this.isFilteredUserAttribute = function(userAttributeKey, filterList) {
391
+ const hashedUserAttribute = self.generateHash(userAttributeKey);
392
+ return filterList && self.inArray(filterList, hashedUserAttribute);
393
+ };
394
+
390
395
  this.isEventType = function(type) {
391
396
  for (var prop in Types.EventType) {
392
397
  if (Types.EventType.hasOwnProperty(prop)) {