diff --git a/packages/common/exceptions/bad-gateway.exception.ts b/packages/common/exceptions/bad-gateway.exception.ts index 83ae75507bd..ba46421b571 100644 --- a/packages/common/exceptions/bad-gateway.exception.ts +++ b/packages/common/exceptions/bad-gateway.exception.ts @@ -1,5 +1,6 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { isString } from '../utils/shared.utils'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Bad Gateway* type errors. @@ -18,7 +19,7 @@ export class BadGatewayException extends HttpException { * @usageNotes * The HTTP response status code will be 502. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 502. @@ -31,12 +32,15 @@ export class BadGatewayException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Bad Gateway', + descriptionOrOptions: string | HttpExceptionOptions = 'Bad Gateway', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +48,7 @@ export class BadGatewayException extends HttpException { HttpStatus.BAD_GATEWAY, ), HttpStatus.BAD_GATEWAY, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/bad-request.exception.ts b/packages/common/exceptions/bad-request.exception.ts index 3d93e229b2c..b9f04fe6256 100644 --- a/packages/common/exceptions/bad-request.exception.ts +++ b/packages/common/exceptions/bad-request.exception.ts @@ -1,5 +1,6 @@ +import { isString } from 'class-validator'; import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Bad Request* type errors. @@ -18,7 +19,7 @@ export class BadRequestException extends HttpException { * @usageNotes * The HTTP response status code will be 400. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 400. @@ -31,12 +32,15 @@ export class BadRequestException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Bad Request', + descriptionOrOptions: string | HttpExceptionOptions = 'Bad Request', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +48,7 @@ export class BadRequestException extends HttpException { HttpStatus.BAD_REQUEST, ), HttpStatus.BAD_REQUEST, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/conflict.exception.ts b/packages/common/exceptions/conflict.exception.ts index 93322515ef9..f7810ffbed2 100644 --- a/packages/common/exceptions/conflict.exception.ts +++ b/packages/common/exceptions/conflict.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Conflict* type errors. @@ -18,7 +18,7 @@ export class ConflictException extends HttpException { * @usageNotes * The HTTP response status code will be 409. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 409. @@ -31,12 +31,19 @@ export class ConflictException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ - constructor(objectOrError?: string | object | any, description = 'Conflict') { + constructor( + objectOrError?: string | object | any, + descriptionOrOptions: string | HttpExceptionOptions = 'Conflict', + ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody(objectOrError, description, HttpStatus.CONFLICT), HttpStatus.CONFLICT, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/forbidden.exception.ts b/packages/common/exceptions/forbidden.exception.ts index 2c10e7a2aa9..8079823d867 100644 --- a/packages/common/exceptions/forbidden.exception.ts +++ b/packages/common/exceptions/forbidden.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Forbidden* type errors. @@ -18,7 +18,7 @@ export class ForbiddenException extends HttpException { * @usageNotes * The HTTP response status code will be 403. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 403. @@ -31,12 +31,15 @@ export class ForbiddenException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Forbidden', + descriptionOrOptions: string | HttpExceptionOptions = 'Forbidden', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class ForbiddenException extends HttpException { HttpStatus.FORBIDDEN, ), HttpStatus.FORBIDDEN, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/gateway-timeout.exception.ts b/packages/common/exceptions/gateway-timeout.exception.ts index e2d06851884..6953f0cb231 100644 --- a/packages/common/exceptions/gateway-timeout.exception.ts +++ b/packages/common/exceptions/gateway-timeout.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Gateway Timeout* type errors. @@ -18,7 +18,7 @@ export class GatewayTimeoutException extends HttpException { * @usageNotes * The HTTP response status code will be 504. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 504. @@ -31,12 +31,15 @@ export class GatewayTimeoutException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Gateway Timeout', + descriptionOrOptions: string | HttpExceptionOptions = 'Gateway Timeout', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class GatewayTimeoutException extends HttpException { HttpStatus.GATEWAY_TIMEOUT, ), HttpStatus.GATEWAY_TIMEOUT, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/gone.exception.ts b/packages/common/exceptions/gone.exception.ts index f319fe558ce..3e080906ab6 100644 --- a/packages/common/exceptions/gone.exception.ts +++ b/packages/common/exceptions/gone.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Gone* type errors. @@ -18,7 +18,7 @@ export class GoneException extends HttpException { * @usageNotes * The HTTP response status code will be 410. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 410. @@ -31,12 +31,19 @@ export class GoneException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ - constructor(objectOrError?: string | object | any, description = 'Gone') { + constructor( + objectOrError?: string | object | any, + descriptionOrOptions: string | HttpExceptionOptions = 'Gone', + ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody(objectOrError, description, HttpStatus.GONE), HttpStatus.GONE, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/http-version-not-supported.exception.ts b/packages/common/exceptions/http-version-not-supported.exception.ts index ded68714b8b..922d75ff08a 100644 --- a/packages/common/exceptions/http-version-not-supported.exception.ts +++ b/packages/common/exceptions/http-version-not-supported.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Http Version Not Supported* type errors. @@ -18,7 +18,7 @@ export class HttpVersionNotSupportedException extends HttpException { * @usageNotes * The HTTP response status code will be 505. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 505. @@ -31,12 +31,17 @@ export class HttpVersionNotSupportedException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'HTTP Version Not Supported', + descriptionOrOptions: + | string + | HttpExceptionOptions = 'HTTP Version Not Supported', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +49,7 @@ export class HttpVersionNotSupportedException extends HttpException { HttpStatus.HTTP_VERSION_NOT_SUPPORTED, ), HttpStatus.HTTP_VERSION_NOT_SUPPORTED, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/http.exception.ts b/packages/common/exceptions/http.exception.ts index 540844298e1..bc166a8a118 100644 --- a/packages/common/exceptions/http.exception.ts +++ b/packages/common/exceptions/http.exception.ts @@ -1,5 +1,16 @@ +import { Logger } from '../services'; import { isObject, isString } from '../utils/shared.utils'; +export interface HttpExceptionOptions { + cause?: Error; + description?: string; +} + +export interface DescriptionAndOptions { + description?: string; + httpExceptionOptions?: HttpExceptionOptions; +} + /** * Defines the base Nest HTTP exception, which is handled by the default * Exceptions Handler. @@ -13,12 +24,22 @@ export class HttpException extends Error { * Instantiate a plain HTTP Exception. * * @example - * `throw new HttpException()` + * 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'), + * }) + * * * @usageNotes * The constructor arguments define the response and the HTTP response status code. - * - The `response` argument (required) defines the JSON response body. + * - The `response` argument (required) defines the JSON response body. alternatively, it can also be + * an error object that is used to define an error [cause](https://nodejs.org/en/blog/release/v16.9.0/#error-cause). * - The `status` argument (required) defines the HTTP Status Code. + * - The `options` argument (optional) defines additional error options. Currently, it supports the `cause` attribute, + * and can be used as an alternative way to specify the error cause: `const error = new HttpException('description', 400, { cause: new Error() });` * * By default, the JSON response body contains two properties: * - `statusCode`: the Http Status Code. @@ -31,12 +52,14 @@ export class HttpException extends Error { * The `status` argument is required, and should be a valid HTTP status code. * Best practice is to use the `HttpStatus` enum imported from `nestjs/common`. * - * @param response string or object describing the error condition. + * @param response string, object describing the error condition or the error cause. * @param status HTTP response status code. + * @param options An object used to add an error cause. */ constructor( private readonly response: string | Record, private readonly status: number, + private readonly options?: HttpExceptionOptions, ) { super(); this.initMessage(); @@ -53,8 +76,16 @@ export class HttpException extends Error { * - https://nodejs.org/en/blog/release/v16.9.0/#error-cause * - https://github.com/microsoft/TypeScript/issues/45167 */ - public initCause() { + public initCause(): void { + if (this.options?.cause) { + this.cause = this.options.cause; + return; + } + if (this.response instanceof Error) { + Logger.warn( + 'DEPRECATED! Passing the error cause as the first argument to HttpException constructor is deprecated. You should use the "options" parameter instead: new HttpException("message", 400, { cause: new Error("Some Error") }) ', + ); this.cause = this.response; } } @@ -87,15 +118,52 @@ export class HttpException extends Error { } public static createBody( - objectOrError: object | string, + objectOrErrorMessage: object | string, description?: string, statusCode?: number, ) { - if (!objectOrError) { + if (!objectOrErrorMessage) { return { statusCode, message: description }; } - return isObject(objectOrError) && !Array.isArray(objectOrError) - ? objectOrError - : { statusCode, message: objectOrError, error: description }; + return isObject(objectOrErrorMessage) && + !Array.isArray(objectOrErrorMessage) + ? objectOrErrorMessage + : { statusCode, message: objectOrErrorMessage, error: description }; + } + + public static getDescriptionFrom( + descriptionOrOptions: string | HttpExceptionOptions, + ): string { + return isString(descriptionOrOptions) + ? descriptionOrOptions + : descriptionOrOptions?.description; + } + + public static getHttpExceptionOptionsFrom( + descriptionOrOptions: string | HttpExceptionOptions, + ): HttpExceptionOptions { + return isString(descriptionOrOptions) ? {} : descriptionOrOptions; + } + + /** + * Utility method used to extract the error description and httpExceptionOptions from the given argument. + * This is used by inheriting classes to correctly parse both options. + * @returns the error description and the httpExceptionOptions as an object. + */ + public static extractDescriptionAndOptionsFrom( + descriptionOrOptions: string | HttpExceptionOptions, + ): DescriptionAndOptions { + const description = isString(descriptionOrOptions) + ? descriptionOrOptions + : descriptionOrOptions?.description; + + const httpExceptionOptions = isString(descriptionOrOptions) + ? {} + : descriptionOrOptions; + + return { + description, + httpExceptionOptions, + }; } } diff --git a/packages/common/exceptions/im-a-teapot.exception.ts b/packages/common/exceptions/im-a-teapot.exception.ts index 4b345681ea5..76c67861e23 100644 --- a/packages/common/exceptions/im-a-teapot.exception.ts +++ b/packages/common/exceptions/im-a-teapot.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *ImATeapotException* type errors. @@ -21,7 +21,7 @@ export class ImATeapotException extends HttpException { * @usageNotes * The HTTP response status code will be 418. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 418. @@ -34,12 +34,15 @@ export class ImATeapotException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = `I'm a teapot`, + descriptionOrOptions: string | HttpExceptionOptions = `I'm a teapot`, ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -47,6 +50,7 @@ export class ImATeapotException extends HttpException { HttpStatus.I_AM_A_TEAPOT, ), HttpStatus.I_AM_A_TEAPOT, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/internal-server-error.exception.ts b/packages/common/exceptions/internal-server-error.exception.ts index 673a2e7a20b..c3393cf3782 100644 --- a/packages/common/exceptions/internal-server-error.exception.ts +++ b/packages/common/exceptions/internal-server-error.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Internal Server Error* type errors. @@ -18,7 +18,7 @@ export class InternalServerErrorException extends HttpException { * @usageNotes * The HTTP response status code will be 500. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 500. @@ -31,12 +31,17 @@ export class InternalServerErrorException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Internal Server Error', + descriptionOrOptions: + | string + | HttpExceptionOptions = 'Internal Server Error', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +49,7 @@ export class InternalServerErrorException extends HttpException { HttpStatus.INTERNAL_SERVER_ERROR, ), HttpStatus.INTERNAL_SERVER_ERROR, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/method-not-allowed.exception.ts b/packages/common/exceptions/method-not-allowed.exception.ts index f44ad305bd5..9cf0d838140 100644 --- a/packages/common/exceptions/method-not-allowed.exception.ts +++ b/packages/common/exceptions/method-not-allowed.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Method Not Allowed* type errors. @@ -18,7 +18,7 @@ export class MethodNotAllowedException extends HttpException { * @usageNotes * The HTTP response status code will be 405. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 405. @@ -31,12 +31,15 @@ export class MethodNotAllowedException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Method Not Allowed', + descriptionOrOptions: string | HttpExceptionOptions = 'Method Not Allowed', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class MethodNotAllowedException extends HttpException { HttpStatus.METHOD_NOT_ALLOWED, ), HttpStatus.METHOD_NOT_ALLOWED, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/misdirected.exception.ts b/packages/common/exceptions/misdirected.exception.ts index 5fa2f082c26..547ab443160 100644 --- a/packages/common/exceptions/misdirected.exception.ts +++ b/packages/common/exceptions/misdirected.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Misdirected* type errors. @@ -18,7 +18,7 @@ export class MisdirectedException extends HttpException { * @usageNotes * The HTTP response status code will be 421. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 421. @@ -31,12 +31,15 @@ export class MisdirectedException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Misdirected', + descriptionOrOptions: string | HttpExceptionOptions = 'Misdirected', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class MisdirectedException extends HttpException { HttpStatus.MISDIRECTED, ), HttpStatus.MISDIRECTED, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/not-acceptable.exception.ts b/packages/common/exceptions/not-acceptable.exception.ts index 18e71156ce3..548b2dd7916 100644 --- a/packages/common/exceptions/not-acceptable.exception.ts +++ b/packages/common/exceptions/not-acceptable.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Not Acceptable* type errors. @@ -18,7 +18,7 @@ export class NotAcceptableException extends HttpException { * @usageNotes * The HTTP response status code will be 406. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 406. @@ -31,12 +31,15 @@ export class NotAcceptableException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Not Acceptable', + descriptionOrOptions: string | HttpExceptionOptions = 'Not Acceptable', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class NotAcceptableException extends HttpException { HttpStatus.NOT_ACCEPTABLE, ), HttpStatus.NOT_ACCEPTABLE, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/not-found.exception.ts b/packages/common/exceptions/not-found.exception.ts index a8ab1491a6e..b44a744e0ec 100644 --- a/packages/common/exceptions/not-found.exception.ts +++ b/packages/common/exceptions/not-found.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Not Found* type errors. @@ -18,7 +18,7 @@ export class NotFoundException extends HttpException { * @usageNotes * The HTTP response status code will be 404. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 404. @@ -31,12 +31,15 @@ export class NotFoundException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Not Found', + descriptionOrOptions: string | HttpExceptionOptions = 'Not Found', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class NotFoundException extends HttpException { HttpStatus.NOT_FOUND, ), HttpStatus.NOT_FOUND, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/not-implemented.exception.ts b/packages/common/exceptions/not-implemented.exception.ts index 0576406fcf3..6f691264955 100644 --- a/packages/common/exceptions/not-implemented.exception.ts +++ b/packages/common/exceptions/not-implemented.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Not Implemented* type errors. @@ -18,7 +18,7 @@ export class NotImplementedException extends HttpException { * @usageNotes * The HTTP response status code will be 501. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 501. @@ -30,13 +30,16 @@ export class NotImplementedException extends HttpException { * entire JSON response body, pass an object instead. Nest will serialize the object * and return it as the JSON response body. * - * @param description string or object describing the error condition. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause * @param error a short description of the HTTP error. */ constructor( objectOrError?: string | object | any, - description = 'Not Implemented', + descriptionOrOptions: string | HttpExceptionOptions = 'Not Implemented', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class NotImplementedException extends HttpException { HttpStatus.NOT_IMPLEMENTED, ), HttpStatus.NOT_IMPLEMENTED, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/payload-too-large.exception.ts b/packages/common/exceptions/payload-too-large.exception.ts index 3f967f8340a..73de7d1f2ac 100644 --- a/packages/common/exceptions/payload-too-large.exception.ts +++ b/packages/common/exceptions/payload-too-large.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Payload Too Large* type errors. @@ -18,7 +18,7 @@ export class PayloadTooLargeException extends HttpException { * @usageNotes * The HTTP response status code will be 413. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 413. @@ -31,12 +31,15 @@ export class PayloadTooLargeException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Payload Too Large', + descriptionOrOptions: string | HttpExceptionOptions = 'Payload Too Large', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class PayloadTooLargeException extends HttpException { HttpStatus.PAYLOAD_TOO_LARGE, ), HttpStatus.PAYLOAD_TOO_LARGE, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/precondition-failed.exception.ts b/packages/common/exceptions/precondition-failed.exception.ts index 6e84563963a..d03da41d057 100644 --- a/packages/common/exceptions/precondition-failed.exception.ts +++ b/packages/common/exceptions/precondition-failed.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Precondition Failed* type errors. @@ -18,7 +18,7 @@ export class PreconditionFailedException extends HttpException { * @usageNotes * The HTTP response status code will be 412. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 412. @@ -31,12 +31,15 @@ export class PreconditionFailedException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Precondition Failed', + descriptionOrOptions: string | HttpExceptionOptions = 'Precondition Failed', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class PreconditionFailedException extends HttpException { HttpStatus.PRECONDITION_FAILED, ), HttpStatus.PRECONDITION_FAILED, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/request-timeout.exception.ts b/packages/common/exceptions/request-timeout.exception.ts index 8521e290f76..d91c3cfd1bb 100644 --- a/packages/common/exceptions/request-timeout.exception.ts +++ b/packages/common/exceptions/request-timeout.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Request Timeout* type errors. @@ -18,7 +18,7 @@ export class RequestTimeoutException extends HttpException { * @usageNotes * The HTTP response status code will be 408. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 408. @@ -31,12 +31,15 @@ export class RequestTimeoutException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Request Timeout', + descriptionOrOptions: string | HttpExceptionOptions = 'Request Timeout', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class RequestTimeoutException extends HttpException { HttpStatus.REQUEST_TIMEOUT, ), HttpStatus.REQUEST_TIMEOUT, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/service-unavailable.exception.ts b/packages/common/exceptions/service-unavailable.exception.ts index b7721322f46..2d1c6bd0367 100644 --- a/packages/common/exceptions/service-unavailable.exception.ts +++ b/packages/common/exceptions/service-unavailable.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Service Unavailable* type errors. @@ -18,7 +18,7 @@ export class ServiceUnavailableException extends HttpException { * @usageNotes * The HTTP response status code will be 503. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 503. @@ -31,12 +31,15 @@ export class ServiceUnavailableException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Service Unavailable', + descriptionOrOptions: string | HttpExceptionOptions = 'Service Unavailable', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class ServiceUnavailableException extends HttpException { HttpStatus.SERVICE_UNAVAILABLE, ), HttpStatus.SERVICE_UNAVAILABLE, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/unauthorized.exception.ts b/packages/common/exceptions/unauthorized.exception.ts index 1791316c6e9..b85fa847e33 100644 --- a/packages/common/exceptions/unauthorized.exception.ts +++ b/packages/common/exceptions/unauthorized.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Unauthorized* type errors. @@ -18,7 +18,7 @@ export class UnauthorizedException extends HttpException { * @usageNotes * The HTTP response status code will be 401. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 401. @@ -31,12 +31,15 @@ export class UnauthorizedException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Unauthorized', + descriptionOrOptions: string | HttpExceptionOptions = 'Unauthorized', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +47,7 @@ export class UnauthorizedException extends HttpException { HttpStatus.UNAUTHORIZED, ), HttpStatus.UNAUTHORIZED, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/unprocessable-entity.exception.ts b/packages/common/exceptions/unprocessable-entity.exception.ts index b1cf34b77a9..a4e012cacf0 100644 --- a/packages/common/exceptions/unprocessable-entity.exception.ts +++ b/packages/common/exceptions/unprocessable-entity.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Unprocessable Entity* type errors. @@ -18,7 +18,7 @@ export class UnprocessableEntityException extends HttpException { * @usageNotes * The HTTP response status code will be 422. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 422. @@ -31,12 +31,17 @@ export class UnprocessableEntityException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Unprocessable Entity', + descriptionOrOptions: + | string + | HttpExceptionOptions = 'Unprocessable Entity', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +49,7 @@ export class UnprocessableEntityException extends HttpException { HttpStatus.UNPROCESSABLE_ENTITY, ), HttpStatus.UNPROCESSABLE_ENTITY, + httpExceptionOptions, ); } } diff --git a/packages/common/exceptions/unsupported-media-type.exception.ts b/packages/common/exceptions/unsupported-media-type.exception.ts index eaeab69a9ca..fb8a0e1df44 100644 --- a/packages/common/exceptions/unsupported-media-type.exception.ts +++ b/packages/common/exceptions/unsupported-media-type.exception.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '../enums/http-status.enum'; -import { HttpException } from './http.exception'; +import { HttpException, HttpExceptionOptions } from './http.exception'; /** * Defines an HTTP exception for *Unsupported Media Type* type errors. @@ -18,7 +18,7 @@ export class UnsupportedMediaTypeException extends HttpException { * @usageNotes * The HTTP response status code will be 415. * - The `objectOrError` argument defines the JSON response body or the message string. - * - The `description` argument contains a short description of the HTTP error. + * - The `descriptionOrOptions` argument contains either a short description of the HTTP error or an options object used to provide an underlying error cause. * * By default, the JSON response body contains two properties: * - `statusCode`: this will be the value 415. @@ -31,12 +31,17 @@ export class UnsupportedMediaTypeException extends HttpException { * and return it as the JSON response body. * * @param objectOrError string or object describing the error condition. - * @param description a short description of the HTTP error. + * @param descriptionOrOptions either a short description of the HTTP error or an options object used to provide an underlying error cause */ constructor( objectOrError?: string | object | any, - description = 'Unsupported Media Type', + descriptionOrOptions: + | string + | HttpExceptionOptions = 'Unsupported Media Type', ) { + const { description, httpExceptionOptions } = + HttpException.extractDescriptionAndOptionsFrom(descriptionOrOptions); + super( HttpException.createBody( objectOrError, @@ -44,6 +49,7 @@ export class UnsupportedMediaTypeException extends HttpException { HttpStatus.UNSUPPORTED_MEDIA_TYPE, ), HttpStatus.UNSUPPORTED_MEDIA_TYPE, + httpExceptionOptions, ); } } diff --git a/packages/common/test/exceptions/http.exception.spec.ts b/packages/common/test/exceptions/http.exception.spec.ts index a880c1bfc53..0b7f21ed7fa 100644 --- a/packages/common/test/exceptions/http.exception.spec.ts +++ b/packages/common/test/exceptions/http.exception.spec.ts @@ -1,56 +1,145 @@ +import { Type } from '../../../common'; import { expect } from 'chai'; import { + BadGatewayException, BadRequestException, + ConflictException, + ForbiddenException, + GatewayTimeoutException, + GoneException, HttpException, + HttpVersionNotSupportedException, + ImATeapotException, + InternalServerErrorException, + MethodNotAllowedException, + MisdirectedException, + NotAcceptableException, NotFoundException, + NotImplementedException, + PayloadTooLargeException, + PreconditionFailedException, + RequestTimeoutException, + ServiceUnavailableException, + UnauthorizedException, + UnprocessableEntityException, + UnsupportedMediaTypeException, } from '../../exceptions'; describe('HttpException', () => { - it('should return a response as a string when input is a string', () => { - const message = 'My error message'; - expect(new HttpException(message, 404).getResponse()).to.be.eql( - 'My error message', - ); - }); + describe('getResponse', () => { + it('should return a response as a string when input is a string', () => { + const message = 'My error message'; + expect(new HttpException(message, 404).getResponse()).to.be.eql( + 'My error message', + ); + }); - it('should return a response as an object when input is an object', () => { - const message = { - msg: 'My error message', - reason: 'this can be a human readable reason', - anything: 'else', - }; - expect(new HttpException(message, 404).getResponse()).to.be.eql(message); - }); + it('should return a response as an object when input is an object', () => { + const message = { + msg: 'My error message', + reason: 'this can be a human readable reason', + anything: 'else', + }; + expect(new HttpException(message, 404).getResponse()).to.be.eql(message); + }); - it('should return a message from a built-in exception as an object', () => { - const message = 'My error message'; - expect(new BadRequestException(message).getResponse()).to.be.eql({ - statusCode: 400, - error: 'Bad Request', - message: 'My error message', + it('should return a message from a built-in exception as an object', () => { + const message = 'My error message'; + expect(new BadRequestException(message).getResponse()).to.be.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'My error message', + }); }); - }); - it('should return an object even when the message is undefined', () => { - expect(new BadRequestException().getResponse()).to.be.eql({ - statusCode: 400, - message: 'Bad Request', + it('should return an object even when the message is undefined', () => { + expect(new BadRequestException().getResponse()).to.be.eql({ + statusCode: 400, + message: 'Bad Request', + }); }); }); - it('should return a status code', () => { - expect(new BadRequestException().getStatus()).to.be.eql(400); - expect(new NotFoundException().getStatus()).to.be.eql(404); - }); + describe('built-in exceptions', () => { + describe('getStatus', () => { + it('should return given status code', () => { + const testCases: [Type, number][] = [ + [BadRequestException, 400], + [UnauthorizedException, 401], + [ForbiddenException, 403], + [NotFoundException, 404], + [MethodNotAllowedException, 405], + [NotAcceptableException, 406], + [RequestTimeoutException, 408], + [ConflictException, 409], + [GoneException, 410], + [PreconditionFailedException, 412], + [PayloadTooLargeException, 413], + [UnsupportedMediaTypeException, 415], + [ImATeapotException, 418], + [MisdirectedException, 421], + [UnprocessableEntityException, 422], + [InternalServerErrorException, 500], + [NotImplementedException, 501], + [BadGatewayException, 502], + [ServiceUnavailableException, 503], + [GatewayTimeoutException, 504], + [HttpVersionNotSupportedException, 505], + ]; - it('should return a response', () => { - expect(new BadRequestException().getResponse()).to.be.eql({ - message: 'Bad Request', - statusCode: 400, + testCases.forEach(([ExceptionClass, expectedStatus]) => { + expect(new ExceptionClass().getStatus()).to.be.eql(expectedStatus); + }); + }); }); - expect(new NotFoundException().getResponse()).to.be.eql({ - message: 'Not Found', - statusCode: 404, + + describe('getResponse', () => { + it('should return a response with default message and status code', () => { + const testCases: [Type, number, string][] = [ + [BadRequestException, 400, 'Bad Request'], + [UnauthorizedException, 401, 'Unauthorized'], + [ForbiddenException, 403, 'Forbidden'], + [NotFoundException, 404, 'Not Found'], + [MethodNotAllowedException, 405, 'Method Not Allowed'], + [NotAcceptableException, 406, 'Not Acceptable'], + [RequestTimeoutException, 408, 'Request Timeout'], + [ConflictException, 409, 'Conflict'], + [GoneException, 410, 'Gone'], + [PreconditionFailedException, 412, 'Precondition Failed'], + [PayloadTooLargeException, 413, 'Payload Too Large'], + [UnsupportedMediaTypeException, 415, 'Unsupported Media Type'], + [ImATeapotException, 418, "I'm a teapot"], + [MisdirectedException, 421, 'Misdirected'], + [UnprocessableEntityException, 422, 'Unprocessable Entity'], + [InternalServerErrorException, 500, 'Internal Server Error'], + [NotImplementedException, 501, 'Not Implemented'], + [BadGatewayException, 502, 'Bad Gateway'], + [ServiceUnavailableException, 503, 'Service Unavailable'], + [GatewayTimeoutException, 504, 'Gateway Timeout'], + [HttpVersionNotSupportedException, 505, 'HTTP Version Not Supported'], + ]; + + testCases.forEach( + ([ExceptionClass, expectedStatus, expectedMessage]) => { + expect(new ExceptionClass().getResponse()).to.be.eql({ + message: expectedMessage, + statusCode: expectedStatus, + }); + }, + ); + }); + + it('should return a response with an "error" attribute when description was provided as the "option" object', () => { + const badRequestError = new BadRequestException('ErrorMessage', { + description: 'Some error description', + }); + + expect(badRequestError.getResponse()).to.be.eql({ + message: 'ErrorMessage', + error: 'Some error description', + statusCode: 400, + }); + }); }); }); @@ -59,31 +148,28 @@ describe('HttpException', () => { expect(error instanceof Error).to.be.true; }); - it('should be serializable', () => { - const message = 'Some Error'; - const error = new HttpException(message, 400); - expect(`${error}`).to.be.eql(`HttpException: ${message}`); - }); + describe('when serializing', () => { + describe('and "response" parameter is a string', () => { + it('should concatenate HttpException with the given message', () => { + const responseAsString = 'Some Error'; + const error = new HttpException(responseAsString, 400); + expect(`${error}`).to.be.eql(`HttpException: ${responseAsString}`); + expect(`${error}`.includes('[object Object]')).to.not.be.true; + }); + }); - describe('when "response" is an object', () => { - it('should use default message', () => { - const obj = { foo: 'bar' }; - const error = new HttpException(obj, 400); - const badRequestError = new BadRequestException(obj); + describe('and "response" parameter is an object', () => { + it('should use default message', () => { + const responseAsObject = { foo: 'bar' }; + const error = new HttpException(responseAsObject, 400); + const badRequestError = new BadRequestException(responseAsObject); - expect(`${error}`).to.be.eql(`HttpException: Http Exception`); - expect(`${badRequestError}`).to.be.eql( - `BadRequestException: Bad Request Exception`, - ); - expect(`${error}`.includes('[object Object]')).to.not.be.true; - expect(`${badRequestError}`.includes('[object Object]')).to.not.be.true; - }); - describe('otherwise', () => { - it('should concat strings', () => { - const test = 'test message'; - const error = new HttpException(test, 400); - expect(`${error}`).to.be.eql(`HttpException: ${test}`); + expect(`${error}`).to.be.eql(`HttpException: Http Exception`); + expect(`${badRequestError}`).to.be.eql( + `BadRequestException: Bad Request Exception`, + ); expect(`${error}`.includes('[object Object]')).to.not.be.true; + expect(`${badRequestError}`.includes('[object Object]')).to.not.be.true; }); }); }); @@ -131,6 +217,9 @@ describe('HttpException', () => { }); describe('initCause', () => { + const errorCause = new Error('An internal error cause'); + const customDescription = 'custom description'; + it('configures a cause when message is an instance of error', () => { const message = new Error('Some Error'); const error = new HttpException(message, 400); @@ -139,5 +228,52 @@ describe('HttpException', () => { expect(cause).to.be.eql(message); }); + + it('configures a cause when message is a string and the options object is passed', () => { + const error = new HttpException(customDescription, 400, { + cause: errorCause, + }); + + expect(`${error}`).to.be.eql(`HttpException: ${customDescription}`); + const { cause } = error; + + expect(cause).to.be.eql(errorCause); + }); + + it('configures a cause when using a bult-in exception with options', () => { + const builtInErrorClasses = [ + BadGatewayException, + BadRequestException, + ConflictException, + ForbiddenException, + GatewayTimeoutException, + GoneException, + HttpVersionNotSupportedException, + ImATeapotException, + InternalServerErrorException, + MethodNotAllowedException, + MisdirectedException, + NotAcceptableException, + NotFoundException, + NotImplementedException, + PayloadTooLargeException, + PreconditionFailedException, + RequestTimeoutException, + ServiceUnavailableException, + UnauthorizedException, + UnprocessableEntityException, + UnsupportedMediaTypeException, + ]; + + builtInErrorClasses.forEach(ExceptionClass => { + const error = new ExceptionClass(customDescription, { + cause: errorCause, + }); + + const { cause } = error; + + expect(cause).to.be.eql(errorCause); + }); + }); }); });