-
Notifications
You must be signed in to change notification settings - Fork 114
/
i18n-validation-exception.filter.ts
108 lines (98 loc) · 3.12 KB
/
i18n-validation-exception.filter.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import {
ArgumentsHost,
Catch,
ExceptionFilter,
ValidationError,
} from '@nestjs/common';
import iterate from 'iterare';
import { I18nContext } from '../i18n.context';
import {
I18nValidationError,
I18nValidationExceptionFilterDetailedErrorsOption,
I18nValidationExceptionFilterErrorFormatterOption,
I18nValidationException,
} from '../interfaces';
import { mapChildrenToValidationErrors, formatI18nErrors } from '../utils';
type I18nValidationExceptionFilterOptions =
| I18nValidationExceptionFilterDetailedErrorsOption
| I18nValidationExceptionFilterErrorFormatterOption;
@Catch(I18nValidationException)
export class I18nValidationExceptionFilter implements ExceptionFilter {
constructor(
private readonly options: I18nValidationExceptionFilterOptions = {
detailedErrors: true,
},
) {}
catch(exception: I18nValidationException, host: ArgumentsHost) {
const i18n = I18nContext.current();
const errors = formatI18nErrors(exception.errors ?? [], i18n.service, {
lang: i18n.lang,
});
const normalizedErrors = this.normalizeValidationErrors(errors);
switch (host.getType() as string) {
case 'http':
const response = host.switchToHttp().getResponse();
const responseBody = this.buildResponseBody(
host,
exception,
normalizedErrors,
);
response
.status(this.options.errorHttpStatusCode || exception.getStatus())
.send(responseBody);
break;
case 'graphql':
exception.errors = normalizedErrors as I18nValidationError[];
return exception;
}
}
private isWithErrorFormatter(
options: I18nValidationExceptionFilterOptions,
): options is I18nValidationExceptionFilterErrorFormatterOption {
return 'errorFormatter' in options;
}
protected normalizeValidationErrors(
validationErrors: ValidationError[],
): string[] | I18nValidationError[] | object {
if (
this.isWithErrorFormatter(this.options) &&
!('detailedErrors' in this.options)
)
return this.options.errorFormatter(validationErrors);
if (
!this.isWithErrorFormatter(this.options) &&
!this.options.detailedErrors
)
return this.flattenValidationErrors(validationErrors);
return validationErrors;
}
protected flattenValidationErrors(
validationErrors: ValidationError[],
): string[] {
return iterate(validationErrors)
.map((error) => mapChildrenToValidationErrors(error))
.flatten()
.filter((item) => !!item.constraints)
.map((item) => Object.values(item.constraints))
.flatten()
.toArray();
}
protected buildResponseBody(
host: ArgumentsHost,
exc: I18nValidationException,
error: string[] | I18nValidationError[] | object,
) {
if ('responseBodyFormatter' in this.options) {
return this.options.responseBodyFormatter(host, exc, error);
} else {
return {
statusCode:
this.options.errorHttpStatusCode === undefined
? exc.getStatus()
: this.options.errorHttpStatusCode,
message: error,
error: exc.getResponse(),
};
}
}
}