@mparticle/web-sdk 3.11.0 → 3.12.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/README.md +23 -18
- package/dist/mparticle.common.js +32 -56
- package/dist/mparticle.esm.js +32 -56
- package/dist/mparticle.js +160 -142
- package/dist/types/src/kitBlocking.d.ts +2 -0
- package/dist/types/src/persistence.interfaces.d.ts +1 -2
- package/dist/types/src/roktManager.d.ts +2 -0
- package/package.json +5 -1
- package/src/consent.ts +31 -16
- package/src/events.ts +8 -6
- package/src/identity.ts +0 -4
- package/src/kitBlocking.ts +73 -52
- package/src/nativeSdkHelpers.ts +5 -6
- package/src/persistence.interfaces.ts +0 -2
- package/src/persistence.ts +24 -107
- package/src/roktManager.ts +37 -5
package/src/consent.ts
CHANGED
|
@@ -285,37 +285,52 @@ export default function Consent(this: IConsent, mpInstance: IMParticleWebSDKInst
|
|
|
285
285
|
|
|
286
286
|
fromMinifiedJsonObject(json: IMinifiedConsentJSONObject): ConsentState {
|
|
287
287
|
const state: ConsentState = self.createConsentState();
|
|
288
|
-
if (json
|
|
288
|
+
if (!isObject(json)) {
|
|
289
|
+
return state;
|
|
290
|
+
}
|
|
291
|
+
if (isObject(json.gdpr)) {
|
|
289
292
|
for (const purpose in json.gdpr) {
|
|
290
293
|
if (hasOwnProp(json.gdpr, purpose)) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
json.gdpr[purpose]
|
|
294
|
-
json.gdpr[purpose].d,
|
|
295
|
-
json.gdpr[purpose].l,
|
|
296
|
-
json.gdpr[purpose].h
|
|
294
|
+
state.addGDPRConsentState(
|
|
295
|
+
purpose,
|
|
296
|
+
privacyConsentFromMinifiedJson(json.gdpr[purpose])
|
|
297
297
|
);
|
|
298
|
-
state.addGDPRConsentState(purpose, gdprConsent);
|
|
299
298
|
}
|
|
300
299
|
}
|
|
301
300
|
}
|
|
302
301
|
|
|
303
|
-
if (json.ccpa) {
|
|
302
|
+
if (isObject(json.ccpa)) {
|
|
304
303
|
if (hasOwnProp(json.ccpa, CCPAPurpose)) {
|
|
305
|
-
|
|
306
|
-
json.ccpa[CCPAPurpose]
|
|
307
|
-
json.ccpa[CCPAPurpose].ts,
|
|
308
|
-
json.ccpa[CCPAPurpose].d,
|
|
309
|
-
json.ccpa[CCPAPurpose].l,
|
|
310
|
-
json.ccpa[CCPAPurpose].h
|
|
304
|
+
state.setCCPAConsentState(
|
|
305
|
+
privacyConsentFromMinifiedJson(json.ccpa[CCPAPurpose])
|
|
311
306
|
);
|
|
312
|
-
state.setCCPAConsentState(ccpaConsent);
|
|
313
307
|
}
|
|
314
308
|
}
|
|
315
309
|
return state;
|
|
316
310
|
},
|
|
317
311
|
};
|
|
318
312
|
|
|
313
|
+
function privacyConsentFromMinifiedJson(
|
|
314
|
+
minifiedConsent: IPrivacyV2DTO
|
|
315
|
+
): PrivacyConsentState | null {
|
|
316
|
+
if (!isObject(minifiedConsent)) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
const timestampIsObject =
|
|
320
|
+
typeof minifiedConsent.ts === 'object' &&
|
|
321
|
+
minifiedConsent.ts !== null;
|
|
322
|
+
if (timestampIsObject) {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
return self.createPrivacyConsent(
|
|
326
|
+
minifiedConsent.c,
|
|
327
|
+
minifiedConsent.ts,
|
|
328
|
+
minifiedConsent.d,
|
|
329
|
+
minifiedConsent.l,
|
|
330
|
+
minifiedConsent.h
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
319
334
|
// TODO: Refactor this method into a constructor
|
|
320
335
|
this.createConsentState = function(
|
|
321
336
|
this: ConsentState,
|
package/src/events.ts
CHANGED
|
@@ -402,7 +402,7 @@ export default function Events(
|
|
|
402
402
|
eventType: valueof<typeof EventType>
|
|
403
403
|
): void {
|
|
404
404
|
let elements: ArrayLike<Element> | Element[] = [],
|
|
405
|
-
handler = function(e: Event): void {
|
|
405
|
+
handler = function(e: Event, element: DOMHandlerElement): void {
|
|
406
406
|
const timeoutHandler = function(): void {
|
|
407
407
|
if (element.href) {
|
|
408
408
|
window.location.href = element.href;
|
|
@@ -447,7 +447,6 @@ export default function Events(
|
|
|
447
447
|
);
|
|
448
448
|
}
|
|
449
449
|
},
|
|
450
|
-
element: DOMHandlerElement,
|
|
451
450
|
i: number;
|
|
452
451
|
|
|
453
452
|
if (!selector) {
|
|
@@ -472,17 +471,20 @@ export default function Events(
|
|
|
472
471
|
);
|
|
473
472
|
|
|
474
473
|
for (i = 0; i < elements.length; i++) {
|
|
475
|
-
element = elements[i] as DOMHandlerElement;
|
|
474
|
+
const element = elements[i] as DOMHandlerElement;
|
|
475
|
+
const elementHandler = function(e: Event): void {
|
|
476
|
+
handler(e, element);
|
|
477
|
+
};
|
|
476
478
|
|
|
477
479
|
if (element.addEventListener) {
|
|
478
480
|
// Modern browsers
|
|
479
|
-
element.addEventListener(domEvent,
|
|
481
|
+
element.addEventListener(domEvent, elementHandler, false);
|
|
480
482
|
} else if (element.attachEvent) {
|
|
481
483
|
// IE < 9
|
|
482
|
-
element.attachEvent('on' + domEvent,
|
|
484
|
+
element.attachEvent('on' + domEvent, elementHandler);
|
|
483
485
|
} else {
|
|
484
486
|
// All other browsers
|
|
485
|
-
(element as any)['on' + domEvent] =
|
|
487
|
+
(element as any)['on' + domEvent] = elementHandler;
|
|
486
488
|
}
|
|
487
489
|
}
|
|
488
490
|
} else {
|
package/src/identity.ts
CHANGED
|
@@ -1498,10 +1498,6 @@ export default function Identity(
|
|
|
1498
1498
|
mpInstance._Persistence.update();
|
|
1499
1499
|
mpInstance._Store.syncPersistenceData();
|
|
1500
1500
|
|
|
1501
|
-
mpInstance._Persistence.findPrevCookiesBasedOnUI(
|
|
1502
|
-
identityApiData
|
|
1503
|
-
);
|
|
1504
|
-
|
|
1505
1501
|
// https://go.mparticle.com/work/SQDSDKS-6357
|
|
1506
1502
|
mpInstance._Store.context =
|
|
1507
1503
|
identityApiResult.context || mpInstance._Store.context;
|
package/src/kitBlocking.ts
CHANGED
|
@@ -20,6 +20,10 @@ const DataPlanMatchType = {
|
|
|
20
20
|
ProductImpression: "product_impression"
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
const withNoChange = (event: SDKEvent): SDKEvent => event;
|
|
24
|
+
const withoutUserAttributes = (event: SDKEvent): SDKEvent => ({ ...event, UserAttributes: {} });
|
|
25
|
+
const withoutUserIdentities = (event: SDKEvent): SDKEvent => ({ ...event, UserIdentities: [] });
|
|
26
|
+
|
|
23
27
|
/*
|
|
24
28
|
inspiration from https://github.com/mParticle/data-planning-node/blob/master/src/data_planning/data_plan_event_validator.ts
|
|
25
29
|
but modified to only include commerce events, custom events, screen views, and removes validation
|
|
@@ -318,29 +322,51 @@ export default class KitBlocker {
|
|
|
318
322
|
then product attributes if applicable, then user attributes,
|
|
319
323
|
then the user identities
|
|
320
324
|
*/
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return event;
|
|
336
|
-
} catch(e) {
|
|
325
|
+
if (event) {
|
|
326
|
+
event = this.applyBlockingStep('the event and its attributes', this.transformEventAndEventAttributes, event, withNoChange);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (event && event.EventDataType === Types.MessageType.Commerce) {
|
|
330
|
+
event = this.applyBlockingStep('product attributes', this.transformProductAttributes, event, withNoChange);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (event) {
|
|
334
|
+
event = this.applyBlockingStep('user attributes', this.transformUserAttributes, event, withoutUserAttributes);
|
|
335
|
+
event = this.applyBlockingStep('user identities', this.transformUserIdentities, event, withoutUserIdentities);
|
|
336
|
+
}
|
|
337
|
+
|
|
337
338
|
return event;
|
|
338
|
-
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
applyBlockingStep(
|
|
342
|
+
filteredData: string,
|
|
343
|
+
step: (event: SDKEvent) => SDKEvent,
|
|
344
|
+
event: SDKEvent,
|
|
345
|
+
onFailure: (event: SDKEvent) => SDKEvent
|
|
346
|
+
): SDKEvent {
|
|
347
|
+
try {
|
|
348
|
+
return step.call(this, event);
|
|
349
|
+
} catch (stepError) {
|
|
350
|
+
const fallbackEvent = onFailure(event);
|
|
351
|
+
this.reportFailedStep(filteredData, stepError);
|
|
352
|
+
return fallbackEvent;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
reportFailedStep(filteredData: string, stepError: unknown): void {
|
|
357
|
+
try {
|
|
358
|
+
this.mpInstance.Logger.error('Kit blocking could not filter ' + filteredData + ': ' + stepError);
|
|
359
|
+
} catch {
|
|
360
|
+
// Best-effort: a logger or error value that throws must not stop the remaining steps.
|
|
361
|
+
}
|
|
339
362
|
}
|
|
340
363
|
|
|
341
364
|
transformEventAndEventAttributes(event: SDKEvent): SDKEvent {
|
|
342
365
|
const clonedEvent = {...event};
|
|
343
366
|
const baseEvent: BaseEvent = convertEvent(clonedEvent);
|
|
367
|
+
if (!baseEvent) {
|
|
368
|
+
return clonedEvent;
|
|
369
|
+
}
|
|
344
370
|
const matchKey: string = this.getMatchKey(baseEvent);
|
|
345
371
|
const matchedEvent = this.dataPlanMatchLookups[matchKey];
|
|
346
372
|
|
|
@@ -364,7 +390,7 @@ export default class KitBlocker {
|
|
|
364
390
|
return clonedEvent;
|
|
365
391
|
}
|
|
366
392
|
if (matchedEvent) {
|
|
367
|
-
for (const key of Object.keys(clonedEvent.EventAttributes)) {
|
|
393
|
+
for (const key of Object.keys(clonedEvent.EventAttributes ?? {})) {
|
|
368
394
|
if (!matchedEvent[key]) {
|
|
369
395
|
delete clonedEvent.EventAttributes[key];
|
|
370
396
|
}
|
|
@@ -384,13 +410,18 @@ export default class KitBlocker {
|
|
|
384
410
|
const matchKey: string = this.getProductAttributeMatchKey(baseEvent);
|
|
385
411
|
const matchedEvent = this.dataPlanMatchLookups[matchKey];
|
|
386
412
|
|
|
387
|
-
function
|
|
388
|
-
productList
|
|
413
|
+
function withPlannedAttributesOnly(plannedAttributes: { [key: string]: boolean }, productList: SDKProduct[]): SDKProduct[] {
|
|
414
|
+
return productList?.map(product => {
|
|
415
|
+
if (!product.Attributes) {
|
|
416
|
+
return product;
|
|
417
|
+
}
|
|
418
|
+
const attributes = {};
|
|
389
419
|
for (const productKey of Object.keys(product.Attributes)) {
|
|
390
|
-
if (
|
|
391
|
-
|
|
420
|
+
if (plannedAttributes[productKey] === true) {
|
|
421
|
+
attributes[productKey] = product.Attributes[productKey];
|
|
392
422
|
}
|
|
393
423
|
}
|
|
424
|
+
return { ...product, Attributes: attributes };
|
|
394
425
|
});
|
|
395
426
|
}
|
|
396
427
|
|
|
@@ -414,17 +445,18 @@ export default class KitBlocker {
|
|
|
414
445
|
return clonedEvent;
|
|
415
446
|
}
|
|
416
447
|
if (matchedEvent) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
clonedEvent.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
448
|
+
if (clonedEvent.ProductAction) {
|
|
449
|
+
clonedEvent.ProductAction = {
|
|
450
|
+
...clonedEvent.ProductAction,
|
|
451
|
+
ProductList: withPlannedAttributesOnly(matchedEvent, clonedEvent.ProductAction.ProductList),
|
|
452
|
+
};
|
|
453
|
+
} else if (clonedEvent.ProductImpressions) {
|
|
454
|
+
clonedEvent.ProductImpressions = clonedEvent.ProductImpressions.map(impression => ({
|
|
455
|
+
...impression,
|
|
456
|
+
ProductList: withPlannedAttributesOnly(matchedEvent, impression.ProductList),
|
|
457
|
+
}));
|
|
458
|
+
} else {
|
|
459
|
+
this.mpInstance.Logger.warning('Product Not Supported ')
|
|
428
460
|
}
|
|
429
461
|
|
|
430
462
|
return clonedEvent;
|
|
@@ -439,17 +471,15 @@ export default class KitBlocker {
|
|
|
439
471
|
transformUserAttributes(event: SDKEvent) {
|
|
440
472
|
const clonedEvent = {...event};
|
|
441
473
|
if (this.blockUserAttributes) {
|
|
442
|
-
/*
|
|
443
|
-
If the user attribute is not found in the matchedAttributes
|
|
444
|
-
then remove it from event.UserAttributes as it is blocked
|
|
445
|
-
*/
|
|
446
474
|
const matchedAttributes = this.dataPlanMatchLookups['user_attributes'];
|
|
447
|
-
if (this.mpInstance._Helpers.isObject(matchedAttributes)) {
|
|
475
|
+
if (this.mpInstance._Helpers.isObject(matchedAttributes) && clonedEvent.UserAttributes) {
|
|
476
|
+
const plannedUserAttributes = {};
|
|
448
477
|
for (const ua of Object.keys(clonedEvent.UserAttributes)) {
|
|
449
|
-
if (
|
|
450
|
-
|
|
478
|
+
if (matchedAttributes[ua] === true) {
|
|
479
|
+
plannedUserAttributes[ua] = clonedEvent.UserAttributes[ua];
|
|
451
480
|
}
|
|
452
481
|
}
|
|
482
|
+
clonedEvent.UserAttributes = plannedUserAttributes;
|
|
453
483
|
}
|
|
454
484
|
}
|
|
455
485
|
|
|
@@ -507,27 +537,18 @@ export default class KitBlocker {
|
|
|
507
537
|
}
|
|
508
538
|
|
|
509
539
|
transformUserIdentities(event: SDKEvent) {
|
|
510
|
-
/*
|
|
511
|
-
If the user identity is not found in matchedIdentities
|
|
512
|
-
then remove it from event.UserIdentities as it is blocked.
|
|
513
|
-
event.UserIdentities is of type [{Identity: 'id1', Type: 7}, ...]
|
|
514
|
-
and so to compare properly in matchedIdentities, each Type needs
|
|
515
|
-
to be converted to an identityName
|
|
516
|
-
*/
|
|
517
540
|
const clonedEvent = {...event};
|
|
518
541
|
|
|
519
542
|
if (this.blockUserIdentities) {
|
|
520
543
|
const matchedIdentities = this.dataPlanMatchLookups['user_identities'];
|
|
521
544
|
if (this.mpInstance._Helpers.isObject(matchedIdentities)) {
|
|
522
545
|
if (clonedEvent?.UserIdentities?.length) {
|
|
523
|
-
clonedEvent.UserIdentities.
|
|
546
|
+
clonedEvent.UserIdentities = clonedEvent.UserIdentities.filter(uiByType => {
|
|
524
547
|
const identityName = Types.IdentityType.getIdentityName(
|
|
525
548
|
this.mpInstance._Helpers.parseNumber(uiByType.Type)
|
|
526
549
|
);
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
clonedEvent.UserIdentities.splice(i, 1);
|
|
530
|
-
}
|
|
550
|
+
|
|
551
|
+
return matchedIdentities[identityName];
|
|
531
552
|
});
|
|
532
553
|
}
|
|
533
554
|
}
|
package/src/nativeSdkHelpers.ts
CHANGED
|
@@ -118,7 +118,7 @@ export default function NativeSdkHelpers(
|
|
|
118
118
|
this.isBridgeV1Available = function(): boolean {
|
|
119
119
|
if (
|
|
120
120
|
mpInstance._Store.SDKConfig.useNativeSdk ||
|
|
121
|
-
|
|
121
|
+
getAndroidBridge(androidBridgeNameBase) ||
|
|
122
122
|
mpInstance._Store.SDKConfig.isIOS
|
|
123
123
|
) {
|
|
124
124
|
return true;
|
|
@@ -160,14 +160,13 @@ export default function NativeSdkHelpers(
|
|
|
160
160
|
};
|
|
161
161
|
|
|
162
162
|
this.sendViaBridgeV1 = function(path: string, value: string): void {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
) {
|
|
163
|
+
const androidBridge = getAndroidBridge(androidBridgeNameBase);
|
|
164
|
+
|
|
165
|
+
if (androidBridge && androidBridge.hasOwnProperty(path)) {
|
|
167
166
|
mpInstance.Logger.verbose(
|
|
168
167
|
Messages.InformationMessages.SendAndroid + path
|
|
169
168
|
);
|
|
170
|
-
|
|
169
|
+
androidBridge[path](value);
|
|
171
170
|
} else if (mpInstance._Store.SDKConfig.isIOS) {
|
|
172
171
|
mpInstance.Logger.verbose(
|
|
173
172
|
Messages.InformationMessages.SendIOS + path
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
IdentityApiData,
|
|
3
2
|
MPID,
|
|
4
3
|
Product,
|
|
5
4
|
UserIdentities,
|
|
@@ -111,7 +110,6 @@ export interface IPersistence {
|
|
|
111
110
|
domain: string,
|
|
112
111
|
maxCookieSize: number
|
|
113
112
|
): string;
|
|
114
|
-
findPrevCookiesBasedOnUI(identityApiData: IdentityApiData): void;
|
|
115
113
|
encodePersistence(persistence: string): string;
|
|
116
114
|
decodePersistence(persistenceString: string | null): string | void;
|
|
117
115
|
getCookieDomain(): string;
|
package/src/persistence.ts
CHANGED
|
@@ -3,7 +3,7 @@ import Polyfill from './polyfill';
|
|
|
3
3
|
import * as Utils from './utils';
|
|
4
4
|
import { IMParticleWebSDKInstance } from './mp-instance';
|
|
5
5
|
import { IPersistence, IPersistenceMinified } from './persistence.interfaces';
|
|
6
|
-
import {
|
|
6
|
+
import { MPID } from '@mparticle/web-sdk';
|
|
7
7
|
import { CookieSyncDates } from './cookieSyncManager';
|
|
8
8
|
import { Dictionary } from './utils';
|
|
9
9
|
import { IMParticleInstanceManager } from './sdkRuntimeModels';
|
|
@@ -85,9 +85,9 @@ export default function _Persistence(
|
|
|
85
85
|
) {
|
|
86
86
|
if (!cookies) {
|
|
87
87
|
self.storeDataInMemory(localStorageData);
|
|
88
|
-
return;
|
|
88
|
+
return undefined;
|
|
89
89
|
}
|
|
90
|
-
const allData =
|
|
90
|
+
const allData = localStorageData || cookies;
|
|
91
91
|
self.storeDataInMemory(allData);
|
|
92
92
|
self.expireCookies(mpInstance._Store.storageName);
|
|
93
93
|
return allData;
|
|
@@ -122,21 +122,24 @@ export default function _Persistence(
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
function clearCorruptStorage(): void {
|
|
125
|
+
function clearCorruptStorage(cookieWasLoaded: boolean): void {
|
|
126
126
|
if (
|
|
127
127
|
self.useLocalStorage() &&
|
|
128
128
|
mpInstance._Store.isLocalStorageAvailable
|
|
129
129
|
) {
|
|
130
|
-
|
|
130
|
+
if (!cookieWasLoaded) {
|
|
131
|
+
localStorage.removeItem(mpInstance._Store.storageName);
|
|
132
|
+
}
|
|
131
133
|
return;
|
|
132
134
|
}
|
|
133
135
|
self.expireCookies(mpInstance._Store.storageName);
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
this.initializeStorage = function(): void {
|
|
139
|
+
let cookies: IPersistenceMinified | null = null;
|
|
137
140
|
try {
|
|
138
141
|
const localStorageData = self.getLocalStorage();
|
|
139
|
-
|
|
142
|
+
cookies = self.getCookie();
|
|
140
143
|
|
|
141
144
|
// https://go.mparticle.com/work/SQDSDKS-6045
|
|
142
145
|
setFirstRunFromExistingData(localStorageData, cookies);
|
|
@@ -156,7 +159,7 @@ export default function _Persistence(
|
|
|
156
159
|
} catch (e) {
|
|
157
160
|
// If cookies or local storage is corrupt, we want to remove it
|
|
158
161
|
// so that in the future, initializeStorage will work
|
|
159
|
-
clearCorruptStorage();
|
|
162
|
+
clearCorruptStorage(Boolean(cookies));
|
|
160
163
|
mpInstance.Logger.error('Error initializing storage: ' + e);
|
|
161
164
|
}
|
|
162
165
|
};
|
|
@@ -417,10 +420,7 @@ export default function _Persistence(
|
|
|
417
420
|
l,
|
|
418
421
|
parts,
|
|
419
422
|
name,
|
|
420
|
-
cookie
|
|
421
|
-
result: string | Dictionary<string> | undefined = key
|
|
422
|
-
? undefined
|
|
423
|
-
: {};
|
|
423
|
+
cookie;
|
|
424
424
|
|
|
425
425
|
mpInstance.Logger.verbose(Messages.InformationMessages.CookieSearch);
|
|
426
426
|
|
|
@@ -443,25 +443,23 @@ export default function _Persistence(
|
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
if (key && key === name) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
446
|
+
const decodedPersistence = self.decodePersistence(
|
|
447
|
+
(mpInstance._Helpers as Dictionary).converted(cookie)
|
|
448
|
+
);
|
|
449
|
+
const persistence = decodedPersistence
|
|
450
|
+
? JSON.parse(decodedPersistence)
|
|
451
|
+
: null;
|
|
449
452
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
453
|
+
if (mpInstance._Helpers.isObject(persistence)) {
|
|
454
|
+
mpInstance.Logger.verbose(
|
|
455
|
+
Messages.InformationMessages.CookieFound
|
|
456
|
+
);
|
|
457
|
+
return persistence;
|
|
458
|
+
}
|
|
454
459
|
}
|
|
455
460
|
}
|
|
456
461
|
|
|
457
|
-
|
|
458
|
-
mpInstance.Logger.verbose(Messages.InformationMessages.CookieFound);
|
|
459
|
-
return JSON.parse(
|
|
460
|
-
self.decodePersistence(result as string) as string
|
|
461
|
-
);
|
|
462
|
-
} else {
|
|
463
|
-
return null;
|
|
464
|
-
}
|
|
462
|
+
return null;
|
|
465
463
|
};
|
|
466
464
|
|
|
467
465
|
// https://go.mparticle.com/work/SQDSDKS-5022
|
|
@@ -746,85 +744,6 @@ export default function _Persistence(
|
|
|
746
744
|
);
|
|
747
745
|
}
|
|
748
746
|
|
|
749
|
-
function cookieUiMatchesRequestedIdentity(
|
|
750
|
-
cookieUIs,
|
|
751
|
-
requestedIdentityType: string,
|
|
752
|
-
requestedValue
|
|
753
|
-
): boolean {
|
|
754
|
-
for (let cookieUIType in cookieUIs) {
|
|
755
|
-
if (
|
|
756
|
-
requestedIdentityType === cookieUIType &&
|
|
757
|
-
requestedValue === cookieUIs[cookieUIType]
|
|
758
|
-
) {
|
|
759
|
-
return true;
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
return false;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
function findMpidForRequestedIdentity(
|
|
766
|
-
persistence: IPersistenceMinified,
|
|
767
|
-
requestedIdentityType: string,
|
|
768
|
-
requestedValue
|
|
769
|
-
) {
|
|
770
|
-
let matchedUser;
|
|
771
|
-
for (let key in persistence) {
|
|
772
|
-
// any value in persistence that has an MPID key will be an MPID to search through
|
|
773
|
-
// other keys on the cookie are currentSessionMPIDs and currentMPID which should not be searched
|
|
774
|
-
if (
|
|
775
|
-
!mpInstance._Helpers.isObject(persistence[key]) ||
|
|
776
|
-
!persistence[key].mpid
|
|
777
|
-
) {
|
|
778
|
-
continue;
|
|
779
|
-
}
|
|
780
|
-
if (
|
|
781
|
-
cookieUiMatchesRequestedIdentity(
|
|
782
|
-
persistence[key].ui,
|
|
783
|
-
requestedIdentityType,
|
|
784
|
-
requestedValue
|
|
785
|
-
)
|
|
786
|
-
) {
|
|
787
|
-
matchedUser = key;
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
return matchedUser;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
function findMpidMatchingIdentities(
|
|
794
|
-
persistence: IPersistenceMinified | null,
|
|
795
|
-
identityApiData: IdentityApiData
|
|
796
|
-
) {
|
|
797
|
-
let matchedUser;
|
|
798
|
-
for (let requestedIdentityType in identityApiData.userIdentities) {
|
|
799
|
-
if (!persistence || !Object.keys(persistence).length) {
|
|
800
|
-
continue;
|
|
801
|
-
}
|
|
802
|
-
const match = findMpidForRequestedIdentity(
|
|
803
|
-
persistence,
|
|
804
|
-
requestedIdentityType,
|
|
805
|
-
identityApiData.userIdentities[requestedIdentityType]
|
|
806
|
-
);
|
|
807
|
-
if (match) {
|
|
808
|
-
matchedUser = match;
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
return matchedUser;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
this.findPrevCookiesBasedOnUI = function(identityApiData: IdentityApiData): void {
|
|
815
|
-
const persistence = mpInstance._Persistence.getPersistence();
|
|
816
|
-
if (!identityApiData) {
|
|
817
|
-
return;
|
|
818
|
-
}
|
|
819
|
-
const matchedUser = findMpidMatchingIdentities(
|
|
820
|
-
persistence,
|
|
821
|
-
identityApiData
|
|
822
|
-
);
|
|
823
|
-
if (matchedUser) {
|
|
824
|
-
self.storeDataInMemory(persistence, matchedUser);
|
|
825
|
-
}
|
|
826
|
-
};
|
|
827
|
-
|
|
828
747
|
function isNonEmptyArrayOrObject(value): boolean {
|
|
829
748
|
if (Array.isArray(value)) {
|
|
830
749
|
return value.length > 0;
|
|
@@ -947,8 +866,6 @@ export default function _Persistence(
|
|
|
947
866
|
continue;
|
|
948
867
|
}
|
|
949
868
|
if (!SDKv2NonMPIDCookieKeys[mpid]) {
|
|
950
|
-
// Written as an object by encodeMpidRecords, then read as one by
|
|
951
|
-
// findMpidForRequestedIdentity and copied by copyNonCurrentUserMpids.
|
|
952
869
|
if (!mpInstance._Helpers.isObject(persistence[mpid])) {
|
|
953
870
|
delete persistence[mpid];
|
|
954
871
|
continue;
|
package/src/roktManager.ts
CHANGED
|
@@ -26,6 +26,8 @@ import { subscribeToRouteChange } from "./routeChangeMonitor";
|
|
|
26
26
|
|
|
27
27
|
const PASSBACK_CONVERSION_TRACKING_ID = 'passbackconversiontrackingid';
|
|
28
28
|
|
|
29
|
+
type AttributeTargetUser = Pick<IMParticleUser, 'getUserIdentities' | 'setUserAttributes'>;
|
|
30
|
+
|
|
29
31
|
// https://docs.rokt.com/developers/integration-guides/web/library/attributes
|
|
30
32
|
export type RoktAttributeValueArray = Array<string | number | boolean>;
|
|
31
33
|
export type RoktAttributeValueType = string | number | boolean | undefined | null;
|
|
@@ -374,7 +376,11 @@ export default class RoktManager {
|
|
|
374
376
|
});
|
|
375
377
|
}
|
|
376
378
|
|
|
377
|
-
|
|
379
|
+
const userAttributes = this.toUserAttributes(mappedAttributes);
|
|
380
|
+
|
|
381
|
+
if (isEmpty(newIdentities)) {
|
|
382
|
+
this.setUserAttributes(this.currentUser, userAttributes);
|
|
383
|
+
} else {
|
|
378
384
|
// Fire-and-forget identify — best-effort, does not block selectPlacements
|
|
379
385
|
try {
|
|
380
386
|
this.identityService.identify(
|
|
@@ -391,6 +397,7 @@ export default class RoktManager {
|
|
|
391
397
|
'Background identify failed with HTTP ' + httpCode
|
|
392
398
|
);
|
|
393
399
|
}
|
|
400
|
+
this.setUserAttributes(result?.getUser?.(), userAttributes, newIdentities);
|
|
394
401
|
// Drain any selectPlacements calls that were deferred while
|
|
395
402
|
// identify was in-flight. By the time this callback fires,
|
|
396
403
|
// identityCallInFlight has already been reset to false.
|
|
@@ -402,6 +409,7 @@ export default class RoktManager {
|
|
|
402
409
|
'Background identify threw an error: ' +
|
|
403
410
|
getErrorMessage(error)
|
|
404
411
|
);
|
|
412
|
+
this.setUserAttributes(this.identityService.getCurrentUser(), userAttributes, newIdentities);
|
|
405
413
|
}
|
|
406
414
|
}
|
|
407
415
|
|
|
@@ -411,8 +419,6 @@ export default class RoktManager {
|
|
|
411
419
|
// so stale identity state does not persist beyond this invocation.
|
|
412
420
|
const finalUserIdentities = { ...currentUserIdentities, ...newIdentities };
|
|
413
421
|
|
|
414
|
-
this.setUserAttributes(mappedAttributes);
|
|
415
|
-
|
|
416
422
|
const timeOnSite = this.store?.getTimeOnSite?.();
|
|
417
423
|
const totalTimeOnSite = this.store?.getTotalTimeOnSite?.();
|
|
418
424
|
|
|
@@ -641,7 +647,7 @@ export default class RoktManager {
|
|
|
641
647
|
return Boolean(this.kit && this.kit.launcher);
|
|
642
648
|
}
|
|
643
649
|
|
|
644
|
-
private
|
|
650
|
+
private toUserAttributes(attributes: RoktAttributes): Dictionary<RoktAttributeValueType> {
|
|
645
651
|
const reservedAttributes = ['sandbox'];
|
|
646
652
|
const filteredAttributes = {};
|
|
647
653
|
|
|
@@ -652,13 +658,39 @@ export default class RoktManager {
|
|
|
652
658
|
}
|
|
653
659
|
}
|
|
654
660
|
|
|
661
|
+
return filteredAttributes;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
private setUserAttributes(
|
|
665
|
+
user: AttributeTargetUser | null | undefined,
|
|
666
|
+
userAttributes: Dictionary<RoktAttributeValueType>,
|
|
667
|
+
identitiesUserMustNotContradict: UserIdentities = {}
|
|
668
|
+
): void {
|
|
669
|
+
if (!user) {
|
|
670
|
+
this.logger.warning('selectPlacements attributes were not set because there is no user to set them on.');
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
|
|
655
674
|
try {
|
|
656
|
-
this.
|
|
675
|
+
if (this.hasContradictingIdentity(user, identitiesUserMustNotContradict)) {
|
|
676
|
+
this.logger.warning(
|
|
677
|
+
'selectPlacements attributes were not set because identify did not resolve to a user with the passed email or hashed email.'
|
|
678
|
+
);
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
user.setUserAttributes(userAttributes);
|
|
657
682
|
} catch (error) {
|
|
658
683
|
this.logger.error('Error setting user attributes: ' + error);
|
|
659
684
|
}
|
|
660
685
|
}
|
|
661
686
|
|
|
687
|
+
private hasContradictingIdentity(user: AttributeTargetUser, identities: UserIdentities): boolean {
|
|
688
|
+
return Object.keys(identities).some((identityType) => {
|
|
689
|
+
const userValue = user.getUserIdentities()?.userIdentities?.[identityType];
|
|
690
|
+
return Boolean(userValue) && userValue !== identities[identityType];
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
|
|
662
694
|
private mapPlacementAttributes(attributes: RoktAttributes, placementAttributesMapping: Dictionary<string>[]): RoktAttributes {
|
|
663
695
|
const mappingLookup: { [key: string]: string } = {};
|
|
664
696
|
for (const mapping of placementAttributesMapping) {
|