-
-
Notifications
You must be signed in to change notification settings - Fork 7.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(common): add error options object #10460
feat(common): add error options object #10460
Conversation
add error options object to HttpException constructor to allow use of error cause along with custom message.
* throw new HttpException() | ||
* throw new HttpException('message', HttpStatus.BAD_REQUEST) | ||
* throw new HttpException({ reason: 'this can be a human readable reason' }, HttpStatus.BAD_REQUEST) | ||
* throw new HttpException(new Error('Cause Error'), HttpStatus.BAD_REQUEST) | ||
* throw new HttpException('custom message', HttpStatus.BAD_REQUEST, { | ||
* cause: new Error('Cause Error'), | ||
* }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a bunch of examples to better illustrate the possibilities here.
Pull Request Test Coverage Report for Build 47ec2c16-536f-4fcd-8acd-7e7f64581b60
💛 - Coveralls |
this.cause = this.options.cause; | ||
return; | ||
} | ||
|
||
if (this.response instanceof Error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have yet to define what is the expected behavior when someone mistakenly passes both the first argument as an Error and also the options object with a cause. In that case, should we always consider the cause property or throw an error, @kamilmysliwiec ?
Following up on this ☝️ here.
Since we now have a dedicated options object, perhaps we can just log the warning down when this.response instanceof Error === true
saying that from now on, the { cause: ... }
option should be used instead (and the previous syntax is deprecated)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we could do that, but then this would be a breaking change to the users that are already using the first parameter as the error cause. Is it okay to break this interface and just log a warning?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thiagomini the idea is to log the warning now and introduce a breaking change as part of the next major release
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmmm I see, alright then, I'll keep both options and just log the deprecated message.
add deprecated warning for HttpException class when using the first argument as the error cause.
add error cause option to bad request exception.
change BadRequestException constructor parameters assignment to be clearer.
expect(cause).to.be.eql(causeError); | ||
}); | ||
|
||
it('configures a cause when using a built-in exception with options', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a POC for how we could use the options
object with other built-in exceptions, like the BadRequestException
. The modification here, as you can see in the class implementation, was to make the second argument either an object or a string. When it's an object, then we consider it as an option
, otherwise, we consider it to be the old description
parameter. @kamilmysliwiec , what do you think about that interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds great!
description = 'Bad Request', | ||
descriptionOrOptions: | ||
| string | ||
| (HttpExceptionOptions & { description?: string }) = 'Bad Request', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inline type here is provisory. I may change it as soon as we validate an interface for these errors.
rearrange HttpException spec file test blocks to increase readability, better separating tests by methods and contexts.
rename HttpException createBody method parameter to better represent its usage. When this parameter is a string, it is used as a value to a 'message' key in the final object.
add test for HttpException's getResponse method when used with a built-in exception and providing the "description" parameter as part of the "options" object.
}; | ||
expect(new HttpException(message, 404).getResponse()).to.be.eql(message); | ||
}); | ||
describe('getResponse', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, I decided to group the related tests together under a common describe
block. Many tests were actually testing the result for getResponse()
method, while others were related to serialization. This aggregation made it easier for me to understand what was being tested. If you have strong feelings against it @kamilmysliwiec , let me know and I can revert it. However, this separation helped me notice there was a duplicated test for serialization. the test under the otherwise
describe block was basically the same as the it('should be serializable', () => { }
. So, in conclusion, I think this was a positive change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good!
@@ -112,15 +112,16 @@ export class HttpException extends Error { | |||
} | |||
|
|||
public static createBody( | |||
objectOrError: object | string, | |||
objectOrErrorMessage: object | string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed this parameter to reflect its usage better. When this is a string, it's used as the message
attribute below, so this new name seemed a better fit.
const badRequestError = new BadRequestException('ErrorMessage', { | ||
description: 'Some error description', | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test exemplifies how we can use the description
parameter inside the options
object. Before my changes, the BadRequestException
second argument was a simple string for the error's description. With the addition of the cause
attribute, I had to put them both under the same object literal.
add description attribute to HttpExceptionOptions interface so we can reuse it among children exceptions.
add error cause option to BadGatewayException.
extract functions that provide valid values for description and http exception options to the HttpException class
create HttpException#extractDescriptionAndOptionsFrom method to return both description and options at the same time
add docs for HttpException.getHttpExceptionOptionsFrom explaining its intended usage.
test error cause option for BadRequestException.
aggregate HttpException children classes tests in a single run to reduce the code duplication
add error cause option to ConflictException.
update HttpException children classes docs to reflect the changes to the "description" parameter
add error cause option to ForbiddenException.
add error cause option to GatewayTimeoutException.
add error cause option to GoneException.
add error cause option to HttpVersionNotSupportedException.
add error cause option to ImATeapotException.
add error cause option to InternalServerErrorException.
add error cause option to MethodNotAllowedException.
add error cause option to MisdirectedException.
add error cause option to NotAcceptableException.
add error cause option to NotFoundException.
add error cause option to NotImplementedException.
add error cause option to PayloadTooLargeException.
add error cause option to PreconditionFailedException.
add error cause option to RequestTimeoutException.
add error cause option to ServiceUnavailableException.
add error cause option to UnauthorizedException.
add error cause option to UnprocessableEntityException.
add error cause option to UnsupportedMediaTypeException.
reuse errorCause variable in HttpException test
add tests for all HttpException children classes and fix NotAcceptableException wrong status code.
add "getStatus" tests for all HttpException children classes
describe('built-in exceptions', () => { | ||
describe('getStatus', () => { | ||
it('should return given status code', () => { | ||
const testCases: [Type<HttpException>, number][] = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Decided to add tests here to guarantee the changes I've made didn't break anything. Fortunately, this really prevented me from breaking NotAcceptableException
that I mistakenly changed its status code.
|
||
describe('getResponse', () => { | ||
it('should return a response with default message and status code', () => { | ||
const testCases: [Type<HttpException>, number, string][] = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests for all built-in exceptions just to make sure everything is still working.
Hey, @kamilmysliwiec , you might want to review this commit-by-commit now that the number of files changed increased a lot. |
LGTM |
add error options object to HttpException constructor to allow use of error cause along with custom message.
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
We can only pass either a custom description or a cause error object.
Issue Number: #10392
What is the new behavior?
We can now use both custom messages and error cause with the following API:
Does this PR introduce a breaking change?
Other information
We have yet to define what is the expected behavior when someone mistakenly passes both the first argument as an Error and also the
options
object with a cause. In that case, should we always consider thecause
property or throw an error, @kamilmysliwiec ?