@lenne.tech/nest-server 3.2.1 → 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 (131) 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 +14 -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/model.helper.js +0 -1
  17. package/dist/core/common/helpers/model.helper.js.map +1 -1
  18. package/dist/core/common/helpers/service.helper.d.ts +6 -8
  19. package/dist/core/common/helpers/service.helper.js +34 -12
  20. package/dist/core/common/helpers/service.helper.js.map +1 -1
  21. package/dist/core/common/inputs/combined-filter.input.d.ts +8 -2
  22. package/dist/core/common/inputs/combined-filter.input.js +16 -5
  23. package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
  24. package/dist/core/common/inputs/core-input.input.d.ts +8 -0
  25. package/dist/core/common/inputs/core-input.input.js +15 -0
  26. package/dist/core/common/inputs/core-input.input.js.map +1 -0
  27. package/dist/core/common/inputs/filter.input.d.ts +7 -1
  28. package/dist/core/common/inputs/filter.input.js +14 -1
  29. package/dist/core/common/inputs/filter.input.js.map +1 -1
  30. package/dist/core/common/inputs/single-filter.input.d.ts +2 -1
  31. package/dist/core/common/inputs/single-filter.input.js +10 -1
  32. package/dist/core/common/inputs/single-filter.input.js.map +1 -1
  33. package/dist/core/common/inputs/sort.input.d.ts +2 -1
  34. package/dist/core/common/inputs/sort.input.js +7 -1
  35. package/dist/core/common/inputs/sort.input.js.map +1 -1
  36. package/dist/core/common/interceptors/check-response.interceptor.js +1 -1
  37. package/dist/core/common/interceptors/check-response.interceptor.js.map +1 -1
  38. package/dist/core/common/models/core-model.model.d.ts +6 -0
  39. package/dist/core/common/models/core-model.model.js +10 -3
  40. package/dist/core/common/models/core-model.model.js.map +1 -1
  41. package/dist/core/common/models/core-persistence.model.d.ts +5 -29
  42. package/dist/core/common/models/core-persistence.model.js +19 -41
  43. package/dist/core/common/models/core-persistence.model.js.map +1 -1
  44. package/dist/core/common/pipes/check-input.pipe.d.ts +2 -3
  45. package/dist/core/common/pipes/check-input.pipe.js +5 -20
  46. package/dist/core/common/pipes/check-input.pipe.js.map +1 -1
  47. package/dist/core/common/pipes/map-and-validate.pipe.d.ts +4 -0
  48. package/dist/core/common/pipes/map-and-validate.pipe.js +40 -0
  49. package/dist/core/common/pipes/map-and-validate.pipe.js.map +1 -0
  50. package/dist/core/common/types/plain-input.type.d.ts +3 -0
  51. package/dist/core/common/types/plain-input.type.js +3 -0
  52. package/dist/core/common/types/plain-input.type.js.map +1 -0
  53. package/dist/core/modules/auth/core-auth.model.d.ts +3 -1
  54. package/dist/core/modules/auth/core-auth.model.js +7 -1
  55. package/dist/core/modules/auth/core-auth.model.js.map +1 -1
  56. package/dist/core/modules/auth/core-auth.resolver.d.ts +1 -1
  57. package/dist/core/modules/user/core-user.model.d.ts +3 -0
  58. package/dist/core/modules/user/core-user.model.js +9 -4
  59. package/dist/core/modules/user/core-user.model.js.map +1 -1
  60. package/dist/core/modules/user/core-user.service.d.ts +9 -13
  61. package/dist/core/modules/user/core-user.service.js +37 -62
  62. package/dist/core/modules/user/core-user.service.js.map +1 -1
  63. package/dist/core/modules/user/inputs/core-user-create.input.js.map +1 -1
  64. package/dist/core/modules/user/inputs/core-user.input.d.ts +2 -2
  65. package/dist/core/modules/user/inputs/core-user.input.js +3 -3
  66. package/dist/core/modules/user/inputs/core-user.input.js.map +1 -1
  67. package/dist/core.module.js +2 -3
  68. package/dist/core.module.js.map +1 -1
  69. package/dist/index.d.ts +6 -2
  70. package/dist/index.js +6 -2
  71. package/dist/index.js.map +1 -1
  72. package/dist/main.js +0 -2
  73. package/dist/main.js.map +1 -1
  74. package/dist/server/common/models/persistence.model.d.ts +1 -0
  75. package/dist/server/common/models/persistence.model.js +4 -0
  76. package/dist/server/common/models/persistence.model.js.map +1 -1
  77. package/dist/server/modules/auth/auth.model.d.ts +1 -0
  78. package/dist/server/modules/auth/auth.model.js +4 -0
  79. package/dist/server/modules/auth/auth.model.js.map +1 -1
  80. package/dist/server/modules/user/inputs/user-create.input.js.map +1 -1
  81. package/dist/server/modules/user/inputs/user.input.js.map +1 -1
  82. package/dist/server/modules/user/user.model.d.ts +3 -2
  83. package/dist/server/modules/user/user.model.js +9 -5
  84. package/dist/server/modules/user/user.model.js.map +1 -1
  85. package/dist/server/modules/user/user.resolver.d.ts +2 -2
  86. package/dist/server/modules/user/user.resolver.js +11 -13
  87. package/dist/server/modules/user/user.resolver.js.map +1 -1
  88. package/dist/server/modules/user/user.service.d.ts +7 -10
  89. package/dist/server/modules/user/user.service.js +7 -7
  90. package/dist/server/modules/user/user.service.js.map +1 -1
  91. package/dist/tsconfig.build.tsbuildinfo +1 -1
  92. package/package.json +1 -1
  93. package/src/core/common/args/filter.args.ts +22 -1
  94. package/src/core/common/args/pagination.args.ts +42 -7
  95. package/src/core/common/decorators/restricted.decorator.ts +22 -5
  96. package/src/core/common/helpers/context.helper.ts +14 -3
  97. package/src/core/common/helpers/filter.helper.ts +3 -3
  98. package/src/core/common/helpers/input.helper.ts +17 -11
  99. package/src/core/common/helpers/model.helper.ts +0 -3
  100. package/src/core/common/helpers/service.helper.ts +41 -18
  101. package/src/core/common/inputs/combined-filter.input.ts +30 -9
  102. package/src/core/common/inputs/core-input.input.ts +36 -0
  103. package/src/core/common/inputs/filter.input.ts +27 -3
  104. package/src/core/common/inputs/single-filter.input.ts +7 -6
  105. package/src/core/common/inputs/sort.input.ts +4 -3
  106. package/src/core/common/interceptors/check-response.interceptor.ts +2 -2
  107. package/src/core/common/models/core-model.model.ts +30 -1
  108. package/src/core/common/models/core-persistence.model.ts +33 -120
  109. package/src/core/common/pipes/check-input.pipe.ts +13 -33
  110. package/src/core/common/pipes/map-and-validate.pipe.ts +32 -0
  111. package/src/core/common/types/plain-input.type.ts +6 -0
  112. package/src/core/modules/auth/core-auth.model.ts +15 -1
  113. package/src/core/modules/auth/core-auth.resolver.ts +1 -1
  114. package/src/core/modules/user/core-user.model.ts +16 -4
  115. package/src/core/modules/user/core-user.service.ts +58 -110
  116. package/src/core/modules/user/inputs/core-user-create.input.ts +4 -0
  117. package/src/core/modules/user/inputs/core-user.input.ts +7 -3
  118. package/src/core.module.ts +11 -5
  119. package/src/index.ts +6 -2
  120. package/src/main.ts +0 -4
  121. package/src/server/common/models/persistence.model.ts +13 -0
  122. package/src/server/modules/auth/auth.model.ts +13 -0
  123. package/src/server/modules/user/inputs/user-create.input.ts +4 -0
  124. package/src/server/modules/user/inputs/user.input.ts +4 -0
  125. package/src/server/modules/user/user.model.ts +18 -5
  126. package/src/server/modules/user/user.resolver.ts +15 -19
  127. package/src/server/modules/user/user.service.ts +9 -10
  128. package/dist/core/common/pipes/map.pipe.d.ts +0 -4
  129. package/dist/core/common/pipes/map.pipe.js +0 -25
  130. package/dist/core/common/pipes/map.pipe.js.map +0 -1
  131. package/src/core/common/pipes/map.pipe.ts +0 -16
