Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 36 additions & 8 deletions lib/requestwrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export class RequestWrapper {
} catch (e) {
// ignore the error, use the object, and tack on a warning
errorBody = axiosError.data;
errorBody.warning = 'body contains circular reference';
errorBody.warning = 'Body contains circular reference';
}

error.body = errorBody;
Expand All @@ -270,14 +270,20 @@ export class RequestWrapper {
if (isAuthenticationError(axiosError)) {
error.message = 'Access is denied due to invalid credentials.';
}

} else if (axiosError.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
error.message = 'Response not received. Body of error is HTTP ClientRequest object';
error.body = axiosError.request;

error.message = axiosError.message;
error.statusText = axiosError.code;
error.body = 'Response not received - no connection was made to the service.';

// when a request to a private cloud instance has an ssl problem, it never connects and follows this branch of the error handling
if (isSelfSignedCertificateError(axiosError)) {
error.message = `If you're trying to call a service on ICP or Cloud Pak for Data, you ` +
`may not have a valid SSL certificate. If you need to access the service without setting that up, try using ` +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about?

The service you are trying to call doesn't use valid certificates. You can disable the SSL certificate validation using the disableSslVerification option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with that. I just copied the message that @lpatino10 has in Java for consistency. Would you rather this change?

`the disableSslVerification option in your client configuration and your authentication configuration if applicable.`;
}
} else {
// Something happened in setting up the request that triggered an Error
error.message = axiosError.message;
Expand Down Expand Up @@ -311,11 +317,11 @@ function parsePath(path: string, params: Object): string {
*/
function isAuthenticationError(error: any): boolean {
let isAuthErr = false;
const code = error.status;
const body = error.data;
const code: number = error.status || null;
const body: any = error.data || {};

// handle specific error from iam service, should be relevant across platforms
const isIamServiceError = body.context &&
const isIamServiceError: boolean = body.context &&
body.context.url &&
body.context.url.indexOf('iam') > -1;

Expand All @@ -326,6 +332,28 @@ function isAuthenticationError(error: any): boolean {
return isAuthErr;
}

/**
* Determine if the error is due to a bad self signed certificate
* @private
* @param {Object} error - error object returned from axios
* @returns {boolean} true if error is due to an SSL error
*/
function isSelfSignedCertificateError(error: any): boolean {
let result = false;

const sslCode = 'DEPTH_ZERO_SELF_SIGNED_CERT';
const sslMessage = 'self signed certificate';

const hasSslCode = error.code === sslCode;
const hasSslMessage = hasStringProperty(error, 'message') && error.message.includes(sslMessage);

if (hasSslCode || hasSslMessage) {
result = true;
}

return result;
}

/**
* Return true if object has a specified property that is a string
* @private
Expand Down
38 changes: 33 additions & 5 deletions test/unit/requestWrapper.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,10 +384,9 @@ describe('formatError', () => {
'x-global-transaction-id': 'fhd7s8hfudj9ksoo0wpnd78a',
},
},
request: {
message: 'request was made but no response was received',
},
request: 'fake-http-request-object',
message: 'error in building the request',
code: 'SOME_STATUS_KEY',
};

it('should get the message from errors[0].message', () => {
Expand Down Expand Up @@ -486,8 +485,37 @@ describe('formatError', () => {
delete basicAxiosError.response;
const error = requestWrapperInstance.formatError(basicAxiosError);
expect(error instanceof Error).toBe(true);
expect(error.message).toBe('Response not received. Body of error is HTTP ClientRequest object');
expect(error.body).toEqual(basicAxiosError.request);
expect(error.message).toBe('error in building the request');
expect(error.statusText).toBe('SOME_STATUS_KEY');
expect(error.body).toBe('Response not received - no connection was made to the service.');
});

it('check the SSL error handler - message condition', () => {
// save the original message
const originalMessage = basicAxiosError.message;

basicAxiosError.message = 'request has self signed certificate';
const error = requestWrapperInstance.formatError(basicAxiosError);

// put the original message back in, before expectations in case they fail
basicAxiosError.message = originalMessage;

expect(error instanceof Error).toBe(true);
expect(error.message).toMatch(/may not have a valid SSL certificate/);
});

it('check the SSL error handler - code condition', () => {
// save the original code
const originalCode = basicAxiosError.code;

basicAxiosError.code = 'DEPTH_ZERO_SELF_SIGNED_CERT';
const error = requestWrapperInstance.formatError(basicAxiosError);

// put the original message back in, before expectations in case they fail
basicAxiosError.code = originalCode;

expect(error instanceof Error).toBe(true);
expect(error.message).toMatch(/may not have a valid SSL certificate/);
});

it('check the message flow', () => {
Expand Down