@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.
Files changed (129) hide show
  1. package/dist/core/common/args/filter.args.js +6 -8
  2. package/dist/core/common/args/filter.args.js.map +1 -1
  3. package/dist/core/common/args/pagination.args.js +16 -16
  4. package/dist/core/common/args/pagination.args.js.map +1 -1
  5. package/dist/core/common/decorators/graphql-service-options.decorator.js +5 -0
  6. package/dist/core/common/decorators/graphql-service-options.decorator.js.map +1 -1
  7. package/dist/core/common/decorators/rest-service-options.decorator.d.ts +2 -0
  8. package/dist/core/common/decorators/rest-service-options.decorator.js +14 -0
  9. package/dist/core/common/decorators/rest-service-options.decorator.js.map +1 -0
  10. package/dist/core/common/decorators/restricted.decorator.js +1 -1
  11. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  12. package/dist/core/common/decorators/translatable.decorator.d.ts +3 -0
  13. package/dist/core/common/decorators/translatable.decorator.js +22 -0
  14. package/dist/core/common/decorators/translatable.decorator.js.map +1 -0
  15. package/dist/core/common/decorators/unified-field.decorator.d.ts +25 -0
  16. package/dist/core/common/decorators/unified-field.decorator.js +144 -0
  17. package/dist/core/common/decorators/unified-field.decorator.js.map +1 -0
  18. package/dist/core/common/helpers/config.helper.js +1 -1
  19. package/dist/core/common/helpers/config.helper.js.map +1 -1
  20. package/dist/core/common/helpers/db.helper.js +1 -1
  21. package/dist/core/common/helpers/db.helper.js.map +1 -1
  22. package/dist/core/common/helpers/graphql.helper.js.map +1 -1
  23. package/dist/core/common/helpers/input.helper.js +2 -2
  24. package/dist/core/common/helpers/input.helper.js.map +1 -1
  25. package/dist/core/common/helpers/service.helper.d.ts +1 -0
  26. package/dist/core/common/helpers/service.helper.js +14 -2
  27. package/dist/core/common/helpers/service.helper.js.map +1 -1
  28. package/dist/core/common/inputs/combined-filter.input.js +8 -4
  29. package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
  30. package/dist/core/common/inputs/filter.input.js +10 -7
  31. package/dist/core/common/inputs/filter.input.js.map +1 -1
  32. package/dist/core/common/inputs/single-filter.input.js +26 -18
  33. package/dist/core/common/inputs/single-filter.input.js.map +1 -1
  34. package/dist/core/common/inputs/sort.input.js +10 -4
  35. package/dist/core/common/inputs/sort.input.js.map +1 -1
  36. package/dist/core/common/models/core-persistence.model.js +18 -11
  37. package/dist/core/common/models/core-persistence.model.js.map +1 -1
  38. package/dist/core/common/pipes/map-and-validate.pipe.js +12 -3
  39. package/dist/core/common/pipes/map-and-validate.pipe.js.map +1 -1
  40. package/dist/core/common/services/brevo.service.js +1 -1
  41. package/dist/core/common/services/brevo.service.js.map +1 -1
  42. package/dist/core/common/services/config.service.js +1 -1
  43. package/dist/core/common/services/config.service.js.map +1 -1
  44. package/dist/core/common/services/email.service.js +1 -1
  45. package/dist/core/common/services/email.service.js.map +1 -1
  46. package/dist/core/common/services/model-doc.service.js +1 -1
  47. package/dist/core/common/services/model-doc.service.js.map +1 -1
  48. package/dist/core/common/services/module.service.js +2 -1
  49. package/dist/core/common/services/module.service.js.map +1 -1
  50. package/dist/core/common/services/template.service.js +2 -2
  51. package/dist/core/common/services/template.service.js.map +1 -1
  52. package/dist/core/modules/auth/core-auth.model.js +10 -11
  53. package/dist/core/modules/auth/core-auth.model.js.map +1 -1
  54. package/dist/core/modules/auth/guards/auth.guard.js +1 -1
  55. package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
  56. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js +25 -21
  57. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js.map +1 -1
  58. package/dist/core/modules/auth/services/core-auth.service.js +1 -1
  59. package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
  60. package/dist/core/modules/health-check/core-health-check-result.model.js +18 -11
  61. package/dist/core/modules/health-check/core-health-check-result.model.js.map +1 -1
  62. package/dist/core/modules/user/core-user.service.js +2 -2
  63. package/dist/core/modules/user/core-user.service.js.map +1 -1
  64. package/dist/core/modules/user/inputs/core-user-create.input.js +6 -3
  65. package/dist/core/modules/user/inputs/core-user-create.input.js.map +1 -1
  66. package/dist/core/modules/user/inputs/core-user.input.js +32 -18
  67. package/dist/core/modules/user/inputs/core-user.input.js.map +1 -1
  68. package/dist/core.module.js +1 -1
  69. package/dist/core.module.js.map +1 -1
  70. package/dist/index.d.ts +3 -0
  71. package/dist/index.js +3 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/main.js +2 -2
  74. package/dist/main.js.map +1 -1
  75. package/dist/server/common/models/persistence.model.js +1 -1
  76. package/dist/server/common/models/persistence.model.js.map +1 -1
  77. package/dist/server/modules/auth/inputs/auth-sign-up.input.js +11 -4
  78. package/dist/server/modules/auth/inputs/auth-sign-up.input.js.map +1 -1
  79. package/dist/server/modules/file/file.resolver.js +2 -2
  80. package/dist/server/modules/file/file.resolver.js.map +1 -1
  81. package/dist/server/modules/user/outputs/find-and-count-users-result.output.js +11 -4
  82. package/dist/server/modules/user/outputs/find-and-count-users-result.output.js.map +1 -1
  83. package/dist/server/modules/user/user.service.js +1 -1
  84. package/dist/server/modules/user/user.service.js.map +1 -1
  85. package/dist/test/test.helper.js +1 -1
  86. package/dist/test/test.helper.js.map +1 -1
  87. package/dist/tsconfig.build.tsbuildinfo +1 -1
  88. package/package.json +23 -23
  89. package/src/core/common/args/filter.args.ts +7 -10
  90. package/src/core/common/args/pagination.args.ts +17 -17
  91. package/src/core/common/decorators/graphql-service-options.decorator.ts +8 -0
  92. package/src/core/common/decorators/rest-service-options.decorator.ts +22 -0
  93. package/src/core/common/decorators/restricted.decorator.ts +1 -2
  94. package/src/core/common/decorators/translatable.decorator.ts +24 -0
  95. package/src/core/common/decorators/unified-field.decorator.ts +243 -0
  96. package/src/core/common/helpers/config.helper.ts +1 -4
  97. package/src/core/common/helpers/db.helper.ts +1 -2
  98. package/src/core/common/helpers/graphql.helper.ts +0 -1
  99. package/src/core/common/helpers/input.helper.ts +2 -3
  100. package/src/core/common/helpers/service.helper.ts +18 -3
  101. package/src/core/common/inputs/combined-filter.input.ts +9 -5
  102. package/src/core/common/inputs/filter.input.ts +14 -11
  103. package/src/core/common/inputs/single-filter.input.ts +28 -19
  104. package/src/core/common/inputs/sort.input.ts +11 -5
  105. package/src/core/common/models/core-persistence.model.ts +19 -12
  106. package/src/core/common/pipes/map-and-validate.pipe.ts +15 -4
  107. package/src/core/common/services/brevo.service.ts +1 -2
  108. package/src/core/common/services/config.service.ts +1 -2
  109. package/src/core/common/services/email.service.ts +1 -2
  110. package/src/core/common/services/model-doc.service.ts +1 -2
  111. package/src/core/common/services/module.service.ts +2 -2
  112. package/src/core/common/services/template.service.ts +2 -3
  113. package/src/core/modules/auth/core-auth.model.ts +11 -12
  114. package/src/core/modules/auth/guards/auth.guard.ts +1 -2
  115. package/src/core/modules/auth/inputs/core-auth-sign-in.input.ts +27 -23
  116. package/src/core/modules/auth/services/core-auth.service.ts +1 -2
  117. package/src/core/modules/health-check/core-health-check-result.model.ts +19 -12
  118. package/src/core/modules/user/core-user.service.ts +2 -3
  119. package/src/core/modules/user/inputs/core-user-create.input.ts +7 -4
  120. package/src/core/modules/user/inputs/core-user.input.ts +34 -20
  121. package/src/core.module.ts +1 -2
  122. package/src/index.ts +3 -0
  123. package/src/main.ts +2 -3
  124. package/src/server/common/models/persistence.model.ts +1 -2
  125. package/src/server/modules/auth/inputs/auth-sign-up.input.ts +12 -5
  126. package/src/server/modules/file/file.resolver.ts +2 -3
  127. package/src/server/modules/user/outputs/find-and-count-users-result.output.ts +12 -5
  128. package/src/server/modules/user/user.service.ts +1 -2
  129. 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.0.0",
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.17",
74
- "@nestjs/core": "11.0.17",
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.17",
80
- "@nestjs/schedule": "5.0.1",
81
- "@nestjs/swagger": "11.1.3",
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.1",
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.10.0",
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.15.0",
101
- "mongoose": "7.8.6",
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": "6.10.1",
105
- "nodemon": "3.1.9",
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": "1.0.8",
118
- "@nestjs/cli": "11.0.6",
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.17",
121
- "@swc/cli": "0.6.0",
122
- "@swc/core": "1.11.21",
123
- "@swc/jest": "0.2.37",
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.14.1",
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.30.1",
136
- "@typescript-eslint/parser": "8.30.1",
135
+ "@typescript-eslint/eslint-plugin": "8.32.0",
136
+ "@typescript-eslint/parser": "8.32.0",
137
137
  "coffeescript": "2.7.0",
