@mparticle/web-sdk 2.19.1 → 2.19.3

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/dist/mparticle.js CHANGED
@@ -725,7 +725,7 @@ var mParticle = (function () {
725
725
  Environment: Environment
726
726
  };
727
727
 
728
- var version = "2.19.1";
728
+ var version = "2.19.3";
729
729
 
730
730
  var Constants = {
731
731
  sdkVersion: version,
@@ -2304,132 +2304,144 @@ var mParticle = (function () {
2304
2304
 
2305
2305
  BatchUploader.prototype.prepareAndUpload = function (triggerFuture, useBeacon) {
2306
2306
  return __awaiter(this, void 0, void 0, function () {
2307
- var currentUser, currentEvents, newBatches, batchesToUpload, batchesThatDidNotUpload, promises;
2307
+ var currentUser, currentEvents, newBatches, batchesToUpload, batchesThatDidNotUpload;
2308
2308
 
2309
- var _a;
2310
-
2311
- var _this_1 = this;
2312
-
2313
- return __generator(this, function (_b) {
2314
- currentUser = this.mpInstance.Identity.getCurrentUser();
2315
- currentEvents = this.eventsQueuedForProcessing;
2316
- this.eventsQueuedForProcessing = [];
2317
- newBatches = BatchUploader.createNewBatches(currentEvents, currentUser, this.mpInstance);
2309
+ var _a, _b;
2318
2310
 
2319
- if (!isEmpty(newBatches)) {
2320
- (_a = this.batchesQueuedForProcessing).push.apply(_a, newBatches);
2321
- }
2311
+ return __generator(this, function (_c) {
2312
+ switch (_c.label) {
2313
+ case 0:
2314
+ currentUser = this.mpInstance.Identity.getCurrentUser();
2315
+ currentEvents = this.eventsQueuedForProcessing;
2316
+ this.eventsQueuedForProcessing = [];
2317
+ newBatches = BatchUploader.createNewBatches(currentEvents, currentUser, this.mpInstance);
2322
2318
 
2323
- batchesToUpload = this.batchesQueuedForProcessing;
2324
- batchesThatDidNotUpload = [];
2325
- this.batchesQueuedForProcessing = [];
2326
- promises = batchesToUpload.map(function (upload) {
2327
- return _this_1.upload(_this_1.mpInstance.Logger, upload, useBeacon);
2328
- }); // Iterate through fulfilled promises and store any remaining batches
2329
- // for future re-transmission attempts
2319
+ if (!isEmpty(newBatches)) {
2320
+ (_a = this.batchesQueuedForProcessing).push.apply(_a, newBatches);
2321
+ }
2330
2322
 
2331
- if (!isEmpty(promises)) {
2332
- Promise.all(promises).then(function (batchResponses) {
2333
- batchResponses.forEach(function (batch) {
2334
- return !isEmpty(batch) ? batchesThatDidNotUpload.push(batch) : null;
2335
- });
2336
- })["catch"](function (error) {
2337
- _this_1.mpInstance.Logger.error("Error processing batches during upload attempt: ".concat(error));
2338
- })["finally"](function () {
2339
- var _a; // Any batches that did not upload should be put back into the queue for processing
2323
+ batchesToUpload = this.batchesQueuedForProcessing;
2324
+ this.batchesQueuedForProcessing = [];
2325
+ return [4
2326
+ /*yield*/
2327
+ , this.uploadBatches(this.mpInstance.Logger, batchesToUpload, useBeacon)];
2340
2328
 
2329
+ case 1:
2330
+ batchesThatDidNotUpload = _c.sent(); // Batches that do not successfully upload are added back to the process queue
2331
+ // in the order they were created so that we can attempt re-transmission in
2332
+ // the same sequence. This is to prevent any potential data corruption.
2341
2333
 
2342
2334
  if (!isEmpty(batchesThatDidNotUpload)) {
2343
- (_a = _this_1.batchesQueuedForProcessing).unshift.apply(_a, batchesThatDidNotUpload);
2335
+ // TODO: https://go.mparticle.com/work/SQDSDKS-5165
2336
+ (_b = this.batchesQueuedForProcessing).unshift.apply(_b, batchesThatDidNotUpload);
2344
2337
  }
2345
- });
2346
- }
2347
2338
 
2348
- if (triggerFuture) {
2349
- this.triggerUploadInterval(triggerFuture, false);
2350
- }
2339
+ if (triggerFuture) {
2340
+ this.triggerUploadInterval(triggerFuture, false);
2341
+ }
2351
2342
 
2352
- return [2
2353
- /*return*/
2354
- ];
2343
+ return [2
2344
+ /*return*/
2345
+ ];
2346
+ }
2355
2347
  });
2356
2348
  });
2357
- };
2349
+ }; // TODO: Refactor to use logger as a class method
2350
+ // https://go.mparticle.com/work/SQDSDKS-5167
2351
+
2358
2352
 
2359
- BatchUploader.prototype.upload = function (logger, batch, useBeacon) {
2353
+ BatchUploader.prototype.uploadBatches = function (logger, batches, useBeacon) {
2360
2354
  return __awaiter(this, void 0, void 0, function () {
2361
- var fetchPayload, blob, response, error_1;
2355
+ var uploads, i, fetchPayload, blob, response, e_1;
2362
2356
  return __generator(this, function (_a) {
2363
2357
  switch (_a.label) {
2364
2358
  case 0:
2365
- if (isEmpty(batch) || isEmpty(batch.events)) {
2359
+ uploads = batches.filter(function (batch) {
2360
+ return !isEmpty(batch.events);
2361
+ });
2362
+
2363
+ if (isEmpty(uploads)) {
2366
2364
  return [2
2367
2365
  /*return*/
2368
2366
  , null];
2369
2367
  }
2370
2368
 
2371
- logger.verbose("Uploading batches: ".concat(JSON.stringify(batch)));
2369
+ logger.verbose("Uploading batches: ".concat(JSON.stringify(uploads)));
2370
+ logger.verbose("Batch count: ".concat(uploads.length));
2371
+ i = 0;
2372
+ _a.label = 1;
2373
+
2374
+ case 1:
2375
+ if (!(i < uploads.length)) return [3
2376
+ /*break*/
2377
+ , 6];
2372
2378
  fetchPayload = {
2373
2379
  method: 'POST',
2374
2380
  headers: {
2375
2381
  Accept: BatchUploader.CONTENT_TYPE,
2376
2382
  'Content-Type': 'text/plain;charset=UTF-8'
2377
2383
  },
2378
- body: JSON.stringify(batch)
2384
+ body: JSON.stringify(uploads[i])
2379
2385
  };
2380
2386
  if (!(useBeacon && this.isBeaconAvailable())) return [3
2381
2387
  /*break*/
2382
- , 1];
2388
+ , 2];
2383
2389
  blob = new Blob([fetchPayload.body], {
2384
2390
  type: 'text/plain;charset=UTF-8'
2385
2391
  });
2386
2392
  navigator.sendBeacon(this.uploadUrl, blob);
2387
2393
  return [3
2388
2394
  /*break*/
2389
- , 4];
2395
+ , 5];
2390
2396
 
2391
- case 1:
2392
- _a.trys.push([1, 3,, 4]);
2397
+ case 2:
2398
+ _a.trys.push([2, 4,, 5]);
2393
2399
 
2394
2400
  return [4
2395
2401
  /*yield*/
2396
2402
  , this.uploader.upload(fetchPayload)];
2397
2403
 
2398
- case 2:
2399
- response = _a.sent(); // TODO: Should we make this a switch statement instead?
2404
+ case 3:
2405
+ response = _a.sent();
2400
2406
 
2401
2407
  if (response.status >= 200 && response.status < 300) {
2402
- logger.verbose("Upload success for request ID: ".concat(batch.source_request_id));
2403
- return [2
2404
- /*return*/
2405
- , null];
2408
+ logger.verbose("Upload success for request ID: ".concat(uploads[i].source_request_id));
2406
2409
  } else if (response.status >= 500 || response.status === 429) {
2407
- logger.error("HTTP error status ".concat(response.status, " received")); // Server error, return current batch and try again later
2410
+ logger.error("HTTP error status ".concat(response.status, " received")); // Server error, add back current batches and try again later
2408
2411
 
2409
2412
  return [2
2410
2413
  /*return*/
2411
- , batch];
2414
+ , uploads.slice(i, uploads.length)];
2412
2415
  } else if (response.status >= 401) {
2413
- logger.error("HTTP error status ".concat(response.status, " while uploading - please verify your API key.")); // if we're getting a 401, assume we'll keep getting a 401
2414
- // so return the upload so it can be stored for later use
2416
+ logger.error("HTTP error status ".concat(response.status, " while uploading - please verify your API key.")); //if we're getting a 401, assume we'll keep getting a 401 and clear the uploads.
2415
2417
 
2416
2418
  return [2
2417
2419
  /*return*/
2418
- , batch];
2420
+ , null];
2421
+ } else {
2422
+ // In case there is an HTTP error we did not anticipate.
2423
+ console.error("HTTP error status ".concat(response.status, " while uploading events."), response);
2424
+ throw new Error("Uncaught HTTP Error ".concat(response.status, ". Batch upload will be re-attempted."));
2419
2425
  }
2420
2426
 
2421
2427
  return [3
2422
2428
  /*break*/
2423
- , 4];
2429
+ , 5];
2424
2430
 
2425
- case 3:
2426
- error_1 = _a.sent();
2427
- logger.error("Error sending event to mParticle servers. ".concat(error_1));
2431
+ case 4:
2432
+ e_1 = _a.sent();
2433
+ logger.error("Error sending event to mParticle servers. ".concat(e_1));
2428
2434
  return [2
2429
2435
  /*return*/
2430
- , batch];
2436
+ , uploads.slice(i, uploads.length)];
2431
2437
 
2432
- case 4:
2438
+ case 5:
2439
+ i++;
2440
+ return [3
2441
+ /*break*/
2442
+ , 1];
2443
+
2444
+ case 6:
2433
2445
  return [2
2434
2446
  /*return*/
2435
2447
  , null];
@@ -3193,6 +3205,11 @@ var mParticle = (function () {
3193
3205
  return filteredUserAttributes;
3194
3206
  };
3195
3207
 
3208
+ this.isFilteredUserAttribute = function (userAttributeKey, filterList) {
3209
+ var hashedUserAttribute = self.generateHash(userAttributeKey);
3210
+ return filterList && self.inArray(filterList, hashedUserAttribute);
3211
+ };
3212
+
3196
3213
  this.isEventType = function (type) {
3197
3214
  for (var prop in Types.EventType) {
3198
3215
  if (Types.EventType.hasOwnProperty(prop)) {
@@ -6171,6 +6188,10 @@ var mParticle = (function () {
6171
6188
 
6172
6189
  function Forwarders(mpInstance, kitBlocker) {
6173
6190
  var self = this;
6191
+ var UserAttributeActionTypes = {
6192
+ setUserAttribute: 'setUserAttribute',
6193
+ removeUserAttribute: 'removeUserAttribute'
6194
+ };
6174
6195
 
6175
6196
  this.initForwarders = function (userIdentities, forwardingStatsCallback) {
6176
6197
  var user = mpInstance.Identity.getCurrentUser();
@@ -6405,26 +6426,34 @@ var mParticle = (function () {
6405
6426
  }
6406
6427
  };
6407
6428
 
6408
- this.callSetUserAttributeOnForwarders = function (key, value) {
6409
- if (kitBlocker && kitBlocker.isAttributeKeyBlocked(key)) {
6429
+ this.handleForwarderUserAttributes = function (functionNameKey, key, value) {
6430
+ if (kitBlocker && kitBlocker.isAttributeKeyBlocked(key) || !mpInstance._Store.activeForwarders.length) {
6410
6431
  return;
6411
6432
  }
6412
6433
 
6413
- if (mpInstance._Store.activeForwarders.length) {
6414
- mpInstance._Store.activeForwarders.forEach(function (forwarder) {
6415
- if (forwarder.setUserAttribute && forwarder.userAttributeFilters && !mpInstance._Helpers.inArray(forwarder.userAttributeFilters, mpInstance._Helpers.generateHash(key))) {
6416
- try {
6417
- var result = forwarder.setUserAttribute(key, value);
6434
+ mpInstance._Store.activeForwarders.forEach(function (forwarder) {
6435
+ var forwarderFunction = forwarder[functionNameKey];
6418
6436
 
6419
- if (result) {
6420
- mpInstance.Logger.verbose(result);
6421
- }
6422
- } catch (e) {
6423
- mpInstance.Logger.error(e);
6424
- }
6437
+ if (!forwarderFunction || mpInstance._Helpers.isFilteredUserAttribute(key, forwarder.userAttributeFilters)) {
6438
+ return;
6439
+ }
6440
+
6441
+ try {
6442
+ var result;
6443
+
6444
+ if (functionNameKey === UserAttributeActionTypes.setUserAttribute) {
6445
+ result = forwarder.setUserAttribute(key, value);
6446
+ } else if (functionNameKey === UserAttributeActionTypes.removeUserAttribute) {
6447
+ result = forwarder.removeUserAttribute(key);
6425
6448
  }
6426
- });
6427
- }
6449
+
6450
+ if (result) {
6451
+ mpInstance.Logger.verbose(result);
6452
+ }
6453
+ } catch (e) {
6454
+ mpInstance.Logger.error(e);
6455
+ }
6456
+ });
6428
6457
  };
6429
6458
 
6430
6459
  this.setForwarderUserIdentities = function (userIdentities) {
@@ -7629,7 +7658,7 @@ var mParticle = (function () {
7629
7658
 
7630
7659
  mpInstance._Forwarders.initForwarders(self.IdentityAPI.getCurrentUser().getUserIdentities(), mpInstance._APIClient.prepareForwardingStats);
7631
7660
 
7632
- mpInstance._Forwarders.callSetUserAttributeOnForwarders(key, newValue);
7661
+ mpInstance._Forwarders.handleForwarderUserAttributes('setUserAttribute', key, newValue);
7633
7662
  }
7634
7663
  }
7635
7664
  },
@@ -7698,7 +7727,7 @@ var mParticle = (function () {
7698
7727
 
7699
7728
  mpInstance._Forwarders.initForwarders(self.IdentityAPI.getCurrentUser().getUserIdentities(), mpInstance._APIClient.prepareForwardingStats);
7700
7729
 
7701
- mpInstance._Forwarders.applyToForwarders('removeUserAttribute', key);
7730
+ mpInstance._Forwarders.handleForwarderUserAttributes('removeUserAttribute', key, null);
7702
7731
  }
7703
7732
  },
7704
7733
 
@@ -7776,7 +7805,7 @@ var mParticle = (function () {
7776
7805
 
7777
7806
  mpInstance._Forwarders.initForwarders(self.IdentityAPI.getCurrentUser().getUserIdentities(), mpInstance._APIClient.prepareForwardingStats);
7778
7807
 
7779
- mpInstance._Forwarders.callSetUserAttributeOnForwarders(key, arrayCopy);
7808
+ mpInstance._Forwarders.handleForwarderUserAttributes('setUserAttribute', key, arrayCopy);
7780
7809
  }
7781
7810
  },
7782
7811
 
@@ -7799,7 +7828,7 @@ var mParticle = (function () {
7799
7828
  if (userAttributes) {
7800
7829
  for (var prop in userAttributes) {
7801
7830
  if (userAttributes.hasOwnProperty(prop)) {
7802
- mpInstance._Forwarders.applyToForwarders('removeUserAttribute', prop);
7831
+ mpInstance._Forwarders.handleForwarderUserAttributes('removeUserAttribute', prop, null);
7803
7832
  }
7804
7833
 
7805
7834
  this.removeUserAttribute(prop);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "2.19.1",
3
+ "version": "2.19.3",
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",
@@ -211,6 +211,7 @@ export class BatchUploader {
211
211
  triggerFuture: boolean,
212
212
  useBeacon: boolean
213
213
  ): Promise<void> {
214
+ // Fetch current user so that events can be grouped by MPID
214
215
  const currentUser = this.mpInstance.Identity.getCurrentUser();
215
216
 
216
217
  const currentEvents = this.eventsQueuedForProcessing;
@@ -221,43 +222,27 @@ export class BatchUploader {
221
222
  currentUser,
222
223
  this.mpInstance
223
224
  );
225
+
224
226
  if (!isEmpty(newBatches)) {
225
227
  this.batchesQueuedForProcessing.push(...newBatches);
226
228
  }
227
229
 
230
+ // Clear out pending batches to avoid any potential duplication
228
231
  const batchesToUpload = this.batchesQueuedForProcessing;
229
- const batchesThatDidNotUpload: Batch[] = [];
230
232
  this.batchesQueuedForProcessing = [];
231
233
 
232
- // Create an array of promises as we try to upload each batch indvidually
233
- const promises: Promise<Batch>[] = batchesToUpload.map((upload) => {
234
- return this.upload(this.mpInstance.Logger, upload, useBeacon);
235
- });
234
+ const batchesThatDidNotUpload = await this.uploadBatches(
235
+ this.mpInstance.Logger,
236
+ batchesToUpload,
237
+ useBeacon
238
+ );
236
239
 
237
- // Iterate through fulfilled promises and store any remaining batches
238
- // for future re-transmission attempts
239
- if (!isEmpty(promises)) {
240
- Promise.all(promises)
241
- .then((batchResponses) => {
242
- batchResponses.forEach((batch) =>
243
- !isEmpty(batch)
244
- ? batchesThatDidNotUpload.push(batch)
245
- : null
246
- );
247
- })
248
- .catch((error) => {
249
- this.mpInstance.Logger.error(
250
- `Error processing batches during upload attempt: ${error}`
251
- );
252
- })
253
- .finally(() => {
254
- // Any batches that did not upload should be put back into the queue for processing
255
- if (!isEmpty(batchesThatDidNotUpload)) {
256
- this.batchesQueuedForProcessing.unshift(
257
- ...batchesThatDidNotUpload
258
- );
259
- }
260
- });
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);
261
246
  }
262
247
 
263
248
  if (triggerFuture) {
@@ -265,64 +250,79 @@ export class BatchUploader {
265
250
  }
266
251
  }
267
252
 
268
- 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(
269
256
  logger: SDKLoggerApi,
270
- batch: Batch,
257
+ batches: Batch[],
271
258
  useBeacon: boolean
272
- ): Promise<Batch | null> {
273
- if (isEmpty(batch) || isEmpty(batch.events)) {
259
+ ): Promise<Batch[] | null> {
260
+ // Filter out any batches that don't have events
261
+ const uploads = batches.filter((batch) => !isEmpty(batch.events));
262
+
263
+ if (isEmpty(uploads)) {
274
264
  return null;
275
265
  }
276
266
 
277
- logger.verbose(`Uploading batches: ${JSON.stringify(batch)}`);
278
-
279
- const fetchPayload: fetchPayload = {
280
- method: 'POST',
281
- headers: {
282
- Accept: BatchUploader.CONTENT_TYPE,
283
- 'Content-Type': 'text/plain;charset=UTF-8',
284
- },
285
- body: JSON.stringify(batch),
286
- };
287
-
288
- // TODO: Make beacon its own function
289
- // beacon is only used on onbeforeunload onpagehide events
290
- if (useBeacon && this.isBeaconAvailable()) {
291
- let blob = new Blob([fetchPayload.body], {
292
- type: 'text/plain;charset=UTF-8',
293
- });
294
- navigator.sendBeacon(this.uploadUrl, blob);
295
- } else {
296
- try {
297
- const response = await this.uploader.upload(fetchPayload);
298
-
299
- // TODO: Should we make this a switch statement instead?
300
- if (response.status >= 200 && response.status < 300) {
301
- logger.verbose(
302
- `Upload success for request ID: ${batch.source_request_id}`
303
- );
267
+ logger.verbose(`Uploading batches: ${JSON.stringify(uploads)}`);
268
+ logger.verbose(`Batch count: ${uploads.length}`);
269
+
270
+ for (let i = 0; i < uploads.length; i++) {
271
+ const fetchPayload: fetchPayload = {
272
+ method: 'POST',
273
+ headers: {
274
+ Accept: BatchUploader.CONTENT_TYPE,
275
+ 'Content-Type': 'text/plain;charset=UTF-8',
276
+ },
277
+ body: JSON.stringify(uploads[i]),
278
+ };
304
279
 
305
- return null;
306
- } else if (response.status >= 500 || response.status === 429) {
307
- logger.error(
308
- `HTTP error status ${response.status} received`
309
- );
280
+ // beacon is only used on onbeforeunload onpagehide events
281
+ if (useBeacon && this.isBeaconAvailable()) {
282
+ let blob = new Blob([fetchPayload.body], {
283
+ type: 'text/plain;charset=UTF-8',
284
+ });
285
+ navigator.sendBeacon(this.uploadUrl, blob);
286
+ } else {
287
+ try {
288
+ const response = await this.uploader.upload(fetchPayload);
289
+
290
+ if (response.status >= 200 && response.status < 300) {
291
+ logger.verbose(
292
+ `Upload success for request ID: ${uploads[i].source_request_id}`
293
+ );
294
+ } else if (
295
+ response.status >= 500 ||
296
+ response.status === 429
297
+ ) {
298
+ logger.error(
299
+ `HTTP error status ${response.status} received`
300
+ );
301
+ // Server error, add back current batches and try again later
302
+ return uploads.slice(i, uploads.length);
303
+ } else if (response.status >= 401) {
304
+ logger.error(
305
+ `HTTP error status ${response.status} while uploading - please verify your API key.`
306
+ );
307
+ //if we're getting a 401, assume we'll keep getting a 401 and clear the uploads.
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
+ );
310
315
 
311
- // Server error, return current batch and try again later
312
- return batch;
313
- } else if (response.status >= 401) {
316
+ throw new Error(
317
+ `Uncaught HTTP Error ${response.status}. Batch upload will be re-attempted.`
318
+ );
319
+ }
320
+ } catch (e) {
314
321
  logger.error(
315
- `HTTP error status ${response.status} while uploading - please verify your API key.`
322
+ `Error sending event to mParticle servers. ${e}`
316
323
  );
317
- // if we're getting a 401, assume we'll keep getting a 401
318
- // so return the upload so it can be stored for later use
319
- return batch;
324
+ return uploads.slice(i, uploads.length);
320
325
  }
321
- } catch (error) {
322
- logger.error(
323
- `Error sending event to mParticle servers. ${error}`
324
- );
325
- return batch;
326
326
  }
327
327
  }
328
328
  return null;
package/src/forwarders.js CHANGED
@@ -3,6 +3,12 @@ import filteredMparticleUser from './filteredMparticleUser';
3
3
 
4
4
  export default function Forwarders(mpInstance, kitBlocker) {
5
5
  var self = this;
6
+
7
+ const UserAttributeActionTypes = {
8
+ setUserAttribute: 'setUserAttribute',
9
+ removeUserAttribute: 'removeUserAttribute',
10
+ };
11
+
6
12
  this.initForwarders = function(userIdentities, forwardingStatsCallback) {
7
13
  var user = mpInstance.Identity.getCurrentUser();
8
14
  if (
@@ -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) {
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)) {