From d92476f84194930dd14e3dacef227a48ed17a58e Mon Sep 17 00:00:00 2001 From: maaz519 Date: Thu, 28 May 2026 01:13:05 +0530 Subject: [PATCH] fix(api): use PrismaClientKnownRequestError instanceof, add ConflictException for duplicate routes Co-Authored-By: Claude Sonnet 4.6 --- .../src/modules/routes/routes.controller.ts | 4 ++-- .../src/modules/routes/routes.service.spec.ts | 18 +++++++++++++++-- apps/api/src/modules/routes/routes.service.ts | 20 +++++++++++++------ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/apps/api/src/modules/routes/routes.controller.ts b/apps/api/src/modules/routes/routes.controller.ts index c4e9af5..6c4e4e0 100644 --- a/apps/api/src/modules/routes/routes.controller.ts +++ b/apps/api/src/modules/routes/routes.controller.ts @@ -11,8 +11,8 @@ export class RoutesController { } @Post() - create(@Body() body: { sourceGroupId?: string; targetGroupId?: string }) { - return this.routesService.create(body.sourceGroupId ?? '', body.targetGroupId ?? ''); + create(@Body() body: { sourceGroupId: string; targetGroupId: string }) { + return this.routesService.create(body.sourceGroupId, body.targetGroupId); } @Delete(':id') diff --git a/apps/api/src/modules/routes/routes.service.spec.ts b/apps/api/src/modules/routes/routes.service.spec.ts index af7ae2c..b6c3754 100644 --- a/apps/api/src/modules/routes/routes.service.spec.ts +++ b/apps/api/src/modules/routes/routes.service.spec.ts @@ -1,5 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { BadRequestException, NotFoundException } from '@nestjs/common'; +import { BadRequestException, ConflictException, NotFoundException } from '@nestjs/common'; +import { Prisma } from '@prisma/client'; import { RoutesService } from './routes.service'; import { PrismaService } from '../../prisma/prisma.service'; @@ -77,6 +78,15 @@ describe('RoutesService', () => { it('throws BadRequestException when targetGroupId is empty', async () => { await expect(service.create('grp_1', '')).rejects.toThrow(BadRequestException); }); + + it('throws ConflictException when route already exists (Prisma P2002)', async () => { + const p2002 = new Prisma.PrismaClientKnownRequestError('Unique constraint', { + code: 'P2002', + clientVersion: '6.0.0', + }); + mockPrisma.syncRoute.create.mockRejectedValueOnce(p2002); + await expect(service.create('grp_1', 'grp_2')).rejects.toThrow(ConflictException); + }); }); describe('remove', () => { @@ -86,7 +96,11 @@ describe('RoutesService', () => { }); it('throws NotFoundException when route does not exist (Prisma P2025)', async () => { - mockPrisma.syncRoute.delete.mockRejectedValueOnce({ code: 'P2025' }); + const p2025 = new Prisma.PrismaClientKnownRequestError('Record not found', { + code: 'P2025', + clientVersion: '6.0.0', + }); + mockPrisma.syncRoute.delete.mockRejectedValueOnce(p2025); await expect(service.remove('bad_id')).rejects.toThrow(NotFoundException); }); }); diff --git a/apps/api/src/modules/routes/routes.service.ts b/apps/api/src/modules/routes/routes.service.ts index 6acb6eb..f81b3b7 100644 --- a/apps/api/src/modules/routes/routes.service.ts +++ b/apps/api/src/modules/routes/routes.service.ts @@ -1,4 +1,5 @@ -import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; +import { BadRequestException, ConflictException, Injectable, NotFoundException } from '@nestjs/common'; +import { Prisma } from '@prisma/client'; import { PrismaService } from '../../prisma/prisma.service'; const routeInclude = { @@ -22,17 +23,24 @@ export class RoutesService { if (!sourceGroupId || !targetGroupId) { throw new BadRequestException('sourceGroupId and targetGroupId are required'); } - return this.prisma.syncRoute.create({ - data: { sourceGroupId, targetGroupId }, - include: routeInclude, - }); + try { + return await this.prisma.syncRoute.create({ + data: { sourceGroupId, targetGroupId }, + include: routeInclude, + }); + } catch (e) { + if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') { + throw new ConflictException('A route between these groups already exists'); + } + throw e; + } } async remove(id: string) { try { await this.prisma.syncRoute.delete({ where: { id } }); } catch (e) { - if ((e as { code?: string })?.code === 'P2025') { + if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2025') { throw new NotFoundException(`Route ${id} not found`); } throw e;