@mparticle/web-sdk 2.19.0 → 2.19.2
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.common.js +53 -37
- package/dist/mparticle.esm.js +53 -37
- package/dist/mparticle.js +181 -162
- package/package.json +1 -1
- package/src/apiClient.ts +1 -1
- package/src/batchUploader.ts +109 -146
- package/src/{consent.js → consent.ts} +228 -72
- package/src/cookieSyncManager.js +40 -0
- package/src/mockBatchCreator.ts +3 -0
- package/src/sdkRuntimeModels.ts +4 -3
- package/src/sdkToEventsApiConverter.ts +1 -1
- package/src/serverModel.ts +34 -32
- package/src/store.ts +5 -4
- package/src/consent.interfaces.ts +0 -89
package/package.json
CHANGED
package/src/apiClient.ts
CHANGED
package/src/batchUploader.ts
CHANGED
|
@@ -9,35 +9,17 @@ 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
|
-
|
|
24
12
|
export class BatchUploader {
|
|
25
|
-
//
|
|
13
|
+
//we upload JSON, but this content type is required to avoid a CORS preflight request
|
|
26
14
|
static readonly CONTENT_TYPE: string = 'text/plain;charset=UTF-8';
|
|
27
15
|
static readonly MINIMUM_INTERVAL_MILLIS: number = 500;
|
|
28
16
|
uploadIntervalMillis: number;
|
|
29
|
-
|
|
30
|
-
|
|
17
|
+
pendingEvents: SDKEvent[];
|
|
18
|
+
pendingUploads: Batch[];
|
|
31
19
|
mpInstance: MParticleWebSDK;
|
|
32
20
|
uploadUrl: string;
|
|
33
21
|
batchingEnabled: boolean;
|
|
34
|
-
private uploader: AsyncUploader;
|
|
35
22
|
|
|
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
|
-
*/
|
|
41
23
|
constructor(mpInstance: MParticleWebSDK, uploadInterval: number) {
|
|
42
24
|
this.mpInstance = mpInstance;
|
|
43
25
|
this.uploadIntervalMillis = uploadInterval;
|
|
@@ -46,8 +28,8 @@ export class BatchUploader {
|
|
|
46
28
|
if (this.uploadIntervalMillis < BatchUploader.MINIMUM_INTERVAL_MILLIS) {
|
|
47
29
|
this.uploadIntervalMillis = BatchUploader.MINIMUM_INTERVAL_MILLIS;
|
|
48
30
|
}
|
|
49
|
-
this.
|
|
50
|
-
this.
|
|
31
|
+
this.pendingEvents = [];
|
|
32
|
+
this.pendingUploads = [];
|
|
51
33
|
|
|
52
34
|
const { SDKConfig, devToken } = this.mpInstance._Store;
|
|
53
35
|
const baseUrl = this.mpInstance._Helpers.createServiceUrl(
|
|
@@ -56,20 +38,15 @@ export class BatchUploader {
|
|
|
56
38
|
);
|
|
57
39
|
this.uploadUrl = `${baseUrl}/events`;
|
|
58
40
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.triggerUploadInterval(true, false);
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
this.prepareAndUpload(true, false);
|
|
43
|
+
}, this.uploadIntervalMillis);
|
|
64
44
|
this.addEventListeners();
|
|
65
45
|
}
|
|
66
46
|
|
|
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
|
|
69
47
|
private addEventListeners() {
|
|
70
48
|
const _this = this;
|
|
71
49
|
|
|
72
|
-
// visibility change is a document property, not window
|
|
73
50
|
document.addEventListener('visibilitychange', () => {
|
|
74
51
|
_this.prepareAndUpload(false, _this.isBeaconAvailable());
|
|
75
52
|
});
|
|
@@ -88,32 +65,16 @@ export class BatchUploader {
|
|
|
88
65
|
return false;
|
|
89
66
|
}
|
|
90
67
|
|
|
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
|
-
*/
|
|
105
68
|
queueEvent(event: SDKEvent): void {
|
|
106
69
|
if (!isEmpty(event)) {
|
|
107
|
-
this.
|
|
70
|
+
this.pendingEvents.push(event);
|
|
108
71
|
this.mpInstance.Logger.verbose(
|
|
109
72
|
`Queuing event: ${JSON.stringify(event)}`
|
|
110
73
|
);
|
|
111
74
|
this.mpInstance.Logger.verbose(
|
|
112
|
-
`Queued event count: ${this.
|
|
75
|
+
`Queued event count: ${this.pendingEvents.length}`
|
|
113
76
|
);
|
|
114
77
|
|
|
115
|
-
// TODO: Remove this check once the v2 code path is removed
|
|
116
|
-
// https://go.mparticle.com/work/SQDSDKS-3720
|
|
117
78
|
if (
|
|
118
79
|
!this.batchingEnabled ||
|
|
119
80
|
Types.TriggerUploadType[event.EventDataType]
|
|
@@ -132,7 +93,7 @@ export class BatchUploader {
|
|
|
132
93
|
* @param sdkEvents current pending events
|
|
133
94
|
* @param defaultUser the user to reference for events that are missing data
|
|
134
95
|
*/
|
|
135
|
-
private static
|
|
96
|
+
private static createNewUploads(
|
|
136
97
|
sdkEvents: SDKEvent[],
|
|
137
98
|
defaultUser: MParticleUser,
|
|
138
99
|
mpInstance: MParticleWebSDK
|
|
@@ -180,8 +141,9 @@ export class BatchUploader {
|
|
|
180
141
|
mpInstance._Store.SDKConfig.onCreateBatch;
|
|
181
142
|
|
|
182
143
|
if (onCreateBatchCallback) {
|
|
183
|
-
uploadBatchObject =
|
|
184
|
-
|
|
144
|
+
uploadBatchObject = onCreateBatchCallback(
|
|
145
|
+
uploadBatchObject
|
|
146
|
+
);
|
|
185
147
|
if (uploadBatchObject) {
|
|
186
148
|
uploadBatchObject.modified = true;
|
|
187
149
|
} else {
|
|
@@ -207,122 +169,112 @@ export class BatchUploader {
|
|
|
207
169
|
* @param triggerFuture whether to trigger the loop again - for manual/forced uploads this should be false
|
|
208
170
|
* @param useBeacon whether to use the beacon API - used when the page is being unloaded
|
|
209
171
|
*/
|
|
210
|
-
private async prepareAndUpload(
|
|
211
|
-
triggerFuture: boolean,
|
|
212
|
-
useBeacon: boolean
|
|
213
|
-
): Promise<void> {
|
|
172
|
+
private async prepareAndUpload(triggerFuture: boolean, useBeacon: boolean) {
|
|
214
173
|
const currentUser = this.mpInstance.Identity.getCurrentUser();
|
|
215
174
|
|
|
216
|
-
const currentEvents = this.
|
|
217
|
-
this.
|
|
218
|
-
|
|
219
|
-
const newBatches = BatchUploader.createNewBatches(
|
|
175
|
+
const currentEvents = this.pendingEvents;
|
|
176
|
+
this.pendingEvents = [];
|
|
177
|
+
const newUploads = BatchUploader.createNewUploads(
|
|
220
178
|
currentEvents,
|
|
221
179
|
currentUser,
|
|
222
180
|
this.mpInstance
|
|
223
181
|
);
|
|
224
|
-
if (
|
|
225
|
-
this.
|
|
182
|
+
if (newUploads && newUploads.length) {
|
|
183
|
+
this.pendingUploads.push(...newUploads);
|
|
226
184
|
}
|
|
227
185
|
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
});
|
|
186
|
+
const currentUploads = this.pendingUploads;
|
|
187
|
+
this.pendingUploads = [];
|
|
188
|
+
const remainingUploads: Batch[] = await this.upload(
|
|
189
|
+
this.mpInstance.Logger,
|
|
190
|
+
currentUploads,
|
|
191
|
+
useBeacon
|
|
192
|
+
);
|
|
193
|
+
if (remainingUploads && remainingUploads.length) {
|
|
194
|
+
this.pendingUploads.unshift(...remainingUploads);
|
|
261
195
|
}
|
|
262
196
|
|
|
263
197
|
if (triggerFuture) {
|
|
264
|
-
|
|
198
|
+
setTimeout(() => {
|
|
199
|
+
this.prepareAndUpload(true, false);
|
|
200
|
+
}, this.uploadIntervalMillis);
|
|
265
201
|
}
|
|
266
202
|
}
|
|
267
203
|
|
|
268
204
|
private async upload(
|
|
269
205
|
logger: SDKLoggerApi,
|
|
270
|
-
|
|
206
|
+
_uploads: Batch[],
|
|
271
207
|
useBeacon: boolean
|
|
272
|
-
): Promise<Batch
|
|
273
|
-
|
|
208
|
+
): Promise<Batch[]> {
|
|
209
|
+
let uploader;
|
|
210
|
+
|
|
211
|
+
// Filter out any batches that don't have events
|
|
212
|
+
const uploads = _uploads.filter(upload => !isEmpty(upload.events));
|
|
213
|
+
|
|
214
|
+
if (isEmpty(uploads)) {
|
|
274
215
|
return null;
|
|
275
216
|
}
|
|
276
217
|
|
|
277
|
-
logger.verbose(`Uploading batches: ${JSON.stringify(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
);
|
|
218
|
+
logger.verbose(`Uploading batches: ${JSON.stringify(uploads)}`);
|
|
219
|
+
logger.verbose(`Batch count: ${uploads.length}`);
|
|
220
|
+
|
|
221
|
+
for (let i = 0; i < uploads.length; i++) {
|
|
222
|
+
const fetchPayload: fetchPayload = {
|
|
223
|
+
method: 'POST',
|
|
224
|
+
headers: {
|
|
225
|
+
Accept: BatchUploader.CONTENT_TYPE,
|
|
226
|
+
'Content-Type': 'text/plain;charset=UTF-8',
|
|
227
|
+
},
|
|
228
|
+
body: JSON.stringify(uploads[i]),
|
|
229
|
+
};
|
|
304
230
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
231
|
+
// beacon is only used on onbeforeunload onpagehide events
|
|
232
|
+
if (useBeacon && this.isBeaconAvailable()) {
|
|
233
|
+
let blob = new Blob([fetchPayload.body], {
|
|
234
|
+
type: 'text/plain;charset=UTF-8',
|
|
235
|
+
});
|
|
236
|
+
navigator.sendBeacon(this.uploadUrl, blob);
|
|
237
|
+
} 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
|
+
try {
|
|
246
|
+
const response = await uploader.upload(
|
|
247
|
+
fetchPayload,
|
|
248
|
+
uploads,
|
|
249
|
+
i
|
|
309
250
|
);
|
|
310
251
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
252
|
+
if (response.status >= 200 && response.status < 300) {
|
|
253
|
+
logger.verbose(
|
|
254
|
+
`Upload success for request ID: ${uploads[i].source_request_id}`
|
|
255
|
+
);
|
|
256
|
+
} else if (
|
|
257
|
+
response.status >= 500 ||
|
|
258
|
+
response.status === 429
|
|
259
|
+
) {
|
|
260
|
+
logger.error(
|
|
261
|
+
`HTTP error status ${response.status} received`
|
|
262
|
+
);
|
|
263
|
+
//server error, add back current events and try again later
|
|
264
|
+
return uploads.slice(i, uploads.length);
|
|
265
|
+
} else if (response.status >= 401) {
|
|
266
|
+
logger.error(
|
|
267
|
+
`HTTP error status ${response.status} while uploading - please verify your API key.`
|
|
268
|
+
);
|
|
269
|
+
//if we're getting a 401, assume we'll keep getting a 401 and clear the uploads.
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
} catch (e) {
|
|
314
273
|
logger.error(
|
|
315
|
-
`
|
|
274
|
+
`Error sending event to mParticle servers. ${e}`
|
|
316
275
|
);
|
|
317
|
-
|
|
318
|
-
// so return the upload so it can be stored for later use
|
|
319
|
-
return batch;
|
|
276
|
+
return uploads.slice(i, uploads.length);
|
|
320
277
|
}
|
|
321
|
-
} catch (error) {
|
|
322
|
-
logger.error(
|
|
323
|
-
`Error sending event to mParticle servers. ${error}`
|
|
324
|
-
);
|
|
325
|
-
return batch;
|
|
326
278
|
}
|
|
327
279
|
}
|
|
328
280
|
return null;
|
|
@@ -331,24 +283,34 @@ export class BatchUploader {
|
|
|
331
283
|
|
|
332
284
|
abstract class AsyncUploader {
|
|
333
285
|
url: string;
|
|
334
|
-
|
|
286
|
+
logger: SDKLoggerApi;
|
|
335
287
|
|
|
336
|
-
constructor(url: string) {
|
|
288
|
+
constructor(url: string, logger: SDKLoggerApi) {
|
|
337
289
|
this.url = url;
|
|
290
|
+
this.logger = logger;
|
|
338
291
|
}
|
|
339
292
|
}
|
|
340
293
|
|
|
341
294
|
class FetchUploader extends AsyncUploader {
|
|
342
|
-
|
|
295
|
+
private async upload(
|
|
296
|
+
fetchPayload: fetchPayload,
|
|
297
|
+
uploads: Batch[],
|
|
298
|
+
i: number
|
|
299
|
+
) {
|
|
343
300
|
const response: XHRResponse = await fetch(this.url, fetchPayload);
|
|
344
301
|
return response;
|
|
345
302
|
}
|
|
346
303
|
}
|
|
347
304
|
|
|
348
305
|
class XHRUploader extends AsyncUploader {
|
|
349
|
-
|
|
306
|
+
private async upload(
|
|
307
|
+
fetchPayload: fetchPayload,
|
|
308
|
+
uploads: Batch[],
|
|
309
|
+
i: number
|
|
310
|
+
) {
|
|
350
311
|
const response: XHRResponse = await this.makeRequest(
|
|
351
312
|
this.url,
|
|
313
|
+
this.logger,
|
|
352
314
|
fetchPayload.body
|
|
353
315
|
);
|
|
354
316
|
return response;
|
|
@@ -356,6 +318,7 @@ class XHRUploader extends AsyncUploader {
|
|
|
356
318
|
|
|
357
319
|
private async makeRequest(
|
|
358
320
|
url: string,
|
|
321
|
+
logger: SDKLoggerApi,
|
|
359
322
|
data: string
|
|
360
323
|
): Promise<XMLHttpRequest> {
|
|
361
324
|
const xhr: XMLHttpRequest = new XMLHttpRequest();
|