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,44 +1,34 @@
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)[],
private readonly logger = new Logger(BaseWsExceptionFilter.name); ) => BaseWsExceptionFilter<WebSocketException> = (handledExceptions) => {
private readonly caughtExceptions: (typeof WebSocketException)[]; @Catch(...handledExceptions)
class WsExceptionFilter extends BaseWsExceptionFilter<WebSocketException> {
private readonly logger = new Logger(BaseWsExceptionFilter.name);
constructor(caughtExceptions: (typeof WebSocketException)[]) { catch(exception: WebSocketException, host: ArgumentsHost) {
super(); const socket = host.switchToWs().getClient() as Socket;
this.caughtExceptions = caughtExceptions; const method = host.switchToWs().getPattern();
} const data: object = host.switchToWs().getData();
this.logger.log(
catch(exception: WebSocketException, host: ArgumentsHost) { `Caught exception: ${exception}
const socket = host.switchToWs().getClient() as Socket;
const method = host.switchToWs().getPattern();
const data: object = host.switchToWs().getData();
this.logger.log(
`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 ( switch (method) {
!this.caughtExceptions.find( case ClientEvent.CREATE_LOBBY:
(caughtException) => exception instanceof caughtException, emitCreateLobbyError(socket, { error: exception.errorCode });
) break;
) { }
this.logger.fatal("Uncaught exception", exception)
super.catch(exception, host);
}
switch (method) {
case ClientEvent.CREATE_LOBBY:
emitCreateLobbyError(socket, { error: exception.errorCode });
break;
} }
} }
} return new WsExceptionFilter();
};