Enforce strongly typed API contract in exception filters

landing-page-layout
MiguelMLorente 2025-02-06 22:23:35 +01:00
parent 5d55631812
commit e2bf337b25
3 changed files with 25 additions and 38 deletions

View File

@ -10,7 +10,7 @@ import { PlayerService } from './players/player.service';
import { GameService } from 'src/games/game.service'; import { GameService } from 'src/games/game.service';
import { Socket } from 'socket.io'; import { Socket } from 'socket.io';
import { ClientEvent, CreateLobbyEvent, JoinLobbyEvent } from 'interface'; import { ClientEvent, CreateLobbyEvent, JoinLobbyEvent } from 'interface';
import { WsExceptionFilter } from './websocket-exception-filter'; import { createWsExceptionFilter } from './websocket-exception-filter';
import { PlayerNotFoundException } from './exceptions'; import { PlayerNotFoundException } from './exceptions';
@WebSocketGateway({ cors: true }) @WebSocketGateway({ cors: true })
@ -26,7 +26,7 @@ export class AppService implements OnGatewayConnection {
this.playerService.createPlayer(client.id); this.playerService.createPlayer(client.id);
} }
@UseFilters(new WsExceptionFilter([PlayerNotFoundException])) @UseFilters(createWsExceptionFilter([PlayerNotFoundException]))
@SubscribeMessage(ClientEvent.CREATE_LOBBY) @SubscribeMessage(ClientEvent.CREATE_LOBBY)
handleCreateLobby( handleCreateLobby(
@ConnectedSocket() client: Socket, @ConnectedSocket() client: Socket,

View File

@ -21,9 +21,6 @@ export class PlayerNotFoundException extends WebSocketException {
export class GameNotFoundException extends WebSocketException { export class GameNotFoundException extends WebSocketException {
constructor(gameId: string) { constructor(gameId: string) {
super( super('Unable to find game from ID: ' + gameId, ErrorCode.GAME_NOT_FOUND);
'Unable to find game from ID: ' + gameId,
ErrorCode.GAME_NOT_FOUND,
);
} }
} }

View File

@ -1,19 +1,16 @@
import { ArgumentsHost, Catch, Logger } from '@nestjs/common'; import { ArgumentsHost, Catch, Logger } from '@nestjs/common';
import { BaseWsExceptionFilter } from '@nestjs/websockets'; import { BaseWsExceptionFilter } from '@nestjs/websockets';
import { GameNotFoundException, PlayerNotFoundException, WebSocketException } from './exceptions'; import { WebSocketException } from './exceptions';
import { Socket } from 'socket.io'; import { Socket } from 'socket.io';
import { ClientEvent } from 'interface'; import { ClientEvent } from 'interface';
import { emitCreateLobbyError } from 'interface'; import { emitCreateLobbyError } from 'interface';
@Catch(PlayerNotFoundException, GameNotFoundException) export const createWsExceptionFilter: (
export class WsExceptionFilter extends BaseWsExceptionFilter<WebSocketException> { exceptionList: (typeof WebSocketException)[],
) => BaseWsExceptionFilter<WebSocketException> = (handledExceptions) => {
@Catch(...handledExceptions)
class WsExceptionFilter extends BaseWsExceptionFilter<WebSocketException> {
private readonly logger = new Logger(BaseWsExceptionFilter.name); private readonly logger = new Logger(BaseWsExceptionFilter.name);
private readonly caughtExceptions: (typeof WebSocketException)[];
constructor(caughtExceptions: (typeof WebSocketException)[]) {
super();
this.caughtExceptions = caughtExceptions;
}
catch(exception: WebSocketException, host: ArgumentsHost) { catch(exception: WebSocketException, host: ArgumentsHost) {
const socket = host.switchToWs().getClient() as Socket; const socket = host.switchToWs().getClient() as Socket;
@ -23,18 +20,9 @@ export class WsExceptionFilter extends BaseWsExceptionFilter<WebSocketException>
`Caught exception: ${exception} `Caught exception: ${exception}
for request to ${method} for request to ${method}
from client ${socket.id} from client ${socket.id}
with input ${data}`, with input ${JSON.stringify(data)}`,
); );
if (
!this.caughtExceptions.find(
(caughtException) => exception instanceof caughtException,
)
) {
this.logger.fatal("Uncaught exception", exception)
super.catch(exception, host);
}
switch (method) { switch (method) {
case ClientEvent.CREATE_LOBBY: case ClientEvent.CREATE_LOBBY:
emitCreateLobbyError(socket, { error: exception.errorCode }); emitCreateLobbyError(socket, { error: exception.errorCode });
@ -42,3 +30,5 @@ export class WsExceptionFilter extends BaseWsExceptionFilter<WebSocketException>
} }
} }
} }
return new WsExceptionFilter();
};