1- import Constants , { HTTP_ACCEPTED , HTTP_OK } from './constants' ;
1+ import Constants , { HTTP_ACCEPTED , HTTP_BAD_REQUEST , HTTP_OK } from './constants' ;
22import {
33 AsyncUploader ,
44 FetchUploader ,
55 XHRUploader ,
66 IFetchPayload ,
77} from './uploaders' ;
88import { CACHE_HEADER } from './identity-utils' ;
9- import { parseNumber } from './utils' ;
9+ import { parseNumber , valueof } from './utils' ;
1010import {
1111 IAliasCallback ,
1212 IAliasRequest ,
@@ -15,7 +15,6 @@ import {
1515 IIdentityAPIRequestData ,
1616} from './identity.interfaces' ;
1717import {
18- Callback ,
1918 IdentityApiData ,
2019 MPID ,
2120 UserIdentities ,
@@ -53,9 +52,8 @@ export interface IIdentityApiClient {
5352 getIdentityResponseFromXHR : ( response : XMLHttpRequest ) => IIdentityResponse ;
5453}
5554
56- export interface IAliasResponseBody {
57- message ?: string ;
58- }
55+ // A successfull Alias request will return a 202 with no body
56+ export interface IAliasResponseBody { }
5957
6058interface IdentityApiRequestPayload extends IFetchPayload {
6159 headers : {
@@ -65,6 +63,23 @@ interface IdentityApiRequestPayload extends IFetchPayload {
6563 } ;
6664}
6765
66+ type HTTP_STATUS_CODES = typeof HTTP_OK | typeof HTTP_ACCEPTED ;
67+
68+ interface IdentityApiError {
69+ code : string ;
70+ message : string ;
71+ }
72+
73+ interface IdentityApiErrorResponse {
74+ Errors : IdentityApiError [ ] ,
75+ ErrorCode : string ,
76+ StatusCode : valueof < HTTP_STATUS_CODES > ;
77+ RequestId : string ;
78+ }
79+
80+ // All Identity Api Responses have the same structure, except for Alias
81+ interface IAliasErrorResponse extends IdentityApiError { }
82+
6883export default function IdentityAPIClient (
6984 this : IIdentityApiClient ,
7085 mpInstance : MParticleWebSDK
@@ -99,55 +114,72 @@ export default function IdentityAPIClient(
99114 try {
100115 const response : Response = await uploader . upload ( uploadPayload ) ;
101116
102- let message : string ;
103117 let aliasResponseBody : IAliasResponseBody ;
104-
105- // FetchUploader returns the response as a JSON object that we have to await
106- if ( response . json ) {
107- // 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.
108- try {
109- aliasResponseBody = await response . json ( ) ;
110- } catch ( e ) {
111- verbose ( 'The request has no response body' ) ;
112- }
113- } else {
114- // https://go.mparticle.com/work/SQDSDKS-6568
115- // XHRUploader returns the response as a string that we need to parse
116- const xhrResponse = ( response as unknown ) as XMLHttpRequest ;
117-
118- aliasResponseBody = xhrResponse . responseText
119- ? JSON . parse ( xhrResponse . responseText )
120- : '' ;
121- }
122-
118+ let message : string ;
123119 let errorMessage : string ;
124120
125121 switch ( response . status ) {
126- case HTTP_OK :
122+ // A successfull Alias request will return without a body
127123 case HTTP_ACCEPTED :
124+ case HTTP_OK :
128125 // https://go.mparticle.com/work/SQDSDKS-6670
129- message =
130- 'Successfully sent forwarding stats to mParticle Servers' ;
126+ message = 'Received Alias Response from server: ' + JSON . stringify ( response . status ) ;
131127 break ;
132- default :
133- // 400 has an error message, but 403 doesn't
134- if ( aliasResponseBody ?. message ) {
135- errorMessage = aliasResponseBody . message ;
128+
129+ // Our Alias Request API will 400 if there is an issue with the request body (ie timestamps are too far
130+ // in the past or MPIDs don't exist).
131+ // A 400 will return an error in the response body and will go through the happy path to report the error
132+ case HTTP_BAD_REQUEST :
133+ // response.json will always exist on a fetch, but can only be await-ed when the
134+ // response is not empty, otherwise it will throw an error.
135+ if ( response . json ) {
136+ try {
137+ aliasResponseBody = await response . json ( ) ;
138+ } catch ( e ) {
139+ verbose ( 'The request has no response body' ) ;
140+ }
141+ } else {
142+ // https://go.mparticle.com/work/SQDSDKS-6568
143+ // XHRUploader returns the response as a string that we need to parse
144+ const xhrResponse = ( response as unknown ) as XMLHttpRequest ;
145+
146+ aliasResponseBody = xhrResponse . responseText
147+ ? JSON . parse ( xhrResponse . responseText )
148+ : '' ;
136149 }
150+
151+ const errorResponse : IAliasErrorResponse = aliasResponseBody as unknown as IAliasErrorResponse ;
152+
153+ if ( errorResponse ?. message ) {
154+ errorMessage = errorResponse . message ;
155+ }
156+
137157 message =
138158 'Issue with sending Alias Request to mParticle Servers, received HTTP Code of ' +
139159 response . status ;
160+
161+ if ( errorResponse ?. code ) {
162+ message += ' - ' + errorResponse . code ;
163+ }
164+
165+ break ;
166+
167+ // Any unhandled errors, such as 500 or 429, will be caught here as well
168+ default : {
169+ throw new Error ( 'Received HTTP Code of ' + response . status ) ;
170+ }
171+
140172 }
141173
142174 verbose ( message ) ;
143175 invokeAliasCallback ( aliasCallback , response . status , errorMessage ) ;
144176 } catch ( e ) {
145- const err = e as Error ;
146- error ( 'Error sending alias request to mParticle servers. ' + err ) ;
177+ const errorMessage = ( e as Error ) . message || e . toString ( ) ;
178+ error ( 'Error sending alias request to mParticle servers. ' + errorMessage ) ;
147179 invokeAliasCallback (
148180 aliasCallback ,
149181 HTTPCodes . noHttpCoverage ,
150- err . message
182+ errorMessage ,
151183 ) ;
152184 }
153185 } ;
@@ -197,33 +229,67 @@ export default function IdentityAPIClient(
197229 } ,
198230 body : JSON . stringify ( identityApiRequest ) ,
199231 } ;
232+ mpInstance . _Store . identityCallInFlight = true ;
200233
201234 try {
202- mpInstance . _Store . identityCallInFlight = true ;
203235 const response : Response = await uploader . upload ( fetchPayload ) ;
204236
205237 let identityResponse : IIdentityResponse ;
238+ let message : string ;
239+
240+ switch ( response . status ) {
241+ case HTTP_ACCEPTED :
242+ case HTTP_OK :
243+
244+ // Our Identity API will return a 400 error if there is an issue with the requeest body
245+ // such as if the body is empty or one of the attributes is missing or malformed
246+ // A 400 will return an error in the response body and will go through the happy path to report the error
247+ case HTTP_BAD_REQUEST :
248+
249+ // FetchUploader returns the response as a JSON object that we have to await
250+ if ( response . json ) {
251+ // https://go.mparticle.com/work/SQDSDKS-6568
252+ // FetchUploader returns the response as a JSON object that we have to await
253+ const responseBody : IdentityResultBody = await response . json ( ) ;
254+
255+ identityResponse = this . getIdentityResponseFromFetch (
256+ response ,
257+ responseBody
258+ ) ;
259+ } else {
260+ identityResponse = this . getIdentityResponseFromXHR (
261+ ( response as unknown ) as XMLHttpRequest
262+ ) ;
263+ }
206264
207- if ( response . json ) {
208- // https://go.mparticle.com/work/SQDSDKS-6568
209- // FetchUploader returns the response as a JSON object that we have to await
210- const responseBody : IdentityResultBody = await response . json ( ) ;
211-
212- identityResponse = this . getIdentityResponseFromFetch (
213- response ,
214- responseBody
215- ) ;
216- } else {
217- identityResponse = this . getIdentityResponseFromXHR (
218- ( response as unknown ) as XMLHttpRequest
219- ) ;
265+ if ( identityResponse . status === HTTP_BAD_REQUEST ) {
266+ const errorResponse : IdentityApiErrorResponse = identityResponse . responseText as unknown as IdentityApiErrorResponse ;
267+ message = 'Issue with sending Identity Request to mParticle Servers, received HTTP Code of ' + identityResponse . status ;
268+
269+ if ( errorResponse ?. Errors ) {
270+ const errorMessage = errorResponse . Errors . map ( ( error ) => error . message ) . join ( ', ' ) ;
271+ message += ' - ' + errorMessage ;
272+ }
273+
274+ } else {
275+ message = 'Received Identity Response from server: ' ;
276+ message += JSON . stringify ( identityResponse . responseText ) ;
277+ }
278+
279+ break ;
280+
281+ // Our Identity API will return:
282+ // - 401 if the `x-mp-key` is incorrect or missing
283+ // - 403 if the there is a permission or account issue related to the `x-mp-key`
284+ // 401 and 403 have no response bodies and should be rejected outright
285+ default : {
286+ throw new Error ( 'Received HTTP Code of ' + response . status ) ;
287+ }
220288 }
221289
222- verbose (
223- 'Received Identity Response from server: ' +
224- JSON . stringify ( identityResponse . responseText )
225- ) ;
290+ mpInstance . _Store . identityCallInFlight = false ;
226291
292+ verbose ( message ) ;
227293 parseIdentityResponse (
228294 identityResponse ,
229295 previousMPID ,
@@ -234,15 +300,16 @@ export default function IdentityAPIClient(
234300 false
235301 ) ;
236302 } catch ( err ) {
303+ mpInstance . _Store . identityCallInFlight = false ;
304+
237305 const errorMessage = ( err as Error ) . message || err . toString ( ) ;
238306
239- mpInstance . _Store . identityCallInFlight = false ;
307+ error ( 'Error sending identity request to servers' + ' - ' + errorMessage ) ;
240308 invokeCallback (
241309 callback ,
242310 HTTPCodes . noHttpCoverage ,
243311 errorMessage ,
244312 ) ;
245- error ( 'Error sending identity request to servers' + ' - ' + err ) ;
246313 }
247314 } ;
248315
0 commit comments