@lenne.tech/nest-server 3.2.0 → 3.3.2
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/dist/core/common/args/filter.args.d.ts +5 -0
- package/dist/core/common/args/filter.args.js +10 -0
- package/dist/core/common/args/filter.args.js.map +1 -1
- package/dist/core/common/args/pagination.args.d.ts +8 -1
- package/dist/core/common/args/pagination.args.js +24 -6
- package/dist/core/common/args/pagination.args.js.map +1 -1
- package/dist/core/common/decorators/restricted.decorator.d.ts +3 -0
- package/dist/core/common/decorators/restricted.decorator.js +14 -5
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/helpers/context.helper.js +15 -5
- package/dist/core/common/helpers/context.helper.js.map +1 -1
- package/dist/core/common/helpers/filter.helper.d.ts +3 -3
- package/dist/core/common/helpers/filter.helper.js.map +1 -1
- package/dist/core/common/helpers/graphql.helper.d.ts +4 -4
- package/dist/core/common/helpers/input.helper.js +13 -7
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/helpers/model.helper.js +0 -1
- package/dist/core/common/helpers/model.helper.js.map +1 -1
- package/dist/core/common/helpers/service.helper.d.ts +11 -8
- package/dist/core/common/helpers/service.helper.js +50 -12
- package/dist/core/common/helpers/service.helper.js.map +1 -1
- package/dist/core/common/inputs/combined-filter.input.d.ts +8 -2
- package/dist/core/common/inputs/combined-filter.input.js +16 -5
- package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
- package/dist/core/common/inputs/core-input.input.d.ts +8 -0
- package/dist/core/common/inputs/core-input.input.js +15 -0
- package/dist/core/common/inputs/core-input.input.js.map +1 -0
- package/dist/core/common/inputs/filter.input.d.ts +7 -1
- package/dist/core/common/inputs/filter.input.js +14 -1
- package/dist/core/common/inputs/filter.input.js.map +1 -1
- package/dist/core/common/inputs/single-filter.input.d.ts +2 -1
- package/dist/core/common/inputs/single-filter.input.js +10 -1
- package/dist/core/common/inputs/single-filter.input.js.map +1 -1
- package/dist/core/common/inputs/sort.input.d.ts +2 -1
- package/dist/core/common/inputs/sort.input.js +7 -1
- package/dist/core/common/inputs/sort.input.js.map +1 -1
- package/dist/core/common/interceptors/check-response.interceptor.js +1 -1
- package/dist/core/common/interceptors/check-response.interceptor.js.map +1 -1
- package/dist/core/common/interfaces/core-persistence-model.interface.d.ts +1 -0
- package/dist/core/common/interfaces/server-options.interface.d.ts +3 -15
- package/dist/core/common/models/core-model.model.d.ts +6 -0
- package/dist/core/common/models/core-model.model.js +14 -5
- package/dist/core/common/models/core-model.model.js.map +1 -1
- package/dist/core/common/models/core-persistence.model.d.ts +5 -29
- package/dist/core/common/models/core-persistence.model.js +19 -41
- package/dist/core/common/models/core-persistence.model.js.map +1 -1
- package/dist/core/common/pipes/check-input.pipe.d.ts +2 -3
- package/dist/core/common/pipes/check-input.pipe.js +5 -20
- package/dist/core/common/pipes/check-input.pipe.js.map +1 -1
- package/dist/core/common/pipes/map-and-validate.pipe.d.ts +4 -0
- package/dist/core/common/pipes/map-and-validate.pipe.js +40 -0
- package/dist/core/common/pipes/map-and-validate.pipe.js.map +1 -0
- package/dist/core/common/types/plain-input.type.d.ts +3 -0
- package/dist/core/common/types/plain-input.type.js +3 -0
- package/dist/core/common/types/plain-input.type.js.map +1 -0
- package/dist/core/modules/auth/core-auth.model.d.ts +3 -1
- package/dist/core/modules/auth/core-auth.model.js +7 -1
- package/dist/core/modules/auth/core-auth.model.js.map +1 -1
- package/dist/core/modules/auth/core-auth.module.d.ts +5 -2
- package/dist/core/modules/auth/core-auth.module.js +25 -17
- package/dist/core/modules/auth/core-auth.module.js.map +1 -1
- package/dist/core/modules/auth/core-auth.resolver.d.ts +1 -1
- package/dist/core/modules/user/core-user.model.d.ts +3 -0
- package/dist/core/modules/user/core-user.model.js +9 -4
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/core/modules/user/core-user.service.d.ts +9 -13
- package/dist/core/modules/user/core-user.service.js +38 -67
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/core/modules/user/inputs/core-user-create.input.js.map +1 -1
- package/dist/core/modules/user/inputs/core-user.input.d.ts +2 -2
- package/dist/core/modules/user/inputs/core-user.input.js +3 -3
- package/dist/core/modules/user/inputs/core-user.input.js.map +1 -1
- package/dist/core.module.js +4 -4
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/main.js +0 -2
- package/dist/main.js.map +1 -1
- package/dist/server/common/models/persistence.model.d.ts +4 -2
- package/dist/server/common/models/persistence.model.js +6 -2
- package/dist/server/common/models/persistence.model.js.map +1 -1
- package/dist/server/modules/auth/auth.model.d.ts +1 -0
- package/dist/server/modules/auth/auth.model.js +4 -0
- package/dist/server/modules/auth/auth.model.js.map +1 -1
- package/dist/server/modules/user/inputs/user-create.input.js.map +1 -1
- package/dist/server/modules/user/inputs/user.input.js.map +1 -1
- package/dist/server/modules/user/user.model.d.ts +3 -2
- package/dist/server/modules/user/user.model.js +9 -5
- package/dist/server/modules/user/user.model.js.map +1 -1
- package/dist/server/modules/user/user.resolver.d.ts +2 -2
- package/dist/server/modules/user/user.resolver.js +11 -13
- package/dist/server/modules/user/user.resolver.js.map +1 -1
- package/dist/server/modules/user/user.service.d.ts +6 -6
- package/dist/server/modules/user/user.service.js +12 -4
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/test/test.helper.d.ts +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +26 -26
- package/src/core/common/args/filter.args.ts +22 -1
- package/src/core/common/args/pagination.args.ts +42 -7
- package/src/core/common/decorators/restricted.decorator.ts +22 -5
- package/src/core/common/helpers/context.helper.ts +14 -3
- package/src/core/common/helpers/filter.helper.ts +3 -3
- package/src/core/common/helpers/input.helper.ts +17 -11
- package/src/core/common/helpers/model.helper.ts +0 -3
- package/src/core/common/helpers/service.helper.ts +95 -30
- package/src/core/common/inputs/combined-filter.input.ts +30 -9
- package/src/core/common/inputs/core-input.input.ts +36 -0
- package/src/core/common/inputs/filter.input.ts +27 -3
- package/src/core/common/inputs/single-filter.input.ts +7 -6
- package/src/core/common/inputs/sort.input.ts +4 -3
- package/src/core/common/interceptors/check-response.interceptor.ts +2 -2
- package/src/core/common/interfaces/core-persistence-model.interface.ts +1 -0
- package/src/core/common/interfaces/server-options.interface.ts +3 -33
- package/src/core/common/models/core-model.model.ts +42 -3
- package/src/core/common/models/core-persistence.model.ts +33 -120
- package/src/core/common/pipes/check-input.pipe.ts +13 -33
- package/src/core/common/pipes/map-and-validate.pipe.ts +32 -0
- package/src/core/common/types/plain-input.type.ts +6 -0
- package/src/core/modules/auth/core-auth.model.ts +15 -1
- package/src/core/modules/auth/core-auth.module.ts +38 -22
- package/src/core/modules/auth/core-auth.resolver.ts +1 -1
- package/src/core/modules/user/core-user.model.ts +16 -4
- package/src/core/modules/user/core-user.service.ts +59 -115
- package/src/core/modules/user/inputs/core-user-create.input.ts +4 -0
- package/src/core/modules/user/inputs/core-user.input.ts +7 -3
- package/src/core.module.ts +13 -6
- package/src/index.ts +6 -2
- package/src/main.ts +0 -4
- package/src/server/common/models/persistence.model.ts +16 -2
- package/src/server/modules/auth/auth.model.ts +13 -0
- package/src/server/modules/user/inputs/user-create.input.ts +4 -0
- package/src/server/modules/user/inputs/user.input.ts +4 -0
- package/src/server/modules/user/user.model.ts +18 -5
- package/src/server/modules/user/user.resolver.ts +15 -19
- package/src/server/modules/user/user.service.ts +22 -7
- package/src/test/test.helper.ts +1 -1
- package/dist/core/common/pipes/map.pipe.d.ts +0 -4
- package/dist/core/common/pipes/map.pipe.js +0 -25
- package/dist/core/common/pipes/map.pipe.js.map +0 -1
- package/src/core/common/pipes/map.pipe.ts +0 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "3.2
|
|
3
|
+
"version": "3.3.2",
|
|
4
4
|
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
"node": ">= 16.13.0"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@apollo/
|
|
56
|
-
"@apollo
|
|
55
|
+
"@apollo/gateway": "0.48.1",
|
|
56
|
+
"@nestjs/apollo": "10.0.2",
|
|
57
57
|
"@nestjs/common": "8.2.6",
|
|
58
58
|
"@nestjs/core": "8.2.6",
|
|
59
|
-
"@nestjs/graphql": "
|
|
59
|
+
"@nestjs/graphql": "10.0.2",
|
|
60
60
|
"@nestjs/jwt": "8.0.0",
|
|
61
61
|
"@nestjs/mongoose": "9.0.2",
|
|
62
62
|
"@nestjs/passport": "8.1.0",
|
|
@@ -67,22 +67,24 @@
|
|
|
67
67
|
"@types/jest": "27.4.0",
|
|
68
68
|
"@types/lodash": "4.14.178",
|
|
69
69
|
"@types/multer": "1.4.7",
|
|
70
|
-
"@types/node": "
|
|
70
|
+
"@types/node": "17.0.17",
|
|
71
71
|
"@types/node-mailjet": "3.3.8",
|
|
72
72
|
"@types/nodemailer": "6.4.4",
|
|
73
73
|
"@types/passport": "1.0.7",
|
|
74
74
|
"@types/supertest": "2.0.11",
|
|
75
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
76
|
-
"@typescript-eslint/parser": "5.
|
|
77
|
-
"apollo-server-core": "3.6.
|
|
78
|
-
"apollo-server-express": "3.6.
|
|
75
|
+
"@typescript-eslint/eslint-plugin": "5.11.0",
|
|
76
|
+
"@typescript-eslint/parser": "5.11.0",
|
|
77
|
+
"apollo-server-core": "3.6.3",
|
|
78
|
+
"apollo-server-express": "3.6.3",
|
|
79
79
|
"bcrypt": "5.0.1",
|
|
80
80
|
"class-transformer": "0.5.1",
|
|
81
81
|
"class-validator": "0.13.2",
|
|
82
82
|
"coffeescript": "2.6.1",
|
|
83
83
|
"ejs": "3.1.6",
|
|
84
|
-
"
|
|
85
|
-
"
|
|
84
|
+
"eslint": "8.9.0",
|
|
85
|
+
"eslint-config-prettier": "8.3.0",
|
|
86
|
+
"fastify": "3.27.1",
|
|
87
|
+
"graphql": "16.3.0",
|
|
86
88
|
"graphql-subscriptions": "2.0.0",
|
|
87
89
|
"grunt": "1.4.1",
|
|
88
90
|
"grunt-bg-shell": "2.3.3",
|
|
@@ -90,35 +92,33 @@
|
|
|
90
92
|
"grunt-contrib-watch": "1.1.0",
|
|
91
93
|
"grunt-sync": "0.8.2",
|
|
92
94
|
"husky": "7.0.4",
|
|
93
|
-
"jest": "27.
|
|
94
|
-
"json-to-graphql-query": "2.2.
|
|
95
|
-
"light-my-request": "4.7.
|
|
95
|
+
"jest": "27.5.1",
|
|
96
|
+
"json-to-graphql-query": "2.2.2",
|
|
97
|
+
"light-my-request": "4.7.1",
|
|
96
98
|
"lodash": "4.17.21",
|
|
97
|
-
"mongodb": "4.3.
|
|
98
|
-
"mongoose": "6.1
|
|
99
|
+
"mongodb": "4.3.1",
|
|
100
|
+
"mongoose": "6.2.1",
|
|
99
101
|
"multer": "1.4.4",
|
|
100
102
|
"node-mailjet": "3.3.5",
|
|
101
103
|
"nodemailer": "6.7.2",
|
|
102
104
|
"nodemon": "2.0.15",
|
|
103
105
|
"passport": "0.5.2",
|
|
104
106
|
"passport-jwt": "4.0.0",
|
|
107
|
+
"prettier": "2.5.1",
|
|
108
|
+
"pretty-quick": "3.1.3",
|
|
105
109
|
"reflect-metadata": "0.1.13",
|
|
106
110
|
"rimraf": "3.0.2",
|
|
107
|
-
"rxjs": "7.5.
|
|
111
|
+
"rxjs": "7.5.4",
|
|
108
112
|
"supertest": "6.2.2",
|
|
113
|
+
"ts-jest": "27.1.3",
|
|
109
114
|
"ts-morph": "13.0.3",
|
|
110
|
-
"ts-node": "10.
|
|
111
|
-
"tsconfig-paths": "3.12.0"
|
|
115
|
+
"ts-node": "10.5.0",
|
|
116
|
+
"tsconfig-paths": "3.12.0",
|
|
117
|
+
"typescript": "4.5.5"
|
|
112
118
|
},
|
|
113
119
|
"devDependencies": {
|
|
114
|
-
"eslint": "8.7.0",
|
|
115
|
-
"eslint-config-prettier": "8.3.0",
|
|
116
120
|
"find-file-up": "2.0.1",
|
|
117
|
-
"pm2": "5.1.2"
|
|
118
|
-
"prettier": "2.5.1",
|
|
119
|
-
"pretty-quick": "3.1.3",
|
|
120
|
-
"ts-jest": "27.1.3",
|
|
121
|
-
"typescript": "4.5.5"
|
|
121
|
+
"pm2": "5.1.2"
|
|
122
122
|
},
|
|
123
123
|
"jest": {
|
|
124
124
|
"collectCoverage": true,
|
|
@@ -13,5 +13,26 @@ export class FilterArgs extends PaginationArgs {
|
|
|
13
13
|
nullable: true,
|
|
14
14
|
})
|
|
15
15
|
@IsOptional()
|
|
16
|
-
filter?: FilterInput;
|
|
16
|
+
filter?: FilterInput = undefined;
|
|
17
|
+
|
|
18
|
+
// ===================================================================================================================
|
|
19
|
+
// Methods
|
|
20
|
+
// ===================================================================================================================
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Mapping for Subtypes
|
|
24
|
+
*/
|
|
25
|
+
map(
|
|
26
|
+
data: Partial<this> | Record<string, any>,
|
|
27
|
+
options: {
|
|
28
|
+
cloneDeep?: boolean;
|
|
29
|
+
funcAllowed?: boolean;
|
|
30
|
+
mapId?: boolean;
|
|
31
|
+
} = {}
|
|
32
|
+
): this {
|
|
33
|
+
super.map(data, options);
|
|
34
|
+
this.filter = data.filter ? FilterInput.map(data.filter, options) : undefined;
|
|
35
|
+
Object.keys(this).forEach((key) => this[key] === undefined && delete this[key]);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
17
38
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { IsOptional, Max } from 'class-validator';
|
|
2
2
|
import { ArgsType, Field, Int } from '@nestjs/graphql';
|
|
3
|
+
import { ModelHelper } from '../helpers/model.helper';
|
|
4
|
+
import { CoreInput } from '../inputs/core-input.input';
|
|
3
5
|
import { SortInput } from '../inputs/sort.input';
|
|
4
6
|
|
|
5
7
|
@ArgsType()
|
|
6
|
-
export class PaginationArgs {
|
|
8
|
+
export class PaginationArgs extends CoreInput {
|
|
7
9
|
/**
|
|
8
10
|
* Limit for pagination
|
|
9
11
|
*/
|
|
@@ -14,7 +16,7 @@ export class PaginationArgs {
|
|
|
14
16
|
})
|
|
15
17
|
@IsOptional()
|
|
16
18
|
@Max(100)
|
|
17
|
-
limit?: number =
|
|
19
|
+
limit?: number = undefined;
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Offset for pagination
|
|
@@ -25,7 +27,7 @@ export class PaginationArgs {
|
|
|
25
27
|
defaultValue: 0,
|
|
26
28
|
})
|
|
27
29
|
@IsOptional()
|
|
28
|
-
offset?: number =
|
|
30
|
+
offset?: number = undefined;
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
33
|
* Alias for offset
|
|
@@ -33,10 +35,10 @@ export class PaginationArgs {
|
|
|
33
35
|
@Field((type) => Int, {
|
|
34
36
|
description: 'Alias for offset',
|
|
35
37
|
nullable: true,
|
|
36
|
-
defaultValue:
|
|
38
|
+
defaultValue: undefined,
|
|
37
39
|
})
|
|
38
40
|
@IsOptional()
|
|
39
|
-
skip?: number =
|
|
41
|
+
skip?: number = undefined;
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
44
|
* Sorting for pagination
|
|
@@ -46,7 +48,7 @@ export class PaginationArgs {
|
|
|
46
48
|
nullable: true,
|
|
47
49
|
})
|
|
48
50
|
@IsOptional()
|
|
49
|
-
sort?: SortInput[];
|
|
51
|
+
sort?: SortInput[] = undefined;
|
|
50
52
|
|
|
51
53
|
/**
|
|
52
54
|
* Alias for limit
|
|
@@ -58,5 +60,38 @@ export class PaginationArgs {
|
|
|
58
60
|
})
|
|
59
61
|
@IsOptional()
|
|
60
62
|
@Max(100)
|
|
61
|
-
take?: number =
|
|
63
|
+
take?: number = undefined;
|
|
64
|
+
|
|
65
|
+
// ===================================================================================================================
|
|
66
|
+
// Methods
|
|
67
|
+
// ===================================================================================================================
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Initialize instance with default values instead of undefined
|
|
71
|
+
*/
|
|
72
|
+
init(): this {
|
|
73
|
+
super.init();
|
|
74
|
+
this.limit = this.limit === undefined ? 25 : this.limit;
|
|
75
|
+
this.offset = this.offset === undefined ? 0 : this.offset;
|
|
76
|
+
this.skip = this.skip === undefined ? 0 : this.skip;
|
|
77
|
+
this.take = this.take === undefined ? 0 : this.take;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Mapping for Subtypes
|
|
83
|
+
*/
|
|
84
|
+
map(
|
|
85
|
+
data: Partial<this> | Record<string, any>,
|
|
86
|
+
options: {
|
|
87
|
+
cloneDeep?: boolean;
|
|
88
|
+
funcAllowed?: boolean;
|
|
89
|
+
mapId?: boolean;
|
|
90
|
+
} = {}
|
|
91
|
+
): this {
|
|
92
|
+
super.map(data, options);
|
|
93
|
+
this.sort = ModelHelper.maps(data.sort, SortInput, options.cloneDeep);
|
|
94
|
+
Object.keys(this).forEach((key) => this[key] === undefined && delete this[key]);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
62
97
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
+
import { UnauthorizedException } from '@nestjs/common';
|
|
2
3
|
import { RoleEnum } from '../enums/role.enum';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -31,8 +32,15 @@ export const getRestricted = (object: unknown, propertyKey: string) => {
|
|
|
31
32
|
export const checkRestricted = (
|
|
32
33
|
data: any,
|
|
33
34
|
user: { id: any; hasRole: (roles: string[]) => boolean },
|
|
35
|
+
options: { ignoreUndefined?: boolean; throwError?: boolean } = {},
|
|
34
36
|
processedObjects: any[] = []
|
|
35
37
|
) => {
|
|
38
|
+
const config = {
|
|
39
|
+
ignoreUndefined: true,
|
|
40
|
+
throwError: true,
|
|
41
|
+
...options,
|
|
42
|
+
};
|
|
43
|
+
|
|
36
44
|
// Primitives
|
|
37
45
|
if (!data || typeof data !== 'object') {
|
|
38
46
|
return data;
|
|
@@ -47,11 +55,16 @@ export const checkRestricted = (
|
|
|
47
55
|
// Array
|
|
48
56
|
if (Array.isArray(data)) {
|
|
49
57
|
// Check array items
|
|
50
|
-
return data.map((item) => checkRestricted(item, user, processedObjects));
|
|
58
|
+
return data.map((item) => checkRestricted(item, user, options, processedObjects));
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
// Object
|
|
54
62
|
for (const propertyKey of Object.keys(data)) {
|
|
63
|
+
// Check undefined
|
|
64
|
+
if (data[propertyKey] === undefined && config.ignoreUndefined) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
55
68
|
// Get roles
|
|
56
69
|
const roles = getRestricted(data, propertyKey);
|
|
57
70
|
|
|
@@ -71,20 +84,24 @@ export const checkRestricted = (
|
|
|
71
84
|
data.ownerIds.some((item) => (item.id ? item.id.toString() === userId : item.toString() === userId)))
|
|
72
85
|
)
|
|
73
86
|
) {
|
|
74
|
-
//
|
|
75
|
-
|
|
87
|
+
// The user does not have the required rights and is not the owner
|
|
88
|
+
if (config.throwError) {
|
|
89
|
+
throw new UnauthorizedException('Current user is not allowed to set ' + propertyKey);
|
|
90
|
+
}
|
|
76
91
|
continue;
|
|
77
92
|
}
|
|
78
93
|
} else {
|
|
79
94
|
// The user does not have the required rights
|
|
80
|
-
|
|
95
|
+
if (config.throwError) {
|
|
96
|
+
throw new UnauthorizedException('Current user is not allowed to set ' + propertyKey);
|
|
97
|
+
}
|
|
81
98
|
continue;
|
|
82
99
|
}
|
|
83
100
|
}
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
// Check property data
|
|
87
|
-
data[propertyKey] = checkRestricted(data[propertyKey], user, processedObjects);
|
|
104
|
+
data[propertyKey] = checkRestricted(data[propertyKey], user, options, processedObjects);
|
|
88
105
|
}
|
|
89
106
|
|
|
90
107
|
// Return processed data
|
|
@@ -16,15 +16,26 @@ export class Context {
|
|
|
16
16
|
|
|
17
17
|
// Init data
|
|
18
18
|
let user: { [key: string]: any };
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
let ctx: any = null;
|
|
20
|
+
try {
|
|
21
|
+
ctx = GqlExecutionContext.create(context)?.getContext();
|
|
22
|
+
} catch (e) {
|
|
23
|
+
// console.log(e);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let args: any;
|
|
27
|
+
try {
|
|
28
|
+
args = GqlExecutionContext.create(context)?.getArgs();
|
|
29
|
+
} catch (e) {
|
|
30
|
+
// console.log(e);
|
|
31
|
+
}
|
|
21
32
|
|
|
22
33
|
// Get data
|
|
23
34
|
if (ctx) {
|
|
24
35
|
// User from GraphQL context
|
|
25
36
|
user = ctx?.user || ctx?.req?.user;
|
|
26
37
|
} else {
|
|
27
|
-
const request = context.switchToHttp()
|
|
38
|
+
const request = context?.switchToHttp ? context.switchToHttp()?.getRequest() : null;
|
|
28
39
|
if (request) {
|
|
29
40
|
args = request.body;
|
|
30
41
|
|
|
@@ -13,14 +13,14 @@ export class Filter {
|
|
|
13
13
|
* Convert filter arguments to a query array
|
|
14
14
|
* @param filterArgs
|
|
15
15
|
*/
|
|
16
|
-
public static convertFilterArgsToQuery<T = any>(filterArgs: FilterArgs): [FilterQuery<T>, QueryOptions] {
|
|
16
|
+
public static convertFilterArgsToQuery<T = any>(filterArgs: Partial<FilterArgs>): [FilterQuery<T>, QueryOptions] {
|
|
17
17
|
return [Filter.generateFilterQuery(filterArgs.filter), Filter.generateFindOptions(filterArgs)];
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Generate filter query
|
|
22
22
|
*/
|
|
23
|
-
public static generateFilterQuery<T = any>(filter?: FilterInput): FilterQuery<T> | any {
|
|
23
|
+
public static generateFilterQuery<T = any>(filter?: Partial<FilterInput>): FilterQuery<T> | any {
|
|
24
24
|
// Check filter
|
|
25
25
|
if (!filter) {
|
|
26
26
|
return undefined;
|
|
@@ -98,7 +98,7 @@ export class Filter {
|
|
|
98
98
|
/**
|
|
99
99
|
* Generate find options
|
|
100
100
|
*/
|
|
101
|
-
public static generateFindOptions<T = any>(filterArgs: FilterArgs): QueryOptions {
|
|
101
|
+
public static generateFindOptions<T = any>(filterArgs: Partial<FilterArgs>): QueryOptions {
|
|
102
102
|
// Check filterArgs
|
|
103
103
|
if (!filterArgs) {
|
|
104
104
|
return {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BadRequestException } from '@nestjs/common';
|
|
2
|
-
import {
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
3
|
import { validate } from 'class-validator';
|
|
4
4
|
import * as _ from 'lodash';
|
|
5
5
|
import { checkRestricted } from '../decorators/restricted.decorator';
|
|
@@ -17,21 +17,27 @@ export class InputHelper {
|
|
|
17
17
|
metatype?
|
|
18
18
|
): Promise<any> {
|
|
19
19
|
// Return value if it is only a basic type
|
|
20
|
-
if (!metatype || this.isBasicType(metatype)) {
|
|
20
|
+
if (typeof value !== 'object' || !metatype || this.isBasicType(metatype)) {
|
|
21
21
|
return value;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const errors = await validate(object);
|
|
31
|
-
if (errors.length > 0) {
|
|
32
|
-
throw new BadRequestException('Validation failed');
|
|
24
|
+
// Convert to metatype
|
|
25
|
+
if (!(value instanceof metatype)) {
|
|
26
|
+
if ((metatype as any)?.map) {
|
|
27
|
+
value = (metatype as any)?.map(value);
|
|
28
|
+
} else {
|
|
29
|
+
value = plainToInstance(metatype, value);
|
|
33
30
|
}
|
|
34
31
|
}
|
|
32
|
+
|
|
33
|
+
// Validate
|
|
34
|
+
const errors = await validate(value);
|
|
35
|
+
if (errors.length > 0) {
|
|
36
|
+
throw new BadRequestException('Validation failed');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Remove restricted values if roles are missing
|
|
40
|
+
value = checkRestricted(value, user);
|
|
35
41
|
return value;
|
|
36
42
|
}
|
|
37
43
|
|
|
@@ -77,9 +77,6 @@ export class ModelHelper {
|
|
|
77
77
|
// Merge target with prepared source
|
|
78
78
|
Object.assign(target, preparedSource);
|
|
79
79
|
|
|
80
|
-
// Remove all props with undefined
|
|
81
|
-
Object.keys(target).forEach((key) => target[key] === undefined && delete target[key]);
|
|
82
|
-
|
|
83
80
|
// Return target
|
|
84
81
|
return target;
|
|
85
82
|
}
|
|
@@ -10,52 +10,85 @@ export class ServiceHelper {
|
|
|
10
10
|
/**
|
|
11
11
|
* Prepare input before save
|
|
12
12
|
*/
|
|
13
|
-
static async prepareInput(
|
|
14
|
-
input:
|
|
13
|
+
static async prepareInput<T = any>(
|
|
14
|
+
input: T,
|
|
15
15
|
currentUser: { [key: string]: any; id: string },
|
|
16
|
-
options: {
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
options: {
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
create?: boolean;
|
|
19
|
+
clone?: boolean;
|
|
20
|
+
getNewArray?: boolean;
|
|
21
|
+
removeUndefined?: boolean;
|
|
22
|
+
} = {}
|
|
23
|
+
): Promise<T> {
|
|
19
24
|
// Configuration
|
|
20
25
|
const config = {
|
|
21
26
|
checkRoles: false,
|
|
22
27
|
clone: false,
|
|
23
28
|
create: false,
|
|
29
|
+
getNewArray: false,
|
|
30
|
+
removeUndefined: false,
|
|
24
31
|
...options,
|
|
25
32
|
};
|
|
26
33
|
|
|
27
|
-
//
|
|
34
|
+
// Check input
|
|
35
|
+
if (typeof input !== 'object') {
|
|
36
|
+
return input;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Process array
|
|
40
|
+
if (Array.isArray(input)) {
|
|
41
|
+
const processedArray = input.map(
|
|
42
|
+
async (item) => await ServiceHelper.prepareInput(item, currentUser, options)
|
|
43
|
+
) as any;
|
|
44
|
+
return config.getNewArray ? processedArray : input;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Clone input
|
|
28
48
|
if (config.clone) {
|
|
29
|
-
input
|
|
49
|
+
if ((input as Record<string, any>).mapDeep && typeof (input as any).mapDeep === 'function') {
|
|
50
|
+
input = await Object.getPrototypeOf(input).mapDeep(input);
|
|
51
|
+
} else {
|
|
52
|
+
input = _.cloneDeep(input);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Remove undefined properties to avoid unwanted overwrites
|
|
57
|
+
if (config.removeUndefined) {
|
|
58
|
+
Object.keys(input).forEach((key) => input[key] === undefined && delete input[key]);
|
|
30
59
|
}
|
|
31
60
|
|
|
32
61
|
// Process roles
|
|
33
|
-
if (
|
|
62
|
+
if (
|
|
63
|
+
config.checkRoles &&
|
|
64
|
+
(input as Record<string, any>).roles &&
|
|
65
|
+
(!currentUser?.hasRole || !currentUser.hasRole(RoleEnum.ADMIN))
|
|
66
|
+
) {
|
|
34
67
|
if (!(currentUser as any)?.roles) {
|
|
35
68
|
throw new UnauthorizedException('Missing roles of current user');
|
|
36
69
|
} else {
|
|
37
|
-
const allowedRoles = _.intersection(input.roles, (currentUser as any).roles);
|
|
38
|
-
if (allowedRoles.length !== input.roles.length) {
|
|
39
|
-
const missingRoles = _.difference(input.roles, (currentUser as any).roles);
|
|
70
|
+
const allowedRoles = _.intersection((input as Record<string, any>).roles, (currentUser as any).roles);
|
|
71
|
+
if (allowedRoles.length !== (input as Record<string, any>).roles.length) {
|
|
72
|
+
const missingRoles = _.difference((input as Record<string, any>).roles, (currentUser as any).roles);
|
|
40
73
|
throw new UnauthorizedException('Current user not allowed setting roles: ' + missingRoles);
|
|
41
74
|
}
|
|
42
|
-
input.roles = allowedRoles;
|
|
75
|
+
(input as Record<string, any>).roles = allowedRoles;
|
|
43
76
|
}
|
|
44
77
|
}
|
|
45
78
|
|
|
46
79
|
// Hash password
|
|
47
|
-
if (input.password) {
|
|
48
|
-
input.password = await bcrypt.hash((input as any).password, 10);
|
|
80
|
+
if ((input as Record<string, any>).password) {
|
|
81
|
+
(input as Record<string, any>).password = await bcrypt.hash((input as any).password, 10);
|
|
49
82
|
}
|
|
50
83
|
|
|
51
84
|
// Set creator
|
|
52
85
|
if (config.create && currentUser) {
|
|
53
|
-
input.createdBy = currentUser.id;
|
|
86
|
+
(input as Record<string, any>).createdBy = currentUser.id;
|
|
54
87
|
}
|
|
55
88
|
|
|
56
89
|
// Set updater
|
|
57
90
|
if (currentUser) {
|
|
58
|
-
input.updatedBy = currentUser.id;
|
|
91
|
+
(input as Record<string, any>).updatedBy = currentUser.id;
|
|
59
92
|
}
|
|
60
93
|
|
|
61
94
|
// Return prepared input
|
|
@@ -65,39 +98,71 @@ export class ServiceHelper {
|
|
|
65
98
|
/**
|
|
66
99
|
* Prepare output before return
|
|
67
100
|
*/
|
|
68
|
-
static async prepareOutput<T =
|
|
101
|
+
static async prepareOutput<T = { [key: string]: any; map: (...args: any[]) => any }>(
|
|
69
102
|
output: any,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
103
|
+
options: {
|
|
104
|
+
[key: string]: any;
|
|
105
|
+
clone?: boolean;
|
|
106
|
+
getNewArray?: boolean;
|
|
107
|
+
removeUndefined?: boolean;
|
|
108
|
+
targetModel?: new (...args: any[]) => T;
|
|
109
|
+
} = {}
|
|
110
|
+
): Promise<T | T[] | any> {
|
|
75
111
|
// Configuration
|
|
76
112
|
const config = {
|
|
77
|
-
clone:
|
|
113
|
+
clone: false,
|
|
114
|
+
getNewArray: false,
|
|
115
|
+
removeUndefined: false,
|
|
116
|
+
targetModel: undefined,
|
|
78
117
|
...options,
|
|
79
118
|
};
|
|
80
119
|
|
|
120
|
+
// Check output
|
|
121
|
+
if (typeof output !== 'object') {
|
|
122
|
+
return output;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Process array
|
|
126
|
+
if (Array.isArray(output)) {
|
|
127
|
+
const processedArray = output.map(async (item) => await ServiceHelper.prepareOutput(item, options)) as any;
|
|
128
|
+
return config.getNewArray ? processedArray : output;
|
|
129
|
+
}
|
|
130
|
+
|
|
81
131
|
// Clone output
|
|
82
132
|
if (config.clone) {
|
|
83
|
-
output
|
|
133
|
+
if (output.mapDeep && typeof output.mapDeep === 'function') {
|
|
134
|
+
output = await Object.getPrototypeOf(output).mapDeep(output);
|
|
135
|
+
} else {
|
|
136
|
+
output = _.cloneDeep(output);
|
|
137
|
+
}
|
|
84
138
|
}
|
|
85
139
|
|
|
86
140
|
// Map output if target model exist
|
|
87
|
-
if (
|
|
88
|
-
(
|
|
141
|
+
if (config.targetModel) {
|
|
142
|
+
output = await (config.targetModel as any).map(output);
|
|
89
143
|
}
|
|
90
144
|
|
|
91
145
|
// Remove password if exists
|
|
92
|
-
|
|
146
|
+
if (output.password) {
|
|
147
|
+
output.password = undefined;
|
|
148
|
+
}
|
|
93
149
|
|
|
94
150
|
// Remove verification token if exists
|
|
95
|
-
|
|
151
|
+
if (output.verificationToken) {
|
|
152
|
+
output.verificationToken = undefined;
|
|
153
|
+
}
|
|
96
154
|
|
|
97
155
|
// Remove password reset token if exists
|
|
98
|
-
|
|
156
|
+
if (output.passwordResetToken) {
|
|
157
|
+
output.passwordResetToken = undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Remove undefined properties to avoid unwanted overwrites
|
|
161
|
+
if (config.removeUndefined) {
|
|
162
|
+
Object.keys(output).forEach((key) => output[key] === undefined && delete output[key]);
|
|
163
|
+
}
|
|
99
164
|
|
|
100
|
-
// Return prepared
|
|
165
|
+
// Return prepared output
|
|
101
166
|
return output;
|
|
102
167
|
}
|
|
103
168
|
}
|
|
@@ -1,26 +1,47 @@
|
|
|
1
1
|
import { Field, InputType } from '@nestjs/graphql';
|
|
2
2
|
import { LogicalOperatorEnum } from '../enums/logical-operator.enum';
|
|
3
|
+
import { ModelHelper } from '../helpers/model.helper';
|
|
4
|
+
import { CoreInput } from './core-input.input';
|
|
3
5
|
import { FilterInput } from './filter.input';
|
|
4
6
|
|
|
5
7
|
@InputType({
|
|
6
8
|
description: 'Combination of multiple filters via logical operator',
|
|
7
9
|
})
|
|
8
|
-
export class CombinedFilterInput {
|
|
10
|
+
export class CombinedFilterInput extends CoreInput {
|
|
9
11
|
/**
|
|
10
|
-
* Logical Operator to combine filters
|
|
12
|
+
* Logical Operator to combine filters
|
|
11
13
|
*/
|
|
12
14
|
@Field((type) => LogicalOperatorEnum, {
|
|
13
|
-
description: 'Logical Operator to combine filters
|
|
14
|
-
nullable: true,
|
|
15
|
+
description: 'Logical Operator to combine filters',
|
|
15
16
|
})
|
|
16
|
-
logicalOperator
|
|
17
|
+
logicalOperator: LogicalOperatorEnum = undefined;
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
* Filters to combine via logical operator
|
|
20
|
+
* Filters to combine via logical operator
|
|
20
21
|
*/
|
|
21
22
|
@Field((type) => [FilterInput], {
|
|
22
|
-
description: 'Filters to combine via logical operator
|
|
23
|
-
nullable: true,
|
|
23
|
+
description: 'Filters to combine via logical operator',
|
|
24
24
|
})
|
|
25
|
-
filters: FilterInput[];
|
|
25
|
+
filters: FilterInput[] = undefined;
|
|
26
|
+
|
|
27
|
+
// ===================================================================================================================
|
|
28
|
+
// Methods
|
|
29
|
+
// ===================================================================================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Mapping for Subtypes
|
|
33
|
+
*/
|
|
34
|
+
map(
|
|
35
|
+
data: Partial<this> | Record<string, any>,
|
|
36
|
+
options: {
|
|
37
|
+
cloneDeep?: boolean;
|
|
38
|
+
funcAllowed?: boolean;
|
|
39
|
+
mapId?: boolean;
|
|
40
|
+
} = {}
|
|
41
|
+
): this {
|
|
42
|
+
super.map(data, options);
|
|
43
|
+
this.filters = ModelHelper.maps(data.filters, FilterInput, options.cloneDeep);
|
|
44
|
+
Object.keys(this).forEach((key) => this[key] === undefined && delete this[key]);
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
26
47
|
}
|