@lenne.tech/nest-server 11.0.0 → 11.1.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/dist/core/common/args/filter.args.js +6 -8
- package/dist/core/common/args/filter.args.js.map +1 -1
- package/dist/core/common/args/pagination.args.js +16 -16
- package/dist/core/common/args/pagination.args.js.map +1 -1
- package/dist/core/common/decorators/graphql-service-options.decorator.js +5 -0
- package/dist/core/common/decorators/graphql-service-options.decorator.js.map +1 -1
- package/dist/core/common/decorators/rest-service-options.decorator.d.ts +2 -0
- package/dist/core/common/decorators/rest-service-options.decorator.js +14 -0
- package/dist/core/common/decorators/rest-service-options.decorator.js.map +1 -0
- package/dist/core/common/decorators/restricted.decorator.js +1 -1
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/decorators/translatable.decorator.d.ts +3 -0
- package/dist/core/common/decorators/translatable.decorator.js +22 -0
- package/dist/core/common/decorators/translatable.decorator.js.map +1 -0
- package/dist/core/common/decorators/unified-field.decorator.d.ts +25 -0
- package/dist/core/common/decorators/unified-field.decorator.js +144 -0
- package/dist/core/common/decorators/unified-field.decorator.js.map +1 -0
- package/dist/core/common/helpers/config.helper.js +1 -1
- package/dist/core/common/helpers/config.helper.js.map +1 -1
- package/dist/core/common/helpers/db.helper.js +1 -1
- package/dist/core/common/helpers/db.helper.js.map +1 -1
- package/dist/core/common/helpers/graphql.helper.js.map +1 -1
- package/dist/core/common/helpers/input.helper.js +2 -2
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/helpers/service.helper.d.ts +1 -0
- package/dist/core/common/helpers/service.helper.js +14 -2
- package/dist/core/common/helpers/service.helper.js.map +1 -1
- package/dist/core/common/inputs/combined-filter.input.js +8 -4
- package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
- package/dist/core/common/inputs/filter.input.js +10 -7
- package/dist/core/common/inputs/filter.input.js.map +1 -1
- package/dist/core/common/inputs/single-filter.input.js +26 -18
- package/dist/core/common/inputs/single-filter.input.js.map +1 -1
- package/dist/core/common/inputs/sort.input.js +10 -4
- package/dist/core/common/inputs/sort.input.js.map +1 -1
- package/dist/core/common/models/core-persistence.model.js +18 -11
- package/dist/core/common/models/core-persistence.model.js.map +1 -1
- package/dist/core/common/pipes/map-and-validate.pipe.js +12 -3
- package/dist/core/common/pipes/map-and-validate.pipe.js.map +1 -1
- package/dist/core/common/services/brevo.service.js +1 -1
- package/dist/core/common/services/brevo.service.js.map +1 -1
- package/dist/core/common/services/config.service.js +1 -1
- package/dist/core/common/services/config.service.js.map +1 -1
- package/dist/core/common/services/email.service.js +1 -1
- package/dist/core/common/services/email.service.js.map +1 -1
- package/dist/core/common/services/model-doc.service.js +1 -1
- package/dist/core/common/services/model-doc.service.js.map +1 -1
- package/dist/core/common/services/module.service.js +2 -1
- package/dist/core/common/services/module.service.js.map +1 -1
- package/dist/core/common/services/template.service.js +2 -2
- package/dist/core/common/services/template.service.js.map +1 -1
- package/dist/core/modules/auth/core-auth.model.js +10 -11
- package/dist/core/modules/auth/core-auth.model.js.map +1 -1
- package/dist/core/modules/auth/guards/auth.guard.js +1 -1
- package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js +25 -21
- package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js.map +1 -1
- package/dist/core/modules/auth/services/core-auth.service.js +1 -1
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/health-check/core-health-check-result.model.js +18 -11
- package/dist/core/modules/health-check/core-health-check-result.model.js.map +1 -1
- package/dist/core/modules/user/core-user.service.js +2 -2
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/core/modules/user/inputs/core-user-create.input.js +6 -3
- package/dist/core/modules/user/inputs/core-user-create.input.js.map +1 -1
- package/dist/core/modules/user/inputs/core-user.input.js +32 -18
- package/dist/core/modules/user/inputs/core-user.input.js.map +1 -1
- package/dist/core.module.js +1 -1
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/main.js +2 -2
- package/dist/main.js.map +1 -1
- package/dist/server/common/models/persistence.model.js +1 -1
- package/dist/server/common/models/persistence.model.js.map +1 -1
- package/dist/server/modules/auth/inputs/auth-sign-up.input.js +11 -4
- package/dist/server/modules/auth/inputs/auth-sign-up.input.js.map +1 -1
- package/dist/server/modules/file/file.resolver.js +2 -2
- package/dist/server/modules/file/file.resolver.js.map +1 -1
- package/dist/server/modules/user/outputs/find-and-count-users-result.output.js +11 -4
- package/dist/server/modules/user/outputs/find-and-count-users-result.output.js.map +1 -1
- package/dist/server/modules/user/user.service.js +1 -1
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/test/test.helper.js +1 -1
- package/dist/test/test.helper.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +23 -23
- package/src/core/common/args/filter.args.ts +7 -10
- package/src/core/common/args/pagination.args.ts +17 -17
- package/src/core/common/decorators/graphql-service-options.decorator.ts +8 -0
- package/src/core/common/decorators/rest-service-options.decorator.ts +22 -0
- package/src/core/common/decorators/restricted.decorator.ts +1 -2
- package/src/core/common/decorators/translatable.decorator.ts +24 -0
- package/src/core/common/decorators/unified-field.decorator.ts +243 -0
- package/src/core/common/helpers/config.helper.ts +1 -4
- package/src/core/common/helpers/db.helper.ts +1 -2
- package/src/core/common/helpers/graphql.helper.ts +0 -1
- package/src/core/common/helpers/input.helper.ts +2 -3
- package/src/core/common/helpers/service.helper.ts +18 -3
- package/src/core/common/inputs/combined-filter.input.ts +9 -5
- package/src/core/common/inputs/filter.input.ts +14 -11
- package/src/core/common/inputs/single-filter.input.ts +28 -19
- package/src/core/common/inputs/sort.input.ts +11 -5
- package/src/core/common/models/core-persistence.model.ts +19 -12
- package/src/core/common/pipes/map-and-validate.pipe.ts +15 -4
- package/src/core/common/services/brevo.service.ts +1 -2
- package/src/core/common/services/config.service.ts +1 -2
- package/src/core/common/services/email.service.ts +1 -2
- package/src/core/common/services/model-doc.service.ts +1 -2
- package/src/core/common/services/module.service.ts +2 -2
- package/src/core/common/services/template.service.ts +2 -3
- package/src/core/modules/auth/core-auth.model.ts +11 -12
- package/src/core/modules/auth/guards/auth.guard.ts +1 -2
- package/src/core/modules/auth/inputs/core-auth-sign-in.input.ts +27 -23
- package/src/core/modules/auth/services/core-auth.service.ts +1 -2
- package/src/core/modules/health-check/core-health-check-result.model.ts +19 -12
- package/src/core/modules/user/core-user.service.ts +2 -3
- package/src/core/modules/user/inputs/core-user-create.input.ts +7 -4
- package/src/core/modules/user/inputs/core-user.input.ts +34 -20
- package/src/core.module.ts +1 -2
- package/src/index.ts +3 -0
- package/src/main.ts +2 -3
- package/src/server/common/models/persistence.model.ts +1 -2
- package/src/server/modules/auth/inputs/auth-sign-up.input.ts +12 -5
- package/src/server/modules/file/file.resolver.ts +2 -3
- package/src/server/modules/user/outputs/find-and-count-users-result.output.ts +12 -5
- package/src/server/modules/user/user.service.ts +1 -2
- package/src/test/test.helper.ts +2 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.1.0",
|
|
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",
|
|
@@ -70,26 +70,26 @@
|
|
|
70
70
|
"@lenne.tech/mongoose-gridfs": "1.4.2",
|
|
71
71
|
"@lenne.tech/multer-gridfs-storage": "5.0.6",
|
|
72
72
|
"@nestjs/apollo": "13.1.0",
|
|
73
|
-
"@nestjs/common": "11.0
|
|
74
|
-
"@nestjs/core": "11.0
|
|
73
|
+
"@nestjs/common": "11.1.0",
|
|
74
|
+
"@nestjs/core": "11.1.0",
|
|
75
75
|
"@nestjs/graphql": "13.1.0",
|
|
76
76
|
"@nestjs/jwt": "11.0.0",
|
|
77
77
|
"@nestjs/mongoose": "11.0.3",
|
|
78
78
|
"@nestjs/passport": "11.0.5",
|
|
79
|
-
"@nestjs/platform-express": "11.0
|
|
80
|
-
"@nestjs/schedule": "
|
|
81
|
-
"@nestjs/swagger": "11.
|
|
79
|
+
"@nestjs/platform-express": "11.1.0",
|
|
80
|
+
"@nestjs/schedule": "6.0.0",
|
|
81
|
+
"@nestjs/swagger": "11.2.0",
|
|
82
82
|
"@nestjs/terminus": "11.0.0",
|
|
83
83
|
"apollo-server-core": "3.13.0",
|
|
84
84
|
"apollo-server-express": "3.13.0",
|
|
85
85
|
"bcrypt": "5.1.1",
|
|
86
86
|
"class-transformer": "0.5.1",
|
|
87
|
-
"class-validator": "0.14.
|
|
87
|
+
"class-validator": "0.14.2",
|
|
88
88
|
"compression": "1.8.0",
|
|
89
89
|
"cookie-parser": "1.4.7",
|
|
90
90
|
"dotenv": "16.5.0",
|
|
91
91
|
"ejs": "3.1.10",
|
|
92
|
-
"graphql": "16.
|
|
92
|
+
"graphql": "16.11.0",
|
|
93
93
|
"graphql-query-complexity": "1.1.0",
|
|
94
94
|
"graphql-subscriptions": "3.0.0",
|
|
95
95
|
"graphql-upload": "15.0.2",
|
|
@@ -97,12 +97,12 @@
|
|
|
97
97
|
"json-to-graphql-query": "2.3.0",
|
|
98
98
|
"light-my-request": "6.6.0",
|
|
99
99
|
"lodash": "4.17.21",
|
|
100
|
-
"mongodb": "6.
|
|
101
|
-
"mongoose": "7.8.
|
|
100
|
+
"mongodb": "6.16.0",
|
|
101
|
+
"mongoose": "7.8.7",
|
|
102
102
|
"multer": "1.4.5-lts.2",
|
|
103
103
|
"node-mailjet": "6.0.8",
|
|
104
|
-
"nodemailer": "
|
|
105
|
-
"nodemon": "3.1.
|
|
104
|
+
"nodemailer": "7.0.3",
|
|
105
|
+
"nodemon": "3.1.10",
|
|
106
106
|
"passport": "0.7.0",
|
|
107
107
|
"passport-jwt": "4.0.1",
|
|
108
108
|
"reflect-metadata": "0.2.2",
|
|
@@ -114,13 +114,13 @@
|
|
|
114
114
|
"devDependencies": {
|
|
115
115
|
"@babel/plugin-proposal-private-methods": "7.18.6",
|
|
116
116
|
"@compodoc/compodoc": "1.1.26",
|
|
117
|
-
"@lenne.tech/eslint-config-ts": "
|
|
118
|
-
"@nestjs/cli": "11.0.
|
|
117
|
+
"@lenne.tech/eslint-config-ts": "2.0.1",
|
|
118
|
+
"@nestjs/cli": "11.0.7",
|
|
119
119
|
"@nestjs/schematics": "11.0.5",
|
|
120
|
-
"@nestjs/testing": "11.0
|
|
121
|
-
"@swc/cli": "0.
|
|
122
|
-
"@swc/core": "1.11.
|
|
123
|
-
"@swc/jest": "0.2.
|
|
120
|
+
"@nestjs/testing": "11.1.0",
|
|
121
|
+
"@swc/cli": "0.7.5",
|
|
122
|
+
"@swc/core": "1.11.24",
|
|
123
|
+
"@swc/jest": "0.2.38",
|
|
124
124
|
"@types/compression": "1.7.5",
|
|
125
125
|
"@types/cookie-parser": "1.4.8",
|
|
126
126
|
"@types/ejs": "3.1.5",
|
|
@@ -128,15 +128,15 @@
|
|
|
128
128
|
"@types/jest": "29.5.14",
|
|
129
129
|
"@types/lodash": "4.17.16",
|
|
130
130
|
"@types/multer": "1.4.12",
|
|
131
|
-
"@types/node": "22.
|
|
131
|
+
"@types/node": "22.15.17",
|
|
132
132
|
"@types/nodemailer": "6.4.17",
|
|
133
133
|
"@types/passport": "1.0.17",
|
|
134
134
|
"@types/supertest": "6.0.3",
|
|
135
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
136
|
-
"@typescript-eslint/parser": "8.
|
|
135
|
+
"@typescript-eslint/eslint-plugin": "8.32.0",
|
|
136
|
+
"@typescript-eslint/parser": "8.32.0",
|
|
137
137
|
"coffeescript": "2.7.0",
|
|
138
|
-
"eslint": "9.
|
|
139
|
-
"eslint-config-prettier": "10.1.
|
|
138
|
+
"eslint": "9.26.0",
|
|
139
|
+
"eslint-config-prettier": "10.1.5",
|
|
140
140
|
"eslint-plugin-unused-imports": "4.1.4",
|
|
141
141
|
"find-file-up": "2.0.1",
|
|
142
142
|
"grunt": "1.6.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ArgsType
|
|
2
|
-
import { IsOptional } from 'class-validator';
|
|
1
|
+
import { ArgsType } from '@nestjs/graphql';
|
|
3
2
|
|
|
3
|
+
import { UnifiedField } from '../decorators/unified-field.decorator';
|
|
4
4
|
import { FilterInput } from '../inputs/filter.input';
|
|
5
5
|
import { PaginationArgs } from './pagination.args';
|
|
6
6
|
|
|
@@ -9,20 +9,17 @@ export class FilterArgs extends PaginationArgs {
|
|
|
9
9
|
/**
|
|
10
10
|
* Filtering
|
|
11
11
|
*/
|
|
12
|
-
@
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
@UnifiedField({
|
|
13
|
+
isOptional: true,
|
|
14
|
+
type: FilterInput,
|
|
15
15
|
})
|
|
16
|
-
@IsOptional()
|
|
17
16
|
filter?: FilterInput = undefined;
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* Get a specific number of random samples from filter results
|
|
21
20
|
*/
|
|
22
|
-
@
|
|
23
|
-
|
|
24
|
-
'Request only a specified number of samples from the filter results; if not specified, all results are returned.',
|
|
25
|
-
nullable: true,
|
|
21
|
+
@UnifiedField({
|
|
22
|
+
isOptional: true,
|
|
26
23
|
})
|
|
27
24
|
samples?: number = undefined;
|
|
28
25
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ArgsType,
|
|
2
|
-
import { IsOptional } from 'class-validator';
|
|
1
|
+
import { ArgsType, Int } from '@nestjs/graphql';
|
|
3
2
|
|
|
3
|
+
import { UnifiedField } from '../decorators/unified-field.decorator';
|
|
4
4
|
import { maps } from '../helpers/model.helper';
|
|
5
5
|
import { CoreInput } from '../inputs/core-input.input';
|
|
6
6
|
import { SortInput } from '../inputs/sort.input';
|
|
@@ -10,51 +10,51 @@ export class PaginationArgs extends CoreInput {
|
|
|
10
10
|
/**
|
|
11
11
|
* Limit for pagination
|
|
12
12
|
*/
|
|
13
|
-
@
|
|
13
|
+
@UnifiedField({
|
|
14
14
|
description: 'Limit specifies the maximum number of elements found that are to be returned',
|
|
15
|
-
|
|
15
|
+
isOptional: true,
|
|
16
|
+
type: Int,
|
|
16
17
|
})
|
|
17
|
-
@IsOptional()
|
|
18
18
|
limit?: number = undefined;
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Alias for skip
|
|
22
22
|
*/
|
|
23
|
-
@
|
|
23
|
+
@UnifiedField({
|
|
24
24
|
description: 'Alias for skip',
|
|
25
|
-
|
|
25
|
+
isOptional: true,
|
|
26
|
+
type: Int,
|
|
26
27
|
})
|
|
27
|
-
@IsOptional()
|
|
28
28
|
offset?: number = undefined;
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Skip for pagination
|
|
32
32
|
*/
|
|
33
|
-
@
|
|
33
|
+
@UnifiedField({
|
|
34
34
|
description: 'Skip specifies how many found elements should be skipped on return',
|
|
35
|
-
|
|
35
|
+
isOptional: true,
|
|
36
|
+
type: Int,
|
|
36
37
|
})
|
|
37
|
-
@IsOptional()
|
|
38
38
|
skip?: number = undefined;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Sorting for pagination
|
|
42
42
|
*/
|
|
43
|
-
@
|
|
43
|
+
@UnifiedField({
|
|
44
44
|
description: 'Sorting the returned elements',
|
|
45
|
-
|
|
45
|
+
isOptional: true,
|
|
46
|
+
type: () => SortInput,
|
|
46
47
|
})
|
|
47
|
-
@IsOptional()
|
|
48
48
|
sort?: SortInput[] = undefined;
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Alias for limit
|
|
52
52
|
*/
|
|
53
|
-
@
|
|
53
|
+
@UnifiedField({
|
|
54
54
|
description: 'Alias for limit',
|
|
55
|
-
|
|
55
|
+
isOptional: true,
|
|
56
|
+
type: () => Int,
|
|
56
57
|
})
|
|
57
|
-
@IsOptional()
|
|
58
58
|
take?: number = undefined;
|
|
59
59
|
|
|
60
60
|
// ===================================================================================================================
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import { GqlExecutionContext } from '@nestjs/graphql';
|
|
2
3
|
|
|
3
4
|
import { currentUserDec, graphqlPopulateDec } from '../helpers/decorator.helper';
|
|
4
5
|
import { ServiceOptions } from '../interfaces/service-options.interface';
|
|
@@ -8,6 +9,7 @@ import { ServiceOptions } from '../interfaces/service-options.interface';
|
|
|
8
9
|
*
|
|
9
10
|
* Includes following properties of ServiceOptions:
|
|
10
11
|
* - currentUser
|
|
12
|
+
* - language
|
|
11
13
|
* - populate
|
|
12
14
|
*
|
|
13
15
|
* Configuration via Decorator data:
|
|
@@ -22,8 +24,14 @@ import { ServiceOptions } from '../interfaces/service-options.interface';
|
|
|
22
24
|
*/
|
|
23
25
|
export const GraphQLServiceOptions = createParamDecorator(
|
|
24
26
|
(data: { gqlPath?: string; ignoreSelections?: boolean }, ctx: ExecutionContext): ServiceOptions => {
|
|
27
|
+
const gqlContext = GqlExecutionContext.create(ctx);
|
|
28
|
+
const request = gqlContext.getContext().req;
|
|
29
|
+
|
|
30
|
+
const language = request?.headers?.['accept-language'];
|
|
31
|
+
|
|
25
32
|
return {
|
|
26
33
|
currentUser: currentUserDec(null, ctx),
|
|
34
|
+
language,
|
|
27
35
|
populate: graphqlPopulateDec(data, ctx),
|
|
28
36
|
};
|
|
29
37
|
},
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { currentUserDec } from '../helpers/decorator.helper';
|
|
4
|
+
import { ServiceOptions } from '../interfaces/service-options.interface';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get standard ServiceOptions for REST-Requests
|
|
8
|
+
*
|
|
9
|
+
* Includes following properties of ServiceOptions:
|
|
10
|
+
* - currentUser
|
|
11
|
+
* - language
|
|
12
|
+
*/
|
|
13
|
+
export const RESTServiceOptions = createParamDecorator((ctx: ExecutionContext): ServiceOptions => {
|
|
14
|
+
const request = ctx.switchToHttp().getRequest();
|
|
15
|
+
|
|
16
|
+
const language = request?.headers?.['accept-language'];
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
currentUser: currentUserDec(null, ctx),
|
|
20
|
+
language,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { UnauthorizedException } from '@nestjs/common';
|
|
2
2
|
import 'reflect-metadata';
|
|
3
|
+
import _ = require('lodash');
|
|
3
4
|
|
|
4
5
|
import { ProcessType } from '../enums/process-type.enum';
|
|
5
6
|
import { RoleEnum } from '../enums/role.enum';
|
|
6
7
|
import { equalIds, getIncludedIds } from '../helpers/db.helper';
|
|
7
8
|
import { RequireAtLeastOne } from '../types/required-at-least-one.type';
|
|
8
9
|
|
|
9
|
-
import _ = require('lodash');
|
|
10
|
-
|
|
11
10
|
/**
|
|
12
11
|
* Restricted meta key
|
|
13
12
|
*/
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
|
|
3
|
+
const TRANSLATABLE_KEY = 'custom:translatable';
|
|
4
|
+
|
|
5
|
+
export function getTranslatablePropertyKeys(target: unknown): string[] {
|
|
6
|
+
// for classes
|
|
7
|
+
if (typeof target === 'function') {
|
|
8
|
+
return Reflect.getMetadata(TRANSLATABLE_KEY, target) || [];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// for instances
|
|
12
|
+
if (typeof target === 'object' && target.constructor) {
|
|
13
|
+
return Reflect.getMetadata(TRANSLATABLE_KEY, target.constructor) || [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function Translatable(): PropertyDecorator {
|
|
20
|
+
return (target: object, propertyKey: string | symbol) => {
|
|
21
|
+
const existingProperties: string[] = Reflect.getMetadata(TRANSLATABLE_KEY, target.constructor) || [];
|
|
22
|
+
Reflect.defineMetadata(TRANSLATABLE_KEY, [...existingProperties, propertyKey], target.constructor);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { Field, FieldOptions } from '@nestjs/graphql';
|
|
2
|
+
import { ApiProperty, ApiPropertyOptional, ApiPropertyOptions } from '@nestjs/swagger';
|
|
3
|
+
import { EnumAllowedTypes } from '@nestjs/swagger/dist/interfaces/schema-object-metadata.interface';
|
|
4
|
+
import { Type } from 'class-transformer';
|
|
5
|
+
import {
|
|
6
|
+
IsArray,
|
|
7
|
+
IsBoolean,
|
|
8
|
+
IsDate,
|
|
9
|
+
IsEnum,
|
|
10
|
+
IsNotEmpty,
|
|
11
|
+
IsNumber,
|
|
12
|
+
IsObject,
|
|
13
|
+
IsOptional,
|
|
14
|
+
IsString,
|
|
15
|
+
ValidateIf,
|
|
16
|
+
ValidateNested,
|
|
17
|
+
ValidationOptions,
|
|
18
|
+
} from 'class-validator';
|
|
19
|
+
import { GraphQLScalarType } from 'graphql';
|
|
20
|
+
|
|
21
|
+
import { RoleEnum } from '../enums/role.enum';
|
|
22
|
+
import { Restricted, RestrictedType } from './restricted.decorator';
|
|
23
|
+
|
|
24
|
+
export interface UnifiedFieldOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Indicates whether the property is an array.
|
|
27
|
+
*
|
|
28
|
+
* This value is typically determined automatically based on the property type or metadata.
|
|
29
|
+
*
|
|
30
|
+
* However, cases involving complex or dynamic type definitions (e.g., union types, generics, or factory functions)
|
|
31
|
+
* may result in inaccurate detection.
|
|
32
|
+
*
|
|
33
|
+
* When in doubt, explicitly set this property to ensure correct behavior.
|
|
34
|
+
*/
|
|
35
|
+
array?: boolean;
|
|
36
|
+
/** Description used for both Swagger & Gql */
|
|
37
|
+
description?: string;
|
|
38
|
+
/** Enum for class-validator */
|
|
39
|
+
enum?: { enum: EnumAllowedTypes; options?: ValidationOptions };
|
|
40
|
+
/** Example value for swagger api documentation */
|
|
41
|
+
example?: any;
|
|
42
|
+
/** Options for graphql */
|
|
43
|
+
gqlOptions?: FieldOptions;
|
|
44
|
+
/** Default: false */
|
|
45
|
+
isOptional?: boolean;
|
|
46
|
+
/** Restricted roles */
|
|
47
|
+
roles?: RestrictedType | RoleEnum | RoleEnum[];
|
|
48
|
+
/** Options for swagger api documentation */
|
|
49
|
+
swaggerApiOptions?: ApiPropertyOptions;
|
|
50
|
+
/** Type of the field, if not specified, it will be determined automatically.
|
|
51
|
+
*
|
|
52
|
+
* Required if the field is an array (inferred automatically or via the array flag).
|
|
53
|
+
*
|
|
54
|
+
* Enums should be defined via the enum option.
|
|
55
|
+
*
|
|
56
|
+
* Supports:
|
|
57
|
+
* - A factory function that returns the type: `() => MyType`
|
|
58
|
+
* - A GraphQL scalar: `GraphQLScalarType`
|
|
59
|
+
* - A class constructor: `MyClass`
|
|
60
|
+
* */
|
|
61
|
+
type?: (() => any) | GraphQLScalarType | (new (...args: any[]) => any) | Record<number | string, number | string>; // Enums;
|
|
62
|
+
/** Condition for validation */
|
|
63
|
+
validateIf?: (obj: any, val: any) => boolean;
|
|
64
|
+
/** Validation options for class-validator */
|
|
65
|
+
validationOptions?: ValidationOptions;
|
|
66
|
+
/** Custom validators, when using this option, all built-in validators are ignored */
|
|
67
|
+
validator?: (opts: ValidationOptions) => PropertyDecorator[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function UnifiedField(opts: UnifiedFieldOptions = {}): PropertyDecorator {
|
|
71
|
+
return (target: any, propertyKey: string | symbol) => {
|
|
72
|
+
const metadataType = Reflect.getMetadata('design:type', target, propertyKey);
|
|
73
|
+
const userType = opts.type;
|
|
74
|
+
const isArrayField = opts.array === true || metadataType === Array;
|
|
75
|
+
|
|
76
|
+
// Throwing because metatype only returns the generic array, but not the type of the array items
|
|
77
|
+
if (metadataType === Array && !userType) {
|
|
78
|
+
throw new Error(`Array field '${String(propertyKey)}' of '${String(target)}' must have an explicit type`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const resolvedTypeFn = (): any => {
|
|
82
|
+
if (opts.enum?.enum) {
|
|
83
|
+
return opts.enum.enum; // Ensure enums are handled directly
|
|
84
|
+
}
|
|
85
|
+
if (userType) {
|
|
86
|
+
if (userType instanceof GraphQLScalarType) { // Case if it's a scalar
|
|
87
|
+
return userType;
|
|
88
|
+
}
|
|
89
|
+
if (
|
|
90
|
+
typeof userType === 'function'
|
|
91
|
+
&& userType.prototype
|
|
92
|
+
&& userType.prototype.constructor === userType
|
|
93
|
+
) { // Case if it's a function
|
|
94
|
+
return userType;
|
|
95
|
+
}
|
|
96
|
+
try { // case if its a factory
|
|
97
|
+
return (userType as () => any)();
|
|
98
|
+
} catch {
|
|
99
|
+
return userType;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return metadataType;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const baseType = resolvedTypeFn();
|
|
106
|
+
|
|
107
|
+
// Prepare merged options
|
|
108
|
+
const gqlOpts: FieldOptions = { ...opts.gqlOptions };
|
|
109
|
+
const swaggerOpts: ApiPropertyOptions = { ...opts.swaggerApiOptions };
|
|
110
|
+
const valOpts: ValidationOptions = { ...opts.validationOptions };
|
|
111
|
+
|
|
112
|
+
// Optionality
|
|
113
|
+
if (opts.isOptional) {
|
|
114
|
+
gqlOpts.nullable = true;
|
|
115
|
+
swaggerOpts.nullable = true;
|
|
116
|
+
} else {
|
|
117
|
+
gqlOpts.nullable = false;
|
|
118
|
+
swaggerOpts.nullable = false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Description
|
|
122
|
+
const defaultDesc = opts.description ?? `${String(propertyKey)} of ${target.constructor.name}`;
|
|
123
|
+
gqlOpts.description = gqlOpts.description ?? defaultDesc;
|
|
124
|
+
swaggerOpts.description = swaggerOpts.description ?? defaultDesc;
|
|
125
|
+
|
|
126
|
+
// Swagger example
|
|
127
|
+
if (opts.example !== undefined) {
|
|
128
|
+
swaggerOpts.example = swaggerOpts.example ?? opts.example;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (opts.enum && opts.enum.enum) {
|
|
132
|
+
swaggerOpts.enum = opts.enum.enum;
|
|
133
|
+
}
|
|
134
|
+
// Array handling
|
|
135
|
+
if (isArrayField) {
|
|
136
|
+
swaggerOpts.isArray = true;
|
|
137
|
+
IsArray(valOpts)(target, propertyKey);
|
|
138
|
+
valOpts.each = true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (opts.isOptional) {
|
|
142
|
+
IsOptional(valOpts)(target, propertyKey);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Type function for gql
|
|
146
|
+
const gqlTypeFn = isArrayField
|
|
147
|
+
? () => [opts.enum?.enum || resolvedTypeFn()]
|
|
148
|
+
: () => opts.enum?.enum || resolvedTypeFn();
|
|
149
|
+
|
|
150
|
+
// Gql decorator
|
|
151
|
+
Field(gqlTypeFn, gqlOpts)(target, propertyKey);
|
|
152
|
+
|
|
153
|
+
const ApiDec = opts.isOptional ? ApiPropertyOptional : ApiProperty;
|
|
154
|
+
|
|
155
|
+
ApiDec({
|
|
156
|
+
deprecated: swaggerOpts.deprecated,
|
|
157
|
+
description: swaggerOpts.description,
|
|
158
|
+
enum: swaggerOpts.enum,
|
|
159
|
+
example: swaggerOpts.example,
|
|
160
|
+
examples: swaggerOpts.examples,
|
|
161
|
+
isArray: swaggerOpts.isArray,
|
|
162
|
+
nullable: swaggerOpts.nullable,
|
|
163
|
+
pattern: swaggerOpts.pattern,
|
|
164
|
+
type: () => resolvedTypeFn(),
|
|
165
|
+
})(target, propertyKey);
|
|
166
|
+
|
|
167
|
+
// Conditional validation
|
|
168
|
+
if (opts.validateIf) {
|
|
169
|
+
ValidateIf(opts.validateIf)(target, propertyKey);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// isOptional validation
|
|
173
|
+
if (opts.isOptional) {
|
|
174
|
+
IsOptional()(target, propertyKey);
|
|
175
|
+
} else {
|
|
176
|
+
IsNotEmpty()(target, propertyKey);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Custom or builtin validator
|
|
180
|
+
if (opts.validator) {
|
|
181
|
+
opts.validator(valOpts).forEach(d => d(target, propertyKey));
|
|
182
|
+
} else {
|
|
183
|
+
const validator = getBuiltInValidator(baseType, valOpts, isArrayField, target);
|
|
184
|
+
if (validator) {
|
|
185
|
+
validator(target, propertyKey);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Enum validation
|
|
190
|
+
if (opts.enum) {
|
|
191
|
+
IsEnum(opts.enum.enum, opts.enum.options)(target, propertyKey);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Check if it's a primitive, if not apply transform
|
|
195
|
+
if (!isPrimitive(baseType) && !opts.enum && !isGraphQLScalar(baseType)) {
|
|
196
|
+
Type(() => baseType)(target, propertyKey);
|
|
197
|
+
ValidateNested({ each: isArrayField })(target, propertyKey);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Roles
|
|
201
|
+
if (opts.roles) {
|
|
202
|
+
const rolesArr = Array.isArray(opts.roles) ? opts.roles : [opts.roles];
|
|
203
|
+
Restricted(...rolesArr)(target, propertyKey);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function getBuiltInValidator(
|
|
209
|
+
type: any,
|
|
210
|
+
opts: ValidationOptions,
|
|
211
|
+
each: boolean,
|
|
212
|
+
target: any,
|
|
213
|
+
): ((t: any, k: string | symbol) => void) | null {
|
|
214
|
+
const map = new Map<any, PropertyDecorator>([
|
|
215
|
+
[Boolean, IsBoolean(opts)],
|
|
216
|
+
[Date, IsDate(opts)],
|
|
217
|
+
[Number, IsNumber({}, opts)],
|
|
218
|
+
[Object, IsObject(opts)],
|
|
219
|
+
[String, IsString(opts)],
|
|
220
|
+
]);
|
|
221
|
+
const decorator = map.get(type);
|
|
222
|
+
if (!decorator) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
if (each) {
|
|
226
|
+
return (t, k) => decorator(target, k);
|
|
227
|
+
}
|
|
228
|
+
return decorator;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function isGraphQLScalar(type: any): boolean {
|
|
232
|
+
// CustomScalar check (The CustomScalar interface implements these functions below)
|
|
233
|
+
return type
|
|
234
|
+
&& typeof type === 'function'
|
|
235
|
+
&& typeof type.prototype?.serialize === 'function'
|
|
236
|
+
&& typeof type.prototype?.parseValue === 'function'
|
|
237
|
+
&& typeof type.prototype?.parseLiteral === 'function'
|
|
238
|
+
|| type instanceof GraphQLScalarType;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function isPrimitive(fn: any): boolean {
|
|
242
|
+
return [Boolean, Date, Number, String].includes(fn);
|
|
243
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as dotenv from 'dotenv';
|
|
2
|
+
import _ = require('lodash');
|
|
2
3
|
import * as process from 'node:process';
|
|
3
4
|
import { join } from 'path';
|
|
4
5
|
|
|
5
|
-
import _ = require('lodash');
|
|
6
|
-
|
|
7
6
|
/**
|
|
8
7
|
* Helper class for configurations
|
|
9
8
|
* @deprecated use functions directly
|
|
@@ -28,8 +27,6 @@ export default class Config {
|
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
30
|
/**
|
|
34
31
|
* Get environment configuration (deeply merged into config object set via options)
|
|
35
32
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FieldNode, GraphQLResolveInfo, SelectionNode } from 'graphql';
|
|
2
|
+
import _ = require('lodash');
|
|
2
3
|
import { Document, Model, PopulateOptions, Query, Types } from 'mongoose';
|
|
3
4
|
|
|
4
5
|
import { ResolveSelector } from '../interfaces/resolve-selector.interface';
|
|
@@ -8,8 +9,6 @@ import { IdsType } from '../types/ids.type';
|
|
|
8
9
|
import { StringOrObjectId } from '../types/string-or-object-id.type';
|
|
9
10
|
import { removePropertiesDeep } from './input.helper';
|
|
10
11
|
|
|
11
|
-
import _ = require('lodash');
|
|
12
|
-
|
|
13
12
|
// =====================================================================================================================
|
|
14
13
|
// Export functions
|
|
15
14
|
// =====================================================================================================================
|
|
@@ -4,6 +4,8 @@ import { validate } from 'class-validator';
|
|
|
4
4
|
import { ValidatorOptions } from 'class-validator/types/validation/ValidatorOptions';
|
|
5
5
|
import { Kind } from 'graphql/index';
|
|
6
6
|
import * as inspector from 'inspector';
|
|
7
|
+
import _ = require('lodash');
|
|
8
|
+
import rfdc = require('rfdc');
|
|
7
9
|
import * as util from 'util';
|
|
8
10
|
|
|
9
11
|
import { checkRestricted } from '../decorators/restricted.decorator';
|
|
@@ -12,9 +14,6 @@ import { RoleEnum } from '../enums/role.enum';
|
|
|
12
14
|
import { merge } from './config.helper';
|
|
13
15
|
import { equalIds } from './db.helper';
|
|
14
16
|
|
|
15
|
-
import _ = require('lodash');
|
|
16
|
-
import rfdc = require('rfdc');
|
|
17
|
-
|
|
18
17
|
/**
|
|
19
18
|
* Helper class for inputs
|
|
20
19
|
* @deprecated use functions directly
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { UnauthorizedException } from '@nestjs/common';
|
|
2
|
+
import bcrypt = require('bcrypt');
|
|
2
3
|
import { plainToInstance } from 'class-transformer';
|
|
3
4
|
import { sha256 } from 'js-sha256';
|
|
5
|
+
import _ = require('lodash');
|
|
4
6
|
import { Types } from 'mongoose';
|
|
5
7
|
|
|
8
|
+
import { getTranslatablePropertyKeys } from '../decorators/translatable.decorator';
|
|
6
9
|
import { RoleEnum } from '../enums/role.enum';
|
|
7
10
|
import { PrepareInputOptions } from '../interfaces/prepare-input-options.interface';
|
|
8
11
|
import { PrepareOutputOptions } from '../interfaces/prepare-output-options.interface';
|
|
@@ -12,9 +15,6 @@ import { ConfigService } from '../services/config.service';
|
|
|
12
15
|
import { getStringIds } from './db.helper';
|
|
13
16
|
import { clone, processDeep } from './input.helper';
|
|
14
17
|
|
|
15
|
-
import bcrypt = require('bcrypt');
|
|
16
|
-
import _ = require('lodash');
|
|
17
|
-
|
|
18
18
|
/**
|
|
19
19
|
* Helper class for services
|
|
20
20
|
* @deprecated use functions directly
|
|
@@ -187,6 +187,7 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
|
|
|
187
187
|
circles?: boolean;
|
|
188
188
|
clone?: boolean;
|
|
189
189
|
getNewArray?: boolean;
|
|
190
|
+
language?: string;
|
|
190
191
|
objectIdsToStrings?: boolean;
|
|
191
192
|
proto?: boolean;
|
|
192
193
|
removeSecrets?: boolean;
|
|
@@ -273,6 +274,20 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
|
|
|
273
274
|
}
|
|
274
275
|
}
|
|
275
276
|
|
|
277
|
+
// Add translated values of current selected language if _translations object exists
|
|
278
|
+
if (config.targetModel && config.language && typeof output === 'object' && '_translations' in output) {
|
|
279
|
+
const translation = output._translations?.[options.language];
|
|
280
|
+
|
|
281
|
+
if (typeof translation === 'object') {
|
|
282
|
+
const keys = getTranslatablePropertyKeys(config.targetModel);
|
|
283
|
+
for (const key of keys) {
|
|
284
|
+
if (translation[key] != null) {
|
|
285
|
+
output[key] = translation[key];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
276
291
|
// Return prepared output
|
|
277
292
|
return output;
|
|
278
293
|
}
|