fix(api): use PrismaClientKnownRequestError instanceof, add ConflictException for duplicate routes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,8 +11,8 @@ export class RoutesController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
create(@Body() body: { sourceGroupId?: string; targetGroupId?: string }) {
|
create(@Body() body: { sourceGroupId: string; targetGroupId: string }) {
|
||||||
return this.routesService.create(body.sourceGroupId ?? '', body.targetGroupId ?? '');
|
return this.routesService.create(body.sourceGroupId, body.targetGroupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
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 { RoutesService } from './routes.service';
|
||||||
import { PrismaService } from '../../prisma/prisma.service';
|
import { PrismaService } from '../../prisma/prisma.service';
|
||||||
|
|
||||||
@@ -77,6 +78,15 @@ describe('RoutesService', () => {
|
|||||||
it('throws BadRequestException when targetGroupId is empty', async () => {
|
it('throws BadRequestException when targetGroupId is empty', async () => {
|
||||||
await expect(service.create('grp_1', '')).rejects.toThrow(BadRequestException);
|
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', () => {
|
describe('remove', () => {
|
||||||
@@ -86,7 +96,11 @@ describe('RoutesService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws NotFoundException when route does not exist (Prisma P2025)', async () => {
|
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);
|
await expect(service.remove('bad_id')).rejects.toThrow(NotFoundException);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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';
|
import { PrismaService } from '../../prisma/prisma.service';
|
||||||
|
|
||||||
const routeInclude = {
|
const routeInclude = {
|
||||||
@@ -22,17 +23,24 @@ export class RoutesService {
|
|||||||
if (!sourceGroupId || !targetGroupId) {
|
if (!sourceGroupId || !targetGroupId) {
|
||||||
throw new BadRequestException('sourceGroupId and targetGroupId are required');
|
throw new BadRequestException('sourceGroupId and targetGroupId are required');
|
||||||
}
|
}
|
||||||
return this.prisma.syncRoute.create({
|
try {
|
||||||
|
return await this.prisma.syncRoute.create({
|
||||||
data: { sourceGroupId, targetGroupId },
|
data: { sourceGroupId, targetGroupId },
|
||||||
include: routeInclude,
|
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) {
|
async remove(id: string) {
|
||||||
try {
|
try {
|
||||||
await this.prisma.syncRoute.delete({ where: { id } });
|
await this.prisma.syncRoute.delete({ where: { id } });
|
||||||
} catch (e) {
|
} 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 new NotFoundException(`Route ${id} not found`);
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
Reference in New Issue
Block a user