@smartsoft001/models 2.45.0 → 2.47.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/.eslintrc.json +2 -9
- package/README.md +23 -6
- package/package.json +2 -2
- package/src/index.ts +1 -1
- package/src/lib/decorators/field/field.decorator.ts +49 -47
- package/src/lib/decorators/model/model.decorator.ts +5 -5
- package/src/lib/interfaces.ts +103 -101
- package/src/lib/utils.spec.ts +130 -116
- package/src/lib/utils.ts +26 -26
package/.eslintrc.json
CHANGED
|
@@ -15,16 +15,9 @@
|
|
|
15
15
|
"rules": {}
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
|
-
"files":
|
|
18
|
+
"files": "package.json",
|
|
19
19
|
"parser": "jsonc-eslint-parser",
|
|
20
|
-
"rules": {
|
|
21
|
-
"@nx/dependency-checks": [
|
|
22
|
-
"error",
|
|
23
|
-
{
|
|
24
|
-
"ignoredFiles": ["{projectRoot}/esbuild.config.{js,ts,mjs,mts}"]
|
|
25
|
-
}
|
|
26
|
-
]
|
|
27
|
-
}
|
|
20
|
+
"rules": {}
|
|
28
21
|
}
|
|
29
22
|
]
|
|
30
23
|
}
|
package/README.md
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
## Decorators
|
|
2
|
+
### @Model Decorator
|
|
3
|
+
Used to annotate a class as a "model," adding metadata and a custom toJSON method. This method serializes the instance
|
|
4
|
+
into JSON format, including fields marked with specific metadata. Associates the model with metadata (IModelOptions)
|
|
5
|
+
using reflection, allowing for dynamic handling of models at runtime.
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
### @Field Decorator
|
|
8
|
+
Used to annotate properties of a model class, providing additional metadata (IFieldOptions) such as field type,
|
|
9
|
+
requirements, and custom behavior for serialization or validation. Defines how properties should be accessed, mutated,
|
|
10
|
+
and serialized, including special handling for arrays and specific types.
|
|
4
11
|
|
|
5
|
-
##
|
|
12
|
+
## Utility Functions
|
|
13
|
+
### Metadata Retrieval
|
|
14
|
+
Functions like **getModelFieldKeys**, **getModelFieldOptions**, **getModelFieldsWithOptions**, and **getModelOptions**
|
|
15
|
+
are used to retrieve metadata about models and their fields. This enables dynamic inspection and manipulation of models.
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
### Model Validation
|
|
18
|
+
**getInvalidFields** - Checks for fields that have invalid values based on specified rules
|
|
19
|
+
(such as being required during "create" or "update" operations) and permissions. This is useful for form validation or
|
|
20
|
+
API input validation.
|
|
8
21
|
|
|
9
|
-
|
|
22
|
+
### Model Casting
|
|
23
|
+
**castModel** - Adjusts an instance of a model by removing fields that do not conform to the specified mode
|
|
24
|
+
("create", "update") or the provided permissions. This ensures the instance is valid and adheres to the constraints
|
|
25
|
+
defined by the model's metadata.
|
|
10
26
|
|
|
11
|
-
|
|
27
|
+
### Model Check
|
|
28
|
+
**isModel** - Determines if an object is a model decorated with the @Model decorator.
|
package/package.json
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"reflect-metadata": "^0.2.1",
|
|
6
6
|
"rxjs": "^7.8.1",
|
|
7
|
-
"@smartsoft001/utils": "^2.
|
|
7
|
+
"@smartsoft001/utils": "^2.47.0"
|
|
8
8
|
},
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.47.0",
|
|
10
10
|
"main": "./src/index.js",
|
|
11
11
|
"typings": "./src/index.d.ts"
|
|
12
12
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,55 +1,57 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
|
|
3
|
-
import {ObjectService} from
|
|
3
|
+
import { ObjectService } from '@smartsoft001/utils';
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import { FieldType, IFieldOptions } from '../../interfaces';
|
|
6
|
+
import * as symbols from '../../symbols';
|
|
7
7
|
|
|
8
8
|
export const Field = FieldDecorator;
|
|
9
9
|
export function FieldDecorator(options?: IFieldOptions) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
options.type = FieldType.text;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (options.classType) {
|
|
27
|
-
(target as any)['_' + key] = (target as any)[key];
|
|
28
|
-
delete (target as any)[key];
|
|
29
|
-
|
|
30
|
-
Object.defineProperty(target, key, {
|
|
31
|
-
get: function () {
|
|
32
|
-
if (!this['_' + key] && options?.type === FieldType.array) {
|
|
33
|
-
this['_' + key] = [];
|
|
34
|
-
}
|
|
35
|
-
return this['_' + key];
|
|
36
|
-
},
|
|
37
|
-
set: function (v: any) {
|
|
38
|
-
this['_' + key] = options?.type === FieldType.array && v ?
|
|
39
|
-
v.map((i: any) => ObjectService.createByType(i, options?.classType))
|
|
40
|
-
: ObjectService.createByType(v, options?.classType);
|
|
41
|
-
},
|
|
42
|
-
enumerable: true,
|
|
43
|
-
configurable: true
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
if (!(target as any).constructor['__properties']) {
|
|
47
|
-
(target as any).constructor['__properties'] = {};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
(target as any).constructor['__properties'][key] = true;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
Reflect.defineMetadata(symbols.SYMBOL_FIELD, options, target as any, key);
|
|
10
|
+
return <T>(target: T, key: string) => {
|
|
11
|
+
options = options ? { ...options } : {};
|
|
12
|
+
|
|
13
|
+
if (!(target as any).constructor['__fields']) {
|
|
14
|
+
(target as any).constructor['__fields'] = {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
(target as any).constructor['__fields'][key] = true;
|
|
18
|
+
|
|
19
|
+
if (!options.type && key === 'password') {
|
|
20
|
+
options.type = FieldType.password;
|
|
21
|
+
} else if (!options.type) {
|
|
22
|
+
options.type = FieldType.text;
|
|
54
23
|
}
|
|
24
|
+
|
|
25
|
+
if (options.classType) {
|
|
26
|
+
(target as any)['_' + key] = (target as any)[key];
|
|
27
|
+
delete (target as any)[key];
|
|
28
|
+
|
|
29
|
+
Object.defineProperty(target, key, {
|
|
30
|
+
get: function () {
|
|
31
|
+
if (!this['_' + key] && options?.type === FieldType.array) {
|
|
32
|
+
this['_' + key] = [];
|
|
33
|
+
}
|
|
34
|
+
return this['_' + key];
|
|
35
|
+
},
|
|
36
|
+
set: function (v: any) {
|
|
37
|
+
this['_' + key] =
|
|
38
|
+
options?.type === FieldType.array && v
|
|
39
|
+
? v.map((i: any) =>
|
|
40
|
+
ObjectService.createByType(i, options?.classType),
|
|
41
|
+
)
|
|
42
|
+
: ObjectService.createByType(v, options?.classType);
|
|
43
|
+
},
|
|
44
|
+
enumerable: true,
|
|
45
|
+
configurable: true,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (!(target as any).constructor['__properties']) {
|
|
49
|
+
(target as any).constructor['__properties'] = {};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
(target as any).constructor['__properties'][key] = true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Reflect.defineMetadata(symbols.SYMBOL_FIELD, options, target as any, key);
|
|
56
|
+
};
|
|
55
57
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import { IModelOptions } from '../../interfaces';
|
|
4
|
+
import * as symbols from '../../symbols';
|
|
5
5
|
|
|
6
6
|
export const Model = ModelDecorator;
|
|
7
7
|
export function ModelDecorator(options?: IModelOptions) {
|
|
8
|
-
return function<T>(target: any) {
|
|
8
|
+
return function <T>(target: any) {
|
|
9
9
|
options = options ? { ...options } : {};
|
|
10
10
|
|
|
11
11
|
Reflect.defineMetadata(symbols.SYMBOL_MODEL, options, target);
|
|
@@ -14,12 +14,12 @@ export function ModelDecorator(options?: IModelOptions) {
|
|
|
14
14
|
const result = { ...this };
|
|
15
15
|
|
|
16
16
|
if (this.constructor['__properties']) {
|
|
17
|
-
Object.keys(this.constructor['__properties']).forEach(protoKey => {
|
|
17
|
+
Object.keys(this.constructor['__properties']).forEach((protoKey) => {
|
|
18
18
|
result[protoKey] = this['_' + protoKey];
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
return result;
|
|
23
23
|
};
|
|
24
|
-
}
|
|
24
|
+
};
|
|
25
25
|
}
|
package/src/lib/interfaces.ts
CHANGED
|
@@ -1,152 +1,154 @@
|
|
|
1
|
-
import {Observable} from
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
2
|
|
|
3
3
|
export enum FieldTypeDef {
|
|
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
|
-
|
|
4
|
+
address = 'address',
|
|
5
|
+
currency = 'currency',
|
|
6
|
+
date = 'date',
|
|
7
|
+
dateTime = 'dateTime',
|
|
8
|
+
dateWithEdit = 'dateWithEdit',
|
|
9
|
+
email = 'email',
|
|
10
|
+
enum = 'enum',
|
|
11
|
+
file = 'file',
|
|
12
|
+
flag = 'flag',
|
|
13
|
+
int = 'int',
|
|
14
|
+
nip = 'nip',
|
|
15
|
+
object = 'object',
|
|
16
|
+
array = 'array',
|
|
17
|
+
password = 'password',
|
|
18
|
+
radio = 'radio',
|
|
19
|
+
text = 'text',
|
|
20
|
+
strings = 'strings',
|
|
21
|
+
ints = 'ints',
|
|
22
|
+
longText = 'longText',
|
|
23
|
+
color = 'color',
|
|
24
|
+
logo = 'logo',
|
|
25
|
+
check = 'check',
|
|
26
|
+
phoneNumber = 'phoneNumber',
|
|
27
|
+
phoneNumberPl = 'phoneNumberPl',
|
|
28
|
+
pesel = 'pesel',
|
|
29
|
+
pdf = 'pdf',
|
|
30
|
+
video = 'video',
|
|
31
|
+
attachment = 'attachment',
|
|
32
|
+
dateRange = 'dateRange',
|
|
33
|
+
image = 'image',
|
|
34
|
+
float = 'float',
|
|
35
35
|
}
|
|
36
36
|
export const FieldType: typeof FieldTypeDef = FieldTypeDef;
|
|
37
37
|
|
|
38
38
|
export interface ISpecification {
|
|
39
|
-
|
|
39
|
+
readonly criteria: any;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export interface IModelFilter {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
label?: string;
|
|
44
|
+
fieldType?: FieldTypeDef;
|
|
45
|
+
key: string;
|
|
46
|
+
type: '=' | '!=' | '>=' | '<=' | '<' | '>' | '~=';
|
|
47
|
+
possibilities$?: Observable<{ id: any; text: string }[]>;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export interface IModelStep {
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
number: number;
|
|
52
|
+
name: string;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export interface IModelMetadata {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
titleKey?: string;
|
|
57
|
+
permissions?: Array<string>;
|
|
58
|
+
filters?: Array<IModelFilter>;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export interface IFieldMetadata extends IFieldModifyMetadata {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
type?: FieldTypeDef;
|
|
63
|
+
classType?: any;
|
|
64
|
+
possibilities?: Array<any> | any;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
export interface IFieldModifyMetadata {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
required?: boolean;
|
|
69
|
+
focused?: boolean;
|
|
70
|
+
confirm?: boolean;
|
|
71
|
+
permissions?: Array<string>;
|
|
72
|
+
unique?: boolean | IFieldUniqueMetadata;
|
|
73
|
+
defaltValue?: () => any;
|
|
74
|
+
enabled?: ISpecification;
|
|
75
|
+
hide?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* @desc - Model step configuration
|
|
78
|
+
*/
|
|
79
|
+
step?: IModelStep;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export interface IFieldEditMetadata extends IFieldModifyMetadata {
|
|
83
|
-
|
|
83
|
+
multi?: boolean;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
export interface IFieldListMetadata {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
87
|
+
order?: number;
|
|
88
|
+
filter?: boolean;
|
|
89
|
+
permissions?: Array<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Configuration for dynamic list table data
|
|
92
|
+
* @param {string} dynamic.headerKey - column header object key
|
|
93
|
+
* @param {string} dynamic.rowKey - column row value object key
|
|
94
|
+
*/
|
|
95
|
+
dynamic?: { headerKey: string; rowKey: string };
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
export interface IFieldDetailsMetadata {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
order?: number;
|
|
100
|
+
permissions?: Array<string>;
|
|
101
|
+
enabled?: ISpecification;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
export interface IModelOptions {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
105
|
+
titleKey?: string;
|
|
106
|
+
filters?: Array<IModelFilter>;
|
|
107
|
+
create?: IModelModeOptions;
|
|
108
|
+
update?: IModelModeOptions;
|
|
109
|
+
list?: IModelModeOptions;
|
|
110
|
+
details?: IModelModeOptions;
|
|
111
|
+
remove?: IModelModeOptions;
|
|
112
|
+
customs?: Array<IModelModeOptionsCustom>;
|
|
113
|
+
/**
|
|
114
|
+
* @desc - Allow export field data
|
|
115
|
+
*/
|
|
116
|
+
export?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* @desc - Allow import field data
|
|
119
|
+
*/
|
|
120
|
+
import?: boolean;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
export interface IModelModeOptions {
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
permissions?: Array<string>;
|
|
125
|
+
enabled?: ISpecification;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
export interface IModelModeOptionsCustom extends IModelModeOptions {
|
|
129
|
-
|
|
129
|
+
mode: string;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
export interface IFieldOptions extends IFieldMetadata {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
133
|
+
create?: IFieldModifyMetadata | boolean;
|
|
134
|
+
update?: IFieldEditMetadata | boolean;
|
|
135
|
+
list?: IFieldListMetadata | boolean;
|
|
136
|
+
details?: IFieldDetailsMetadata | boolean;
|
|
137
|
+
customs?: Array<IModelMetadataCustom>;
|
|
138
|
+
search?: boolean;
|
|
139
|
+
info?: string;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
export interface IModelMetadataCustom extends IModelMetadata {
|
|
143
|
-
|
|
143
|
+
mode: string;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
export interface IFieldUniqueMetadata {
|
|
147
|
-
|
|
147
|
+
withFields?: Array<string>;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
export interface IFieldCustomMetadata
|
|
151
|
-
|
|
150
|
+
export interface IFieldCustomMetadata
|
|
151
|
+
extends IFieldModifyMetadata,
|
|
152
|
+
IFieldListMetadata {
|
|
153
|
+
mode: string;
|
|
152
154
|
}
|
package/src/lib/utils.spec.ts
CHANGED
|
@@ -1,137 +1,151 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { Field } from './decorators/field/field.decorator';
|
|
2
|
+
import { Model } from './decorators/model/model.decorator';
|
|
3
|
+
import { FieldType } from './interfaces';
|
|
4
|
+
import {
|
|
5
|
+
castModel,
|
|
6
|
+
getInvalidFields,
|
|
7
|
+
getModelFieldKeys,
|
|
8
|
+
getModelFieldOptions,
|
|
9
|
+
isModel,
|
|
10
|
+
} from './utils';
|
|
5
11
|
|
|
6
12
|
describe('shared-models: utils', () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
13
|
+
describe('getModelFieldKeys()', () => {
|
|
14
|
+
it('should return empty array when fields is not defined', () => {
|
|
15
|
+
@Model({})
|
|
16
|
+
class Test {}
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
const result = getModelFieldKeys(Test);
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
expect(result).toStrictEqual([]);
|
|
21
|
+
});
|
|
17
22
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
it('should return field keys array', () => {
|
|
24
|
+
@Model({})
|
|
25
|
+
class Test {
|
|
26
|
+
@Field({}) test1!: any;
|
|
27
|
+
@Field({}) test2!: any;
|
|
28
|
+
@Field({}) test3!: any;
|
|
29
|
+
}
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
const result = getModelFieldKeys(Test);
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
});
|
|
33
|
+
expect(result).toStrictEqual(['test1', 'test2', 'test3']);
|
|
30
34
|
});
|
|
35
|
+
});
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
describe('getModelFieldOptions', () => {
|
|
38
|
+
it('should return options', () => {
|
|
39
|
+
const options = { required: true, type: FieldType.password };
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
@Model({})
|
|
42
|
+
class Test {
|
|
43
|
+
@Field(options) test!: string;
|
|
44
|
+
}
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
});
|
|
46
|
+
expect(getModelFieldOptions(new Test(), 'test')).toStrictEqual(options);
|
|
43
47
|
});
|
|
48
|
+
});
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
describe('isModel', () => {
|
|
51
|
+
it('should return true when model type', () => {
|
|
52
|
+
@Model({})
|
|
53
|
+
class Test {
|
|
54
|
+
@Field({}) test!: string;
|
|
55
|
+
}
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
expect(isModel(new Test())).toBe(true);
|
|
58
|
+
});
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
it('should return false when other type', () => {
|
|
61
|
+
expect(isModel(new Date())).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('getInvalidFields', () => {
|
|
66
|
+
it('should return empty array when correct', () => {
|
|
67
|
+
@Model({})
|
|
68
|
+
class Test {
|
|
69
|
+
@Field({}) test!: string;
|
|
70
|
+
@Field({
|
|
71
|
+
required: true,
|
|
72
|
+
})
|
|
73
|
+
test2!: string;
|
|
74
|
+
@Field({
|
|
75
|
+
create: {
|
|
76
|
+
required: true,
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
test3!: string;
|
|
80
|
+
@Field({
|
|
81
|
+
create: {
|
|
82
|
+
required: false,
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
test4!: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const instance = new Test();
|
|
89
|
+
instance.test2 = '12';
|
|
90
|
+
instance.test3 = '21';
|
|
91
|
+
|
|
92
|
+
expect(getInvalidFields(instance, 'create', null as any).length).toBe(0);
|
|
58
93
|
});
|
|
59
94
|
|
|
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
|
-
it('should return array when incorrect', () => {
|
|
88
|
-
@Model({})
|
|
89
|
-
class Test {
|
|
90
|
-
@Field({}) test!: string;
|
|
91
|
-
@Field({
|
|
92
|
-
required: true
|
|
93
|
-
}) test2!: string;
|
|
94
|
-
@Field({
|
|
95
|
-
create: {
|
|
96
|
-
required: true
|
|
97
|
-
}
|
|
98
|
-
}) test3!: string;
|
|
99
|
-
@Field({
|
|
100
|
-
create: {
|
|
101
|
-
required: false
|
|
102
|
-
}
|
|
103
|
-
}) test4!: string;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const instance = new Test();
|
|
107
|
-
instance.test2 = "12";
|
|
108
|
-
|
|
109
|
-
expect(getInvalidFields(instance, 'create', null as any).length).toBe(1);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should return empty array when not model', () => {
|
|
113
|
-
expect(getInvalidFields(new Date(), 'create', null as any).length).toBe(0);
|
|
114
|
-
});
|
|
95
|
+
it('should return array when incorrect', () => {
|
|
96
|
+
@Model({})
|
|
97
|
+
class Test {
|
|
98
|
+
@Field({}) test!: string;
|
|
99
|
+
@Field({
|
|
100
|
+
required: true,
|
|
101
|
+
})
|
|
102
|
+
test2!: string;
|
|
103
|
+
@Field({
|
|
104
|
+
create: {
|
|
105
|
+
required: true,
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
test3!: string;
|
|
109
|
+
@Field({
|
|
110
|
+
create: {
|
|
111
|
+
required: false,
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
test4!: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const instance = new Test();
|
|
118
|
+
instance.test2 = '12';
|
|
119
|
+
|
|
120
|
+
expect(getInvalidFields(instance, 'create', null as any).length).toBe(1);
|
|
115
121
|
});
|
|
116
122
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
123
|
+
it('should return empty array when not model', () => {
|
|
124
|
+
expect(getInvalidFields(new Date(), 'create', null as any).length).toBe(
|
|
125
|
+
0,
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('castModel', () => {
|
|
131
|
+
it('should remove unused fields', () => {
|
|
132
|
+
@Model({})
|
|
133
|
+
class Test {
|
|
134
|
+
@Field({}) test!: string;
|
|
135
|
+
@Field({
|
|
136
|
+
create: true,
|
|
137
|
+
})
|
|
138
|
+
test2!: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const instance = new Test();
|
|
142
|
+
instance.test = 'asd';
|
|
143
|
+
instance.test2 = 'aaa';
|
|
144
|
+
|
|
145
|
+
castModel(instance, 'create', null as any);
|
|
146
|
+
|
|
147
|
+
expect(instance.test).not.toBeDefined();
|
|
148
|
+
expect(instance.test2).toBeDefined();
|
|
136
149
|
});
|
|
150
|
+
});
|
|
137
151
|
});
|
package/src/lib/utils.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { IFieldOptions, IModelOptions } from
|
|
2
|
-
import { SYMBOL_FIELD, SYMBOL_MODEL } from
|
|
1
|
+
import { IFieldOptions, IModelOptions } from './interfaces';
|
|
2
|
+
import { SYMBOL_FIELD, SYMBOL_MODEL } from './symbols';
|
|
3
3
|
|
|
4
4
|
export function getModelFieldKeys<T>(type: T): Array<string> {
|
|
5
|
-
if (!(type as any)[
|
|
6
|
-
return Object.keys((type as any)[
|
|
5
|
+
if (!(type as any)['__fields']) return [];
|
|
6
|
+
return Object.keys((type as any)['__fields']);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function getModelFieldOptions<T>(
|
|
10
10
|
instance: T,
|
|
11
|
-
fieldKey: string
|
|
11
|
+
fieldKey: string,
|
|
12
12
|
): IFieldOptions {
|
|
13
13
|
return Reflect.getMetadata(SYMBOL_FIELD, instance as any, fieldKey);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function getModelFieldsWithOptions<T>(
|
|
17
|
-
instance: T
|
|
17
|
+
instance: T,
|
|
18
18
|
): Array<{ key: string; options: IFieldOptions }> {
|
|
19
19
|
const keys = getModelFieldKeys((instance as any).constructor);
|
|
20
20
|
|
|
@@ -37,8 +37,8 @@ export function isModel<T>(instance: T): boolean {
|
|
|
37
37
|
|
|
38
38
|
export function getInvalidFields<T>(
|
|
39
39
|
instance: T,
|
|
40
|
-
mode:
|
|
41
|
-
permissions: Array<string
|
|
40
|
+
mode: 'create' | 'update' | string,
|
|
41
|
+
permissions: Array<string>,
|
|
42
42
|
): Array<string> {
|
|
43
43
|
const result: any[] = [];
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ export function getInvalidFields<T>(
|
|
|
46
46
|
let required = options.required;
|
|
47
47
|
|
|
48
48
|
if (
|
|
49
|
-
(mode ===
|
|
49
|
+
(mode === 'create' || mode === 'update') &&
|
|
50
50
|
options[mode] &&
|
|
51
51
|
options[mode]?.constructor
|
|
52
52
|
) {
|
|
@@ -58,15 +58,18 @@ export function getInvalidFields<T>(
|
|
|
58
58
|
(options[mode] as IFieldOptions).permissions
|
|
59
59
|
) {
|
|
60
60
|
required = (options[mode] as IFieldOptions)?.permissions?.some((op) =>
|
|
61
|
-
permissions.some((p) => p === op)
|
|
61
|
+
permissions.some((p) => p === op),
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
required &&
|
|
68
|
+
((instance as any)[key] === null ||
|
|
69
|
+
(instance as any)[key] === undefined ||
|
|
70
|
+
(instance as any)[key] === '')
|
|
71
|
+
)
|
|
72
|
+
result.push(key);
|
|
70
73
|
});
|
|
71
74
|
|
|
72
75
|
return result;
|
|
@@ -74,18 +77,17 @@ export function getInvalidFields<T>(
|
|
|
74
77
|
|
|
75
78
|
export function castModel<T>(
|
|
76
79
|
instance: T,
|
|
77
|
-
mode:
|
|
78
|
-
permissions: Array<string
|
|
80
|
+
mode: 'create' | 'update' | string,
|
|
81
|
+
permissions: Array<string>,
|
|
79
82
|
): void {
|
|
80
83
|
if (!isModel(instance)) return;
|
|
81
84
|
|
|
82
85
|
const fieldsWithOptions = getModelFieldsWithOptions(instance);
|
|
83
86
|
|
|
84
87
|
Object.keys(instance as any)
|
|
85
|
-
.filter((key) => key !==
|
|
88
|
+
.filter((key) => key !== 'id')
|
|
86
89
|
.forEach((key) => {
|
|
87
|
-
const fieldWidthOptions =
|
|
88
|
-
fieldsWithOptions.find((f) => f.key === key);
|
|
90
|
+
const fieldWidthOptions = fieldsWithOptions.find((f) => f.key === key);
|
|
89
91
|
|
|
90
92
|
if (!fieldWidthOptions) {
|
|
91
93
|
delete (instance as any)[key];
|
|
@@ -93,21 +95,19 @@ export function castModel<T>(
|
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
if (
|
|
96
|
-
(mode ===
|
|
98
|
+
(mode === 'create' || mode === 'update') &&
|
|
97
99
|
(!fieldWidthOptions.options[mode] ||
|
|
98
100
|
(permissions &&
|
|
99
101
|
(fieldWidthOptions.options[mode] as IFieldOptions).permissions &&
|
|
100
|
-
!(
|
|
101
|
-
mode
|
|
102
|
-
|
|
103
|
-
permissions.some((p) => op === p)
|
|
104
|
-
)))
|
|
102
|
+
!(
|
|
103
|
+
fieldWidthOptions.options[mode] as IFieldOptions
|
|
104
|
+
)?.permissions?.some((op) => permissions.some((p) => op === p))))
|
|
105
105
|
) {
|
|
106
106
|
delete (instance as any)[key];
|
|
107
107
|
return;
|
|
108
108
|
} else if (
|
|
109
|
-
mode !==
|
|
110
|
-
mode !==
|
|
109
|
+
mode !== 'create' &&
|
|
110
|
+
mode !== 'update' &&
|
|
111
111
|
(!fieldWidthOptions.options.customs ||
|
|
112
112
|
!fieldWidthOptions.options.customs.some((c) => c.mode === mode))
|
|
113
113
|
) {
|