138
- "eslint": "9.24.0",
139
- "eslint-config-prettier": "10.1.2",
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, Field } from '@nestjs/graphql';
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
- @Field(() => FilterInput, {
13
- description: 'Input for filtering',
14
- nullable: true,
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
- @Field(() => Number, {
23
- description:
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, Field, Int } from '@nestjs/graphql';
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
- @Field(() => Int, {
13
+ @UnifiedField({
14
14
  description: 'Limit specifies the maximum number of elements found that are to be returned',
15
- nullable: true,
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
- @Field(() => Int, {
23
+ @UnifiedField({
24
24
  description: 'Alias for skip',
25
- nullable: true,
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
- @Field(() => Int, {
33
+ @UnifiedField({
34
34
  description: 'Skip specifies how many found elements should be skipped on return',
35
- nullable: true,
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
- @Field(() => [SortInput], {
43
+ @UnifiedField({
44
44
  description: 'Sorting the returned elements',
45
- nullable: true,
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
- @Field(() => Int, {
53
+ @UnifiedField({
54
54
  description: 'Alias for limit',
55
- nullable: true,
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
  // =====================================================================================================================
@@ -12,7 +12,6 @@ import {
12
12
  ValueNode,
13
13
  VariableNode,
14
14
  } from 'graphql';
15
-
16
15
  import _ = require('lodash');
17
16
 
18
17
  /**
@@ -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
  }