@mparticle/web-sdk 2.27.0 → 2.28.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "2.27.0",
3
+ "version": "2.28.1",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
package/src/apiClient.ts CHANGED
@@ -1,14 +1,13 @@
1
1
  import Constants from './constants';
2
2
  import Types from './types';
3
3
  import { BatchUploader } from './batchUploader';
4
- import { MParticleWebSDK, SDKEvent } from './sdkRuntimeModels';
4
+ import { MParticleWebSDK, SDKEvent, SDKDataPlan } from './sdkRuntimeModels';
5
5
  import KitBlocker from './kitBlocking';
6
6
  import { Dictionary, getRampNumber, isEmpty, parseNumber } from './utils';
7
7
  import { IUploadObject } from './serverModel';
8
8
  import { MPForwarder } from './forwarders.interfaces';
9
- import { IMParticleUser } from './identity-user-interfaces';
10
-
11
- export type ForwardingStatsData = Dictionary<any>;
9
+ import { IMParticleUser, ISDKUserAttributes } from './identity-user-interfaces';
10
+ import { AsyncUploader, FetchUploader, XHRUploader } from './uploaders';
12
11
 
13
12
  export interface IAPIClient {
14
13
  uploader: BatchUploader | null;
@@ -18,18 +17,30 @@ export interface IAPIClient {
18
17
  sendEventToServer: (event: SDKEvent, _options?: Dictionary<any>) => void;
19
18
  sendSingleEventToServer: (event: SDKEvent) => void;
20
19
  sendBatchForwardingStatsToServer: (
21
- forwardingStatsData: ForwardingStatsData,
20
+ forwardingStatsData: IForwardingStatsData,
22
21
  xhr: XMLHttpRequest
23
22
  ) => void;
24
- sendSingleForwardingStatsToServer: (
25
- forwardingStatsData: ForwardingStatsData
26
- ) => void;
23
+ initializeForwarderStatsUploader: () => AsyncUploader;
27
24
  prepareForwardingStats: (
28
25
  forwarder: MPForwarder,
29
26
  event: IUploadObject
30
27
  ) => void;
31
28
  }
32
29
 
30
+ export interface IForwardingStatsData {
31
+ mid: number; // Module Id
32
+ esid: number; // Event Subscription Id
33
+ n: string; // Event Name
34
+ attrs: ISDKUserAttributes; // User Attributes
35
+ sdk: string; // SDK Version
36
+ dt: number; // Data Type
37
+ et: number; // Event Type
38
+ dbg: boolean; // Development Mode (for debugging in Live Stream)
39
+ ct: number; // Current Timestamp
40
+ eec: number; // Expanded Event Count
41
+ dp: SDKDataPlan; // Data Plan
42
+ }
43
+
33
44
  export default function APIClient(
34
45
  this: IAPIClient,
35
46
  mpInstance: MParticleWebSDK,
@@ -161,41 +172,26 @@ export default function APIClient(
161
172
  }
162
173
  };
163
174
 
164
- this.sendSingleForwardingStatsToServer = function(forwardingStatsData) {
165
- let url;
166
- let data;
167
- try {
168
- const xhrCallback = function() {
169
- if (xhr.readyState === 4) {
170
- if (xhr.status === 202) {
171
- mpInstance.Logger.verbose(
172
- 'Successfully sent ' +
173
- xhr.statusText +
174
- ' from server'
175
- );
176
- }
177
- }
178
- };
179
- const xhr = mpInstance._Helpers.createXHR(xhrCallback);
180
- url = mpInstance._Helpers.createServiceUrl(
181
- mpInstance._Store.SDKConfig.v1SecureServiceUrl,
182
- mpInstance._Store.devToken
183
- );
184
- data = forwardingStatsData;
175
+ this.initializeForwarderStatsUploader = (): AsyncUploader => {
176
+ const {
177
+ v1SecureServiceUrl: forwardingDomain,
178
+ } = mpInstance._Store.SDKConfig;
179
+ const { devToken } = mpInstance._Store;
185
180
 
186
- if (xhr) {
187
- xhr.open('post', url + '/Forwarding');
188
- xhr.send(JSON.stringify(data));
189
- }
190
- } catch (e) {
191
- mpInstance.Logger.error(
192
- 'Error sending forwarding stats to mParticle servers.'
193
- );
194
- }
181
+ const uploadUrl: string = `https://${forwardingDomain}${devToken}/Forwarding`;
182
+
183
+ const uploader: AsyncUploader = window.fetch
184
+ ? new FetchUploader(uploadUrl)
185
+ : new XHRUploader(uploadUrl);
186
+
187
+ return uploader;
195
188
  };
196
189
 
197
- this.prepareForwardingStats = function(forwarder, event) {
198
- let forwardingStatsData;
190
+ this.prepareForwardingStats = function(
191
+ forwarder: MPForwarder,
192
+ event:SDKEvent,
193
+ ) : void {
194
+ let forwardingStatsData: IForwardingStatsData;
199
195
  const queue = mpInstance._Forwarders.getForwarderStatsQueue();
200
196
 
201
197
  if (forwarder && forwarder.isVisible) {
@@ -212,6 +208,11 @@ export default function APIClient(
212
208
  eec: event.ExpandedEventCount,
213
209
  dp: event.DataPlan,
214
210
  };
211
+
212
+ const {
213
+ sendSingleForwardingStatsToServer,
214
+ setForwarderStatsQueue,
215
+ } = mpInstance._Forwarders;
215
216
 
216
217
  if (
217
218
  mpInstance._Helpers.getFeatureFlag(
@@ -219,9 +220,9 @@ export default function APIClient(
219
220
  )
220
221
  ) {
221
222
  queue.push(forwardingStatsData);
222
- mpInstance._Forwarders.setForwarderStatsQueue(queue);
223
+ setForwarderStatsQueue(queue);
223
224
  } else {
224
- self.sendSingleForwardingStatsToServer(forwardingStatsData);
225
+ sendSingleForwardingStatsToServer(forwardingStatsData);
225
226
  }
226
227
  }
227
228
  };
@@ -7,12 +7,12 @@ import {
7
7
  SDKInitConfig,
8
8
  } from './sdkRuntimeModels';
9
9
  import { Dictionary } from './utils';
10
-
11
- export type SDKCompleteInitCallback = (
12
- apiKey: string,
13
- config: SDKInitConfig,
14
- mpInstance: MParticleWebSDK
15
- ) => void;
10
+ import {
11
+ AsyncUploader,
12
+ fetchPayload,
13
+ FetchUploader,
14
+ XHRUploader,
15
+ } from './uploaders';
16
16
 
17
17
  export interface IKitConfigs extends IKitFilterSettings {
18
18
  name: string;
@@ -47,13 +47,13 @@ export interface IKitFilterSettings {
47
47
  export interface IFilteringEventAttributeValue {
48
48
  eventAttributeName: string;
49
49
  eventAttributeValue: string;
50
- includeOnMatch: boolean
50
+ includeOnMatch: boolean;
51
51
  }
52
52
 
53
53
  export interface IFilteringUserAttributeValue {
54
54
  userAttributeName: string;
55
55
  userAttributeValue: string;
56
- includeOnMatch: boolean
56
+ includeOnMatch: boolean;
57
57
  }
58
58
  export interface IFilteringConsentRuleValues {
59
59
  includeOnMatch: boolean;
@@ -62,10 +62,9 @@ export interface IFilteringConsentRuleValues {
62
62
 
63
63
  export interface IConsentRuleValue {
64
64
  consentPurpose: string;
65
- hasConsented: boolean
65
+ hasConsented: boolean;
66
66
  }
67
67
 
68
-
69
68
  export interface IPixelConfig {
70
69
  name: string;
71
70
  moduleId: number;
@@ -91,78 +90,92 @@ export interface IConfigResponse {
91
90
  }
92
91
 
93
92
  export interface IConfigAPIClient {
94
- getSDKConfiguration: (
95
- apiKey: string,
96
- config: SDKInitConfig,
97
- completeSDKInitialization: SDKCompleteInitCallback,
98
- mpInstance: MParticleWebSDK
99
- ) => void;
93
+ apiKey: string;
94
+ config: SDKInitConfig;
95
+ mpInstance: MParticleWebSDK;
96
+ getSDKConfiguration: () => Promise<IConfigResponse>;
100
97
  }
101
98
 
102
- export default function ConfigAPIClient(this: IConfigAPIClient) {
103
- this.getSDKConfiguration = (
104
- apiKey,
105
- config,
106
- completeSDKInitialization,
107
- mpInstance
108
- ): void => {
109
- let url: string;
99
+ const buildUrl = (
100
+ configUrl: string,
101
+ apiKey: string,
102
+ dataPlanConfig?: DataPlanConfig | null,
103
+ isDevelopmentMode?: boolean | null
104
+ ): string => {
105
+ const url = configUrl + apiKey + '/config';
106
+ const env = isDevelopmentMode ? '1' : '0';
107
+ const queryParams = [`env=${env}`];
108
+
109
+ const { planId, planVersion } = dataPlanConfig || {};
110
+
111
+ if (planId) {
112
+ queryParams.push(`plan_id=${planId}`);
113
+ }
114
+
115
+ if (planVersion) {
116
+ queryParams.push(`plan_version=${planVersion}`);
117
+ }
118
+
119
+ return `${url}?${queryParams.join('&')}`;
120
+ };
121
+
122
+ export default function ConfigAPIClient(
123
+ this: IConfigAPIClient,
124
+ apiKey: string,
125
+ config: SDKInitConfig,
126
+ mpInstance: MParticleWebSDK
127
+ ): void {
128
+ const baseUrl = 'https://' + mpInstance._Store.SDKConfig.configUrl;
129
+ const { isDevelopmentMode } = config;
130
+ const dataPlan = config.dataPlan as DataPlanConfig;
131
+ const uploadUrl = buildUrl(baseUrl, apiKey, dataPlan, isDevelopmentMode);
132
+ const uploader: AsyncUploader = window.fetch
133
+ ? new FetchUploader(uploadUrl)
134
+ : new XHRUploader(uploadUrl);
135
+
136
+ this.getSDKConfiguration = async (): Promise<IConfigResponse> => {
137
+ let configResponse: IConfigResponse;
138
+ const fetchPayload: fetchPayload = {
139
+ method: 'get',
140
+ headers: {
141
+ Accept: 'text/plain;charset=UTF-8',
142
+ 'Content-Type': 'text/plain;charset=UTF-8',
143
+ },
144
+ body: null,
145
+ };
146
+
110
147
  try {
111
- const xhrCallback = function() {
112
- if (xhr.readyState === 4) {
113
- // when a 200 returns, merge current config with what comes back from config, prioritizing user inputted config
114
- if (xhr.status === 200) {
115
- config = mpInstance._Helpers.extend(
116
- {},
117
- config,
118
- JSON.parse(xhr.responseText)
119
- );
120
- completeSDKInitialization(apiKey, config, mpInstance);
121
- mpInstance.Logger.verbose(
122
- 'Successfully received configuration from server'
123
- );
124
- } else {
125
- // if for some reason a 200 doesn't return, then we initialize with the just the passed through config
126
- completeSDKInitialization(apiKey, config, mpInstance);
127
- mpInstance.Logger.verbose(
128
- 'Issue with receiving configuration from server, received HTTP Code of ' +
129
- xhr.status
130
- );
131
- }
132
- }
133
- };
134
-
135
- const xhr = mpInstance._Helpers.createXHR(xhrCallback);
136
- url =
137
- 'https://' +
138
- mpInstance._Store.SDKConfig.configUrl +
139
- apiKey +
140
- '/config?env=';
141
- if (config.isDevelopmentMode) {
142
- url = url + '1';
143
- } else {
144
- url = url + '0';
145
- }
148
+ const response = await uploader.upload(fetchPayload);
149
+ if (response.status === 200) {
150
+ mpInstance?.Logger?.verbose(
151
+ 'Successfully received configuration from server'
152
+ );
146
153
 
147
- const dataPlan = config.dataPlan as DataPlanConfig;
148
- if (dataPlan) {
149
- if (dataPlan.planId) {
150
- url = url + '&plan_id=' + dataPlan.planId || '';
151
- }
152
- if (dataPlan.planVersion) {
153
- url = url + '&plan_version=' + dataPlan.planVersion || '';
154
+ // https://go.mparticle.com/work/SQDSDKS-6568
155
+ // FetchUploader returns the response as a JSON object that we have to await
156
+ if (response.json) {
157
+ configResponse = await response.json();
158
+ return configResponse;
159
+ } else {
160
+ // https://go.mparticle.com/work/SQDSDKS-6568
161
+ // XHRUploader returns the response as a string that we need to parse
162
+ const xhrResponse = response as unknown as XMLHttpRequest;
163
+ configResponse = JSON.parse(xhrResponse.responseText);
164
+ return configResponse;
154
165
  }
155
166
  }
156
167
 
157
- if (xhr) {
158
- xhr.open('get', url);
159
- xhr.send(null);
160
- }
168
+ mpInstance?.Logger?.verbose(
169
+ 'Issue with receiving configuration from server, received HTTP Code of ' +
170
+ response.statusText
171
+ );
161
172
  } catch (e) {
162
- completeSDKInitialization(apiKey, config, mpInstance);
163
- mpInstance.Logger.error(
173
+ mpInstance?.Logger?.error(
164
174
  'Error getting forwarder configuration from mParticle servers.'
165
175
  );
166
176
  }
177
+
178
+ // Returns the original config object if we cannot retrieve the remote config
179
+ return config as IConfigResponse;
167
180
  };
168
181
  }
package/src/forwarders.js CHANGED
@@ -3,11 +3,16 @@ import filteredMparticleUser from './filteredMparticleUser';
3
3
  import { isEmpty } from './utils';
4
4
  import KitFilterHelper from './kitFilterHelper';
5
5
  import Constants from './constants';
6
+ import APIClient from './apiClient';
6
7
 
7
8
  const { Modify, Identify, Login, Logout } = Constants.IdentityMethods;
8
9
 
9
10
  export default function Forwarders(mpInstance, kitBlocker) {
10
11
  var self = this;
12
+ this.forwarderStatsUploader = new APIClient(
13
+ mpInstance,
14
+ kitBlocker
15
+ ).initializeForwarderStatsUploader();
11
16
 
12
17
  const UserAttributeActionTypes = {
13
18
  setUserAttribute: 'setUserAttribute',
@@ -771,4 +776,31 @@ export default function Forwarders(mpInstance, kitBlocker) {
771
776
  );
772
777
  }
773
778
  };
779
+
780
+ this.sendSingleForwardingStatsToServer = async forwardingStatsData => {
781
+ // https://go.mparticle.com/work/SQDSDKS-6568
782
+ const fetchPayload = {
783
+ method: 'post',
784
+ body: JSON.stringify(forwardingStatsData),
785
+ headers: {
786
+ Accept: 'text/plain;charset=UTF-8',
787
+ 'Content-Type': 'text/plain;charset=UTF-8',
788
+ },
789
+ };
790
+
791
+ const response = await this.forwarderStatsUploader.upload(fetchPayload);
792
+
793
+ let message;
794
+ // This is a fire and forget, so we only need to log the response based on the code, and not return any response body
795
+ if (response.status === 202) {
796
+ // https://go.mparticle.com/work/SQDSDKS-6670
797
+ message = 'Successfully sent forwarding stats to mParticle Servers';
798
+ } else {
799
+ message =
800
+ 'Issue with forwarding stats to mParticle Servers, received HTTP Code of ' +
801
+ response.statusText;
802
+ }
803
+
804
+ mpInstance?.Logger?.verbose(message);
805
+ };
774
806
  }
package/src/identity.js CHANGED
@@ -1665,7 +1665,8 @@ export default function Identity(mpInstance) {
1665
1665
 
1666
1666
  mpInstance.Logger.verbose('Successfully parsed Identity Response');
1667
1667
 
1668
- mpInstance._APIClient.processQueuedEvents();
1668
+ // https://go.mparticle.com/work/SQDSDKS-6654
1669
+ mpInstance._APIClient?.processQueuedEvents();
1669
1670
  } catch (e) {
1670
1671
  if (callback) {
1671
1672
  mpInstance._Helpers.invokeCallback(
@@ -1716,7 +1717,7 @@ export default function Identity(mpInstance) {
1716
1717
  isNewUserIdentityType,
1717
1718
  currentUserInMemory
1718
1719
  );
1719
- mpInstance._APIClient.sendEventToServer(
1720
+ mpInstance._APIClient?.sendEventToServer(
1720
1721
  userIdentityChangeEvent
1721
1722
  );
1722
1723
  }
@@ -1770,7 +1771,7 @@ export default function Identity(mpInstance) {
1770
1771
  user
1771
1772
  );
1772
1773
  if (userAttributeChangeEvent) {
1773
- mpInstance._APIClient.sendEventToServer(userAttributeChangeEvent);
1774
+ mpInstance._APIClient?.sendEventToServer(userAttributeChangeEvent);
1774
1775
  }
1775
1776
  };
1776
1777
 
@@ -1807,7 +1808,7 @@ export default function Identity(mpInstance) {
1807
1808
 
1808
1809
  this.reinitForwardersOnUserChange = function(prevUser, newUser) {
1809
1810
  if (hasMPIDAndUserLoginChanged(prevUser, newUser)) {
1810
- mpInstance._Forwarders.initForwarders(
1811
+ mpInstance._Forwarders?.initForwarders(
1811
1812
  newUser.getUserIdentities().userIdentities,
1812
1813
  mpInstance._APIClient.prepareForwardingStats
1813
1814
  );
@@ -1816,11 +1817,11 @@ export default function Identity(mpInstance) {
1816
1817
 
1817
1818
  this.setForwarderCallbacks = function(user, method) {
1818
1819
  // https://go.mparticle.com/work/SQDSDKS-6036
1819
- mpInstance._Forwarders.setForwarderUserIdentities(
1820
+ mpInstance._Forwarders?.setForwarderUserIdentities(
1820
1821
  user.getUserIdentities().userIdentities
1821
1822
  );
1822
- mpInstance._Forwarders.setForwarderOnIdentityComplete(user, method);
1823
- mpInstance._Forwarders.setForwarderOnUserIdentified(user);
1823
+ mpInstance._Forwarders?.setForwarderOnIdentityComplete(user, method);
1824
+ mpInstance._Forwarders?.setForwarderOnUserIdentified(user);
1824
1825
  };
1825
1826
  }
1826
1827
 
@@ -117,12 +117,21 @@ export default function mParticleInstance(instanceName) {
117
117
  !config.hasOwnProperty('requestConfig') ||
118
118
  config.requestConfig
119
119
  ) {
120
- new ConfigAPIClient().getSDKConfiguration(
120
+ const configApiClient = new ConfigAPIClient(
121
121
  apiKey,
122
122
  config,
123
- completeSDKInitialization,
124
123
  this
125
124
  );
125
+
126
+ configApiClient.getSDKConfiguration().then(result => {
127
+ const mergedConfig = this._Helpers.extend(
128
+ {},
129
+ config,
130
+ result
131
+ );
132
+
133
+ completeSDKInitialization(apiKey, mergedConfig, this);
134
+ });
126
135
  } else {
127
136
  completeSDKInitialization(apiKey, config, this);
128
137
  }
@@ -5,7 +5,7 @@ import {
5
5
  Product,
6
6
  UserIdentities,
7
7
  } from '@mparticle/web-sdk';
8
- import { ForwardingStatsData } from './apiClient';
8
+ import { IForwardingStatsData } from './apiClient';
9
9
  import {
10
10
  IntegrationAttributes,
11
11
  ServerSettings,
@@ -18,7 +18,7 @@ import { UserAttributes } from './identity-user-interfaces';
18
18
  export type UploadsTable = Dictionary<any>;
19
19
  export interface iForwardingStatsBatches {
20
20
  uploadsTable: UploadsTable;
21
- forwardingStatsEventQueue: ForwardingStatsData[];
21
+ forwardingStatsEventQueue: IForwardingStatsData[];
22
22
  }
23
23
 
24
24
  export interface IGlobalStoreV2MinifiedKeys {
@@ -65,6 +65,7 @@ export interface SDKEvent {
65
65
  CurrencyCode: string;
66
66
  DataPlan?: SDKDataPlan;
67
67
  LaunchReferral?: string;
68
+ ExpandedEventCount: number;
68
69
  }
69
70
  export interface SDKGeoLocation {
70
71
  lat: number | string;
@@ -256,7 +257,7 @@ export interface SDKHelpersApi {
256
257
  canLog?(): boolean;
257
258
  createMainStorageName?(workspaceToken: string): string;
258
259
  createProductStorageName?(workspaceToken: string): string;
259
- createServiceUrl(url: string, devToken?: string): void;
260
+ createServiceUrl(url: string, devToken?: string): string;
260
261
  createXHR?(cb: () => void): XMLHttpRequest;
261
262
  extend?(...args: any[]);
262
263
  parseNumber?(value: string | number): number;
package/src/uploaders.ts CHANGED
@@ -33,12 +33,25 @@ export class XHRUploader extends AsyncUploader {
33
33
  public async upload(fetchPayload: fetchPayload): Promise<Response> {
34
34
  const response: Response = await this.makeRequest(
35
35
  this.url,
36
- fetchPayload.body
36
+ fetchPayload.body,
37
+ fetchPayload.method as 'post' | 'get'
37
38
  );
38
39
  return response;
39
40
  }
40
41
 
41
- private async makeRequest(url: string, data: string): Promise<Response> {
42
+ // XHR Ready States
43
+ // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState
44
+ // 0 UNSENT open() has not been called yet.
45
+ // 1 OPENED send() has been called.
46
+ // 2 HEADERS_RECEIVED send() has been called, and headers and status are available.
47
+ // 3 LOADING Downloading; responseText holds partial data.
48
+ // 4 DONE The operation is complete.
49
+
50
+ private async makeRequest(
51
+ url: string,
52
+ data: string,
53
+ method: 'post' | 'get' = 'post'
54
+ ): Promise<Response> {
42
55
  const xhr: XMLHttpRequest = new XMLHttpRequest();
43
56
  return new Promise((resolve, reject) => {
44
57
  xhr.onreadystatechange = () => {
@@ -46,13 +59,13 @@ export class XHRUploader extends AsyncUploader {
46
59
 
47
60
  // Process the response
48
61
  if (xhr.status >= 200 && xhr.status < 300) {
49
- resolve((xhr as unknown) as Response);
62
+ resolve(xhr as unknown as Response);
50
63
  } else {
51
64
  reject(xhr);
52
65
  }
53
66
  };
54
67
 
55
- xhr.open('post', url);
68
+ xhr.open(method, url);
56
69
  xhr.send(data);
57
70
  });
58
71
  }