Compare commits

..

2 Commits

Author SHA1 Message Date
Kamil Myśliwiec
a077ea51c1 Merge branch '11.0.0' into feat/intrinsic-exception 2024-11-21 10:43:36 +01:00
Kamil Myśliwiec
f99617dd6b feat(common): introduce intrinsic exception 2024-11-21 10:09:43 +01:00
9 changed files with 67 additions and 50 deletions

View File

@@ -3,6 +3,7 @@ import {
HttpExceptionBodyMessage,
} from '../interfaces/http/http-exception-body.interface';
import { isNumber, isObject, isString } from '../utils/shared.utils';
import { IntrinsicException } from './intrinsic.exception';
export interface HttpExceptionOptions {
/** original cause of the error */
@@ -23,7 +24,13 @@ export interface DescriptionAndOptions {
*
* @publicApi
*/
export class HttpException extends Error {
export class HttpException extends IntrinsicException {
/**
* Exception cause. Indicates the specific original cause of the error.
* It is used when catching and re-throwing an error with a more-specific or useful error message in order to still have access to the original error.
*/
public cause: unknown;
/**
* Instantiate a plain HTTP Exception.
*
@@ -68,8 +75,6 @@ export class HttpException extends Error {
this.initCause();
}
public cause: unknown;
/**
* Configures error chaining support
*

View File

@@ -1,22 +1,23 @@
export * from './bad-request.exception';
export * from './http.exception';
export * from './unauthorized.exception';
export * from './method-not-allowed.exception';
export * from './not-found.exception';
export * from './forbidden.exception';
export * from './not-acceptable.exception';
export * from './request-timeout.exception';
export * from './conflict.exception';
export * from './gone.exception';
export * from './payload-too-large.exception';
export * from './unsupported-media-type.exception';
export * from './unprocessable-entity.exception';
export * from './internal-server-error.exception';
export * from './not-implemented.exception';
export * from './http-version-not-supported.exception';
export * from './bad-gateway.exception';
export * from './service-unavailable.exception';
export * from './bad-request.exception';
export * from './conflict.exception';
export * from './forbidden.exception';
export * from './gateway-timeout.exception';
export * from './gone.exception';
export * from './http-version-not-supported.exception';
export * from './http.exception';
export * from './im-a-teapot.exception';
export * from './precondition-failed.exception';
export * from './internal-server-error.exception';
export * from './intrinsic.exception';
export * from './method-not-allowed.exception';
export * from './misdirected.exception';
export * from './not-acceptable.exception';
export * from './not-found.exception';
export * from './not-implemented.exception';
export * from './payload-too-large.exception';
export * from './precondition-failed.exception';
export * from './request-timeout.exception';
export * from './service-unavailable.exception';
export * from './unauthorized.exception';
export * from './unprocessable-entity.exception';
export * from './unsupported-media-type.exception';

View File

@@ -0,0 +1,7 @@
/**
* Exception that represents an intrinsic error in the application.
* When thrown, the default exception filter will not log the error message.
*
* @publicApi
*/
export class IntrinsicException extends Error {}

View File

@@ -5,6 +5,7 @@ import {
HttpServer,
HttpStatus,
Inject,
IntrinsicException,
Logger,
Optional,
} from '@nestjs/common';
@@ -68,7 +69,9 @@ export class BaseExceptionFilter<T = any> implements ExceptionFilter<T> {
applicationRef.end(response);
}
return BaseExceptionFilter.logger.error(exception);
if (!(exception instanceof IntrinsicException)) {
BaseExceptionFilter.logger.error(exception);
}
}
public isExceptionObject(err: any): err is Error {

View File

@@ -1,10 +1,13 @@
import { ArgumentsHost, HttpException, Logger } from '@nestjs/common';
import { ArgumentsHost, IntrinsicException, Logger } from '@nestjs/common';
export class ExternalExceptionFilter<T = any, R = any> {
private static readonly logger = new Logger('ExceptionsHandler');
catch(exception: T, host: ArgumentsHost): R | Promise<R> {
if (exception instanceof Error && !(exception instanceof HttpException)) {
if (
exception instanceof Error &&
!(exception instanceof IntrinsicException)
) {
ExternalExceptionFilter.logger.error(exception);
}
throw exception;

View File

@@ -1,5 +1,10 @@
/* eslint-disable prefer-spread */
import { ArgumentsHost, Logger, RpcExceptionFilter } from '@nestjs/common';
import {
ArgumentsHost,
IntrinsicException,
Logger,
RpcExceptionFilter,
} from '@nestjs/common';
import { isObject } from '@nestjs/common/utils/shared.utils';
import { MESSAGES } from '@nestjs/core/constants';
import { Observable, throwError as _throw } from 'rxjs';
@@ -26,8 +31,10 @@ export class BaseRpcExceptionFilter<T = any, R = any>
public handleUnknownError(exception: T, status: string) {
const errorMessage = MESSAGES.UNKNOWN_EXCEPTION_MESSAGE;
const logger = BaseRpcExceptionFilter.logger;
logger.error.apply(logger, [exception]);
if (!(exception instanceof IntrinsicException)) {
const logger = BaseRpcExceptionFilter.logger;
logger.error(exception);
}
return _throw(() => ({ status, message: errorMessage }));
}

View File

@@ -77,18 +77,16 @@ export class ServerNats<
}
public bindEvents(client: Client) {
const subscribe = (channel: string, queue: string) =>
const queue = this.getOptionsProp(this.options, 'queue');
const subscribe = (channel: string) =>
client.subscribe(channel, {
queue,
callback: this.getMessageHandler(channel).bind(this),
});
const defaultQueue = this.getOptionsProp(this.options, 'queue');
const registeredPatterns = [...this.messageHandlers.keys()];
for (const channel of registeredPatterns) {
const handlerRef = this.messageHandlers.get(channel);
const queue = handlerRef.extras?.queue ?? defaultQueue;
const sub = subscribe(channel, queue);
const sub = subscribe(channel);
this.subscriptions.push(sub);
}
}

View File

@@ -113,26 +113,11 @@ describe('ServerNats', () => {
[pattern]: messageHandler,
});
});
it('should subscribe to every pattern', () => {
it('should subscribe to each acknowledge patterns', () => {
server.bindEvents(natsClient);
expect(subscribeSpy.calledWith(pattern)).to.be.true;
});
it('should use a per pattern queue if provided', () => {
const queue = 'test';
untypedServer.messageHandlers = objectToMap({
[pattern]: Object.assign(messageHandler, {
extras: {
queue,
},
}),
});
server.bindEvents(natsClient);
const lastCall = subscribeSpy.lastCall;
expect(lastCall.args[1].queue).to.be.eql(queue);
});
it('should fill the subscriptions array properly', () => {
server.bindEvents(natsClient);
expect(server['subscriptions'].length).to.be.equals(1);

View File

@@ -1,4 +1,9 @@
import { ArgumentsHost, Logger, WsExceptionFilter } from '@nestjs/common';
import {
ArgumentsHost,
IntrinsicException,
Logger,
WsExceptionFilter,
} from '@nestjs/common';
import { isObject } from '@nestjs/common/utils/shared.utils';
import { MESSAGES } from '@nestjs/core/constants';
import { WsException } from '../errors/ws-exception';
@@ -46,7 +51,10 @@ export class BaseWsExceptionFilter<TError = any>
message: MESSAGES.UNKNOWN_EXCEPTION_MESSAGE,
});
return BaseWsExceptionFilter.logger.error(exception);
if (!(exception instanceof IntrinsicException)) {
const logger = BaseWsExceptionFilter.logger;
logger.error(exception);
}
}
public isExceptionObject(err: any): err is Error {