zibri 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/assets/assets.svg +1 -0
- package/assets/favicon-16x16.png +0 -0
- package/assets/favicon-32x32.png +0 -0
- package/assets/favicon-96x96.png +0 -0
- package/assets/favicon.ico +0 -0
- package/assets/logo-original.jpg +0 -0
- package/assets/logo.jpg +0 -0
- package/assets/swagger.png +0 -0
- package/cspell.words.txt +4 -0
- package/eslint.config.mjs +5 -0
- package/jest.config.mjs +25 -0
- package/package.json +50 -0
- package/sandbox/assets/assets.svg +1 -0
- package/sandbox/assets/favicon.ico +0 -0
- package/sandbox/assets/favicon.png +0 -0
- package/sandbox/assets/logo.jpg +0 -0
- package/sandbox/assets/open-api/custom.css +65 -0
- package/sandbox/assets/open-api/swagger-ui-bundle.js +2 -0
- package/sandbox/assets/open-api/swagger-ui-standalone-preset.js +2 -0
- package/sandbox/assets/open-api/swagger-ui.css +3 -0
- package/sandbox/assets/swagger.png +0 -0
- package/sandbox/assets/template/assets.hbs +51 -0
- package/sandbox/assets/template/error.hbs +41 -0
- package/sandbox/assets/template/index.hbs +34 -0
- package/sandbox/assets/template/style.css +76 -0
- package/sandbox/package-lock.json +1923 -0
- package/sandbox/package.json +26 -0
- package/sandbox/src/controllers/test.controller.ts +77 -0
- package/sandbox/src/index.ts +11 -0
- package/sandbox/src/services/dep.service.ts +9 -0
- package/sandbox/src/services/test.service.ts +12 -0
- package/sandbox/tsconfig.json +16 -0
- package/sandbox/webpack.config.js +71 -0
- package/src/application-options.model.ts +10 -0
- package/src/application.ts +73 -0
- package/src/assets/asset-service.interface.ts +9 -0
- package/src/assets/asset.service.ts +171 -0
- package/src/assets/index.ts +2 -0
- package/src/di/decorators/index.ts +2 -0
- package/src/di/decorators/inject.decorator.ts +13 -0
- package/src/di/decorators/injectable.decorator.ts +17 -0
- package/src/di/default/index.ts +1 -0
- package/src/di/default/zibri-di-providers.default.ts +37 -0
- package/src/di/default/zibri-di-tokens.default.ts +12 -0
- package/src/di/di-container.ts +81 -0
- package/src/di/errors/get-dependency-stack-trace.function.ts +11 -0
- package/src/di/errors/index.ts +2 -0
- package/src/di/errors/no-provider.error.ts +36 -0
- package/src/di/index.ts +4 -0
- package/src/di/inject.function.ts +7 -0
- package/src/di/models/di-provider.model.ts +8 -0
- package/src/di/models/di-token.model.ts +3 -0
- package/src/di/models/index.ts +2 -0
- package/src/di/register.function.ts +11 -0
- package/src/encapsulation/index.ts +4 -0
- package/src/encapsulation/is-date.function.ts +10 -0
- package/src/encapsulation/is-numeric.function.ts +9 -0
- package/src/encapsulation/metadata-injection-keys.enum.ts +14 -0
- package/src/encapsulation/metadata.utilities.ts +96 -0
- package/src/encapsulation/reflect.utilities.ts +51 -0
- package/src/entity/decorators/index.ts +1 -0
- package/src/entity/decorators/property.decorator.ts +48 -0
- package/src/entity/index.ts +1 -0
- package/src/error-handling/error-handler.model.ts +3 -0
- package/src/error-handling/error-handler.ts +72 -0
- package/src/error-handling/errors/bad-request.error.ts +9 -0
- package/src/error-handling/errors/http.error.ts +20 -0
- package/src/error-handling/errors/index.ts +6 -0
- package/src/error-handling/errors/internal-server.error.ts +9 -0
- package/src/error-handling/errors/not-found.error.ts +9 -0
- package/src/error-handling/errors/unmatched-route.error.ts +14 -0
- package/src/error-handling/errors/validation.error.ts +24 -0
- package/src/error-handling/index.ts +3 -0
- package/src/error-handling/is-error.function.ts +3 -0
- package/src/global/global-registry.ts +85 -0
- package/src/global/index.ts +1 -0
- package/src/http/http-method.enum.ts +8 -0
- package/src/http/http-status.enum.ts +6 -0
- package/src/http/index.ts +4 -0
- package/src/http/known-header.type.ts +25 -0
- package/src/http/mime-type.enum.ts +8 -0
- package/src/index.ts +16 -0
- package/src/jest.setup.ts +2 -0
- package/src/logging/index.ts +2 -0
- package/src/logging/logger.interface.ts +8 -0
- package/src/logging/logger.ts +87 -0
- package/src/open-api/index.ts +3 -0
- package/src/open-api/open-api-service.interface.ts +10 -0
- package/src/open-api/open-api.model.ts +13 -0
- package/src/open-api/open-api.service.ts +334 -0
- package/src/parsing/body-parser.interface.ts +8 -0
- package/src/parsing/decorators/body-parser.decorator.ts +17 -0
- package/src/parsing/decorators/index.ts +1 -0
- package/src/parsing/functions/index.ts +12 -0
- package/src/parsing/functions/parse-array-query-param.function.ts +15 -0
- package/src/parsing/functions/parse-array-query-param.test.ts +46 -0
- package/src/parsing/functions/parse-boolean-header-param.function.ts +15 -0
- package/src/parsing/functions/parse-boolean-header-param.test.ts +33 -0
- package/src/parsing/functions/parse-boolean-query-param.function.ts +15 -0
- package/src/parsing/functions/parse-boolean-query-param.test.ts +33 -0
- package/src/parsing/functions/parse-date-header-param.function.ts +8 -0
- package/src/parsing/functions/parse-date-header-param.test.ts +32 -0
- package/src/parsing/functions/parse-date-query-param.function.ts +8 -0
- package/src/parsing/functions/parse-date-query-param.test.ts +32 -0
- package/src/parsing/functions/parse-number-header-param.function.ts +8 -0
- package/src/parsing/functions/parse-number-header-param.test.ts +30 -0
- package/src/parsing/functions/parse-number-path-param.function.ts +8 -0
- package/src/parsing/functions/parse-number-path-param.test.ts +30 -0
- package/src/parsing/functions/parse-number-query-param.function.ts +8 -0
- package/src/parsing/functions/parse-number-query-param.test.ts +34 -0
- package/src/parsing/functions/parse-object-query-param.function.ts +15 -0
- package/src/parsing/functions/parse-object-query-param.test.ts +69 -0
- package/src/parsing/functions/parse-string-header-param.function.ts +4 -0
- package/src/parsing/functions/parse-string-path-param.function.ts +4 -0
- package/src/parsing/functions/parse-string-query-param.function.ts +4 -0
- package/src/parsing/index.ts +5 -0
- package/src/parsing/json.body-parser.ts +34 -0
- package/src/parsing/parser.interface.ts +12 -0
- package/src/parsing/parser.ts +85 -0
- package/src/routing/controller-route-configuration.model.ts +9 -0
- package/src/routing/decorators/body.decorator.ts +28 -0
- package/src/routing/decorators/controller.decorator.ts +18 -0
- package/src/routing/decorators/create-http-decorator.function.ts +15 -0
- package/src/routing/decorators/delete.decorator.ts +7 -0
- package/src/routing/decorators/get.decorator.ts +7 -0
- package/src/routing/decorators/index.ts +7 -0
- package/src/routing/decorators/param.decorator.ts +112 -0
- package/src/routing/decorators/patch.decorator.ts +7 -0
- package/src/routing/decorators/post.decorator.ts +7 -0
- package/src/routing/index.ts +5 -0
- package/src/routing/missing-base-route.error.ts +9 -0
- package/src/routing/route-configuration.model.ts +10 -0
- package/src/routing/router.interface.ts +12 -0
- package/src/routing/router.ts +164 -0
- package/src/types/deep-partial.type.ts +10 -0
- package/src/types/index.ts +3 -0
- package/src/types/newable.model.ts +2 -0
- package/src/types/omit-strict.type.ts +4 -0
- package/src/validation/functions/index.ts +11 -0
- package/src/validation/functions/validate-boolean-header-param.function.ts +16 -0
- package/src/validation/functions/validate-boolean-query-param.function.ts +16 -0
- package/src/validation/functions/validate-date-header-param.function.ts +16 -0
- package/src/validation/functions/validate-date-query-param.function.ts +16 -0
- package/src/validation/functions/validate-number-header-param.function.ts +16 -0
- package/src/validation/functions/validate-number-path-param.function.ts +16 -0
- package/src/validation/functions/validate-number-property.function.ts +21 -0
- package/src/validation/functions/validate-number-query-param.function.ts +16 -0
- package/src/validation/functions/validate-string-header-param.function.ts +17 -0
- package/src/validation/functions/validate-string-path-param.function.ts +17 -0
- package/src/validation/functions/validate-string-property.function.ts +21 -0
- package/src/validation/functions/validate-string-query-param.function.ts +17 -0
- package/src/validation/index.ts +3 -0
- package/src/validation/validation-problem.model.ts +9 -0
- package/src/validation/validation-service.interface.ts +9 -0
- package/src/validation/validation.service.ts +295 -0
- package/tsconfig.json +17 -0
- package/tsup.config.ts +13 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { MetadataUtilities } from '../encapsulation';
|
|
2
|
+
import { ArrayPropertyMetadata, PropertyMetadata } from '../entity';
|
|
3
|
+
import { ValidationError } from '../error-handling';
|
|
4
|
+
import { ArrayQueryParamMetadata, HeaderParamMetadata, PathParamMetadata, QueryParamMetadata } from '../routing';
|
|
5
|
+
import { Newable } from '../types';
|
|
6
|
+
import { validateBooleanHeaderParam, validateBooleanQueryParam, validateDateHeaderParam, validateDateQueryParam, validateNumberPathParam, validateNumberProperty, validateNumberQueryParam, validateStringHeaderParam, validateStringPathParam, validateStringProperty, validateStringQueryParam } from './functions';
|
|
7
|
+
import { validateNumberHeaderParam } from './functions/validate-number-header-param.function';
|
|
8
|
+
import { IsRequiredValidationProblem, ValidationProblem } from './validation-problem.model';
|
|
9
|
+
import { ValidationServiceInterface } from './validation-service.interface';
|
|
10
|
+
|
|
11
|
+
type PathParamValidationFunction = (param: unknown, meta: PathParamMetadata) => ValidationProblem[];
|
|
12
|
+
|
|
13
|
+
type QueryParamValidationFunction = (param: unknown, meta: QueryParamMetadata, parentKey: string | undefined) => ValidationProblem[];
|
|
14
|
+
|
|
15
|
+
type HeaderParamValidationFunction = (param: unknown, meta: HeaderParamMetadata) => ValidationProblem[];
|
|
16
|
+
|
|
17
|
+
type PropertyValidationFunction = (
|
|
18
|
+
key: string,
|
|
19
|
+
property: unknown,
|
|
20
|
+
metadata: PropertyMetadata,
|
|
21
|
+
parentKey: string | undefined
|
|
22
|
+
) => ValidationProblem[];
|
|
23
|
+
|
|
24
|
+
export class ValidationService implements ValidationServiceInterface {
|
|
25
|
+
|
|
26
|
+
private readonly pathParamValidationFunctions: Record<PathParamMetadata['type'], PathParamValidationFunction> = {
|
|
27
|
+
string: validateStringPathParam,
|
|
28
|
+
number: validateNumberPathParam
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
private readonly queryParamValidationFunctions: Record<QueryParamMetadata['type'], QueryParamValidationFunction> = {
|
|
32
|
+
string: validateStringQueryParam,
|
|
33
|
+
number: validateNumberQueryParam,
|
|
34
|
+
boolean: validateBooleanQueryParam,
|
|
35
|
+
date: validateDateQueryParam,
|
|
36
|
+
object: this.validateObjectQueryParam.bind(this),
|
|
37
|
+
array: this.validateArrayQueryParam.bind(this)
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
private readonly headerParamValidationFunctions: Record<HeaderParamMetadata['type'], HeaderParamValidationFunction> = {
|
|
41
|
+
string: validateStringHeaderParam,
|
|
42
|
+
number: validateNumberHeaderParam,
|
|
43
|
+
boolean: validateBooleanHeaderParam,
|
|
44
|
+
date: validateDateHeaderParam
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
private readonly propertyValidationFunctions: Record<PropertyMetadata['type'], PropertyValidationFunction> = {
|
|
48
|
+
object: this.validateObjectProperty.bind(this),
|
|
49
|
+
array: this.validateArrayProperty.bind(this),
|
|
50
|
+
number: validateNumberProperty,
|
|
51
|
+
string: validateStringProperty
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
private validateObjectQueryParam(param: unknown, meta: QueryParamMetadata, parentKey: string | undefined): ValidationProblem[] {
|
|
55
|
+
const fullKey: string = parentKey ? `${parentKey}.${meta.name}` : meta.name;
|
|
56
|
+
if (meta.type !== 'object') {
|
|
57
|
+
throw new Error('Tried to do object based validation on a non object value.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (param == undefined && meta.required) {
|
|
61
|
+
return [new IsRequiredValidationProblem(fullKey)];
|
|
62
|
+
}
|
|
63
|
+
if (param == undefined && !meta.required) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
if (typeof param !== 'object') {
|
|
67
|
+
return [{ key: fullKey, message: 'should be an object' }];
|
|
68
|
+
}
|
|
69
|
+
return this.validateModel(param, meta.cls, fullKey);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private validateArrayQueryParam(param: unknown, meta: QueryParamMetadata, parentKey: string | undefined): ValidationProblem[] {
|
|
73
|
+
const fullKey: string = parentKey ? `${parentKey}.${meta.name}` : meta.name;
|
|
74
|
+
if (meta.type !== 'array') {
|
|
75
|
+
throw new Error('Tried to do array based validation on a non array value.');
|
|
76
|
+
}
|
|
77
|
+
if (param == undefined && meta.required) {
|
|
78
|
+
return [new IsRequiredValidationProblem(fullKey)];
|
|
79
|
+
}
|
|
80
|
+
if (param == undefined && !meta.required) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
if (!Array.isArray(param)) {
|
|
84
|
+
return [{ key: meta.name, message: 'should be an array' }];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const res: ValidationProblem[] = [];
|
|
88
|
+
for (let i: number = 0; i < param.length; i++) {
|
|
89
|
+
const item: unknown = param[i];
|
|
90
|
+
const m: QueryParamMetadata = this.getQueryArrayItemMetadata(i, meta);
|
|
91
|
+
const validate: QueryParamValidationFunction | undefined = this.queryParamValidationFunctions[m.type];
|
|
92
|
+
if (validate == undefined) {
|
|
93
|
+
throw new Error(`Unknown type for query parameter "${fullKey}.${m.name}": ${m.type}`);
|
|
94
|
+
}
|
|
95
|
+
const errors: ValidationProblem[] = validate(item, m, fullKey);
|
|
96
|
+
res.push(...errors);
|
|
97
|
+
}
|
|
98
|
+
return res;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
validateHeaderParam(param: unknown, meta: HeaderParamMetadata): void {
|
|
102
|
+
const validate: HeaderParamValidationFunction | undefined = this.headerParamValidationFunctions[meta.type];
|
|
103
|
+
if (validate == undefined) {
|
|
104
|
+
throw new Error(`Unknown type for header parameter "${meta.name}": ${meta.type}`);
|
|
105
|
+
}
|
|
106
|
+
const res: ValidationProblem[] = validate(param, meta);
|
|
107
|
+
if (res.length) {
|
|
108
|
+
throw new ValidationError('header', res);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
validatePathParam(param: unknown, meta: PathParamMetadata): void {
|
|
113
|
+
const validate: PathParamValidationFunction | undefined = this.pathParamValidationFunctions[meta.type];
|
|
114
|
+
if (validate == undefined) {
|
|
115
|
+
throw new Error(`Unknown type for path parameter "${meta.name}": ${meta.type}`);
|
|
116
|
+
}
|
|
117
|
+
const res: ValidationProblem[] = validate(param, meta);
|
|
118
|
+
if (res.length) {
|
|
119
|
+
throw new ValidationError('path', res);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
validateQueryParam(param: unknown, meta: QueryParamMetadata): void {
|
|
124
|
+
const validate: QueryParamValidationFunction | undefined = this.queryParamValidationFunctions[meta.type];
|
|
125
|
+
if (validate == undefined) {
|
|
126
|
+
throw new Error(`Unknown type for query parameter "${meta.name}": ${meta.type}`);
|
|
127
|
+
}
|
|
128
|
+
const res: ValidationProblem[] = validate(param, meta, undefined);
|
|
129
|
+
if (res.length) {
|
|
130
|
+
throw new ValidationError('query', res);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
validateRequestBody(model: unknown, cls: Newable<unknown>): void {
|
|
135
|
+
const res: ValidationProblem[] = this.validateModel(model, cls, undefined);
|
|
136
|
+
if (res.length) {
|
|
137
|
+
throw new ValidationError('body', res);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private validateModel(model: unknown, cls: Newable<unknown>, parentKey: string | undefined): ValidationProblem[] {
|
|
142
|
+
const modelProperties: Record<string, PropertyMetadata> = MetadataUtilities.getModelProperties(cls);
|
|
143
|
+
|
|
144
|
+
const keysOfBody: string[] = Object.keys(model as Record<string, unknown>);
|
|
145
|
+
const keysOfModel: string[] = Object.keys(modelProperties);
|
|
146
|
+
const unknownKeys: string[] = keysOfBody.filter(k => !keysOfModel.includes(k));
|
|
147
|
+
const res: ValidationProblem[] = [];
|
|
148
|
+
for (const key of unknownKeys) {
|
|
149
|
+
const fullKey: string = parentKey ? `${parentKey}.${key}` : key;
|
|
150
|
+
res.push({ key: fullKey, message: 'this key is unknown' });
|
|
151
|
+
}
|
|
152
|
+
for (const [propertyKey, metadata] of Object.entries(modelProperties)) {
|
|
153
|
+
const property: unknown = (model as Record<string, unknown>)[propertyKey];
|
|
154
|
+
const errors: ValidationProblem[] = this.validateProperty(propertyKey, property, metadata, parentKey);
|
|
155
|
+
res.push(...errors);
|
|
156
|
+
}
|
|
157
|
+
return res;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private validateProperty(
|
|
161
|
+
key: string,
|
|
162
|
+
property: unknown,
|
|
163
|
+
metadata: PropertyMetadata,
|
|
164
|
+
parentKey: string | undefined
|
|
165
|
+
): ValidationProblem[] {
|
|
166
|
+
const fullKey: string = parentKey ? `${parentKey}.${key}` : key;
|
|
167
|
+
|
|
168
|
+
const validate: PropertyValidationFunction | undefined = this.propertyValidationFunctions[metadata.type];
|
|
169
|
+
if (validate == undefined) {
|
|
170
|
+
throw new Error(`Unknown type for property "${fullKey}": ${metadata.type}`);
|
|
171
|
+
}
|
|
172
|
+
const res: ValidationProblem[] = validate(key, property, metadata, parentKey);
|
|
173
|
+
return res;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private validateArrayProperty(
|
|
177
|
+
key: string,
|
|
178
|
+
property: unknown,
|
|
179
|
+
metadata: PropertyMetadata,
|
|
180
|
+
parentKey: string | undefined
|
|
181
|
+
): ValidationProblem[] {
|
|
182
|
+
if (metadata.type !== 'array') {
|
|
183
|
+
throw new Error('Tried to do array based validation on a non array value.');
|
|
184
|
+
}
|
|
185
|
+
const fullKey: string = parentKey ? `${parentKey}.${key}` : key;
|
|
186
|
+
|
|
187
|
+
if (property == undefined && metadata.required) {
|
|
188
|
+
return [new IsRequiredValidationProblem(fullKey)];
|
|
189
|
+
}
|
|
190
|
+
if (property == undefined && !metadata.required) {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
if (!Array.isArray(property)) {
|
|
194
|
+
return [{ key: fullKey, message: 'should be an array' }];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const res: ValidationProblem[] = [];
|
|
198
|
+
for (let i: number = 0; i < property.length; i++) {
|
|
199
|
+
const item: unknown = property[i];
|
|
200
|
+
const m: PropertyMetadata = this.getPropertyArrayItemMetadata(metadata);
|
|
201
|
+
const errors: ValidationProblem[] = this.validateProperty(String(i), item, m, key);
|
|
202
|
+
res.push(...errors);
|
|
203
|
+
}
|
|
204
|
+
return res;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private getPropertyArrayItemMetadata(metadata: ArrayPropertyMetadata): PropertyMetadata {
|
|
208
|
+
switch (metadata.itemType) {
|
|
209
|
+
case 'number': {
|
|
210
|
+
return {
|
|
211
|
+
type: 'number',
|
|
212
|
+
required: true
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
case 'string': {
|
|
216
|
+
return {
|
|
217
|
+
type: 'string',
|
|
218
|
+
required: true
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
default: {
|
|
222
|
+
return {
|
|
223
|
+
type: 'object',
|
|
224
|
+
cls: metadata.itemType,
|
|
225
|
+
required: true
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private getQueryArrayItemMetadata(index: number, metadata: ArrayQueryParamMetadata): QueryParamMetadata {
|
|
232
|
+
switch (metadata.itemType) {
|
|
233
|
+
case 'date':
|
|
234
|
+
case 'string':
|
|
235
|
+
case 'boolean':
|
|
236
|
+
case 'number': {
|
|
237
|
+
return {
|
|
238
|
+
name: String(index),
|
|
239
|
+
type: metadata.itemType,
|
|
240
|
+
required: true
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
default: {
|
|
244
|
+
return {
|
|
245
|
+
name: String(index),
|
|
246
|
+
type: 'object',
|
|
247
|
+
cls: metadata.itemType,
|
|
248
|
+
required: true
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private validateObjectProperty(
|
|
255
|
+
key: string,
|
|
256
|
+
property: unknown,
|
|
257
|
+
metadata: PropertyMetadata,
|
|
258
|
+
parentKey: string | undefined
|
|
259
|
+
): ValidationProblem[] {
|
|
260
|
+
if (metadata.type !== 'object') {
|
|
261
|
+
throw new Error('Tried to do object based validation on a non object value.');
|
|
262
|
+
}
|
|
263
|
+
const fullKey: string = parentKey ? `${parentKey}.${key}` : key;
|
|
264
|
+
|
|
265
|
+
if (property == undefined && metadata.required) {
|
|
266
|
+
return [new IsRequiredValidationProblem(fullKey)];
|
|
267
|
+
}
|
|
268
|
+
if (property == undefined && !metadata.required) {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
if (typeof property !== 'object') {
|
|
272
|
+
return [{ key: fullKey, message: 'should be an object' }];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const objectProperties: Record<string, PropertyMetadata> = MetadataUtilities.getModelProperties(metadata.cls);
|
|
276
|
+
const keysOfBody: string[] = Object.keys(property as Record<string, unknown>);
|
|
277
|
+
const keysOfModel: string[] = Object.keys(objectProperties);
|
|
278
|
+
const unknownKeys: string[] = keysOfBody.filter(k => !keysOfModel.includes(k));
|
|
279
|
+
|
|
280
|
+
const res: ValidationProblem[] = [];
|
|
281
|
+
for (const key of unknownKeys) {
|
|
282
|
+
res.push({ key, message: 'this key is unknown' });
|
|
283
|
+
}
|
|
284
|
+
if (res.length) {
|
|
285
|
+
throw new ValidationError('body', res);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
for (const [propertyKey, m] of Object.entries(objectProperties)) {
|
|
289
|
+
const childProperty: unknown = (property as Record<string, unknown>)[propertyKey];
|
|
290
|
+
const errors: ValidationProblem[] = this.validateProperty(propertyKey, childProperty, m, key);
|
|
291
|
+
res.push(...errors);
|
|
292
|
+
}
|
|
293
|
+
return res;
|
|
294
|
+
}
|
|
295
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Node",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"experimentalDecorators": true,
|
|
12
|
+
"emitDecoratorMetadata": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src"]
|
|
17
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ['src/index.ts'],
|
|
5
|
+
outDir: 'dist',
|
|
6
|
+
target: 'node20',
|
|
7
|
+
format: ['esm', 'cjs'],
|
|
8
|
+
ignoreWatch: 'sandbox',
|
|
9
|
+
splitting: false,
|
|
10
|
+
sourcemap: true,
|
|
11
|
+
clean: true,
|
|
12
|
+
dts: true
|
|
13
|
+
});
|