@mparticle/web-sdk 2.28.1 → 2.28.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "2.28.1",
3
+ "version": "2.28.3",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -0,0 +1,89 @@
1
+ import { IAliasRequest, IAliasCallback } from "./identity.interfaces";
2
+ import { MParticleWebSDK } from "./sdkRuntimeModels";
3
+ import Constants from './constants';
4
+ import { FetchUploader, XHRUploader } from './uploaders';
5
+ import { HTTP_ACCEPTED, HTTP_OK } from "./constants";
6
+
7
+
8
+ const { HTTPCodes, Messages } = Constants;
9
+
10
+ interface IAliasResponseBody {
11
+ message?: string
12
+ }
13
+
14
+ export async function sendAliasRequest (mpInstance: MParticleWebSDK, aliasRequest: IAliasRequest, aliasCallback: IAliasCallback) {
15
+ const { verbose, error } = mpInstance.Logger;
16
+ const { invokeAliasCallback } = mpInstance._Helpers;
17
+ const { aliasUrl } = mpInstance._Store.SDKConfig;
18
+ const { devToken: apiKey } = mpInstance._Store;
19
+
20
+ verbose(Messages.InformationMessages.SendAliasHttp);
21
+
22
+ // https://go.mparticle.com/work/SQDSDKS-6750
23
+ const uploadUrl = `https://${aliasUrl}${apiKey}/Alias`;
24
+ const uploader = window.fetch
25
+ ? new FetchUploader(uploadUrl)
26
+ : new XHRUploader(uploadUrl);
27
+
28
+
29
+ // https://go.mparticle.com/work/SQDSDKS-6568
30
+ const uploadPayload = {
31
+ method: 'post',
32
+ headers: {
33
+ Accept: 'text/plain;charset=UTF-8',
34
+ 'Content-Type': 'application/json',
35
+ },
36
+ body: JSON.stringify(aliasRequest),
37
+ };
38
+
39
+ try {
40
+ const response = await uploader.upload(uploadPayload);
41
+
42
+ let message: string;
43
+ let aliasResponseBody: IAliasResponseBody;
44
+
45
+ // FetchUploader returns the response as a JSON object that we have to await
46
+ if (response.json) {
47
+ // HTTP responses of 202, 200, and 403 do not have a response. response.json will always exist on a fetch, but can only be await-ed when the response is not empty, otherwise it will throw an error.
48
+ try {
49
+ aliasResponseBody = await response.json();
50
+ } catch (e) {
51
+ verbose('The request has no response body');
52
+ }
53
+ } else {
54
+ // https://go.mparticle.com/work/SQDSDKS-6568
55
+ // XHRUploader returns the response as a string that we need to parse
56
+ const xhrResponse = response as unknown as XMLHttpRequest;
57
+ // debugger;
58
+ aliasResponseBody = xhrResponse.responseText
59
+ ? JSON.parse(xhrResponse.responseText)
60
+ : '';
61
+ }
62
+
63
+ let errorMessage: string;
64
+
65
+ switch (response.status) {
66
+ case HTTP_OK:
67
+ case HTTP_ACCEPTED:
68
+ // https://go.mparticle.com/work/SQDSDKS-6670
69
+ message =
70
+ 'Successfully sent forwarding stats to mParticle Servers';
71
+ break;
72
+ default:
73
+ // 400 has an error message, but 403 doesn't
74
+ if (aliasResponseBody?.message) {
75
+ errorMessage = aliasResponseBody.message;
76
+ }
77
+ message =
78
+ 'Issue with sending Alias Request to mParticle Servers, received HTTP Code of ' +
79
+ response.status;
80
+ }
81
+
82
+ verbose(message);
83
+ invokeAliasCallback(aliasCallback, response.status, errorMessage);
84
+ } catch (e) {
85
+ const err = e as Error;
86
+ error('Error sending alias request to mParticle servers. ' + err);
87
+ invokeAliasCallback(aliasCallback, HTTPCodes.noHttpCoverage, (err.message));
88
+ }
89
+ };
package/src/constants.ts CHANGED
@@ -189,4 +189,10 @@ export default Constants;
189
189
 
