Let Nest exception filter handle unexpected exceptions

This commit is contained in:
Romain Thouvenin 2024-03-28 11:26:26 +01:00
parent 739d05b095
commit 7f7a51d19b
2 changed files with 142 additions and 169 deletions

View File

@ -1,17 +1,16 @@
import { Controller, Inject, UseInterceptors, UsePipes } from '@nestjs/common';
import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { RpcValidationPipe } from '@mobicoop/ddd-library'; import { RpcValidationPipe } from '@mobicoop/ddd-library';
import { RpcExceptionCode } from '@mobicoop/ddd-library';
import { MatchingPaginatedResponseDto } from '../dtos/matching.paginated.response.dto';
import { QueryBus } from '@nestjs/cqrs';
import { MatchRequestDto } from './dtos/match.request.dto';
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
import { AD_ROUTE_PROVIDER } from '@modules/ad/ad.di-tokens'; import { AD_ROUTE_PROVIDER } from '@modules/ad/ad.di-tokens';
import { MatchMapper } from '@modules/ad/match.mapper';
import { MatchingResult } from '@modules/ad/core/application/queries/match/match.query-handler';
import { CacheInterceptor, CacheKey } from '@nestjs/cache-manager';
import { GeorouterPort } from '@modules/ad/core/application/ports/georouter.port'; import { GeorouterPort } from '@modules/ad/core/application/ports/georouter.port';
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
import { MatchingResult } from '@modules/ad/core/application/queries/match/match.query-handler';
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
import { MatchMapper } from '@modules/ad/match.mapper';
import { CacheInterceptor, CacheKey } from '@nestjs/cache-manager';
import { Controller, Inject, UseInterceptors, UsePipes } from '@nestjs/common';
import { QueryBus } from '@nestjs/cqrs';
import { GrpcMethod } from '@nestjs/microservices';
import { MatchingPaginatedResponseDto } from '../dtos/matching.paginated.response.dto';
import { MatchRequestDto } from './dtos/match.request.dto';
@UsePipes( @UsePipes(
new RpcValidationPipe({ new RpcValidationPipe({
@ -32,24 +31,17 @@ export class MatchGrpcController {
@UseInterceptors(CacheInterceptor) @UseInterceptors(CacheInterceptor)
@GrpcMethod('MatcherService', 'Match') @GrpcMethod('MatcherService', 'Match')
async match(data: MatchRequestDto): Promise<MatchingPaginatedResponseDto> { async match(data: MatchRequestDto): Promise<MatchingPaginatedResponseDto> {
try { const matchingResult: MatchingResult = await this.queryBus.execute(
const matchingResult: MatchingResult = await this.queryBus.execute( new MatchQuery(data, this.routeProvider),
new MatchQuery(data, this.routeProvider), );
); return new MatchingPaginatedResponseDto({
return new MatchingPaginatedResponseDto({ id: matchingResult.id,
id: matchingResult.id, data: matchingResult.matches.map((match: MatchEntity) =>
data: matchingResult.matches.map((match: MatchEntity) => this.matchMapper.toResponse(match),
this.matchMapper.toResponse(match), ),
), page: matchingResult.page,
page: matchingResult.page, perPage: matchingResult.perPage,
perPage: matchingResult.perPage, total: matchingResult.total,
total: matchingResult.total, });
});
} catch (e) {
throw new RpcException({
code: RpcExceptionCode.UNKNOWN,
message: e.message,
});
}
} }
} }

View File

@ -1,4 +1,3 @@
import { RpcExceptionCode } from '@mobicoop/ddd-library';
import { AD_ROUTE_PROVIDER } from '@modules/ad/ad.di-tokens'; import { AD_ROUTE_PROVIDER } from '@modules/ad/ad.di-tokens';
import { MatchingResult } from '@modules/ad/core/application/queries/match/match.query-handler'; import { MatchingResult } from '@modules/ad/core/application/queries/match/match.query-handler';
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types'; import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
@ -14,7 +13,6 @@ import { MatchGrpcController } from '@modules/ad/interface/grpc-controllers/matc
import { MatchMapper } from '@modules/ad/match.mapper'; import { MatchMapper } from '@modules/ad/match.mapper';
import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { QueryBus } from '@nestjs/cqrs'; import { QueryBus } from '@nestjs/cqrs';
import { RpcException } from '@nestjs/microservices';
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { bareMockGeorouter } from '../georouter.mock'; import { bareMockGeorouter } from '../georouter.mock';
@ -56,131 +54,126 @@ const recurrentMatchRequestDto: MatchRequestDto = {
}; };
const mockQueryBus = { const mockQueryBus = {
execute: jest execute: jest.fn().mockImplementationOnce(
.fn() () =>
.mockImplementationOnce( <MatchingResult>{
() => id: '43c83ae2-f4b0-4ac6-b8bf-8071801924d4',
<MatchingResult>{ page: 1,
id: '43c83ae2-f4b0-4ac6-b8bf-8071801924d4', perPage: 10,
page: 1, matches: [
perPage: 10, MatchEntity.create({
matches: [ adId: '53a0bf71-4132-4f7b-a4cc-88c796b6bdf1',
MatchEntity.create({ role: Role.DRIVER,
adId: '53a0bf71-4132-4f7b-a4cc-88c796b6bdf1', frequency: Frequency.RECURRENT,
role: Role.DRIVER, distance: 356041,
frequency: Frequency.RECURRENT, duration: 12647,
distance: 356041, initialDistance: 349251,
duration: 12647, initialDuration: 12103,
initialDistance: 349251, journeys: [
initialDuration: 12103, {
journeys: [ firstDate: new Date('2023-09-01'),
{ lastDate: new Date('2024-08-30'),
firstDate: new Date('2023-09-01'), journeyItems: [
lastDate: new Date('2024-08-30'), new JourneyItem({
journeyItems: [ lat: 48.689445,
new JourneyItem({ lon: 6.17651,
lat: 48.689445, duration: 0,
lon: 6.17651, distance: 0,
duration: 0, actorTimes: [
distance: 0, new ActorTime({
actorTimes: [ role: Role.DRIVER,
new ActorTime({ target: Target.START,
role: Role.DRIVER, firstDatetime: new Date('2023-09-01 07:00'),
target: Target.START, firstMinDatetime: new Date('2023-09-01 06:45'),
firstDatetime: new Date('2023-09-01 07:00'), firstMaxDatetime: new Date('2023-09-01 07:15'),
firstMinDatetime: new Date('2023-09-01 06:45'), lastDatetime: new Date('2024-08-30 07:00'),
firstMaxDatetime: new Date('2023-09-01 07:15'), lastMinDatetime: new Date('2024-08-30 06:45'),
lastDatetime: new Date('2024-08-30 07:00'), lastMaxDatetime: new Date('2024-08-30 07:15'),
lastMinDatetime: new Date('2024-08-30 06:45'), }),
lastMaxDatetime: new Date('2024-08-30 07:15'), ],
}), }),
], new JourneyItem({
}), lat: 48.369445,
new JourneyItem({ lon: 6.67487,
lat: 48.369445, duration: 2100,
lon: 6.67487, distance: 56878,
duration: 2100, actorTimes: [
distance: 56878, new ActorTime({
actorTimes: [ role: Role.DRIVER,
new ActorTime({ target: Target.NEUTRAL,
role: Role.DRIVER, firstDatetime: new Date('2023-09-01 07:35'),
target: Target.NEUTRAL, firstMinDatetime: new Date('2023-09-01 07:20'),
firstDatetime: new Date('2023-09-01 07:35'), firstMaxDatetime: new Date('2023-09-01 07:50'),
firstMinDatetime: new Date('2023-09-01 07:20'), lastDatetime: new Date('2024-08-30 07:35'),
firstMaxDatetime: new Date('2023-09-01 07:50'), lastMinDatetime: new Date('2024-08-30 07:20'),
lastDatetime: new Date('2024-08-30 07:35'), lastMaxDatetime: new Date('2024-08-30 07:50'),
lastMinDatetime: new Date('2024-08-30 07:20'), }),
lastMaxDatetime: new Date('2024-08-30 07:50'), new ActorTime({
}), role: Role.PASSENGER,
new ActorTime({ target: Target.START,
role: Role.PASSENGER, firstDatetime: new Date('2023-09-01 07:32'),
target: Target.START, firstMinDatetime: new Date('2023-09-01 07:17'),
firstDatetime: new Date('2023-09-01 07:32'), firstMaxDatetime: new Date('2023-09-01 07:47'),
firstMinDatetime: new Date('2023-09-01 07:17'), lastDatetime: new Date('2024-08-30 07:32'),
firstMaxDatetime: new Date('2023-09-01 07:47'), lastMinDatetime: new Date('2024-08-30 07:17'),
lastDatetime: new Date('2024-08-30 07:32'), lastMaxDatetime: new Date('2024-08-30 07:47'),
lastMinDatetime: new Date('2024-08-30 07:17'), }),
lastMaxDatetime: new Date('2024-08-30 07:47'), ],
}), }),
], new JourneyItem({
}), lat: 47.98487,
new JourneyItem({ lon: 6.9427,
lat: 47.98487, duration: 3840,
lon: 6.9427, distance: 76491,
duration: 3840, actorTimes: [
distance: 76491, new ActorTime({
actorTimes: [ role: Role.DRIVER,
new ActorTime({ target: Target.NEUTRAL,
role: Role.DRIVER, firstDatetime: new Date('2023-09-01 08:04'),
target: Target.NEUTRAL, firstMinDatetime: new Date('2023-09-01 07:51'),
firstDatetime: new Date('2023-09-01 08:04'), firstMaxDatetime: new Date('2023-09-01 08:19'),
firstMinDatetime: new Date('2023-09-01 07:51'), lastDatetime: new Date('2024-08-30 08:04'),
firstMaxDatetime: new Date('2023-09-01 08:19'), lastMinDatetime: new Date('2024-08-30 07:51'),
lastDatetime: new Date('2024-08-30 08:04'), lastMaxDatetime: new Date('2024-08-30 08:19'),
lastMinDatetime: new Date('2024-08-30 07:51'), }),
lastMaxDatetime: new Date('2024-08-30 08:19'), new ActorTime({
}), role: Role.PASSENGER,
new ActorTime({ target: Target.FINISH,
role: Role.PASSENGER, firstDatetime: new Date('2023-09-01 08:01'),
target: Target.FINISH, firstMinDatetime: new Date('2023-09-01 07:46'),
firstDatetime: new Date('2023-09-01 08:01'), firstMaxDatetime: new Date('2023-09-01 08:16'),
firstMinDatetime: new Date('2023-09-01 07:46'), lastDatetime: new Date('2024-08-30 08:01'),
firstMaxDatetime: new Date('2023-09-01 08:16'), lastMinDatetime: new Date('2024-08-30 07:46'),
lastDatetime: new Date('2024-08-30 08:01'), lastMaxDatetime: new Date('2024-08-30 08:16'),
lastMinDatetime: new Date('2024-08-30 07:46'), }),
lastMaxDatetime: new Date('2024-08-30 08:16'), ],
}), }),
], new JourneyItem({
}), lat: 47.365987,
new JourneyItem({ lon: 7.02154,
lat: 47.365987, duration: 4980,
lon: 7.02154, distance: 96475,
duration: 4980, actorTimes: [
distance: 96475, new ActorTime({
actorTimes: [ role: Role.DRIVER,
new ActorTime({ target: Target.FINISH,
role: Role.DRIVER, firstDatetime: new Date('2023-09-01 08:23'),
target: Target.FINISH, firstMinDatetime: new Date('2023-09-01 08:08'),
firstDatetime: new Date('2023-09-01 08:23'), firstMaxDatetime: new Date('2023-09-01 08:38'),
firstMinDatetime: new Date('2023-09-01 08:08'), lastDatetime: new Date('2024-08-30 08:23'),
firstMaxDatetime: new Date('2023-09-01 08:38'), lastMinDatetime: new Date('2024-08-30 08:08'),
lastDatetime: new Date('2024-08-30 08:23'), lastMaxDatetime: new Date('2024-08-30 08:38'),
lastMinDatetime: new Date('2024-08-30 08:08'), }),
lastMaxDatetime: new Date('2024-08-30 08:38'), ],
}), }),
], ],
}), },
], ],
}, }),
], ],
}), total: 1,
], },
total: 1, ),
},
)
.mockImplementationOnce(() => {
throw new Error();
}),
}; };
const mockMatchMapper = { const mockMatchMapper = {
@ -317,16 +310,4 @@ describe('Match Grpc Controller', () => {
expect(matchingPaginatedResponseDto.perPage).toBe(10); expect(matchingPaginatedResponseDto.perPage).toBe(10);
expect(mockQueryBus.execute).toHaveBeenCalledTimes(1); expect(mockQueryBus.execute).toHaveBeenCalledTimes(1);
}); });
it('should throw a generic RpcException', async () => {
jest.spyOn(mockQueryBus, 'execute');
expect.assertions(3);
try {
await matchGrpcController.match(recurrentMatchRequestDto);
} catch (e: any) {
expect(e).toBeInstanceOf(RpcException);
expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN);
}
expect(mockQueryBus.execute).toHaveBeenCalledTimes(1);
});
}); });