@lenne.tech/nest-server 3.1.2 → 3.3.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 (122) hide show
  1. package/dist/core/common/args/filter.args.d.ts +5 -0
  2. package/dist/core/common/args/filter.args.js +10 -0
  3. package/dist/core/common/args/filter.args.js.map +1 -1
  4. package/dist/core/common/args/pagination.args.d.ts +8 -1
  5. package/dist/core/common/args/pagination.args.js +24 -6
  6. package/dist/core/common/args/pagination.args.js.map +1 -1
  7. package/dist/core/common/decorators/restricted.decorator.d.ts +3 -0
  8. package/dist/core/common/decorators/restricted.decorator.js +16 -5
  9. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  10. package/dist/core/common/helpers/context.helper.js +15 -5
  11. package/dist/core/common/helpers/context.helper.js.map +1 -1
  12. package/dist/core/common/helpers/filter.helper.d.ts +3 -3
  13. package/dist/core/common/helpers/filter.helper.js.map +1 -1
  14. package/dist/core/common/helpers/input.helper.js +13 -7
  15. package/dist/core/common/helpers/input.helper.js.map +1 -1
  16. package/dist/core/common/helpers/service.helper.d.ts +6 -8
  17. package/dist/core/common/helpers/service.helper.js +34 -12
  18. package/dist/core/common/helpers/service.helper.js.map +1 -1
  19. package/dist/core/common/inputs/combined-filter.input.d.ts +8 -2
  20. package/dist/core/common/inputs/combined-filter.input.js +16 -5
  21. package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
  22. package/dist/core/common/inputs/core-input.input.d.ts +8 -0
  23. package/dist/core/common/inputs/core-input.input.js +15 -0
  24. package/dist/core/common/inputs/core-input.input.js.map +1 -0
  25. package/dist/core/common/inputs/filter.input.d.ts +7 -1
  26. package/dist/core/common/inputs/filter.input.js +14 -1
  27. package/dist/core/common/inputs/filter.input.js.map +1 -1
  28. package/dist/core/common/inputs/single-filter.input.d.ts +2 -1
  29. package/dist/core/common/inputs/single-filter.input.js +10 -1
  30. package/dist/core/common/inputs/single-filter.input.js.map +1 -1
  31. package/dist/core/common/inputs/sort.input.d.ts +2 -1
  32. package/dist/core/common/inputs/sort.input.js +7 -1
  33. package/dist/core/common/inputs/sort.input.js.map +1 -1
  34. package/dist/core/common/interceptors/check-response.interceptor.js +1 -1
  35. package/dist/core/common/interceptors/check-response.interceptor.js.map +1 -1
  36. package/dist/core/common/models/core-model.model.d.ts +6 -0
  37. package/dist/core/common/models/core-model.model.js +10 -3
  38. package/dist/core/common/models/core-model.model.js.map +1 -1
  39. package/dist/core/common/models/core-persistence.model.d.ts +5 -29
  40. package/dist/core/common/models/core-persistence.model.js +19 -41
  41. package/dist/core/common/models/core-persistence.model.js.map +1 -1
  42. package/dist/core/common/pipes/check-input.pipe.d.ts +2 -3
  43. package/dist/core/common/pipes/check-input.pipe.js +5 -20
  44. package/dist/core/common/pipes/check-input.pipe.js.map +1 -1
  45. package/dist/core/common/pipes/map-and-validate.pipe.d.ts +4 -0
  46. package/dist/core/common/pipes/map-and-validate.pipe.js +40 -0
  47. package/dist/core/common/pipes/map-and-validate.pipe.js.map +1 -0
  48. package/dist/core/common/types/plain-input.type.d.ts +3 -0
  49. package/dist/core/common/types/plain-input.type.js +3 -0
  50. package/dist/core/common/types/plain-input.type.js.map +1 -0
  51. package/dist/core/modules/auth/core-auth.model.d.ts +3 -1
  52. package/dist/core/modules/auth/core-auth.model.js +7 -1
  53. package/dist/core/modules/auth/core-auth.model.js.map +1 -1
  54. package/dist/core/modules/auth/core-auth.resolver.d.ts +1 -1
  55. package/dist/core/modules/user/core-user.model.d.ts +3 -0
  56. package/dist/core/modules/user/core-user.model.js +10 -4
  57. package/dist/core/modules/user/core-user.model.js.map +1 -1
  58. package/dist/core/modules/user/core-user.service.d.ts +9 -13
  59. package/dist/core/modules/user/core-user.service.js +38 -67
  60. package/dist/core/modules/user/core-user.service.js.map +1 -1
  61. package/dist/core/modules/user/inputs/core-user-create.input.js +4 -0
  62. package/dist/core/modules/user/inputs/core-user-create.input.js.map +1 -1
  63. package/dist/core/modules/user/inputs/core-user.input.d.ts +2 -1
  64. package/dist/core/modules/user/inputs/core-user.input.js +12 -2
  65. package/dist/core/modules/user/inputs/core-user.input.js.map +1 -1
  66. package/dist/core.module.js +2 -3
  67. package/dist/core.module.js.map +1 -1
  68. package/dist/index.d.ts +6 -2
  69. package/dist/index.js +6 -2
  70. package/dist/index.js.map +1 -1
  71. package/dist/server/common/models/persistence.model.d.ts +1 -0
  72. package/dist/server/common/models/persistence.model.js +4 -0
  73. package/dist/server/common/models/persistence.model.js.map +1 -1
  74. package/dist/server/modules/auth/auth.model.d.ts +1 -0
  75. package/dist/server/modules/auth/auth.model.js +4 -0
  76. package/dist/server/modules/auth/auth.model.js.map +1 -1
  77. package/dist/server/modules/user/inputs/user-create.input.js.map +1 -1
  78. package/dist/server/modules/user/inputs/user.input.js.map +1 -1
  79. package/dist/server/modules/user/user.model.d.ts +3 -2
  80. package/dist/server/modules/user/user.model.js +9 -5
  81. package/dist/server/modules/user/user.model.js.map +1 -1
  82. package/dist/server/modules/user/user.resolver.d.ts +2 -2
  83. package/dist/server/modules/user/user.resolver.js +10 -12
  84. package/dist/server/modules/user/user.resolver.js.map +1 -1
  85. package/dist/server/modules/user/user.service.d.ts +7 -9
  86. package/dist/server/modules/user/user.service.js +12 -4
  87. package/dist/server/modules/user/user.service.js.map +1 -1
  88. package/dist/tsconfig.build.tsbuildinfo +1 -1
  89. package/package.json +1 -1
  90. package/src/core/common/args/filter.args.ts +22 -1
  91. package/src/core/common/args/pagination.args.ts +42 -7
  92. package/src/core/common/decorators/restricted.decorator.ts +24 -5
  93. package/src/core/common/helpers/context.helper.ts +14 -3
  94. package/src/core/common/helpers/filter.helper.ts +3 -3
  95. package/src/core/common/helpers/input.helper.ts +17 -11
  96. package/src/core/common/helpers/service.helper.ts +42 -19
  97. package/src/core/common/inputs/combined-filter.input.ts +30 -9
  98. package/src/core/common/inputs/core-input.input.ts +36 -0
  99. package/src/core/common/inputs/filter.input.ts +27 -3
  100. package/src/core/common/inputs/single-filter.input.ts +7 -6
  101. package/src/core/common/inputs/sort.input.ts +4 -3
  102. package/src/core/common/interceptors/check-response.interceptor.ts +2 -2
  103. package/src/core/common/models/core-model.model.ts +30 -1
  104. package/src/core/common/models/core-persistence.model.ts +33 -120
  105. package/src/core/common/pipes/check-input.pipe.ts +13 -33
  106. package/src/core/common/pipes/map-and-validate.pipe.ts +32 -0
  107. package/src/core/common/types/plain-input.type.ts +6 -0
  108. package/src/core/modules/auth/core-auth.model.ts +15 -1
  109. package/src/core/modules/auth/core-auth.resolver.ts +1 -1
  110. package/src/core/modules/user/core-user.model.ts +17 -4
  111. package/src/core/modules/user/core-user.service.ts +59 -115
  112. package/src/core/modules/user/inputs/core-user-create.input.ts +5 -1
  113. package/src/core/modules/user/inputs/core-user.input.ts +13 -8
  114. package/src/core.module.ts +11 -5
  115. package/src/index.ts +6 -2
  116. package/src/server/common/models/persistence.model.ts +13 -0
  117. package/src/server/modules/auth/auth.model.ts +13 -0
  118. package/src/server/modules/user/inputs/user-create.input.ts +4 -0
  119. package/src/server/modules/user/inputs/user.input.ts +4 -0
  120. package/src/server/modules/user/user.model.ts +18 -5
  121. package/src/server/modules/user/user.resolver.ts +15 -19
  122. package/src/server/modules/user/user.service.ts +22 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "3.1.2",