190
190
  // https://go.mparticle.com/work/SQDSDKS-6080
191
191
  export const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
192
- export const MILLIS_IN_ONE_SEC = 1000;
192
+ export const MILLIS_IN_ONE_SEC = 1000;
193
+
194
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
195
+ export const HTTP_OK = 200 as const;
196
+ export const HTTP_ACCEPTED = 202 as const;
197
+ export const HTTP_BAD_REQUEST = 400 as const;
198
+ export const HTTP_FORBIDDEN = 403 as const;
@@ -84,6 +84,14 @@ export interface IdentityCallback {
84
84
  (result: IdentityResult): void;
85
85
  }
86
86
 
87
+ export interface IIdentityResponse {
88
+ // https://go.mparticle.com/work/SQDSDKS-6672
89
+ responseText: IdentityResultBody;
90
+ status: number;
91
+ cacheMaxAge?: number; // Default: 86400
92
+ expireTimestamp?: number;
93
+ }
94
+
87
95
  export interface IdentityResult {
88
96
  httpCode: typeof HTTPCodes;
89
97
  getPreviousUser(): User;
@@ -2,62 +2,95 @@ import Constants, { ONE_DAY_IN_SECONDS, MILLIS_IN_ONE_SEC } from './constants';
2
2
  import { Dictionary, parseNumber, isObject, generateHash } from './utils';
3
3
  import { BaseVault } from './vault';
4
4
  import Types from './types';
5
- import { IdentityApiData, UserIdentities, IdentityCallback } from '@mparticle/web-sdk';
5
+ import {
6
+ IdentityApiData,
7
+ UserIdentities,
8
+ IdentityCallback,
9
+ } from '@mparticle/web-sdk';
6
10
  import { IdentityAPIMethod } from './identity.interfaces';
11
+ import {
12
+ IdentityResultBody,
13
+ IIdentityResponse,
14
+ } from './identity-user-interfaces';
7
15
 
8
16
  const { Identify, Modify, Login, Logout } = Constants.IdentityMethods;
17
+ const CACHE_HEADER = 'x-mp-max-age' as const;
9
18
 
10
19
  export type IdentityCache = BaseVault<Dictionary<ICachedIdentityCall>>;
11
20
 
21
+ // https://go.mparticle.com/work/SQDSDKS-6675
12
22
  export type IParseCachedIdentityResponse = (
13
- cachedIdentity: ICachedIdentityCall,
23
+ cachedIdentity: IIdentityResponse,
14
24
  mpid: string,
15
25
  callback: IdentityCallback,
16
26
  identityApiData: IdentityApiData,
17
27
  identityMethod: string,
18
28
  knownIdentities: IKnownIdentities,
19
29
  fromCachedIdentity: boolean
20
- ) => void
30
+ ) => void;
21
31
 
22
32
  export interface IKnownIdentities extends UserIdentities {
23
33
  device_application_stamp?: string;
24
34
  }
25
35
 
26
36
  export interface ICachedIdentityCall {
37
+ // https://go.mparticle.com/work/SQDSDKS-6672
27
38
  responseText: string;
28
39
  status: number;
29
40
  expireTimestamp: number;
30
41
  }
31
42
 
