@lenne.tech/nest-server 8.5.0 → 8.6.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 (61) hide show
  1. package/dist/core/common/helpers/db.helper.d.ts +1 -1
  2. package/dist/core/common/helpers/db.helper.js +29 -5
  3. package/dist/core/common/helpers/db.helper.js.map +1 -1
  4. package/dist/core/common/helpers/service.helper.d.ts +11 -0
  5. package/dist/core/common/helpers/service.helper.js +24 -1
  6. package/dist/core/common/helpers/service.helper.js.map +1 -1
  7. package/dist/core/common/interfaces/prepare-input-options.interface.d.ts +8 -0
  8. package/dist/core/common/interfaces/prepare-input-options.interface.js +3 -0
  9. package/dist/core/common/interfaces/prepare-input-options.interface.js.map +1 -0
  10. package/dist/core/common/interfaces/prepare-output-options.interface.d.ts +7 -0
  11. package/dist/core/common/interfaces/prepare-output-options.interface.js +3 -0
  12. package/dist/core/common/interfaces/prepare-output-options.interface.js.map +1 -0
  13. package/dist/core/common/interfaces/service-options.interface.d.ts +4 -14
  14. package/dist/core/common/services/module.service.d.ts +1 -1
  15. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.d.ts +5 -0
  16. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js +34 -0
  17. package/dist/core/modules/auth/inputs/core-auth-sign-in.input.js.map +1 -0
  18. package/dist/core/modules/auth/inputs/core-auth-sign-up.input.d.ts +5 -0
  19. package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js +34 -0
  20. package/dist/core/modules/auth/inputs/core-auth-sign-up.input.js.map +1 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +2 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/server/modules/auth/auth.model.js +2 -2
  25. package/dist/server/modules/auth/auth.model.js.map +1 -1
  26. package/dist/server/modules/auth/auth.module.js +7 -2
  27. package/dist/server/modules/auth/auth.module.js.map +1 -1
  28. package/dist/server/modules/auth/auth.resolver.d.ts +8 -3
  29. package/dist/server/modules/auth/auth.resolver.js +33 -10
  30. package/dist/server/modules/auth/auth.resolver.js.map +1 -1
  31. package/dist/server/modules/auth/auth.service.d.ts +15 -0
  32. package/dist/server/modules/auth/auth.service.js +71 -0
  33. package/dist/server/modules/auth/auth.service.js.map +1 -0
  34. package/dist/server/modules/auth/inputs/auth-sign-in.input.d.ts +3 -0
  35. package/dist/server/modules/auth/inputs/auth-sign-in.input.js +18 -0
  36. package/dist/server/modules/auth/inputs/auth-sign-in.input.js.map +1 -0
  37. package/dist/server/modules/auth/inputs/auth-sign-up.input.d.ts +5 -0
  38. package/dist/server/modules/auth/inputs/auth-sign-up.input.js +34 -0
  39. package/dist/server/modules/auth/inputs/auth-sign-up.input.js.map +1 -0
  40. package/dist/server/modules/user/user.resolver.js +12 -11
  41. package/dist/server/modules/user/user.resolver.js.map +1 -1
  42. package/dist/server/modules/user/user.service.js +0 -4
  43. package/dist/server/modules/user/user.service.js.map +1 -1
  44. package/dist/tsconfig.build.tsbuildinfo +1 -1
  45. package/package.json +2 -2
  46. package/src/core/common/helpers/db.helper.ts +27 -5
  47. package/src/core/common/helpers/service.helper.ts +55 -0
  48. package/src/core/common/interfaces/prepare-input-options.interface.ts +11 -0
  49. package/src/core/common/interfaces/prepare-output-options.interface.ts +10 -0
  50. package/src/core/common/interfaces/service-options.interface.ts +4 -14
  51. package/src/core/modules/auth/inputs/core-auth-sign-in.input.ts +18 -0
  52. package/src/core/modules/auth/inputs/core-auth-sign-up.input.ts +18 -0
  53. package/src/index.ts +2 -0
  54. package/src/server/modules/auth/auth.model.ts +5 -5
  55. package/src/server/modules/auth/auth.module.ts +13 -2
  56. package/src/server/modules/auth/auth.resolver.ts +30 -12
  57. package/src/server/modules/auth/auth.service.ts +84 -0
  58. package/src/server/modules/auth/inputs/auth-sign-in.input.ts +10 -0
  59. package/src/server/modules/auth/inputs/auth-sign-up.input.ts +18 -0
  60. package/src/server/modules/user/user.resolver.ts +12 -11
  61. package/src/server/modules/user/user.service.ts +0 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "8.5.0",