@@ -1,20 +1,14 @@
1
- import {
2
- BadRequestException,
3
- NotFoundException,
4
- UnauthorizedException,
5
- UnprocessableEntityException,
6
- } from '@nestjs/common';
1
+ import { BadRequestException, NotFoundException, UnprocessableEntityException } from '@nestjs/common';
7
2
  import * as bcrypt from 'bcrypt';
8
3
  import { PubSub } from 'graphql-subscriptions';
9
4
  import { FilterArgs } from '../../common/args/filter.args';
10
- import { RoleEnum } from '../../common/enums/role.enum';
11
5
  import { Filter } from '../../common/helpers/filter.helper';
6
+ import { ServiceHelper } from '../../common/helpers/service.helper';
12
7
  import { CoreBasicUserService } from './core-basic-user.service';
13
8
  import { CoreUserModel } from './core-user.model';
14
9
  import { CoreUserCreateInput } from './inputs/core-user-create.input';
15
10
  import { CoreUserInput } from './inputs/core-user.input';
16
- import { Model } from 'mongoose';
17
- import * as _ from 'lodash';
11
+ import { Model, Document } from 'mongoose';
18
12
  import * as crypto from 'crypto';
19
13
  import envConfig from '../../../config.env';
20
14
  import { EmailService } from '../../common/services/email.service';
