Implement foreign key violation handling in EmployeesRepository delete methods
- Enhanced the `delete` and `bulkDelete` methods in `EmployeesRepository` to handle foreign key violations by throwing a `ConflictException` with a descriptive message. - Added unit tests to verify that foreign key violations are correctly mapped to `ConflictException` and that unknown errors are rethrown as expected. - Refactored error handling in the `delete` methods to improve clarity and maintainability.
This commit is contained in:
@@ -220,6 +220,26 @@ describe('EmployeesRepository', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('delete maps foreign-key violations to ConflictException', async () => {
|
||||||
|
returning.mockRejectedValueOnce({ code: '23503' });
|
||||||
|
await expect(repository.delete('emp-1')).rejects.toMatchObject({
|
||||||
|
constructor: ConflictException,
|
||||||
|
message: 'Employee is referenced by other records',
|
||||||
|
});
|
||||||
|
|
||||||
|
returning.mockRejectedValueOnce({
|
||||||
|
cause: { code: '23503' },
|
||||||
|
});
|
||||||
|
await expect(repository.delete('emp-1')).rejects.toBeInstanceOf(
|
||||||
|
ConflictException,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('delete rethrows unknown errors', async () => {
|
||||||
|
returning.mockRejectedValueOnce(new Error('db down'));
|
||||||
|
await expect(repository.delete('emp-1')).rejects.toThrow('db down');
|
||||||
|
});
|
||||||
|
|
||||||
it('bulkUpdateStatus and bulkDelete return 0 for empty ids', async () => {
|
it('bulkUpdateStatus and bulkDelete return 0 for empty ids', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
repository.bulkUpdateStatus([], Status.create('active'), 'user-1'),
|
repository.bulkUpdateStatus([], Status.create('active'), 'user-1'),
|
||||||
@@ -240,6 +260,16 @@ describe('EmployeesRepository', () => {
|
|||||||
await expect(repository.bulkDelete(['emp-1'])).resolves.toBe(1);
|
await expect(repository.bulkDelete(['emp-1'])).resolves.toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('bulkDelete maps foreign-key violations to ConflictException', async () => {
|
||||||
|
returning.mockRejectedValueOnce({
|
||||||
|
cause: { code: '23503' },
|
||||||
|
});
|
||||||
|
await expect(repository.bulkDelete(['emp-1'])).rejects.toMatchObject({
|
||||||
|
constructor: ConflictException,
|
||||||
|
message: 'Employee is referenced by other records',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('extendListQuery is a passthrough hook', () => {
|
it('extendListQuery is a passthrough hook', () => {
|
||||||
const qb = { join: true };
|
const qb = { join: true };
|
||||||
expect(repository.extendListQuery(qb, { limit: 10, offset: 0 })).toBe(qb);
|
expect(repository.extendListQuery(qb, { limit: 10, offset: 0 })).toBe(qb);
|
||||||
|
|||||||
@@ -221,6 +221,7 @@ export class EmployeesRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string): Promise<void> {
|
async delete(id: string): Promise<void> {
|
||||||
|
try {
|
||||||
const deleted = await this.db
|
const deleted = await this.db
|
||||||
.delete(employees)
|
.delete(employees)
|
||||||
.where(eq(employees.id, id))
|
.where(eq(employees.id, id))
|
||||||
@@ -228,17 +229,24 @@ export class EmployeesRepository {
|
|||||||
if (deleted.length === 0) {
|
if (deleted.length === 0) {
|
||||||
throw new NotFoundException('Employee not found');
|
throw new NotFoundException('Employee not found');
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.rethrowForeignKeyViolation(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async bulkDelete(ids: string[]): Promise<number> {
|
async bulkDelete(ids: string[]): Promise<number> {
|
||||||
if (ids.length === 0) {
|
if (ids.length === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
const deleted = await this.db
|
const deleted = await this.db
|
||||||
.delete(employees)
|
.delete(employees)
|
||||||
.where(inArray(employees.id, ids))
|
.where(inArray(employees.id, ids))
|
||||||
.returning({ id: employees.id });
|
.returning({ id: employees.id });
|
||||||
return deleted.length;
|
return deleted.length;
|
||||||
|
} catch (error) {
|
||||||
|
this.rethrowForeignKeyViolation(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildListWhere(filters: ListEmployeesFilters): SQL | undefined {
|
private buildListWhere(filters: ListEmployeesFilters): SQL | undefined {
|
||||||
@@ -363,6 +371,14 @@ export class EmployeesRepository {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private rethrowForeignKeyViolation(error: unknown): never {
|
||||||
|
const err = this.unwrapDbError(error);
|
||||||
|
if (err.code === '23503') {
|
||||||
|
throw new ConflictException('Employee is referenced by other records');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
private unwrapDbError(error: unknown): {
|
private unwrapDbError(error: unknown): {
|
||||||
code?: string;
|
code?: string;
|
||||||
constraint?: string;
|
constraint?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user