fix(api): prevent self-loop routes, map P2003 to 400, forward DELETE error body
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -87,6 +87,19 @@ describe('RoutesService', () => {
|
|||||||
mockPrisma.syncRoute.create.mockRejectedValueOnce(p2002);
|
mockPrisma.syncRoute.create.mockRejectedValueOnce(p2002);
|
||||||
await expect(service.create('grp_1', 'grp_2')).rejects.toThrow(ConflictException);
|
await expect(service.create('grp_1', 'grp_2')).rejects.toThrow(ConflictException);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('throws BadRequestException when source and target are the same group', async () => {
|
||||||
|
await expect(service.create('grp_1', 'grp_1')).rejects.toThrow(BadRequestException);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws BadRequestException when a group ID does not exist (Prisma P2003)', async () => {
|
||||||
|
const p2003 = new Prisma.PrismaClientKnownRequestError('Foreign key constraint', {
|
||||||
|
code: 'P2003',
|
||||||
|
clientVersion: '6.0.0',
|
||||||
|
});
|
||||||
|
mockPrisma.syncRoute.create.mockRejectedValueOnce(p2003);
|
||||||
|
await expect(service.create('grp_1', 'bad_grp')).rejects.toThrow(BadRequestException);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('remove', () => {
|
describe('remove', () => {
|
||||||
|
|||||||
@@ -23,14 +23,22 @@ 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');
|
||||||
}
|
}
|
||||||
|
if (sourceGroupId === targetGroupId) {
|
||||||
|
throw new BadRequestException('Source and target groups cannot be the same');
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return await this.prisma.syncRoute.create({
|
return await this.prisma.syncRoute.create({
|
||||||
data: { sourceGroupId, targetGroupId },
|
data: { sourceGroupId, targetGroupId },
|
||||||
include: routeInclude,
|
include: routeInclude,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') {
|
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
throw new ConflictException('A route between these groups already exists');
|
if (e.code === 'P2002') {
|
||||||
|
throw new ConflictException('A route between these groups already exists');
|
||||||
|
}
|
||||||
|
if (e.code === 'P2003') {
|
||||||
|
throw new BadRequestException('One or both group IDs do not exist');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ export async function DELETE(
|
|||||||
) {
|
) {
|
||||||
const { id } = await params;
|
const { id } = await params;
|
||||||
const res = await fetch(`${API_URL}/routes/${id}`, { method: 'DELETE' });
|
const res = await fetch(`${API_URL}/routes/${id}`, { method: 'DELETE' });
|
||||||
return new Response(null, { status: res.status });
|
if (res.status === 204) return new Response(null, { status: 204 });
|
||||||
|
return Response.json(await res.json(), { status: res.status });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user