43
+ // https://go.mparticle.com/work/SQDSDKS-6568
44
+ // Temporary adapter to convert the XMLHttpRequest response to the IIdentityResponse interface
45
+ export const xhrIdentityResponseAdapter = (
46
+ possiblyXhr: XMLHttpRequest | IIdentityResponse
47
+ ): IIdentityResponse => {
48
+ if (possiblyXhr.hasOwnProperty('expireTimestamp')) {
49
+ // If there is an `expireTimestamp`, it is an IIdentityResponse object, so just return it. This indicates it was a previously cached value.
50
+ return possiblyXhr as IIdentityResponse;
51
+ } else {
52
+ // If there is no `expireTimestamp`, then it is an XHR object and needs to be parsed.
53
+ return {
54
+ status: possiblyXhr.status,
55
+
56
+ // Sometimes responseText can be an empty string, such as a 404 response
57
+ responseText: (possiblyXhr as XMLHttpRequest).responseText
58
+ ? JSON.parse((possiblyXhr as XMLHttpRequest).responseText)
59
+ : {},
60
+ cacheMaxAge: parseNumber(
61
+ (possiblyXhr as XMLHttpRequest)?.getResponseHeader(
62
+ CACHE_HEADER
63
+ ) || ''
64
+ ),
65
+ expireTimestamp: 0,
66
+ };
67
+ }
68
+ };
69
+
32
70
  export const cacheOrClearIdCache = (
33
71
  method: string,
34
72
  knownIdentities: IKnownIdentities,
35
73
  idCache: BaseVault<Dictionary<ICachedIdentityCall>>,
36
- xhr: XMLHttpRequest,
37
- parsingCachedResponse: boolean,
74
+ identityResponse: IIdentityResponse,
75
+ parsingCachedResponse: boolean
38
76
  ): void => {
39
77
  // when parsing a response that has already been cached, simply return instead of attempting another cache
40
- if (parsingCachedResponse) { return; }
41
-
42
- const CACHE_HEADER = 'x-mp-max-age';
78
+ if (parsingCachedResponse) {
79
+ return;
80
+ }
43
81
 
44
82
  // default the expire timestamp to one day in milliseconds unless a header comes back
45
- let now = new Date().getTime();
46
- let expireTimestamp = now + ONE_DAY_IN_SECONDS * MILLIS_IN_ONE_SEC;
47
- if (xhr.getAllResponseHeaders().includes(CACHE_HEADER)) {
48
- expireTimestamp =
49
- now + (parseNumber(xhr.getResponseHeader(CACHE_HEADER)) * MILLIS_IN_ONE_SEC);
50
- }
83
+ const expireTimestamp = getExpireTimestamp(identityResponse?.cacheMaxAge);
51
84
 
52
85
  switch (method) {
53
- case Login:
54
- case Identify:
86
+ case Login:
87
+ case Identify:
55
88
  cacheIdentityRequest(
56
89
  method,
57
90
  knownIdentities,
58
91
  expireTimestamp,
59
92
  idCache,
60
- xhr
93
+ identityResponse
61
94
  );
62
95
  break;
63
96
  case Modify:
@@ -65,26 +98,32 @@ export const cacheOrClearIdCache = (
65
98
  idCache.purge();
66
99
  break;
67
100
  }
68
- }
101
+ };
69
102
 