3
+ "version": "3.3.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",
@@ -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 = 25;
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 = 0;
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: 0,
38
+ defaultValue: undefined,
37
39
  })
38
40
  @IsOptional()
39
- skip?: number = 0;
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 = 25;
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,18 +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
- // User is not the owner
75
- delete data[propertyKey];
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
+ }
91
+ continue;
76
92
  }
77
93
  } else {
78
94
  // The user does not have the required rights
79
- delete data[propertyKey];
95
+ if (config.throwError) {
96
+ throw new UnauthorizedException('Current user is not allowed to set ' + propertyKey);
97
+ }
98
+ continue;
80
99
  }
81
100
  }
82
101
  }
83
102
 
84
103
  // Check property data
85
- data[propertyKey] = checkRestricted(data[propertyKey], user, processedObjects);
104
+ data[propertyKey] = checkRestricted(data[propertyKey], user, options, processedObjects);
86
105
  }
87
106
 
88
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
- const ctx: any = GqlExecutionContext.create(context).getContext();
20
- let args: any = GqlExecutionContext.create(context).getArgs();
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().getRequest();
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 { plainToClass } from 'class-transformer';
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
- // Remove restricted values if roles are missing
25
- value = checkRestricted(value, user);
26
-
27
- // Check values
28
- if (metatype) {
29
- const object = plainToClass(metatype, value);
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
 
@@ -11,26 +11,35 @@ export class ServiceHelper {
11
11
  * Prepare input before save
12
12
  */
13
13
  static async prepareInput(
14
- input: { [key: string]: any },
14
+ input: Record<string, any>,
15
15
  currentUser: { [key: string]: any; id: string },
16
- options: { [key: string]: any; create?: boolean; clone?: boolean } = {},
17
- ...args: any[]
16
+ options: { [key: string]: any; create?: boolean; clone?: boolean; removeUndefined?: boolean } = {}
18
17
  ) {
19
18
  // Configuration
20
19
  const config = {
21
- checkRoles: true,
20
+ checkRoles: false,
22
21
  clone: false,
23
22
  create: false,
23
+ removeUndefined: false,
24
24
  ...options,
25
25
  };
26
26
 
27
- // Clone output
27
+ // Clone input
28
28
  if (config.clone) {
29
- input = JSON.parse(JSON.stringify(input));
29
+ if (input.mapDeep && typeof input.mapDeep === 'function') {
30
+ input = Object.getPrototypeOf(input).mapDeep(input);
31
+ } else {
32
+ input = _.cloneDeep(input);
33
+ }
34
+ }
35
+
36
+ // Remove undefined properties to avoid unwanted overwrites
37
+ if (config.removeUndefined) {
38
+ Object.keys(input).forEach((key) => input[key] === undefined && delete input[key]);
30
39
  }
31
40
 
32
41
  // Process roles
33
- if (input.roles && config.checkRoles && (!currentUser?.hasRole || !currentUser.hasRole(RoleEnum.ADMIN))) {
42
+ if (config.checkRoles && input.roles && (!currentUser?.hasRole || !currentUser.hasRole(RoleEnum.ADMIN))) {
34
43
  if (!(currentUser as any)?.roles) {
35
44
  throw new UnauthorizedException('Missing roles of current user');
36
45
  } else {
@@ -67,37 +76,51 @@ export class ServiceHelper {
67
76
  */
68
77
  static async prepareOutput<T = Record<string, any>>(
69
78
  output: any,
70
- userModel: new () => any,
71
- userService: any,
72
- options: { [key: string]: any; clone?: boolean; targetModel?: Partial<T> } = {},
73
- ...args: any[]
79
+ options: { [key: string]: any; clone?: boolean; removeUndefined?: boolean; targetModel?: Partial<T> } = {}
74
80
  ) {
75
81
  // Configuration
76
82
  const config = {
77
- clone: true,
83
+ clone: false,
84
+ removeUndefined: false,
85
+ targetModel: undefined,
78
86
  ...options,
79
87
  };
80
88
 
81
89
  // Clone output
82
90
  if (config.clone) {
83
- output = JSON.parse(JSON.stringify(output));
91
+ if (output.cloneDeep && typeof output.cloneDeep === 'function') {
92
+ output = Object.getPrototypeOf(output).cloneDeep(output);
93
+ } else {
94
+ output = _.cloneDeep(output);
95
+ }
84
96
  }
85
97
 
86
98
  // Map output if target model exist
87
- if (options.targetModel) {
88
- (options.targetModel as any).map(output);
99
+ if (config.targetModel) {
100
+ output = (config.targetModel as any).map(output);
89
101
  }
90
102
 
91
103
  // Remove password if exists
92
- delete output.password;
104
+ if (output.password) {
105
+ output.password = undefined;
106
+ }
93
107
 
94
108
  // Remove verification token if exists
95
- delete output.verificationToken;
109
+ if (output.verificationToken) {
110
+ output.verificationToken = undefined;
111
+ }
96
112
 
97
113
  // Remove password reset token if exists
98
- delete output.passwordResetToken;
114
+ if (output.passwordResetToken) {
115
+ output.passwordResetToken = undefined;
116
+ }
117
+
118
+ // Remove undefined properties to avoid unwanted overwrites
119
+ if (config.removeUndefined) {
120
+ Object.keys(output).forEach((key) => output[key] === undefined && delete output[key]);
121
+ }
99
122
 
100
- // Return prepared user
123
+ // Return prepared output
101
124
  return output;
102
125
  }
103
126
  }
@@ -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. If set the `filters` must be also set.
12
+ * Logical Operator to combine filters
11
13
  */
12
14
  @Field((type) => LogicalOperatorEnum, {
13
- description: 'Logical Operator to combine filters. If set the `filters` must be also set.',
14
- nullable: true,
15
+ description: 'Logical Operator to combine filters',
15
16
  })
16
- logicalOperator?: LogicalOperatorEnum;
17
+ logicalOperator: LogicalOperatorEnum = undefined;
17
18
 
18
19
  /**
19
- * Filters to combine via logical operator. If set `logicalOperator` must be also set.
20
+ * Filters to combine via logical operator
20
21
  */
21
22
  @Field((type) => [FilterInput], {
22
- description: 'Filters to combine via logical operator. If set `logicalOperator` must be also set.',
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
  }
@@ -0,0 +1,36 @@
1
+ import { ModelHelper } from '../helpers/model.helper';
2
+ import { CoreModel } from '../models/core-model.model';
3
+
4
+ /**
5
+ * Core Input
6
+ *
7
+ * All properties (in this class and all classes that extend this class) must be initialized with undefined!
8
+ *
9
+ * In contrast to the core model, properties that are undefined are completely removed from the instance during mapping,
10
+ * so that no existing data is overwritten when a data set is updated, for example. However, re-mapping causes the
11
+ * removed properties to be ignored and no longer automatically integrated into the instance. They must then be
12
+ * reactivated by direct assignment `instance.property = ...`.
13
+ */
14
+ export abstract class CoreInput extends CoreModel {
15
+ /**
16
+ * Map method
17
+ */
18
+ public map(
19
+ data: Partial<this> | Record<string, any>,
20
+ options: {
21
+ cloneDeep?: boolean;
22
+ funcAllowed?: boolean;
23
+ mapId?: boolean;
24
+ } = {}
25
+ ): this {
26
+ const config = {
27
+ cloneDeep: false,
28
+ funcAllowed: false,
29
+ mapId: false,
30
+ ...options,
31
+ };
32
+ const coreInput = ModelHelper.map(data, this, config);
33
+ Object.keys(coreInput).forEach((key) => coreInput[key] === undefined && delete coreInput[key]);
34
+ return coreInput;
35
+ }
36
+ }
@@ -1,5 +1,7 @@
1
1
  import { Field, InputType } from '@nestjs/graphql';
2
+ import { ModelHelper } from '../helpers/model.helper';
2
3
  import { CombinedFilterInput } from './combined-filter.input';
4
+ import { CoreInput } from './core-input.input';
3
5
  import { SingleFilterInput } from './single-filter.input';
4
6
 
5
7
  /**
@@ -8,7 +10,7 @@ import { SingleFilterInput } from './single-filter.input';
8
10
  @InputType({
9
11
  description: 'Input for filtering. The `singleFilter` will be ignored if the `combinedFilter` is set.',
10
12
  })
11
- export class FilterInput {
13
+ export class FilterInput extends CoreInput {
12
14
  /**
13
15
  * Combination of multiple filters via logical operator
14
16
  */
@@ -16,7 +18,7 @@ export class FilterInput {
16
18
  description: 'Combination of multiple filters via logical operator',
17
19
  nullable: true,
18
20
  })
19
- combinedFilter?: CombinedFilterInput;
21
+ combinedFilter?: CombinedFilterInput = undefined;
20
22
 
21
23
  /**
22
24
  * Filter for a single property
@@ -25,5 +27,27 @@ export class FilterInput {
25
27
  description: 'Filter for a single property',
26
28
  nullable: true,
27
29
  })
28
- singleFilter?: SingleFilterInput;
30
+ singleFilter?: SingleFilterInput = undefined;
31
+
32
+ // ===================================================================================================================
33
+ // Methods
34
+ // ===================================================================================================================
35
+
36
+ /**
37
+ * Mapping for Subtypes
38
+ */
39
+ map(
40
+ data: Partial<this> | Record<string, any>,
41
+ options: {
42
+ cloneDeep?: boolean;
43
+ funcAllowed?: boolean;
44
+ mapId?: boolean;
45
+ } = {}
46
+ ): this {
47
+ super.map(data, options);
48
+ this.combinedFilter = data.combinedFilter ? CombinedFilterInput.map(data.combinedFilter, options) : undefined;
49
+ this.singleFilter = data.singleFilter ? SingleFilterInput.map(data.singleFilter, options) : undefined;
50
+ Object.keys(this).forEach((key) => this[key] === undefined && delete this[key]);
51
+ return this;
52
+ }
29
53
  }
@@ -1,17 +1,18 @@
1
1
  import { Field, InputType } from '@nestjs/graphql';
2
2
  import { ComparisonOperatorEnum } from '../enums/comparison-operator.enum';
3
3
  import { JSON } from '../scalars/json.scalar';
4
+ import { CoreInput } from './core-input.input';
4
5
 
5
6
  /**
6
7
  * Input for a configuration of a filter
7
8
  */
8
9
  @InputType({ description: 'Input for a configuration of a filter' })
9
- export class SingleFilterInput {
10
+ export class SingleFilterInput extends CoreInput {
10
11
  /**
11
12
  * Name of the property to be used for the filter'
12
13
  */
13
14
  @Field({ description: 'Name of the property to be used for the filter' })
14
- field: string;
15
+ field: string = undefined;
15
16
 
16
17
  /**
17
18
  * [Negate operator](https://docs.mongodb.com/manual/reference/operator/query/not/)
@@ -20,7 +21,7 @@ export class SingleFilterInput {
20
21
  description: '[Negate operator](https://docs.mongodb.com/manual/reference/operator/query/not/)',
21
22
  nullable: true,
22
23
  })
23
- not?: boolean;
24
+ not?: boolean = undefined;
24
25
 
25
26
  /**
26
27
  * [Comparison operator](https://docs.mongodb.com/manual/reference/operator/query-comparison/)
@@ -28,7 +29,7 @@ export class SingleFilterInput {
28
29
  @Field((type) => ComparisonOperatorEnum, {
29
30
  description: '[Comparison operator](https://docs.mongodb.com/manual/reference/operator/query-comparison/)',
30
31
  })
31
- operator: ComparisonOperatorEnum;
32
+ operator: ComparisonOperatorEnum = undefined;
32
33
 
33
34
  /**
34
35
  * [Options](https://docs.mongodb.com/manual/reference/operator/query/regex/#op._S_options) for
@@ -40,8 +41,8 @@ export class SingleFilterInput {
40
41
  '[REGEX](https://docs.mongodb.com/manual/reference/operator/query/regex/) operator',
41
42
  nullable: true,
42
43
  })
43
- options?: string;
44
+ options?: string = undefined;
44
45
 
45
46
  @Field((type) => JSON, { description: 'Value of the property' })
46
- value: any;
47
+ value: any = undefined;
47
48
  }
@@ -1,20 +1,21 @@
1
1
  import { Field, InputType } from '@nestjs/graphql';
2
2
  import { SortOrderEnum } from '../enums/sort-order.emum';
3
+ import { CoreInput } from './core-input.input';
3
4
 
4
5
  /**
5
6
  * Sorting the returned elements
6
7
  */
7
8
  @InputType({ description: 'Sorting the returned elements' })
8
- export class SortInput {
9
+ export class SortInput extends CoreInput {
9
10
  /**
10
11
  * Field that is to be used for sorting
11
12
  */
12
13
  @Field({ description: 'Field that is to be used for sorting' })
13
- field: string;
14
+ field: string = undefined;
14
15
 
15
16
  /**
16
17
  * SortInput order of the field
17
18
  */
18
19
  @Field((type) => SortOrderEnum, { description: 'SortInput order of the field' })
19
- order: SortOrderEnum;
20
+ order: SortOrderEnum = undefined;
20
21
  }
@@ -19,8 +19,8 @@ export class CheckResponseInterceptor implements NestInterceptor {
19
19
  // Response interception
20
20
  return next.handle().pipe(
21
21
  map((data) => {
22
- // Prepare data for current user
23
- return checkRestricted(data, currentUser);
22
+ // Prepare response data for current user
23
+ return checkRestricted(data, currentUser, { throwError: false });
24
24
  })
25
25
  );
26
26
  }