feat(backend): enforce unique names and surface duplicate errors
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConflictException, Injectable } from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { CreatePieceDto, UpdatePieceDto } from '../shared/dto/piece.dto';
|
||||
@@ -54,20 +54,24 @@ export class PiecesService {
|
||||
}
|
||||
|
||||
async create(createPieceDto: CreatePieceDto) {
|
||||
const created = await this.prisma.piece.create({
|
||||
data: this.buildCreateInput(createPieceDto),
|
||||
include: PIECE_WITH_RELATIONS_INCLUDE,
|
||||
});
|
||||
try {
|
||||
const created = await this.prisma.piece.create({
|
||||
data: this.buildCreateInput(createPieceDto),
|
||||
include: PIECE_WITH_RELATIONS_INCLUDE,
|
||||
});
|
||||
|
||||
await this.applyPieceSkeleton({
|
||||
pieceId: created.id,
|
||||
typePiece: created.typePiece as PieceTypeWithSkeleton | null,
|
||||
});
|
||||
await this.applyPieceSkeleton({
|
||||
pieceId: created.id,
|
||||
typePiece: created.typePiece as PieceTypeWithSkeleton | null,
|
||||
});
|
||||
|
||||
return this.prisma.piece.findUnique({
|
||||
where: { id: created.id },
|
||||
include: PIECE_WITH_RELATIONS_INCLUDE,
|
||||
});
|
||||
return this.prisma.piece.findUnique({
|
||||
where: { id: created.id },
|
||||
include: PIECE_WITH_RELATIONS_INCLUDE,
|
||||
});
|
||||
} catch (error) {
|
||||
this.handlePrismaError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
@@ -111,24 +115,51 @@ export class PiecesService {
|
||||
: { disconnect: true };
|
||||
}
|
||||
|
||||
const updated = await this.prisma.piece.update({
|
||||
where: { id },
|
||||
data,
|
||||
include: PIECE_WITH_RELATIONS_INCLUDE,
|
||||
});
|
||||
try {
|
||||
const updated = await this.prisma.piece.update({
|
||||
where: { id },
|
||||
data,
|
||||
include: PIECE_WITH_RELATIONS_INCLUDE,
|
||||
});
|
||||
|
||||
await this.applyPieceSkeleton({
|
||||
pieceId: updated.id,
|
||||
typePiece: updated.typePiece as PieceTypeWithSkeleton | null,
|
||||
});
|
||||
await this.applyPieceSkeleton({
|
||||
pieceId: updated.id,
|
||||
typePiece: updated.typePiece as PieceTypeWithSkeleton | null,
|
||||
});
|
||||
|
||||
return this.prisma.piece.findUnique({
|
||||
where: { id: updated.id },
|
||||
include: PIECE_WITH_RELATIONS_INCLUDE,
|
||||
});
|
||||
return this.prisma.piece.findUnique({
|
||||
where: { id: updated.id },
|
||||
include: PIECE_WITH_RELATIONS_INCLUDE,
|
||||
});
|
||||
} catch (error) {
|
||||
this.handlePrismaError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
const [machineLinksCount, documentsCount, customFieldValuesCount] =
|
||||
await Promise.all([
|
||||
this.prisma.machinePieceLink.count({
|
||||
where: { pieceId: id },
|
||||
}),
|
||||
this.prisma.document.count({
|
||||
where: { pieceId: id },
|
||||
}),
|
||||
this.prisma.customFieldValue.count({
|
||||
where: { pieceId: id },
|
||||
}),
|
||||
]);
|
||||
|
||||
if (
|
||||
machineLinksCount > 0 ||
|
||||
documentsCount > 0 ||
|
||||
customFieldValuesCount > 0
|
||||
) {
|
||||
throw new ConflictException(
|
||||
'Impossible de supprimer cette pièce car elle possède des éléments liés.',
|
||||
);
|
||||
}
|
||||
|
||||
return this.prisma.piece.delete({
|
||||
where: { id },
|
||||
});
|
||||
@@ -343,6 +374,27 @@ export class PiecesService {
|
||||
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
private handlePrismaError(error: unknown): never {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (error.code === 'P2002' && this.isNameConstraint(error)) {
|
||||
throw new ConflictException('Une pièce avec ce nom existe déjà.');
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
private isNameConstraint(error: Prisma.PrismaClientKnownRequestError) {
|
||||
const { target } = error.meta ?? {};
|
||||
if (Array.isArray(target)) {
|
||||
return target.includes('name');
|
||||
}
|
||||
if (typeof target === 'string') {
|
||||
return target === 'name';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
type PieceTypeWithSkeleton = Prisma.ModelTypeGetPayload<{
|
||||
|
||||
Reference in New Issue
Block a user