@@ -30,7 +24,7 @@ export abstract class CoreUserService<
30
24
  TUserInput extends CoreUserInput,
31
25
  TUserCreateInput extends CoreUserCreateInput
32
26
  > extends CoreBasicUserService<TUser, TUserInput, TUserCreateInput> {
33
- protected constructor(protected readonly userModel: Model<any>, protected emailService: EmailService) {
27
+ protected constructor(protected readonly userModel: Model<TUser & Document>, protected emailService: EmailService) {
34
28
  super(userModel);
35
29
  }
36
30
 
@@ -45,11 +39,11 @@ export abstract class CoreUserService<
45
39
  // Prepare input
46
40
  await this.prepareInput(input, currentUser, { create: true });
47
41
 
48
- // Generate verification token
49
- const newUser = { ...input, ...{ verificationToken: crypto.randomBytes(32).toString('hex') } };
50
-
51
42
  // Create new user
52
- const createdUser = new this.userModel(this.model.map(newUser));
43
+ const createdUser = new this.userModel({
44
+ ...input,
45
+ verificationToken: crypto.randomBytes(32).toString('hex'),
46
+ });
53
47
 
54
48
  try {
55
49
  // Save created user
@@ -63,13 +57,13 @@ export abstract class CoreUserService<
63
57
  }
64
58
 
65
59
  // Prepare output
66
- await this.prepareOutput(createdUser, args[0]);
60
+ const preparedUser = await this.prepareOutput(this.model.map(createdUser), args[0]);
67
61
 
68
62
  // Inform subscriber
69
- pubSub.publish('userCreated', { userCreated: createdUser });
63
+ pubSub.publish('userCreated', { userCreated: preparedUser });
70
64
 
71
65
  // Return created user
72
- return createdUser;
66
+ return preparedUser;
73
67
  }
74
68
 
75
69
  /**
@@ -77,7 +71,7 @@ export abstract class CoreUserService<
77
71
  */
78
72
  async delete(id: string, ...args: any[]): Promise<TUser> {
79
73
  // Search user
80
- let user = await this.userModel.findOne({ _id: id }).exec();
74
+ const user = await this.userModel.findById(id).exec();
81
75
 
82
76
  // Check user
83
77
  if (!user) {
@@ -85,19 +79,23 @@ export abstract class CoreUserService<
85
79
  }
86
80
 
87
81
  // Delete user
88
- await this.userModel.deleteOne({ _id: id }).exec();
82
+ await user.delete();
83
+
84
+ // Prepare output
85
+ const deletedUser = await this.prepareOutput(this.model.map(user), args[0]);
89
86
 
90
- user = this.model.map(user);
87
+ // Inform subscriber
88
+ pubSub.publish('userDeleted', { userDeleted: deletedUser });
91
89
 
92
90
  // Return deleted user
93
- return await this.prepareOutput(user, args[0]);
91
+ return deletedUser;
94
92
  }
95
93
 
96
94
  /**
97
95
  * Get user via ID
98
96
  */
99
97
  async get(id: string, ...args: any[]): Promise<TUser> {
100
- const user = await this.userModel.findOne({ _id: id }).exec();
98
+ const user = await this.userModel.findById(id).exec();
101
99
 
102
100
  if (!user) {
103
101
  throw new NotFoundException();
@@ -116,17 +114,15 @@ export abstract class CoreUserService<
116
114
  (
117
115
  await this.userModel.find(filterQuery[0], null, filterQuery[1]).exec()
118
116
  ).map((user) => {
119
- return this.prepareOutput(user, args[0]);
117
+ return this.prepareOutput(this.model.map(user), args[0]);
120
118
  })
121
119
  );
122
120
  }
123
121
 
124
122
  /**
125
123
  * Verify user with token
126
- *
127
- * @param token
128
124
  */
129
- async verify(token: string): Promise<boolean> {
125
+ async verify(token: string, ...args: any[]): Promise<TUser> {
130
126
  const user = await this.userModel.findOne({ verificationToken: token }).exec();
131
127
 
132
128
  if (!user) {
@@ -141,55 +137,65 @@ export abstract class CoreUserService<
141
137
  throw new Error('User already verified');
142
138
  }
143
139
 
144
- await this.userModel.findByIdAndUpdate(user.id, { $set: { verified: true, verificationToken: null } }).exec();
140
+ // Update user
141
+ await Object.assign(user, {
142
+ verified: true,
143
+ verificationToken: null,
144
+ }).save();
145
+
146
+ // Prepare verified user
147
+ const verifiedUser = this.prepareOutput(this.model.map(user), args[0]);
148
+
149
+ // Inform subscriber
150
+ pubSub.publish('userVerified', { userVerified: verifiedUser });
145
151
 
146
- return true;
152
+ // Return prepared verified user
153
+ return verifiedUser;
147
154
  }
148
155
 
149
156
  /**
150
157
  * Set newpassword for user with token
151
- *
152
- * @param token
153
- * @param newPassword
154
158
  */
155
- async resetPassword(token: string, newPassword: string): Promise<boolean> {
159
+ async resetPassword(token: string, newPassword: string, ...args: any[]): Promise<TUser> {
156
160
  const user = await this.userModel.findOne({ passwordResetToken: token }).exec();
157
161
 
158
162
  if (!user) {
159
163
  throw new NotFoundException();
160
164
  }
161
165
 
162
- const cryptedPassword = await bcrypt.hash(newPassword, 10);
163
- await this.userModel
164
- .findByIdAndUpdate(user.id, { $set: { password: cryptedPassword, passwordResetToken: null } })
165
- .exec();
166
+ // Update user
167
+ await Object.assign(user, {
168
+ password: await bcrypt.hash(newPassword, 10),
169
+ passwordResetToken: null,
170
+ }).save();
166
171
 
167
- return true;
172
+ // Return prepared user with changed password
173
+ return this.prepareOutput(this.model.map(user), args[0]);
168
174
  }
169
175
 
170
176
  /**
171
- * Request email with password reset link
172
- *
173
- * @param email
177
+ * Set password rest token for email
174
178
  */
175
- async sentResetPasswordMail(email: string): Promise<TUser> {
179
+ async setPasswordResetTokenForEmail(email: string, ...args: any[]): Promise<TUser> {
176
180
  const user = await this.userModel.findOne({ email }).exec();
177
181
 
178
182
  if (!user) {
179
183
  throw new NotFoundException();
180
184
  }
181
185
 
186
+ // Set reset token
182
187
  const resetToken = crypto.randomBytes(32).toString('hex');
183
188
  user.passwordResetToken = resetToken;
184
- await this.userModel.findByIdAndUpdate(user.id, { $set: { passwordResetToken: resetToken } }).exec();
189
+ await user.save();
185
190
 
186
- return user;
191
+ // Return user who want to reset the password
192
+ return this.prepareOutput(this.model.map(user), args[0]);
187
193
  }
188
194
 
189
195
  /**
190
196
  * Set roles for specified user
191
197
  */
192
- async setRoles(userId: string, roles: string[]): Promise<TUser> {
198
+ async setRoles(userId: string, roles: string[], ...args: any[]): Promise<TUser> {
193
199
  // Check roles
194
200
  if (!Array.isArray(roles)) {
195
201
  throw new BadRequestException('Missing roles');
@@ -201,7 +207,8 @@ export abstract class CoreUserService<
201
207
  }
202
208
 
203
209
  // Update and return user
204
- return this.userModel.findByIdAndUpdate(userId, { roles }).exec();
210
+ const user = await this.userModel.findByIdAndUpdate(userId, { roles }).exec();
211
+ return this.prepareOutput(this.model.map(user), args[0]);
205
212
  }
206
213
 
207
214
  /**
@@ -209,7 +216,7 @@ export abstract class CoreUserService<
209
216
  */
210
217
  async update(id: string, input: TUserInput, currentUser: TUser, ...args: any[]): Promise<TUser> {
211
218
  // Check if user exists
212
- let user = await this.userModel.findOne({ _id: id }).exec();
219
+ const user = await this.userModel.findById(id).exec();
213
220
 
214
221
  if (!user) {
215
222
  throw new NotFoundException(`User not found with ID: ${id}`);
@@ -219,16 +226,10 @@ export abstract class CoreUserService<
219
226
  await this.prepareInput(input, currentUser);
220
227
 
221
228
  // Update
222
- user.set(input);
223
-
224
- // Save
225
- await user.save();
226
-
227
- // Map for response
228
- user = this.model.map(user);
229
+ await Object.assign(user, input).save();
229
230
 
230
231
  // Return user
231
- return await this.prepareOutput(user as TUser, args[0]);
232
+ return await this.prepareOutput(this.model.map(user), args[0]);
232
233
  }
233
234
 
234
235
  // ===================================================================================================================
@@ -239,44 +240,12 @@ export abstract class CoreUserService<
239
240
  * Prepare input before save
240
241
  */
241
242
  protected async prepareInput(
242
- input: { [key: string]: any },
243
+ input: Record<string, any>,
243
244
  currentUser?: TUser,
244
245
  options: { [key: string]: any; checkRoles?: boolean; clone?: boolean } = {},
245
246
  ...args: any[]
246
247
  ) {
247
- // Configuration
248
- const config = {
249
- checkRoles: false,
250
- clone: false,
251
- ...options,
252
- };
253
-
254
- // Clone input
255
- if (config.clone) {
256
- input = JSON.parse(JSON.stringify(input));
257
- }
258
-
259
- // Process roles
260
- if (input.roles && config.checkRoles && (!currentUser?.hasRole || !currentUser.hasRole(RoleEnum.ADMIN))) {
261
- if (!(currentUser as any)?.roles) {
262
- throw new UnauthorizedException('Missing roles of current user');
263
- } else {
264
- const allowedRoles = _.intersection(input.roles, (currentUser as any).roles);
265
- if (allowedRoles.length !== input.roles.length) {
266
- const missingRoles = _.difference(input.roles, (currentUser as any).roles);
267
- throw new UnauthorizedException('Current user not allowed setting roles: ' + missingRoles);
268
- }
269
- input.roles = allowedRoles;
270
- }
271
- }
272
-
273
- // Hash password
274
- if (input.password) {
275
- input.password = await bcrypt.hash((input as any).password, 10);
276
- }
277
-
278
- // Return prepared input
279
- return input;
248
+ return ServiceHelper.prepareInput(input, currentUser, options);
280
249
  }
281
250
 
282
251
  /**
@@ -287,27 +256,6 @@ export abstract class CoreUserService<
287
256
  options: { [key: string]: any; clone?: boolean } = {},
288
257
  ...args: any[]
289
258
  ): Promise<TUser> {
290
- // Configuration
291
- const config = {
292
- clone: true,
293
- ...options,
294
- };
295
-
296
- // Clone user
297
- if (config.clone) {
298
- user = JSON.parse(JSON.stringify(user));
299
- }
300
-
301
- // Remove password if exists
302
- delete (user as any).password;
303
-
304
- // Remove verification token if exists
305
- delete (user as any).verificationToken;
306
-
307
- // Remove password reset token if exists
308
- delete (user as any).passwordResetToken;
309
-
310
- // Return prepared user
311
- return user;
259
+ return ServiceHelper.prepareOutput(user, options);
312
260
  }
313
261
  }
@@ -4,6 +4,10 @@ import { CoreUserInput } from './core-user.input';
4
4
 
5
5
  /**
6
6
  * User input to create a new user
7
+ *
8
+ * HINT: All properties (in this class and all classes that extend this class) must be initialized with undefined,
9
+ * otherwise the property will not be recognized via Object.keys (this is necessary for mapping) or will be initialized
10
+ * with a default value that may overwrite an existing value in the DB.
7
11
  */
8
12
  @InputType({ description: 'User input to create a new user', isAbstract: true })
9
13
  export abstract class CoreUserCreateInput extends CoreUserInput {
@@ -2,13 +2,17 @@ import { IsEmail, IsOptional } from 'class-validator';
2
2
  import { Field, InputType } from '@nestjs/graphql';
3
3
  import { Restricted } from '../../../common/decorators/restricted.decorator';
4
4
  import { RoleEnum } from '../../../common/enums/role.enum';
5
- import { CoreModel } from '../../../common/models/core-model.model';
5
+ import { CoreInput } from '../../../common/inputs/core-input.input';
6
6
 
7
7
  /**
8
8
  * User input to update a user
9
+ *
10
+ * HINT: All properties (in this class and all classes that extend this class) must be initialized with undefined,
11
+ * otherwise the property will not be recognized via Object.keys (this is necessary for mapping) or will be initialized
12
+ * with a default value that may overwrite an existing value in the DB.
9
13
  */
10
14
  @InputType({ description: 'User input', isAbstract: true })
11
- export abstract class CoreUserInput extends CoreModel {
15
+ export abstract class CoreUserInput extends CoreInput {
12
16
  /**
13
17
  * Email of the user
14
18
  */
@@ -37,7 +41,7 @@ export abstract class CoreUserInput extends CoreModel {
37
41
  @Restricted(RoleEnum.ADMIN)
38
42
  @Field((type) => [String], { description: 'Roles of the user', nullable: true })
39
43
  @IsOptional()
40
- roles?: string[] = [];
44
+ roles?: string[] = undefined;
41
45
 
42
46
  /**
43
47
  * Username / alias of the user
@@ -1,10 +1,10 @@
1
- import { DynamicModule, Global, Module, Scope } from '@nestjs/common';
1
+ import { DynamicModule, Global, Module } from '@nestjs/common';
2
2
  import { APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
3
3
  import { GraphQLModule } from '@nestjs/graphql';
4
4
  import { Config } from './core/common/helpers/config.helper';
5
5
  import { CheckResponseInterceptor } from './core/common/interceptors/check-response.interceptor';
6
6
  import { IServerOptions } from './core/common/interfaces/server-options.interface';
7
- import { CheckInputPipe } from './core/common/pipes/check-input.pipe';
7
+ import { MapAndValidatePipe } from './core/common/pipes/map-and-validate.pipe';
8
8
  import { ConfigService } from './core/common/services/config.service';
9
9
  import { EmailService } from './core/common/services/email.service';
10
10
  import { TemplateService } from './core/common/services/template.service';
@@ -82,11 +82,17 @@ export class CoreModule {
82
82
  },
83
83
 
84
84
  // [Global] The CheckInputPipe checks the permissibility of individual properties of inputs for the resolvers
85
- // in relation to the current user
85
+ // in relation to the current user, replaces MapAndValidatePipe
86
+ // Does not work yet, because context is missing: https://github.com/nestjs/graphql/issues/325
87
+ // {
88
+ // provide: APP_PIPE,
89
+ // useClass: CheckInputPipe,
90
+ // },
91
+
92
+ // [Global] Map plain objects to metatype and validate
86
93
  {
87
94
  provide: APP_PIPE,
88
- scope: Scope.REQUEST,
89
- useClass: CheckInputPipe,
95
+ useClass: MapAndValidatePipe,
90
96
  },
91
97
 
92
98
  // Core Services
package/src/index.ts CHANGED
@@ -26,22 +26,26 @@ export * from './core/common/helpers/input.helper';
26
26
  export * from './core/common/helpers/model.helper';
27
27
  export * from './core/common/helpers/service.helper';
28
28
  export * from './core/common/inputs/combined-filter.input';
29
+ export * from './core/common/inputs/core-input.input';
29
30
  export * from './core/common/inputs/filter.input';
30
31
  export * from './core/common/inputs/single-filter.input';
31
32
  export * from './core/common/inputs/sort.input';
32
33
  export * from './core/common/interceptors/check-response.interceptor';
33
34
  export * from './core/common/interfaces/core-persistence-model.interface';
35
+ export * from './core/common/interfaces/mailjet-options.interface';
34
36
  export * from './core/common/interfaces/server-options.interface';
35
37
  export * from './core/common/models/core-model.model';
36
38
  export * from './core/common/models/core-persistence.model';
37
39
  export * from './core/common/pipes/check-input.pipe';
40
+ export * from './core/common/pipes/map-and-validate.pipe';
38
41
  export * from './core/common/scalars/any.scalar';
39
42
  export * from './core/common/scalars/date.scalar';
40
43
  export * from './core/common/scalars/json.scalar';
41
44
  export * from './core/common/services/config.service';
42
45
  export * from './core/common/services/email.service';
43
- export * from './core/common/services/template.service';
44
46
  export * from './core/common/services/mailjet.service';
47
+ export * from './core/common/services/template.service';
48
+ export * from './core/common/types/plain-input.type';
45
49
 
46
50
  // =====================================================================================================================
47
51
  // Core - Modules - Auth
@@ -64,8 +68,8 @@ export * from './core/modules/auth/jwt.strategy';
64
68
 
65
69
  export * from './core/modules/user/inputs/core-user.input';
66
70
  export * from './core/modules/user/inputs/core-user-create.input';
67
- export * from './core/modules/user/core-user.model';
68
71
  export * from './core/modules/user/core-basic-user.service';
72
+ export * from './core/modules/user/core-user.model';
69
73
  export * from './core/modules/user/core-user.service';
70
74
 
71
75
  // =====================================================================================================================
package/src/main.ts CHANGED
@@ -2,7 +2,6 @@ import { NestFactory } from '@nestjs/core';
2
2
  import { NestExpressApplication } from '@nestjs/platform-express';
3
3
  import envConfig from './config.env';
4
4
  import { ServerModule } from './server/server.module';
5
- import { MapPipe } from './core/common/pipes/map.pipe';
6
5
 
7
6
  /**
8
7
  * Preparations for server start
@@ -14,9 +13,6 @@ async function bootstrap() {
14
13
  ServerModule
15
14
  );
16
15
 
17
- // Add map pipe for mapping inputs to class
18
- server.useGlobalPipes(new MapPipe());
19
-
20
16
  // Asset directory
21
17
  server.useStaticAssets(envConfig.staticAssets.path, envConfig.staticAssets.options);
22
18
 
@@ -37,4 +37,17 @@ export abstract class PersistenceModel extends CorePersistenceModel {
37
37
  })
38
38
  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
39
39
  updatedBy?: User = undefined;
40
+
41
+ // ===========================================================================
42
+ // Properties
43
+ // ===========================================================================
44
+
45
+ /**
46
+ * Initialize instance with default values instead of undefined
47
+ */
48
+ init() {
49
+ super.init();
50
+ // Nothing more to initialize yet
51
+ return this;
52
+ }
40
53
  }
@@ -16,4 +16,17 @@ export class Auth extends CoreAuthModel {
16
16
  */
17
17
  @Field((type) => User, { description: 'User who signed in' })
18
18
  user: User = undefined;
19
+
20
+ // ===================================================================================================================
21
+ // Properties
22
+ // ===================================================================================================================
23
+
24
+ /**
25
+ * Initialize instance with default values instead of undefined
26
+ */
27
+ init() {
28
+ super.init();
29
+ // Nothing more to initialize yet
30
+ return this;
31
+ }
19
32
  }
@@ -3,6 +3,10 @@ import { CoreUserCreateInput } from '../../../../core/modules/user/inputs/core-u
3
3
 
4
4
  /**
5
5
  * User input to create a new user
6
+ *
7
+ * HINT: All properties (in this class and all classes that extend this class) must be initialized with undefined,
8
+ * otherwise the property will not be recognized via Object.keys (this is necessary for mapping) or will be initialized
9
+ * with a default value that may overwrite an existing value in the DB.
6
10
  */
7
11
  @InputType({ description: 'User input to create a new user' })
8
12
  export class UserCreateInput extends CoreUserCreateInput {
@@ -3,6 +3,10 @@ import { CoreUserInput } from '../../../../core/modules/user/inputs/core-user.in
3
3
 
4
4
  /**
5
5
  * User input to update a user
6
+ *
7
+ * HINT: All properties (in this class and all classes that extend this class) must be initialized with undefined,
8
+ * otherwise the property will not be recognized via Object.keys (this is necessary for mapping) or will be initialized
9
+ * with a default value that may overwrite an existing value in the DB.
6
10
  */
7
11
  @InputType({ description: 'User input' })
8
12
  export class UserInput extends CoreUserInput {
@@ -1,16 +1,16 @@
1
1
  import { Field, ObjectType } from '@nestjs/graphql';
2
2
  import { CoreUserModel } from '../../../core/modules/user/core-user.model';
3
3
  import { PersistenceModel } from '../../common/models/persistence.model';
4
- import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
5
- import * as mongoose from 'mongoose';
4
+ import { Prop, Schema as MongooseSchema, SchemaFactory } from '@nestjs/mongoose';
5
+ import { Schema, Document } from 'mongoose';
6
6
 
7
7
  export type UserDocument = User & Document;
8
8
 
9
9
  /**
10
10
  * User schema
11
11
  */
12
- @Schema()
13
12
  @ObjectType({ description: 'User' })
13
+ @MongooseSchema({ timestamps: true })
14
14
  export class User extends CoreUserModel implements PersistenceModel {
15
15
  // ===================================================================================================================
16
16
  // Properties
@@ -32,7 +32,7 @@ export class User extends CoreUserModel implements PersistenceModel {
32
32
  description: 'ID of the user who created the object',
33
33
  nullable: true,
34
34
  })
35
- @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
35
+ @Prop({ type: Schema.Types.ObjectId, ref: 'User' })
36
36
  createdBy: User = undefined;
37
37
 
38
38
  /**
@@ -44,8 +44,21 @@ export class User extends CoreUserModel implements PersistenceModel {
44
44
  description: 'ID of the user who last updated the object',
45
45
  nullable: true,
46
46
  })
47
- @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
47
+ @Prop({ type: Schema.Types.ObjectId, ref: 'User' })
48
48
  updatedBy: User = undefined;
49
+
50
+ // ===================================================================================================================
51
+ // Methods
52
+ // ===================================================================================================================
53
+
54
+ /**
55
+ * Initialize instance with default values instead of undefined
56
+ */
57
+ init() {
58
+ super.init();
59
+ // Nothing more to initialize yet
60
+ return this;
61
+ }
49
62
  }
50
63
 
51
64
  export const UserSchema = SchemaFactory.createForClass(User);
@@ -47,8 +47,8 @@ export class UserResolver {
47
47
  * Request new password for user with email
48
48
  */
49
49
  @Query((returns) => Boolean, { description: 'Request new password for user with email' })
50
- async requestPasswordResetMail(@Args('email') email: string) {
51
- return await this.usersService.requestPasswordResetMail(email);
50
+ async requestPasswordResetMail(@Args('email') email: string): Promise<boolean> {
51
+ return !!(await this.usersService.sendPasswordResetMail(email));
52
52
  }
53
53
 
54
54
  // ===========================================================================
@@ -58,28 +58,29 @@ export class UserResolver {
58
58
  * Verify user with email
59
59
  */
60
60
  @Mutation((returns) => Boolean, { description: 'Verify user with email' })
61
- async verifyUser(@Args('token') token: string) {
62
- return await this.usersService.verify(token);
61
+ async verifyUser(@Args('token') token: string): Promise<boolean> {
62
+ return !!(await this.usersService.verify(token));
63
63
  }
64
64
 
65
65
  /**
66
66
  * Set new password for user with token
67
67
  */
68
68
  @Mutation((returns) => Boolean, { description: 'Set new password for user with token' })
69
- async resetPassword(@Args('token') token: string, @Args('password') password: string) {
70
- return await this.usersService.resetPassword(token, password);
69
+ async resetPassword(@Args('token') token: string, @Args('password') password: string): Promise<boolean> {
70
+ return !!(await this.usersService.resetPassword(token, password));
71
71
  }
72
72
 
73
73
  /**
74
74
  * Create new user
75
75
  */
76
76
  @Mutation((returns) => User, { description: 'Create a new user' })
77
- async createUser(
78
- @Args('input') input: UserCreateInput,
79
- @GraphQLUser() user: User,
80
- @Info() info: GraphQLResolveInfo
81
- ): Promise<User> {
82
- return await this.usersService.create(input, user, info);
77
+ async createUser(@Args('input') input: UserCreateInput, @GraphQLUser() user: User): Promise<User> {
78
+ // Check input
79
+ // Hint: necessary as long as global CheckInputPipe can't access context for current user
80
+ // (see https://github.com/nestjs/graphql/issues/325)
81
+ input = await InputHelper.check(input, user, UserCreateInput);
82
+
83
+ return await this.usersService.create(input, user);
83
84
  }
84
85
 
85
86
  /**
@@ -87,19 +88,14 @@ export class UserResolver {
87
88
  */
88
89
  @Roles(RoleEnum.ADMIN, RoleEnum.OWNER)
89
90
  @Mutation((returns) => User, { description: 'Update existing user' })
90
- async updateUser(
91
- @Args('input', { type: () => UserInput }) input: UserInput,
92
- @Args('id') id: string,
93
- @GraphQLUser() user: User,
94
- @Info() info: GraphQLResolveInfo
95
- ): Promise<User> {
91
+ async updateUser(@Args('input') input: UserInput, @Args('id') id: string, @GraphQLUser() user: User): Promise<User> {
96
92
  // Check input
97
93
  // Hint: necessary as long as global CheckInputPipe can't access context for current user
98
94
  // (see https://github.com/nestjs/graphql/issues/325)
99
95
  input = await InputHelper.check(input, user, UserInput);
100
96
 
101
97
  // Update user
102
- return await this.usersService.update(id, input, user, info);
98
+ return await this.usersService.update(id, input, user);
103
99
  }
104
100
 
105
101
  /**