3
+ "version": "8.6.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",
@@ -112,7 +112,7 @@
112
112
  "prettier": "2.6.2",
113
113
  "pretty-quick": "3.1.3",
114
114
  "supertest": "6.2.3",
115
- "ts-jest": "28.0.1",
115
+ "ts-jest": "28.0.2",
116
116
  "ts-morph": "14.0.0",
117
117
  "ts-node": "10.7.0",
118
118
  "tsconfig-paths": "4.0.0",
@@ -275,19 +275,41 @@ export function getElementsViaIds<T = any>(
275
275
  /**
276
276
  * Get populate options from GraphQL resolve info
277
277
  */
278
- export function getPopulateOptions(info: GraphQLResolveInfo, select?: string): PopulateOptions[] {
278
+ export function getPopulateOptions(info: GraphQLResolveInfo, dotSeparatedSelect?: string): PopulateOptions[] {
279
279
  const result = [];
280
280
 
281
281
  if (!info?.fieldNodes?.length) {
282
282
  return result;
283
283
  }
284
284
 
285
- for (const fieldNode of info.fieldNodes) {
286
- if ((select || fieldNode?.name?.value === select) && fieldNode?.selectionSet?.selections?.length) {
287
- return getPopulatOptionsFromSelections(fieldNode.selectionSet.selections);
285
+ if (dotSeparatedSelect?.length) {
286
+ for (const fieldNode of info.fieldNodes) {
287
+ const selects = dotSeparatedSelect.split('.');
288
+ const fieldNodeName = selects.shift();
289
+ if (fieldNode?.name?.value === fieldNodeName && fieldNode?.selectionSet?.selections?.length) {
290
+ if (!selects.length) {
291
+ return getPopulatOptionsFromSelections(fieldNode.selectionSet.selections);
292
+ } else {
293
+ let selections = fieldNode.selectionSet.selections;
294
+ do {
295
+ if (!selections?.length) {
296
+ break;
297
+ }
298
+ const select = selects.shift();
299
+ for (const selection of selections) {
300
+ if ((selection as FieldNode)?.name?.value === select) {
301
+ selections = (selection as FieldNode)?.selectionSet?.selections;
302
+ if (!selects.length) {
303
+ return getPopulatOptionsFromSelections(selections);
304
+ }
305
+ break;
306
+ }
307
+ }
308
+ } while (selects.length);
309
+ }
310
+ }
288
311
  }
289
312
  }
290
-
291
313
  return result;
292
314
  }
293
315
 
@@ -3,6 +3,10 @@ import * as bcrypt from 'bcrypt';
3
3
  import { plainToInstance } from 'class-transformer';
4
4
  import * as _ from 'lodash';
5
5
  import { RoleEnum } from '../enums/role.enum';
6
+ import { PrepareInputOptions } from '../interfaces/prepare-input-options.interface';
7
+ import { PrepareOutputOptions } from '../interfaces/prepare-output-options.interface';
8
+ import { ResolveSelector } from '../interfaces/resolve-selector.interface';
9
+ import { ServiceOptions } from '../interfaces/service-options.interface';
6
10
 
7
11
  /**
8
12
  * Helper class for services
@@ -213,3 +217,54 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
213
217
  // Return prepared output
214
218
  return output;
215
219
  }
220
+
221
+ /**
222
+ * Prepare service options
223
+ */
224
+ export function prepareServiceOptions(
225
+ serviceOptions: ServiceOptions,
226
+ options?: {
227
+ clone?: boolean;
228
+ inputType?: any;
229
+ outputType?: any;
230
+ subFieldSelection?: string;
231
+ prepareInput?: PrepareInputOptions;
232
+ prepareOutput?: PrepareOutputOptions;
233
+ }
234
+ ): ServiceOptions {
235
+ // Set default values
236
+ const config = {
237
+ clone: true,
238
+ ...options,
239
+ };
240
+
241
+ // Clone
242
+ if (serviceOptions && config.clone) {
243
+ serviceOptions = _.cloneDeep(serviceOptions);
244
+ }
245
+
246
+ // Init if not exists
247
+ serviceOptions = serviceOptions || {};
248
+
249
+ // Set properties
250
+ serviceOptions.inputType = serviceOptions.inputType || options?.inputType;
251
+ serviceOptions.outputType = serviceOptions.outputType || options?.outputType;
252
+
253
+ // Set properties which can deactivate handling when falsy
254
+ if (!serviceOptions.prepareInput && 'prepareInput' in config) {
255
+ serviceOptions.prepareInput = config.prepareInput;
256
+ }
257
+ if (!serviceOptions.prepareOutput && 'prepareOutput' in config) {
258
+ serviceOptions.prepareOutput = config.prepareOutput;
259
+ }
260
+
261
+ // Set subfield selection
262
+ if (config.subFieldSelection) {
263
+ if ((serviceOptions.fieldSelection as ResolveSelector)?.select) {
264
+ (serviceOptions.fieldSelection as ResolveSelector).select += '.' + config.subFieldSelection;
265
+ }
266
+ }
267
+
268
+ // Return (cloned and) prepared service options
269
+ return serviceOptions;
270
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Interface for prepare input options
3
+ */
4
+ export interface PrepareInputOptions {
5
+ [key: string]: any;
6
+ create?: boolean;
7
+ clone?: boolean;
8
+ getNewArray?: boolean;
9
+ removeUndefined?: boolean;
10
+ targetModel?: new (...args: any[]) => any;
11
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Interface for prepare output options
3
+ */
4
+ export interface PrepareOutputOptions {
5
+ [key: string]: any;
6
+ clone?: boolean;
7
+ getNewArray?: boolean;
8
+ removeUndefined?: boolean;
9
+ targetModel?: new (...args: any[]) => any;
10
+ }
@@ -1,5 +1,7 @@
1
1
  import { Model } from 'mongoose';
2
2
  import { FieldSelection } from '../types/field-selection.type';
3
+ import { PrepareInputOptions } from './prepare-input-options.interface';
4
+ import { PrepareOutputOptions } from './prepare-output-options.interface';
3
5
 
4
6
  /**
5
7
  * General service options
@@ -40,24 +42,12 @@ export interface ServiceOptions {
40
42
  // Prepare input configuration:
41
43
  // If {} or not set, then the prepareInput function will run with defaults
42
44
  // If falsy, then the prepareInput function is not executed
43
- prepareInput?: {
44
- [key: string]: any;
45
- create?: boolean;
46
- clone?: boolean;
47
- getNewArray?: boolean;
48
- removeUndefined?: boolean;
49
- };
45
+ prepareInput?: PrepareInputOptions;
50
46
 
51
47
  // Prepare output configuration:
52
48
  // If {} or not set, then the prepareInput function will run with defaults
53
49
  // If falsy, then the prepareInput function is not executed
54
- prepareOutput?: {
55
- [key: string]: any;
56
- clone?: boolean;
57
- getNewArray?: boolean;
58
- removeUndefined?: boolean;
59
- targetModel?: new (...args: any[]) => any;
60
- };
50
+ prepareOutput?: PrepareOutputOptions;
61
51
 
62
52
  // Whether to publish action via GraphQL subscription
63
53
  pubSub?: boolean;
@@ -0,0 +1,18 @@
1
+ import { Field, InputType } from '@nestjs/graphql';
2
+ import { CoreInput } from '../../../common/inputs/core-input.input';
3
+
4
+ /**
5
+ * SignIn input
6
+ */
7
+ @InputType({ description: 'Sign-in input' })
8
+ export class CoreAuthSignInInput extends CoreInput {
9
+ // ===================================================================================================================
10
+ // Properties
11
+ // ===================================================================================================================
12
+
13
+ @Field({ description: 'Email', nullable: false })
14
+ email: string = undefined;
15
+
16
+ @Field({ description: 'Password', nullable: false })
17
+ password: string = undefined;
18
+ }
@@ -0,0 +1,18 @@
1
+ import { Field, InputType } from '@nestjs/graphql';
2
+ import { CoreInput } from '../../../common/inputs/core-input.input';
3
+
4
+ /**
5
+ * SignUp input
6
+ */
7
+ @InputType({ description: 'Sign-up input' })
8
+ export class CoreAuthSignUpInput extends CoreInput {
9
+ // ===================================================================================================================
10
+ // Properties
11
+ // ===================================================================================================================
12
+
13
+ @Field({ description: 'Email', nullable: false })
14
+ email: string = undefined;
15
+
16
+ @Field({ description: 'Password', nullable: false })
17
+ password: string = undefined;
18
+ }
package/src/index.ts CHANGED
@@ -35,6 +35,8 @@ export * from './core/common/inputs/sort.input';
35
35
  export * from './core/common/interceptors/check-response.interceptor';
36
36
  export * from './core/common/interfaces/core-persistence-model.interface';
37
37
  export * from './core/common/interfaces/mailjet-options.interface';
38
+ export * from './core/common/interfaces/prepare-input-options.interface';
39
+ export * from './core/common/interfaces/prepare-output-options.interface';
38
40
  export * from './core/common/interfaces/resolve-selector.interface';
39
41
  export * from './core/common/interfaces/server-options.interface';
40
42
  export * from './core/common/interfaces/service-options.interface';
@@ -3,22 +3,22 @@ import { CoreAuthModel } from '../../../core/modules/auth/core-auth.model';
3
3
  import { User } from '../user/user.model';
4
4
 
5
5
  /**
6
- * CoreAuthModel model for the response after the sign in
6
+ * Authentication data
7
7
  */
8
- @ObjectType({ description: 'Auth' })
8
+ @ObjectType({ description: 'Authentication data' })
9
9
  export class Auth extends CoreAuthModel {
10
10
  // ===================================================================================================================
11
11
  // Properties
12
12
  // ===================================================================================================================
13
13
 
14
14
  /**
15
- * Signed in user
15
+ * Signed-in user
16
16
  */
17
- @Field((type) => User, { description: 'User who signed in' })
17
+ @Field(() => User, { description: 'User who signed in' })
18
18
  user: User = undefined;
19
19
 
20
20
  // ===================================================================================================================
21
- // Properties
21
+ // Methods
22
22
  // ===================================================================================================================
23
23
 
24
24
  /**
@@ -1,9 +1,11 @@
1
1
  import { DynamicModule, Module } from '@nestjs/common';
2
2
  import { JwtModuleOptions } from '@nestjs/jwt';
3
+ import { EmailService } from '../../../core/common/services/email.service';
3
4
  import { CoreAuthModule } from '../../../core/modules/auth/core-auth.module';
4
5
  import { UserModule } from '../user/user.module';
5
6
  import { UserService } from '../user/user.service';
6
7
  import { AuthResolver } from './auth.resolver';
8
+ import { AuthService } from './auth.service';
7
9
 
8
10
  /**
9
11
  * CoreAuthModule to handle user authentication
@@ -17,8 +19,17 @@ export class AuthModule {
17
19
  static forRoot(options: JwtModuleOptions): DynamicModule {
18
20
  return {
19
21
  module: AuthModule,
20
- imports: [CoreAuthModule.forRoot(UserModule, UserService, options)],
21
- providers: [AuthResolver],
22
+ imports: [
23
+ CoreAuthModule.forRoot(UserModule, UserService, {
24
+ ...options,
25
+ ...{
26
+ // imports: [], // Integrate additional Services here to resolve dependencies
27
+ // providers: [] // Integrate additional Providers here to resolve dependencies
28
+ },
29
+ }),
30
+ EmailService,
31
+ ],
32
+ providers: [AuthResolver, AuthService],
22
33
  exports: [AuthResolver, CoreAuthModule],
23
34
  };
24
35
  }
@@ -1,22 +1,40 @@
1
- import { Args, Info, Query, Resolver } from '@nestjs/graphql';
1
+ import { Args, Info, Mutation, Query, Resolver } from '@nestjs/graphql';
2
2
  import { GraphQLResolveInfo } from 'graphql';
3
- import { CoreAuthResolver } from '../../../core/modules/auth/core-auth.resolver';
4
3
  import { Auth } from './auth.model';
4
+ import { AuthService } from './auth.service';
5
+ import { AuthSignInInput } from './inputs/auth-sign-in.input';
6
+ import { AuthSignUpInput } from './inputs/auth-sign-up.input';
5
7
 
6
8
  /**
7
9
  * Authentication resolver for the sign in
8
10
  */
9
- @Resolver((of) => Auth)
10
- export class AuthResolver extends CoreAuthResolver {
11
+ @Resolver(() => Auth)
12
+ export class AuthResolver {
11
13
  /**
12
- * Get user via ID
14
+ * Integrate services
13
15
  */
14
- @Query((returns) => Auth, { description: 'Get JWT token' })
15
- async signIn(
16
- @Args('email') email: string,
17
- @Args('password') password: string,
18
- @Info() info: GraphQLResolveInfo
19
- ): Promise<Auth> {
20
- return (await this.authService.signIn(email, password, { fieldSelection: { info, select: 'signIn' } })) as Auth;
16
+ constructor(private readonly authService: AuthService) {}
17
+
18
+ /**
19
+ * SignIn for User
20
+ */
21
+ @Query(() => Auth, { description: 'Sign in and get JWT token' })
22
+ async signIn(@Info() info: GraphQLResolveInfo, @Args('input') input: AuthSignInInput): Promise<Auth> {
23
+ return this.authService.signIn(input, {
24
+ fieldSelection: { info, select: 'signIn' },
25
+ inputType: AuthSignInInput,
26
+ });
27
+ }
28
+
29
+ /**
30
+ * Sign up for user
31
+ */
32
+ @Mutation(() => Auth, {
33
+ description: 'Sign up user and get JWT token',
34
+ })
35
+ async signUp(@Info() info: GraphQLResolveInfo, @Args('input') input: AuthSignUpInput): Promise<Auth> {
36
+ return this.authService.signUp(input, {
37
+ fieldSelection: { info, select: 'signUp' },
38
+ });
21
39
  }
22
40
  }
@@ -0,0 +1,84 @@
1
+ import { Injectable, UnauthorizedException } from '@nestjs/common';
2
+ import { JwtService } from '@nestjs/jwt';
3
+ import * as bcrypt from 'bcrypt';
4
+ import envConfig from '../../../config.env';
5
+ import { prepareServiceOptions } from '../../../core/common/helpers/service.helper';
6
+ import { ResolveSelector } from '../../../core/common/interfaces/resolve-selector.interface';
7
+ import { ServiceOptions } from '../../../core/common/interfaces/service-options.interface';
8
+ import { EmailService } from '../../../core/common/services/email.service';
9
+ import { JwtPayload } from '../../../core/modules/auth/interfaces/jwt-payload.interface';
10
+ import { UserService } from '../user/user.service';
11
+ import { Auth } from './auth.model';
12
+ import { AuthSignInInput } from './inputs/auth-sign-in.input';
13
+ import { AuthSignUpInput } from './inputs/auth-sign-up.input';
14
+
15
+ @Injectable()
16
+ export class AuthService {
17
+ constructor(
18
+ protected readonly jwtService: JwtService,
19
+ protected readonly emailService: EmailService,
20
+ protected readonly userService: UserService
21
+ ) {}
22
+
23
+ /**
24
+ * Sign in for user
25
+ */
26
+ async signIn(input: AuthSignInInput, serviceOptions?: ServiceOptions): Promise<Auth> {
27
+ // Prepare service options
28
+ const serviceOptionsForUserService = prepareServiceOptions(serviceOptions, {
29
+ // We need password, so we can't use prepare output handling and have to deactivate it
30
+ prepareOutput: null,
31
+
32
+ // Select user field for automatic populate handling via user service
33
+ subFieldSelection: 'user',
34
+ });
35
+
36
+ // Get and check user
37
+ const user = await this.userService.getViaEmail(input.email, serviceOptionsForUserService);
38
+ if (!user) {
39
+ throw new UnauthorizedException();
40
+ }
41
+
42
+ // Check password
43
+ if (!(await bcrypt.compare(input.password, user.password))) {
44
+ throw new UnauthorizedException();
45
+ }
46
+
47
+ // Create JWT and return sign-in data
48
+ const payload: JwtPayload = { email: user.email };
49
+ return Auth.map({
50
+ token: this.jwtService.sign(payload),
51
+ user,
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Register a new user Account
57
+ */
58
+ async signUp(input: AuthSignUpInput, serviceOptions?: ServiceOptions): Promise<Auth> {
59
+ // Prepare service options
60
+ const serviceOptionsForUserService = prepareServiceOptions(serviceOptions, {
61
+ // Select user field for automatic populate handling via user service
62
+ subFieldSelection: 'user',
63
+ });
64
+
65
+ // Get and check user
66
+ const user = await this.userService.create(input, serviceOptionsForUserService);
67
+ if (!user) {
68
+ throw Error('Email Address already in use');
69
+ }
70
+
71
+ // Send email
72
+ await this.emailService.sendMail(user.email, 'Welcome', {
73
+ htmlTemplate: 'welcome',
74
+ templateData: { name: user.username, link: envConfig.email.verificationLink + '/' + user.verificationToken },
75
+ });
76
+
77
+ // Create JWT and return sign-in data
78
+ const payload: JwtPayload = { email: user.email };
79
+ return Auth.map({
80
+ token: this.jwtService.sign(payload),
81
+ user: user,
82
+ });
83
+ }
84
+ }
@@ -0,0 +1,10 @@
1
+ import { InputType } from '@nestjs/graphql';
2
+ import { CoreAuthSignInInput } from '../../../../core/modules/auth/inputs/core-auth-sign-in.input';
3
+
4
+ /**
5
+ * SignIn input
6
+ */
7
+ @InputType({ description: 'Description for AuthSignInInput' })
8
+ export class AuthSignInInput extends CoreAuthSignInInput {
9
+ // Extend UserInput here
10
+ }
@@ -0,0 +1,18 @@
1
+ import { Field, InputType } from '@nestjs/graphql';
2
+ import { CoreAuthSignUpInput } from '../../../../core/modules/auth/inputs/core-auth-sign-up.input';
3
+
4
+ /**
5
+ * SignUp input
6
+ */
7
+ @InputType({ description: 'Description for AuthSignUpInput' })
8
+ export class AuthSignUpInput extends CoreAuthSignUpInput {
9
+ // ===================================================================================================================
10
+ // Properties
11
+ // ===================================================================================================================
12
+
13
+ @Field({ description: 'firstName', nullable: true })
14
+ firstName: string = undefined;
15
+
16
+ @Field({ description: 'lastName', nullable: true })
17
+ lastName: string = undefined;
18
+ }
@@ -14,7 +14,7 @@ import { UserService } from './user.service';
14
14
  /**
15
15
  * Resolver to process with user data
16
16
  */
17
- @Resolver((of) => User)
17
+ @Resolver(() => User)
18
18
  export class UserResolver {
19
19
  /**
20
20
  * Import services
@@ -29,7 +29,7 @@ export class UserResolver {
29
29
  * Get users (via filter)
30
30
  */
31
31
  @Roles(RoleEnum.ADMIN)
32
- @Query((returns) => [User], { description: 'Find users (via filter)' })
32
+ @Query(() => [User], { description: 'Find users (via filter)' })
33
33
  async findUsers(@Info() info: GraphQLResolveInfo, @Args() args?: FilterArgs) {
34
34
  return await this.userService.find(args, {
35
35
  fieldSelection: { info, select: 'findUsers' },
@@ -41,7 +41,7 @@ export class UserResolver {
41
41
  * Get user via ID
42
42
  */
43
43
  @Roles(RoleEnum.S_USER)
44
- @Query((returns) => User, { description: 'Get user with specified ID' })
44
+ @Query(() => User, { description: 'Get user with specified ID' })
45
45
  async getUser(@Args('id') id: string, @Info() info: GraphQLResolveInfo, @GraphQLUser() user: User): Promise<User> {
46
46
  return await this.userService.get(id, {
47
47
  currentUser: user,
@@ -53,7 +53,7 @@ export class UserResolver {
53
53
  /**
54
54
  * Get verified state of user with token
55
55
  */
56
- @Query((returns) => Boolean, { description: 'Get verified state of user with token' })
56
+ @Query(() => Boolean, { description: 'Get verified state of user with token' })
57
57
  async getVerifiedState(@Args('token') token: string) {
58
58
  return await this.userService.getVerifiedState(token);
59
59
  }
@@ -61,7 +61,7 @@ export class UserResolver {
61
61
  /**
62
62
  * Request new password for user with email
63
63
  */
64
- @Query((returns) => Boolean, { description: 'Request new password for user with email' })
64
+ @Query(() => Boolean, { description: 'Request new password for user with email' })
65
65
  async requestPasswordResetMail(@Args('email') email: string): Promise<boolean> {
66
66
  return !!(await this.userService.sendPasswordResetMail(email));
67
67
  }
@@ -73,7 +73,8 @@ export class UserResolver {
73
73
  /**
74
74
  * Create new user
75
75
  */
76
- @Mutation((returns) => User, { description: 'Create a new user' })
76
+ @Roles(RoleEnum.ADMIN)
77
+ @Mutation(() => User, { description: 'Create a new user' })
77
78
  async createUser(
78
79
  @Args('input') input: UserCreateInput,
79
80
  @GraphQLUser() user: User,
@@ -90,7 +91,7 @@ export class UserResolver {
90
91
  * Delete existing user
91
92
  */
92
93
  @Roles(RoleEnum.S_USER)
93
- @Mutation((returns) => User, { description: 'Delete existing user' })
94
+ @Mutation(() => User, { description: 'Delete existing user' })
94
95
  async deleteUser(@Args('id') id: string, @Info() info: GraphQLResolveInfo, @GraphQLUser() user: User): Promise<User> {
95
96
  return await this.userService.delete(id, {
96
97
  currentUser: user,
@@ -102,7 +103,7 @@ export class UserResolver {
102
103
  /**
103
104
  * Set new password for user with token
104
105
  */
105
- @Mutation((returns) => Boolean, { description: 'Set new password for user with token' })
106
+ @Mutation(() => Boolean, { description: 'Set new password for user with token' })
106
107
  async resetPassword(@Args('token') token: string, @Args('password') password: string): Promise<boolean> {
107
108
  return !!(await this.userService.resetPassword(token, password));
108
109
  }
@@ -111,7 +112,7 @@ export class UserResolver {
111
112
  * Update existing user
112
113
  */
113
114
  @Roles(RoleEnum.S_USER)
114
- @Mutation((returns) => User, { description: 'Update existing user' })
115
+ @Mutation(() => User, { description: 'Update existing user' })
115
116
  async updateUser(
116
117
  @Args('input') input: UserInput,
117
118
  @Args('id') id: string,
@@ -130,7 +131,7 @@ export class UserResolver {
130
131
  /**
131
132
  * Verify user with email
132
133
  */
133
- @Mutation((returns) => Boolean, { description: 'Verify user with email' })
134
+ @Mutation(() => Boolean, { description: 'Verify user with email' })
134
135
  async verifyUser(@Args('token') token: string): Promise<boolean> {
135
136
  return !!(await this.userService.verify(token));
136
137
  }
@@ -142,7 +143,7 @@ export class UserResolver {
142
143
  /**
143
144
  * Subscription for created user
144
145
  */
145
- @Subscription((returns) => User, {
146
+ @Subscription(() => User, {
146
147
  filter(this: UserResolver, payload, variables, context) {
147
148
  return context.user.roles.include(RoleEnum.ADMIN);
148
149
  },
@@ -58,20 +58,12 @@ export class UserService extends CoreUserService<User, UserInput, UserCreateInpu
58
58
  await this.pubSub.publish('userCreated', User.map(user));
59
59
  }
60
60
 
61
- // Send email
62
- await this.emailService.sendMail(user.email, 'Welcome', {
63
- htmlTemplate: 'welcome',
64
- templateData: { name: user.username, link: envConfig.email.verificationLink + '/' + user.verificationToken },
65
- });
66
-
67
61
  // Return created user
68
62
  return user;
69
63
  }
70
64
 
71
65
  /**
72
66
  * Request password reset mail
73
- *
74
- * @param email
75
67
  */
76
68
  async sendPasswordResetMail(email: string, serviceOptions?: ServiceOptions): Promise<User> {
77
69
  // Set password reset token