Skip to content

Commit c1cafc2

Browse files
committed
fix(json-api-nestjs-microorm): add format for mikroorm error
1 parent 0ace3ce commit c1cafc2

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

libs/json-api/json-api-nestjs-microorm/src/lib/micro-orm-json-api.module.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
PrepareParams,
55
MODULE_OPTIONS_TOKEN,
66
NestProvider,
7+
ErrorFormatService,
78
} from '@klerick/json-api-nestjs';
89
import { MicroOrmParam } from './type';
910

@@ -18,7 +19,7 @@ import {
1819
CheckRelationNameFactory,
1920
FindOneRowEntityFactory,
2021
} from './factory';
21-
import { MicroOrmUtilService } from './service/micro-orm-util.service';
22+
import { MicroOrmUtilService, MikroOrmFormatErrorService } from './service';
2223

2324
export class MicroOrmJsonApiModule {
2425
static forRoot(options: PrepareParams<MicroOrmParam>): DynamicModule {
@@ -40,6 +41,10 @@ export class MicroOrmJsonApiModule {
4041
CurrentEntityMetadata(),
4142
RunInTransactionFactory(),
4243
EntityPropsMap(options.entities),
44+
{
45+
provide: ErrorFormatService,
46+
useClass: MikroOrmFormatErrorService,
47+
},
4348
];
4449

4550
const currentImport = [
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
export * from './microorm-service';
2+
export * from './mikro-orm-format.error.service';
3+
export * from './micro-orm-util.service';
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import {
2+
ErrorFormatService,
3+
ValidateQueryError,
4+
PrepareParams,
5+
MODULE_OPTIONS_TOKEN,
6+
} from '@klerick/json-api-nestjs';
7+
import {
8+
BadRequestException,
9+
ConflictException,
10+
HttpException,
11+
Inject,
12+
HttpExceptionOptions,
13+
} from '@nestjs/common';
14+
15+
import {
16+
DriverException,
17+
EntityManager,
18+
UniqueConstraintViolationException,
19+
ValidationError,
20+
} from '@mikro-orm/core';
21+
22+
import { CURRENT_ENTITY_MANAGER_TOKEN } from '../constants';
23+
24+
const duplicateItems = (
25+
errorText: string,
26+
options: HttpExceptionOptions,
27+
detail?: string
28+
) => {
29+
errorText = 'Duplicate value';
30+
if (detail) {
31+
const matches = detail.match(/(?<=\().+?(?=\))/gm);
32+
if (matches) {
33+
errorText = `Duplicate value in the "${matches[0]}"`;
34+
}
35+
}
36+
37+
const error: ValidateQueryError = {
38+
code: 'invalid_arguments',
39+
message: errorText,
40+
path: ['data', 'attributes'],
41+
};
42+
43+
return new ConflictException([error], options);
44+
};
45+
46+
export class MikroOrmFormatErrorService extends ErrorFormatService {
47+
@Inject(CURRENT_ENTITY_MANAGER_TOKEN) em!: EntityManager;
48+
@Inject(MODULE_OPTIONS_TOKEN)
49+
private mainConfig!: PrepareParams;
50+
51+
private errorMsg = 'Internal Server Error';
52+
53+
override formatError(error: unknown): HttpException {
54+
try {
55+
if (error instanceof ValidationError) {
56+
return this.formatValidationError(error);
57+
}
58+
if (error instanceof DriverException) {
59+
return this.prepareDataBaseError(error);
60+
}
61+
return super.formatError(error);
62+
} catch (error) {
63+
return super.formatError(error);
64+
}
65+
}
66+
67+
private formatValidationError(error: ValidationError) {
68+
const { message } = error;
69+
70+
const entity = error.getEntity();
71+
const errorObject: ValidateQueryError = {
72+
code: 'invalid_arguments',
73+
message: message.split('\n').at(0) || message,
74+
path: [],
75+
};
76+
if (entity) {
77+
errorObject['path'] = ['data', 'attributes'];
78+
}
79+
80+
const descriptionOrOptions: HttpExceptionOptions = this.mainConfig.options
81+
.debug
82+
? { cause: error }
83+
: {};
84+
85+
return new BadRequestException([errorObject], descriptionOrOptions);
86+
}
87+
88+
private prepareDataBaseError(error: DriverException) {
89+
if (
90+
!this.em
91+
.getPlatform()
92+
.getConfig()
93+
.getDriver()
94+
.constructor.name.startsWith('Postgre')
95+
) {
96+
return super.formatError(error);
97+
}
98+
99+
const { errorCode, errorMsg, detail, table } = {
100+
errorCode: error.code,
101+
errorMsg: error.message,
102+
detail: Reflect.get(error, 'detail') as string,
103+
table: Reflect.get(error, 'table') as string,
104+
};
105+
const descriptionOrOptions: HttpExceptionOptions = this.mainConfig.options
106+
.debug
107+
? { cause: error }
108+
: {};
109+
110+
switch (error.constructor) {
111+
case UniqueConstraintViolationException:
112+
return duplicateItems(errorMsg, descriptionOrOptions, detail);
113+
default:
114+
return super.formatError(error);
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)