@@ -44,80 +44,92 @@ function parsePath(path: string, params: Object): string {
4444}
4545
4646/**
47- * Check if the service/request have error and try to format them.
47+ * Determine if the error is due to bad credentials
48+ * @private
49+ * @param {Object } error - error object returned from axios
50+ * @returns {boolean } true if error is due to authentication
51+ */
52+ function isAuthenticationError ( error : any ) : boolean {
53+ let isAuthErr = false ;
54+ const code = error . status ;
55+ const body = error . data ;
56+
57+ // handle specific error from iam service, should be relevant across platforms
58+ const isIamServiceError = body . context &&
59+ body . context . url &&
60+ body . context . url . indexOf ( 'iam' ) > - 1 ;
61+
62+ if ( code === 401 || code === 403 || isIamServiceError ) {
63+ isAuthErr = true ;
64+ }
65+
66+ return isAuthErr ;
67+ }
68+
69+ /**
70+ * Format error returned by axios
4871 * @param {Function } cb the request callback
4972 * @private
5073 * @returns {request.RequestCallback }
5174 */
52- export function formatErrorIfExists ( cb : Function ) {
53- return ( error , response , body ) => {
54- // eslint-disable-line complexity
55-
56- // If we have an error return it.
57- if ( error ) {
58- // first ensure that it's an instanceof Error
59- if ( ! ( error instanceof Error ) ) {
60- body = error ;
61- error = new Error ( error . message || error . error || error ) ;
62- error . body = body ;
63- }
64- if ( response && response . headers ) {
65- error [ globalTransactionId ] = response . headers [ globalTransactionId ] ;
66- }
67- cb ( error , body , response ) ;
68- return ;
69- }
75+ export function formatError ( axiosError : any ) {
76+ // return an actual error object,
77+ // but make it flexible so we can add properties like 'body'
78+ const error : any = new Error ( ) ;
79+
80+ // axios specific handling
81+ if ( axiosError . response ) {
82+ axiosError = axiosError . response ;
83+ // The request was made and the server responded with a status code
84+ // that falls out of the range of 2xx
85+ delete axiosError . config ;
86+ delete axiosError . request ;
7087
88+ error . name = axiosError . statusText ;
89+ error . code = axiosError . status ;
90+ error . message = axiosError . data . error || axiosError . statusText ;
91+
92+ // some services bury the useful error message within 'data'
93+ // adding it to the error under the key 'body' as a string or object
94+ let errorBody ;
7195 try {
72- // in most cases, request will have already parsed the body as JSON
73- body = JSON . parse ( body ) ;
96+ // try/catch to handle objects with circular references
97+ errorBody = JSON . stringify ( axiosError . data ) ;
7498 } catch ( e ) {
75- // if it fails, just return the body as-is
99+ // ignore the error, use the object, and tack on a warning
100+ errorBody = axiosError . data ;
101+ errorBody . warning = 'body contains circular reference' ;
76102 }
77103
78- // If we have a response and it contains an error
79- if ( body && ( body . error || body . error_code ) ) {
80- // visual recognition sets body.error to a json object with code/description/error_id instead of putting them top-left
81- if ( typeof body . error === 'object' && body . error . description ) {
82- const errObj = body . error ; // just in case there's a body.error.error...
83- Object . keys ( body . error ) . forEach ( key => {
84- body [ key ] = body . error [ key ] ;
85- } ) ;
86- Object . keys ( body . error ) . forEach ( key => {
87- body [ key ] = body . error [ key ] ;
88- } ) ;
89- body . error = errObj . description ;
90- } else if ( typeof body . error === 'object' && typeof body . error . error === 'object' ) {
91- // this can happen with, for example, the assistant createSynonym() API
92- body . rawError = body . error ;
93- body . error = JSON . stringify ( body . error . error ) ;
94- }
95- // language translaton returns json with error_code and error_message
96- error = new Error ( body . error || body . error_message || 'Error Code: ' + body . error_code ) ;
97- error . code = body . error_code ;
98- Object . keys ( body ) . forEach ( key => {
99- error [ key ] = body [ key ] ;
100- } ) ;
101- body = null ;
102- }
103- // If we still don't have an error and there was an error...
104- if ( ! error && ( response . statusCode < 200 || response . statusCode >= 300 ) ) {
105- error = new Error ( typeof body === 'object' ? JSON . stringify ( body ) : body ) ;
106- error . code = response . statusCode ;
107- body = null ;
104+ error . body = errorBody ;
105+
106+ // iam service uses transaction-id
107+ if ( axiosError . headers [ 'transaction-id' ] ) {
108+ error [ 'transaction-id' ] = axiosError . headers [ 'transaction-id' ] ;
108109 }
109110
110- // ensure a more descriptive error message
111- if ( error && ( error . code === 401 || error . code === 403 ) ) {
112- error . body = error . message ;
113- error . message = 'Unauthorized: Access is denied due to invalid credentials.' ;
111+ // other services use x-global-transaction-id
112+ if ( axiosError . headers [ 'x-global-transaction-id' ] ) {
113+ error [ 'x-global-transaction-id' ] = axiosError . headers [ 'x-global-transaction-id' ] ;
114114 }
115- if ( error && response && response . headers ) {
116- error [ globalTransactionId ] = response . headers [ globalTransactionId ] ;
115+
116+ // print a more descriptive error message for auth issues
117+ if ( isAuthenticationError ( axiosError ) ) {
118+ error . message = 'Access is denied due to invalid credentials.' ;
117119 }
118- cb ( error , body , response ) ;
119- return ;
120- } ;
120+
121+ } else if ( axiosError . request ) {
122+ // The request was made but no response was received
123+ // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
124+ // http.ClientRequest in node.js
125+ error . message = axiosError . request ;
126+
127+ } else {
128+ // Something happened in setting up the request that triggered an Error
129+ error . message = axiosError . message ;
130+ }
131+
132+ return error ;
121133}
122134
123135/**
@@ -239,20 +251,6 @@ export function sendRequest(parameters, _callback) {
239251 _callback ( null , res . data , res ) ;
240252 } )
241253 . catch ( error => {
242- if ( error . response ) {
243- // The request was made and the server responded with a status code
244- // that falls out of the range of 2xx
245- delete error . response . config ;
246- delete error . response . request ;
247- _callback ( error . response ) ;
248- } else if ( error . request ) {
249- // The request was made but no response was received
250- // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
251- // http.ClientRequest in node.js
252- _callback ( error . request ) ;
253- } else {
254- // Something happened in setting up the request that triggered an Error
255- _callback ( error . message ) ;
256- }
254+ _callback ( formatError ( error ) ) ;
257255 } ) ;
258256}
0 commit comments