@mparticle/web-sdk 2.25.3 → 2.26.1

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/src/store.ts CHANGED
@@ -20,7 +20,15 @@ import {
20
20
  SDKInitConfig,
21
21
  SDKProduct,
22
22
  } from './sdkRuntimeModels';
23
- import { isNumber, isDataPlanSlug, Dictionary, parseNumber } from './utils';
23
+ import {
24
+ Dictionary,
25
+ isDataPlanSlug,
26
+ isEmpty,
27
+ isNumber,
28
+ isObject,
29
+ parseNumber,
30
+ returnConvertedBoolean,
31
+ } from './utils';
24
32
  import { SDKConsentState } from './consent';
25
33
  import { Kit, MPForwarder } from './forwarders.interfaces';
26
34
  import { IPersistenceMinified } from './persistence.interfaces';
@@ -61,6 +69,7 @@ export interface SDKConfig {
61
69
  aliasUrl?: string;
62
70
  configUrl?: string;
63
71
  identityUrl?: string;
72
+ userAudienceUrl?: string;
64
73
  isIOS?: boolean;
65
74
  maxProducts: number;
66
75
  requestConfig?: boolean;
@@ -70,6 +79,9 @@ export interface SDKConfig {
70
79
  v1SecureServiceUrl?: string;
71
80
  v2SecureServiceUrl?: string;
72
81
  v3SecureServiceUrl?: string;
82
+ webviewBridgeName?: string;
83
+ workspaceToken?: string;
84
+ requiredWebviewBridgeName?: string;
73
85
  }
74
86
 
75
87
  function createSDKConfig(config: SDKInitConfig): SDKConfig {
@@ -113,7 +125,7 @@ interface WrapperSDKInfo {
113
125
 
114
126
  // https://go.mparticle.com/work/SQDSDKS-5954
115
127
  export interface IFeatureFlags {
116
- reportBatching?: string;
128
+ reportBatching?: boolean;
117
129
  eventBatchingIntervalMillis?: number;
118
130
  offlineStorage?: string;
119
131
  directURLRouting?: boolean;
@@ -168,8 +180,14 @@ export interface IStore {
168
180
 
169
181
  getDeviceId?(): string;
170
182
  setDeviceId?(deviceId: string): void;
183
+ getFirstSeenTime?(mpid: MPID): number;
184
+ setFirstSeenTime?(mpid: MPID, time?: number): void;
185
+ getLastSeenTime?(mpid: MPID): number;
186
+ setLastSeenTime?(mpid: MPID, time?: number): void;
171
187
 
188
+ hasInvalidIdentifyRequest?: () => boolean;
172
189
  nullifySession?: () => void;
190
+ processConfig(config: SDKInitConfig): void;
173
191
  }
174
192
 
175
193
  // TODO: Merge this with SDKStoreApi in sdkRuntimeModels
@@ -179,6 +197,13 @@ export default function Store(
179
197
  mpInstance: MParticleWebSDK,
180
198
  apiKey?: string
181
199
  ) {
200
+ const {
201
+ createMainStorageName,
202
+ createProductStorageName,
203
+ } = mpInstance._Helpers;
204
+
205
+ const { isWebviewEnabled } = mpInstance._NativeSdkHelpers;
206
+
182
207
  const defaultStore: Partial<IStore> = {
183
208
  isEnabled: true,
184
209
  sessionAttributes: {},
@@ -260,19 +285,19 @@ export default function Store(
260
285
  this.SDKConfig.flags = {};
261
286
  }
262
287
 
263
- this.SDKConfig.flags = processFlags(
264
- config,
265
- this.SDKConfig as SDKConfig
266
- );
288
+ // We process the initial config that is passed via the SDK init
289
+ // and then we will reprocess the config within the processConfig
290
+ // function when the config is updated from the server
291
+ // https://go.mparticle.com/work/SQDSDKS-6317
292
+ this.SDKConfig.flags = processFlags(config);
267
293
 
268
294
  if (config.deviceId) {
269
295
  this.deviceId = config.deviceId;
270
296
  }
271
297
  if (config.hasOwnProperty('isDevelopmentMode')) {
272
- this.SDKConfig.isDevelopmentMode =
273
- mpInstance._Helpers.returnConvertedBoolean(
274
- config.isDevelopmentMode
275
- );
298
+ this.SDKConfig.isDevelopmentMode = returnConvertedBoolean(
299
+ config.isDevelopmentMode
300
+ );
276
301
  } else {
277
302
  this.SDKConfig.isDevelopmentMode = false;
278
303
  }
@@ -451,6 +476,16 @@ export default function Store(
451
476
  }
452
477
  }
453
478
 
479
+ this.hasInvalidIdentifyRequest = (): boolean => {
480
+ const { identifyRequest } = this.SDKConfig;
481
+ return (
482
+ (isObject(identifyRequest) &&
483
+ isObject(identifyRequest.userIdentities) &&
484
+ isEmpty(identifyRequest.userIdentities)) ||
485
+ !identifyRequest
486
+ );
487
+ };
488
+
454
489
  this.getDeviceId = () => this.deviceId;
455
490
  this.setDeviceId = (deviceId: string) => {
456
491
  this.deviceId = deviceId;
@@ -458,18 +493,118 @@ export default function Store(
458
493
  mpInstance._Persistence.update();
459
494
  };
460
495
 
496
+ this.getFirstSeenTime = (mpid: MPID) => {
497
+ if (!mpid) {
498
+ return null;
499
+ }
500
+
501
+ if (
502
+ this.persistenceData &&
503
+ this.persistenceData[mpid] &&
504
+ this.persistenceData[mpid].fst
505
+ ) {
506
+ return this.persistenceData[mpid].fst;
507
+ } else {
508
+ return null;
509
+ }
510
+ };
511
+
512
+ this.setFirstSeenTime = (mpid: MPID, _time?: number) => {
513
+ if (!mpid) {
514
+ return;
515
+ }
516
+
517
+ const time = _time || new Date().getTime();
518
+
519
+ if (this.persistenceData) {
520
+ if (!this.persistenceData[mpid]) {
521
+ this.persistenceData[mpid] = {};
522
+ }
523
+ if (!this.persistenceData[mpid].fst) {
524
+ this.persistenceData[mpid].fst = time;
525
+ mpInstance._Persistence.savePersistence(this.persistenceData);
526
+ }
527
+ }
528
+ };
529
+
530
+ this.getLastSeenTime = (mpid: MPID) => {
531
+ if (!mpid) {
532
+ return null;
533
+ }
534
+ // // https://go.mparticle.com/work/SQDSDKS-6315
535
+ const currentUser = mpInstance.Identity.getCurrentUser();
536
+ if (mpid === currentUser?.getMPID()) {
537
+ // if the mpid is the current user, its last seen time is the current time
538
+ return new Date().getTime();
539
+ } else if (
540
+ this.persistenceData &&
541
+ this.persistenceData[mpid] &&
542
+ this.persistenceData[mpid].lst
543
+ ) {
544
+ return this.persistenceData[mpid].lst;
545
+ } else {
546
+ return null;
547
+ }
548
+ };
549
+
550
+ this.setLastSeenTime = (mpid: MPID, _time?: number) => {
551
+ if (!mpid) {
552
+ return;
553
+ }
554
+
555
+ const time = _time || new Date().getTime();
556
+
557
+ if (this.persistenceData) {
558
+ if (!this.persistenceData[mpid]) {
559
+ this.persistenceData[mpid] = {};
560
+ }
561
+ if (!this.persistenceData[mpid].lst) {
562
+ this.persistenceData[mpid].lst = time;
563
+ mpInstance._Persistence.savePersistence(this.persistenceData);
564
+ }
565
+ }
566
+ };
567
+
461
568
  this.nullifySession = (): void => {
462
569
  this.sessionId = null;
463
570
  this.dateLastEventSent = null;
464
571
  this.sessionAttributes = {};
465
572
  mpInstance._Persistence.update();
466
573
  };
574
+
575
+ this.processConfig = (config: SDKInitConfig) => {
576
+ const { workspaceToken, requiredWebviewBridgeName } = config;
577
+
578
+ // We should reprocess the flags in case they have changed when we request an updated config
579
+ // such as if the SDK is being self-hosted and the flags are different on the server config
580
+ // https://go.mparticle.com/work/SQDSDKS-6317
581
+ this.SDKConfig.flags = processFlags(config);
582
+
583
+ if (workspaceToken) {
584
+ this.SDKConfig.workspaceToken = workspaceToken;
585
+ } else {
586
+ mpInstance.Logger.warning(
587
+ 'You should have a workspaceToken on your config object for security purposes.'
588
+ );
589
+ }
590
+ // add a new function to apply items to the store that require config to be returned
591
+ this.storageName = createMainStorageName(workspaceToken);
592
+ this.prodStorageName = createProductStorageName(workspaceToken);
593
+
594
+ this.SDKConfig.requiredWebviewBridgeName =
595
+ requiredWebviewBridgeName || workspaceToken;
596
+
597
+ this.webviewBridgeEnabled = isWebviewEnabled(
598
+ this.SDKConfig.requiredWebviewBridgeName,
599
+ this.SDKConfig.minWebviewBridgeVersion
600
+ );
601
+
602
+ this.configurationLoaded = true;
603
+ };
467
604
  }
468
605
 
469
- export function processFlags(
470
- config: SDKInitConfig,
471
- SDKConfig: SDKConfig
472
- ): IFeatureFlags {
606
+ // https://go.mparticle.com/work/SQDSDKS-6317
607
+ export function processFlags(config: SDKInitConfig): IFeatureFlags {
473
608
  const flags: IFeatureFlags = {};
474
609
  const {
475
610
  ReportBatching,
@@ -477,12 +612,14 @@ export function processFlags(
477
612
  OfflineStorage,
478
613
  DirectUrlRouting,
479
614
  CacheIdentity,
615
+ AudienceAPI,
480
616
  } = Constants.FeatureFlags;
481
617
 
482
618
  if (!config.flags) {
483
619
  return {};
484
620
  }
485
621
 
622
+ // https://go.mparticle.com/work/SQDSDKS-6317
486
623
  // Passed in config flags take priority over defaults
487
624
  flags[ReportBatching] = config.flags[ReportBatching] || false;
488
625
  // The server returns stringified numbers, sowe need to parse
@@ -492,6 +629,7 @@ export function processFlags(
492
629
  flags[OfflineStorage] = config.flags[OfflineStorage] || '0';
493
630
  flags[DirectUrlRouting] = config.flags[DirectUrlRouting] === 'True';
494
631
  flags[CacheIdentity] = config.flags[CacheIdentity] === 'True';
632
+ flags[AudienceAPI] = config.flags[AudienceAPI] === 'True';
495
633
 
496
634
  return flags;
497
635
  }
@@ -0,0 +1,59 @@
1
+ export interface fetchPayload {
2
+ method: string;
3
+ headers: {
4
+ Accept: string;
5
+ 'Content-Type'?: string;
6
+ };
7
+ body?: string;
8
+ }
9
+
10
+ export abstract class AsyncUploader {
11
+ url: string;
12
+ public abstract upload(
13
+ fetchPayload: fetchPayload,
14
+ url?: string
15
+ ): Promise<Response>;
16
+
17
+ constructor(url: string) {
18
+ this.url = url;
19
+ }
20
+ }
21
+
22
+ export class FetchUploader extends AsyncUploader {
23
+ public async upload(
24
+ fetchPayload: fetchPayload,
25
+ _url?: string
26
+ ): Promise<Response> {
27
+ const url = _url || this.url;
28
+ return await fetch(url, fetchPayload);
29
+ }
30
+ }
31
+
32
+ export class XHRUploader extends AsyncUploader {
33
+ public async upload(fetchPayload: fetchPayload): Promise<Response> {
34
+ const response: Response = await this.makeRequest(
35
+ this.url,
36
+ fetchPayload.body
37
+ );
38
+ return response;
39
+ }
40
+
41
+ private async makeRequest(url: string, data: string): Promise<Response> {
42
+ const xhr: XMLHttpRequest = new XMLHttpRequest();
43
+ return new Promise((resolve, reject) => {
44
+ xhr.onreadystatechange = () => {
45
+ if (xhr.readyState !== 4) return;
46
+
47
+ // Process the response
48
+ if (xhr.status >= 200 && xhr.status < 300) {
49
+ resolve((xhr as unknown) as Response);
50
+ } else {
51
+ reject(xhr);
52
+ }
53
+ };
54
+
55
+ xhr.open('post', url);
56
+ xhr.send(data);
57
+ });
58
+ }
59
+ }