70
103
  export const cacheIdentityRequest = (
71
104
  method: IdentityAPIMethod,
72
105
  identities: IKnownIdentities,
73
106
  expireTimestamp: number,
74
107
  idCache: IdentityCache,
75
- xhr: XMLHttpRequest
108
+ identityResponse: IIdentityResponse
76
109
  ): void => {
77
- const cache: Dictionary<ICachedIdentityCall> = idCache.retrieve() || ({} as Dictionary<ICachedIdentityCall>);
110
+ const { responseText, status } = identityResponse;
111
+ const cache: Dictionary<ICachedIdentityCall> =
112
+ idCache.retrieve() || ({} as Dictionary<ICachedIdentityCall>);
78
113
  const cacheKey = concatenateIdentities(method, identities);
79
114
  const hashedKey = generateHash(cacheKey);
80
115
 
81
- const { mpid, is_logged_in } = JSON.parse(xhr.responseText);
82
- const cachedResponseText = {
116
+ const { mpid, is_logged_in } = responseText;
117
+ const cachedResponseBody = {
83
118
  mpid,
84
119
  is_logged_in,
85
120
  };
86
121
 
87
- cache[hashedKey] = { responseText: JSON.stringify(cachedResponseText), status: xhr.status, expireTimestamp};
122
+ cache[hashedKey] = {
123
+ responseText: JSON.stringify(cachedResponseBody),
124
+ status,
125
+ expireTimestamp,
126
+ };
88
127
  idCache.store(cache);
89
128
  };
90
129
 
@@ -115,7 +154,8 @@ export const concatenateIdentities = (
115
154
 
116
155
  concatenatedIdentities = userIDArray.reduce(
117
156
  (prevValue: string, currentValue: string, index: number) => {
118
- const idName: string = Types.IdentityType.getIdentityName(index);
157
+ const idName: string =
158
+ Types.IdentityType.getIdentityName(index);
119
159
  return `${prevValue}${idName}=${currentValue};`;
120
160
  },
121
161
  cacheKey
@@ -128,12 +168,12 @@ export const concatenateIdentities = (
128
168
  export const hasValidCachedIdentity = (
129
169
  method: IdentityAPIMethod,
130
170
  proposedUserIdentities: IKnownIdentities,
131
- idCache?: IdentityCache,
171
+ idCache?: IdentityCache
132
172
  ): boolean => {
133
- // There is an edge case where multiple identity calls are taking place
134
- // before identify fires, so there may not be a cache. See what happens when
173
+ // There is an edge case where multiple identity calls are taking place
174
+ // before identify fires, so there may not be a cache. See what happens when
135
175
  // the ? in idCache is removed to the following test
136
- // "queued events contain login mpid instead of identify mpid when calling
176
+ // "queued events contain login mpid instead of identify mpid when calling
137
177
  // login immediately after mParticle initializes"
138
178
  const cache = idCache?.retrieve();
139
179
 
@@ -152,12 +192,12 @@ export const hasValidCachedIdentity = (
152
192
  if (!cache.hasOwnProperty(hashedKey)) {
153
193
  return false;
154
194
  }
155
-
195
+
156
196
  // If there is a valid cache key, compare the expireTimestamp to the current time.
157
197
  // If the current time is greater than the expireTimestamp, it is not a valid
158
198
  // cached identity.
159
199
  const expireTimestamp = cache[hashedKey].expireTimestamp;
160
-
200
+
161
201
  if (expireTimestamp < new Date().getTime()) {
162
202
  return false;
163
203
  } else {
@@ -168,8 +208,8 @@ export const hasValidCachedIdentity = (
168
208
  export const getCachedIdentity = (
169
209
  method: IdentityAPIMethod,
170
210
  proposedUserIdentities: IKnownIdentities,
171
- idCache: IdentityCache,
172
- ): ICachedIdentityCall | null => {
211
+ idCache: IdentityCache
212
+ ): IIdentityResponse | null => {
173
213
  const cacheKey: string = concatenateIdentities(
174
214
  method,
175
215
  proposedUserIdentities
@@ -179,7 +219,11 @@ export const getCachedIdentity = (
179
219
  const cache = idCache.retrieve();
180
220
  const cachedIdentity = cache ? cache[hashedKey] : null;
181
221
 
182
- return cachedIdentity;
222
+ return {
223
+ responseText: parseIdentityResponse(cachedIdentity.responseText),
224
+ expireTimestamp: cachedIdentity.expireTimestamp,
225
+ status: cachedIdentity.status,
226
+ };
183
227
  };
184
228
 
185
229
  // https://go.mparticle.com/work/SQDSDKS-6079
@@ -200,9 +244,12 @@ export const createKnownIdentities = (
200
244
  return identitiesResult;
201
245
  };
202
246
 
203
- export const removeExpiredIdentityCacheDates = (idCache: BaseVault<Dictionary<ICachedIdentityCall>>) => {
204
- const cache: Dictionary<ICachedIdentityCall> = idCache.retrieve() || {} as Dictionary<ICachedIdentityCall>;
205
-
247
+ export const removeExpiredIdentityCacheDates = (
248
+ idCache: BaseVault<Dictionary<ICachedIdentityCall>>
249
+ ) => {
250
+ const cache: Dictionary<ICachedIdentityCall> =
251
+ idCache.retrieve() || ({} as Dictionary<ICachedIdentityCall>);
252
+
206
253
  const currentTime: number = new Date().getTime();
207
254
 
208
255
  // Iterate over the cache and remove any key/value pairs that are expired
@@ -210,10 +257,10 @@ export const removeExpiredIdentityCacheDates = (idCache: BaseVault<Dictionary<IC
210
257
  if (cache[key].expireTimestamp < currentTime) {
211
258
  delete cache[key];
212
259
  }
213
- };
260
+ }
214
261
 
215
262
  idCache.store(cache);
216
- }
263
+ };
217
264
 
218
265
  export const tryCacheIdentity = (
219
266
  knownIdentities: IKnownIdentities,
@@ -223,7 +270,7 @@ export const tryCacheIdentity = (
223
270
  callback: IdentityCallback,
224
271
  identityApiData: IdentityApiData,
225
272
  identityMethod: IdentityAPIMethod
226
- ): boolean => {
273
+ ): boolean => {
227
274
  // https://go.mparticle.com/work/SQDSDKS-6095
228
275
  const shouldReturnCachedIdentity = hasValidCachedIdentity(
229
276
  identityMethod,
@@ -252,4 +299,10 @@ export const tryCacheIdentity = (
252
299
  return true;
253
300
  }
254
301
  return false;
255
- }
302
+ };
303
+
304
+ const getExpireTimestamp = (maxAge: number = ONE_DAY_IN_SECONDS): number =>
305
+ new Date().getTime() + maxAge * MILLIS_IN_ONE_SEC;
306
+
307
+ const parseIdentityResponse = (responseText: string): IdentityResultBody =>
308
+ responseText ? JSON.parse(responseText) : ({} as IdentityResultBody);
@@ -10,6 +10,7 @@ import {
10
10
  IUserIdentityChangeEvent,
11
11
  IMParticleUser,
12
12
  mParticleUserCart,
13
+ IIdentityResponse,
13
14
  } from './identity-user-interfaces';
14
15
  const { platform, sdkVendor, sdkVersion, HTTPCodes } = Constants;
15
16
 
@@ -114,6 +115,15 @@ export interface IAliasRequest {
114
115
  scope?: string;
115
116
  }
116
117
 
118
+ export interface IAliasCallback {
119
+ (result: IAliasResult): void;
120
+ }
121
+
122
+ export interface IAliasResult {
123
+ httpCode: number;
124
+ message: string;
125
+ }
126
+
117
127
  export interface SDKIdentityApi {
118
128
  HTTPCodes: typeof HTTPCodes;
119
129
  identify?(
@@ -170,7 +180,7 @@ export interface IIdentity {
170
180
  userInMemory: IMParticleUser
171
181
  ): IUserIdentityChangeEvent;
172
182
  parseIdentityResponse(
173
- xhr: XMLHttpRequest,
183
+ identityResponse: IIdentityResponse,
174
184
  previousMPID: MPID,
175
185
  callback: IdentityCallback,
176
186
  identityApiData: IdentityApiData,
package/src/identity.js CHANGED
@@ -1,9 +1,10 @@
1
- import Constants from './constants';
1
+ import Constants, { HTTP_OK } from './constants';
2
2
  import Types from './types';
3
3
  import {
4
4
  cacheOrClearIdCache,
5
5
  createKnownIdentities,
6
6
  tryCacheIdentity,
7
+ xhrIdentityResponseAdapter,
7
8
  } from './identity-utils';
8
9
  import AudienceManager from './audienceManager';
9
10
  const { Messages, HTTPCodes, FeatureFlags, IdentityMethods } = Constants;
@@ -1460,7 +1461,7 @@ export default function Identity(mpInstance) {
1460
1461
 
1461
1462
  // https://go.mparticle.com/work/SQDSDKS-6355
1462
1463
  this.parseIdentityResponse = function(
1463
- xhr,
1464
+ identityResponse,
1464
1465
  previousMPID,
1465
1466
  callback,
1466
1467
  identityApiData,
@@ -1486,9 +1487,7 @@ export default function Identity(mpInstance) {
1486
1487
  'Parsing "' + method + '" identity response from server'
1487
1488
  );
1488
1489
 
1489
- identityApiResult = xhr.responseText
1490
- ? JSON.parse(xhr.responseText)
1491
- : null;
1490
+ identityApiResult = identityResponse.responseText ?? null;
1492
1491
 
1493
1492
  mpInstance._Store.isLoggedIn =
1494
1493
  identityApiResult?.is_logged_in || false;
@@ -1513,13 +1512,16 @@ export default function Identity(mpInstance) {
1513
1512
  );
1514
1513
  }
1515
1514
 
1516
- if (xhr.status === 200) {
1515
+ if (identityResponse.status === HTTP_OK) {
1517
1516
  if (getFeatureFlag(CacheIdentity)) {
1517
+ const identityResponseForCache = xhrIdentityResponseAdapter(
1518
+ identityResponse
1519
+ );
1518
1520
  cacheOrClearIdCache(
1519
1521
  method,
1520
1522
  knownIdentities,
1521
1523
  self.idCache,
1522
- xhr,
1524
+ identityResponseForCache,
1523
1525
  parsingCachedResponse
1524
1526
  );
1525
1527
  }
@@ -1642,7 +1644,9 @@ export default function Identity(mpInstance) {
1642
1644
 
1643
1645
  if (callback) {
1644
1646
  const callbackCode =
1645
- xhr.status === 0 ? HTTPCodes.noHttpCoverage : xhr.status;
1647
+ identityResponse.status === 0
1648
+ ? HTTPCodes.noHttpCoverage
1649
+ : identityResponse.status;
1646
1650
 
1647
1651
  mpInstance._Helpers.invokeCallback(
1648
1652
  callback,
@@ -1657,7 +1661,7 @@ export default function Identity(mpInstance) {
1657
1661
  // https://go.mparticle.com/work/SQDSDKS-6500
1658
1662
  mpInstance.Logger.error(
1659
1663
  'Received HTTP response code of ' +
1660
- xhr.status +
1664
+ identityResponse.status +
1661
1665
  ' - ' +
1662
1666
  identityApiResult.errors[0].message
1663
1667
  );
@@ -1671,7 +1675,7 @@ export default function Identity(mpInstance) {
1671
1675
  if (callback) {
1672
1676
  mpInstance._Helpers.invokeCallback(
1673
1677
  callback,
1674
- xhr.status,
1678
+ identityResponse.status,
1675
1679
  identityApiResult || null
1676
1680
  );
1677
1681
  }
@@ -1,4 +1,6 @@
1
1
  import Constants from './constants';
2
+ import { xhrIdentityResponseAdapter } from './identity-utils';
3
+ import { sendAliasRequest } from './aliasRequestApiClient';
2
4
 
3
5
  var HTTPCodes = Constants.HTTPCodes,
4
6
  Messages = Constants.Messages;
@@ -6,59 +8,8 @@ var HTTPCodes = Constants.HTTPCodes,
6
8
  const { Modify } = Constants.IdentityMethods;
7
9
 
8
10
  export default function IdentityAPIClient(mpInstance) {
9
- this.sendAliasRequest = function(aliasRequest, callback) {
10
- var xhr,
11
- xhrCallback = function() {
12
- if (xhr.readyState === 4) {
13
- // https://go.mparticle.com/work/SQDSDKS-6368
14
- mpInstance.Logger.verbose(
15
- 'Received ' + xhr.statusText + ' from server'
16
- );
17
- //only parse error messages from failing requests
18
- if (xhr.status !== 200 && xhr.status !== 202) {
19
- if (xhr.responseText) {
20
- var response = JSON.parse(xhr.responseText);
21
- if (response.hasOwnProperty('message')) {
22
- var errorMessage = response.message;
23
- mpInstance._Helpers.invokeAliasCallback(
24
- callback,
25
- xhr.status,
26
- errorMessage
27
- );
28
- return;
29
- }
30
- }
31
- }
32
- mpInstance._Helpers.invokeAliasCallback(
33
- callback,
34
- xhr.status
35
- );
36
- }
37
- };
38
- mpInstance.Logger.verbose(Messages.InformationMessages.SendAliasHttp);
39
-
40
- xhr = mpInstance._Helpers.createXHR(xhrCallback);
41
- if (xhr) {
42
- try {
43
- xhr.open(
44
- 'post',
45
- mpInstance._Helpers.createServiceUrl(
46
- mpInstance._Store.SDKConfig.aliasUrl,
47
- mpInstance._Store.devToken
48
- ) + '/Alias'
49
- );
50
- xhr.send(JSON.stringify(aliasRequest));
51
- } catch (e) {
52
- mpInstance._Helpers.invokeAliasCallback(
53
- callback,
54
- HTTPCodes.noHttpCoverage,
55
- e
56
- );
57
- mpInstance.Logger.error(
58
- 'Error sending alias request to mParticle servers. ' + e
59
- );
60
- }
61
- }
11
+ this.sendAliasRequest = async function(aliasRequest, callback) {
12
+ await sendAliasRequest(mpInstance, aliasRequest, callback);
62
13
  };
63
14
 
64
15
  this.sendIdentityRequest = function(
@@ -78,8 +29,11 @@ export default function IdentityAPIClient(mpInstance) {
78
29
  mpInstance.Logger.verbose(
79
30
  'Received ' + xhr.statusText + ' from server'
80
31
  );
32
+
33
+ // https://go.mparticle.com/work/SQDSDKS-6565
34
+ const identityResponse = xhrIdentityResponseAdapter(xhr);
81
35
  parseIdentityResponse(
82
- xhr,
36
+ identityResponse,
83
37
  previousMPID,
84
38
  callback,
85
39
  originalIdentityApiData,
@@ -16,7 +16,11 @@ import { IPersistence } from './persistence.interfaces';
16
16
  import { IMPSideloadedKit } from './sideloadedKit';
17
17
  import { ISessionManager } from './sessionManager';
18
18
  import { Kit, MPForwarder } from './forwarders.interfaces';
19
- import { IIdentity, SDKIdentityApi } from './identity.interfaces';
19
+ import {
20
+ IIdentity,
21
+ SDKIdentityApi,
22
+ IAliasCallback,
23
+ } from './identity.interfaces';
20
24
  import {
21
25
  ISDKUserAttributeChangeData,
22
26
  ISDKUserIdentityChanges,
@@ -265,6 +269,11 @@ export interface SDKHelpersApi {
265
269
  generateHash?(value: string): string;
266
270
  // https://go.mparticle.com/work/SQDSDKS-6317
267
271
  getFeatureFlag?(feature: string): boolean | string; // TODO: Feature Constants should be converted to enum
272
+ invokeAliasCallback(
273
+ aliasCallback: IAliasCallback,
274
+ number: number,
275
+ errorMessage: string
276
+ ): void;
268
277
  isDelayedByIntegration?(
269
278
  delayedIntegrations: Dictionary<boolean>,
270
279
  timeoutStart: number,
package/src/uploaders.ts CHANGED
@@ -47,6 +47,7 @@ export class XHRUploader extends AsyncUploader {
47
47
  // 3 LOADING Downloading; responseText holds partial data.
48
48
  // 4 DONE The operation is complete.
49
49
 
50
+ // https://go.mparticle.com/work/SQDSDKS-6736
50
51
  private async makeRequest(
51
52
  url: string,
52
53
  data: string,
@@ -58,13 +59,17 @@ export class XHRUploader extends AsyncUploader {
58
59
  if (xhr.readyState !== 4) return;
59
60
 
60
61
  // Process the response
61
- if (xhr.status >= 200 && xhr.status < 300) {
62
- resolve(xhr as unknown as Response);
63
- } else {
64
- reject(xhr);
65
- }
62
+ // We resolve all xhr responses whose ready state is 4 regardless of HTTP codes that may be errors (400+)
63
+ // because these are valid HTTP responses.
64
+ resolve((xhr as unknown) as Response);
66
65
  };
67
66
 
67
+ // Reject a promise only when there is an xhr error
68
+ xhr.onerror = () => {
69
+ reject((xhr as unknown) as Response);
70
+ };
71
+
72
+
68
73
  xhr.open(method, url);
69
74
  xhr.send(data);
70
75
  });