@lenne.tech/nest-server 3.3.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
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",
@@ -52,11 +52,11 @@
52
52
  "node": ">= 16.13.0"
53
53
  },
54
54
  "dependencies": {
55
- "@apollo/federation": "0.33.9",
56
- "@apollo/gateway": "0.42.3",
55
+ "@apollo/gateway": "0.48.1",
56
+ "@nestjs/apollo": "10.0.2",
57
57
  "@nestjs/common": "8.2.6",
58
58
  "@nestjs/core": "8.2.6",
59
- "@nestjs/graphql": "9.1.2",
59
+ "@nestjs/graphql": "10.0.2",
60
60
  "@nestjs/jwt": "8.0.0",
61
61
  "@nestjs/mongoose": "9.0.2",
62
62
  "@nestjs/passport": "8.1.0",
@@ -67,22 +67,24 @@
67
67
  "@types/jest": "27.4.0",
68
68
  "@types/lodash": "4.14.178",
69
69
  "@types/multer": "1.4.7",
70
- "@types/node": "16.11.21",
70
+ "@types/node": "17.0.17",
71
71
  "@types/node-mailjet": "3.3.8",
72
72
  "@types/nodemailer": "6.4.4",
73
73
  "@types/passport": "1.0.7",
74
74
  "@types/supertest": "2.0.11",
75
- "@typescript-eslint/eslint-plugin": "5.10.0",
76
- "@typescript-eslint/parser": "5.10.0",
77
- "apollo-server-core": "3.6.2",
78
- "apollo-server-express": "3.6.2",
75
+ "@typescript-eslint/eslint-plugin": "5.11.0",
76
+ "@typescript-eslint/parser": "5.11.0",
77
+ "apollo-server-core": "3.6.3",
78
+ "apollo-server-express": "3.6.3",
79
79
  "bcrypt": "5.0.1",
80
80
  "class-transformer": "0.5.1",
81
81
  "class-validator": "0.13.2",
82
82
  "coffeescript": "2.6.1",
83
83
  "ejs": "3.1.6",
84
- "fastify": "3.27.0",
85
- "graphql": "15.8.0",
84
+ "eslint": "8.9.0",
85
+ "eslint-config-prettier": "8.3.0",
86
+ "fastify": "3.27.1",
87
+ "graphql": "16.3.0",
86
88
  "graphql-subscriptions": "2.0.0",
87
89
  "grunt": "1.4.1",
88
90
  "grunt-bg-shell": "2.3.3",
@@ -90,35 +92,33 @@
90
92
  "grunt-contrib-watch": "1.1.0",
91
93
  "grunt-sync": "0.8.2",
92
94
  "husky": "7.0.4",
93
- "jest": "27.4.7",
94
- "json-to-graphql-query": "2.2.0",
95
- "light-my-request": "4.7.0",
95
+ "jest": "27.5.1",
96
+ "json-to-graphql-query": "2.2.2",
97
+ "light-my-request": "4.7.1",
96
98
  "lodash": "4.17.21",
97
- "mongodb": "4.3.0",
98
- "mongoose": "6.1.7",
99
+ "mongodb": "4.3.1",
100
+ "mongoose": "6.2.1",
99
101
  "multer": "1.4.4",
100
102
  "node-mailjet": "3.3.5",
101
103
  "nodemailer": "6.7.2",
102
104
  "nodemon": "2.0.15",
103
105
  "passport": "0.5.2",
104
106
  "passport-jwt": "4.0.0",
107
+ "prettier": "2.5.1",
108
+ "pretty-quick": "3.1.3",
105
109
  "reflect-metadata": "0.1.13",
106
110
  "rimraf": "3.0.2",
107
- "rxjs": "7.5.2",
111
+ "rxjs": "7.5.4",
108
112
  "supertest": "6.2.2",
113
+ "ts-jest": "27.1.3",
109
114
  "ts-morph": "13.0.3",
110
- "ts-node": "10.4.0",
111
- "tsconfig-paths": "3.12.0"
115
+ "ts-node": "10.5.0",
116
+ "tsconfig-paths": "3.12.0",
117
+ "typescript": "4.5.5"
112
118
  },
113
119
  "devDependencies": {
114
- "eslint": "8.7.0",
115
- "eslint-config-prettier": "8.3.0",
116
120
  "find-file-up": "2.0.1",
117
- "pm2": "5.1.2",
118
- "prettier": "2.5.1",
119
- "pretty-quick": "3.1.3",
120
- "ts-jest": "27.1.3",
121
- "typescript": "4.5.5"
121
+ "pm2": "5.1.2"
122
122
  },
123
123
  "jest": {
124
124
  "collectCoverage": true,
@@ -10,24 +10,44 @@ export class ServiceHelper {
10
10
  /**
11
11
  * Prepare input before save
12
12
  */
13
- static async prepareInput(
14
- input: Record<string, any>,
13
+ static async prepareInput<T = any>(
14
+ input: T,
15
15
  currentUser: { [key: string]: any; id: string },
16
- options: { [key: string]: any; create?: boolean; clone?: boolean; removeUndefined?: boolean } = {}
17
- ) {
16
+ options: {
17
+ [key: string]: any;
18
+ create?: boolean;
19
+ clone?: boolean;
20
+ getNewArray?: boolean;
21
+ removeUndefined?: boolean;
22
+ } = {}
23
+ ): Promise<T> {
18
24
  // Configuration
19
25
  const config = {
20
26
  checkRoles: false,
21
27
  clone: false,
22
28
  create: false,
29
+ getNewArray: false,
23
30
  removeUndefined: false,
24
31
  ...options,
25
32
  };
26
33
 
34
+ // Check input
35
+ if (typeof input !== 'object') {
36
+ return input;
37
+ }
38
+
39
+ // Process array
40
+ if (Array.isArray(input)) {
41
+ const processedArray = input.map(
42
+ async (item) => await ServiceHelper.prepareInput(item, currentUser, options)
43
+ ) as any;
44
+ return config.getNewArray ? processedArray : input;
45
+ }
46
+
27
47
  // Clone input
28
48
  if (config.clone) {
29
- if (input.mapDeep && typeof input.mapDeep === 'function') {
30
- input = Object.getPrototypeOf(input).mapDeep(input);
49
+ if ((input as Record<string, any>).mapDeep && typeof (input as any).mapDeep === 'function') {
50
+ input = await Object.getPrototypeOf(input).mapDeep(input);
31
51
  } else {
32
52
  input = _.cloneDeep(input);
33
53
  }
@@ -39,32 +59,36 @@ export class ServiceHelper {
39
59
  }
40
60
 
41
61
  // Process roles
42
- if (config.checkRoles && input.roles && (!currentUser?.hasRole || !currentUser.hasRole(RoleEnum.ADMIN))) {
62
+ if (
63
+ config.checkRoles &&
64
+ (input as Record<string, any>).roles &&
65
+ (!currentUser?.hasRole || !currentUser.hasRole(RoleEnum.ADMIN))
66
+ ) {
43
67
  if (!(currentUser as any)?.roles) {
44
68
  throw new UnauthorizedException('Missing roles of current user');
45
69
  } else {
46
- const allowedRoles = _.intersection(input.roles, (currentUser as any).roles);
47
- if (allowedRoles.length !== input.roles.length) {
48
- const missingRoles = _.difference(input.roles, (currentUser as any).roles);
70
+ const allowedRoles = _.intersection((input as Record<string, any>).roles, (currentUser as any).roles);
71
+ if (allowedRoles.length !== (input as Record<string, any>).roles.length) {
72
+ const missingRoles = _.difference((input as Record<string, any>).roles, (currentUser as any).roles);
49
73
  throw new UnauthorizedException('Current user not allowed setting roles: ' + missingRoles);
50
74
  }
51
- input.roles = allowedRoles;
75
+ (input as Record<string, any>).roles = allowedRoles;
52
76
  }
53
77
  }
54
78
 
55
79
  // Hash password
56
- if (input.password) {
57
- input.password = await bcrypt.hash((input as any).password, 10);
80
+ if ((input as Record<string, any>).password) {
81
+ (input as Record<string, any>).password = await bcrypt.hash((input as any).password, 10);
58
82
  }
59
83
 
60
84
  // Set creator
61
85
  if (config.create && currentUser) {
62
- input.createdBy = currentUser.id;
86
+ (input as Record<string, any>).createdBy = currentUser.id;
63
87
  }
64
88
 
65
89
  // Set updater
66
90
  if (currentUser) {
67
- input.updatedBy = currentUser.id;
91
+ (input as Record<string, any>).updatedBy = currentUser.id;
68
92
  }
69
93
 
70
94
  // Return prepared input
@@ -74,22 +98,40 @@ export class ServiceHelper {
74
98
  /**
75
99
  * Prepare output before return
76
100
  */
77
- static async prepareOutput<T = Record<string, any>>(
101
+ static async prepareOutput<T = { [key: string]: any; map: (...args: any[]) => any }>(
78
102
  output: any,
79
- options: { [key: string]: any; clone?: boolean; removeUndefined?: boolean; targetModel?: Partial<T> } = {}
80
- ) {
103
+ options: {
104
+ [key: string]: any;
105
+ clone?: boolean;
106
+ getNewArray?: boolean;
107
+ removeUndefined?: boolean;
108
+ targetModel?: new (...args: any[]) => T;
109
+ } = {}
110
+ ): Promise<T | T[] | any> {
81
111
  // Configuration
82
112
  const config = {
83
113
  clone: false,
114
+ getNewArray: false,
84
115
  removeUndefined: false,
85
116
  targetModel: undefined,
86
117
  ...options,
87
118
  };
88
119
 
120
+ // Check output
121
+ if (typeof output !== 'object') {
122
+ return output;
123
+ }
124
+
125
+ // Process array
126
+ if (Array.isArray(output)) {
127
+ const processedArray = output.map(async (item) => await ServiceHelper.prepareOutput(item, options)) as any;
128
+ return config.getNewArray ? processedArray : output;
129
+ }
130
+
89
131
  // Clone output
90
132
  if (config.clone) {
91
- if (output.cloneDeep && typeof output.cloneDeep === 'function') {
92
- output = Object.getPrototypeOf(output).cloneDeep(output);
133
+ if (output.mapDeep && typeof output.mapDeep === 'function') {
134
+ output = await Object.getPrototypeOf(output).mapDeep(output);
93
135
  } else {
94
136
  output = _.cloneDeep(output);
95
137
  }
@@ -97,7 +139,7 @@ export class ServiceHelper {
97
139
 
98
140
  // Map output if target model exist
99
141
  if (config.targetModel) {
100
- output = (config.targetModel as any).map(output);
142
+ output = await (config.targetModel as any).map(output);
101
143
  }
102
144
 
103
145
  // Remove password if exists
@@ -12,5 +12,6 @@ export interface ICorePersistenceModel<T extends CorePersistenceModel = any> {
12
12
  merge?: boolean;
13
13
  }
14
14
  ): any;
15
+ init(this: new (...args: any[]) => T, ...args: any[]): any;
15
16
  [key: string]: any;
16
17
  }
@@ -1,8 +1,8 @@
1
- import { GqlModuleOptions } from '@nestjs/graphql';
1
+ import { ApolloDriverConfig } from '@nestjs/apollo';
2
2
  import { JwtModuleOptions } from '@nestjs/jwt';
3
+ import { MongooseModuleOptions } from '@nestjs/mongoose/dist/interfaces/mongoose-options.interface';
3
4
  import { ServeStaticOptions } from '@nestjs/platform-express/interfaces/serve-static-options.interface';
4
5
  import * as SMTPTransport from 'nodemailer/lib/smtp-transport';
5
- import { MongooseModuleOptions } from '@nestjs/mongoose/dist/interfaces/mongoose-options.interface';
6
6
  import { MailjetOptions } from './mailjet-options.interface';
7
7
 
8
8
  /**
@@ -20,37 +20,7 @@ export interface IServerOptions {
20
20
  * see https://docs.nestjs.com/graphql/quick-start
21
21
  * and https://www.apollographql.com/docs/apollo-server/api/apollo-server/
22
22
  */
23
- graphQl?: {
24
- /**
25
- * Autogenerated schema file
26
- * e.g. 'schema.gql'
27
- */
28
- autoSchemaFile?: string;
29
-
30
- /**
31
- * Function for context manipulation
32
- * e.g. ({ req }) => ({ req })
33
- */
34
- context?: (context: { [key: string]: any; req: any }) => { [key: string]: any; req: any };
35
-
36
- /**
37
- * Enables and disables development mode helpers of apollo
38
- * e.g. true
39
- */
40
- debug?: boolean;
41
-
42
- /**
43
- * Determines whether or not to install subscription handlers
44
- * e.g. true
45
- */
46
- installSubscriptionHandlers?: boolean;
47
-
48
- /**
49
- * Enables and disables schema introspection
50
- * e.g. true
51
- */
52
- introspection?: boolean;
53
- } & GqlModuleOptions;
23
+ graphQl?: ApolloDriverConfig;
54
24
 
55
25
  /**
56
26
  * Configuration of JavaScript Web Token (JWT) module
@@ -28,14 +28,19 @@ export abstract class CoreModel {
28
28
  options: {
29
29
  cloneDeep?: boolean;
30
30
  funcAllowed?: boolean;
31
- init?: boolean;
31
+ init?: any;
32
32
  item?: T;
33
33
  mapId?: boolean;
34
34
  } = {}
35
35
  ): T {
36
+ const config = {
37
+ init: true,
38
+ ...options,
39
+ };
40
+
36
41
  const item = options.item || new this();
37
42
  delete options.item;
38
- return item.map(data, options);
43
+ return item.map(data, config);
39
44
  }
40
45
 
41
46
  /**
@@ -52,14 +57,19 @@ export abstract class CoreModel {
52
57
  options: {
53
58
  cloneDeep?: boolean;
54
59
  funcAllowed?: boolean;
55
- init?: boolean;
60
+ init?: any;
56
61
  item?: T;
57
62
  mapId?: boolean;
58
63
  } = {}
59
64
  ): T {
65
+ const config = {
66
+ init: true,
67
+ ...options,
68
+ };
69
+
60
70
  const item = options.item || new this();
61
71
  delete options.item;
62
- return item.mapDeep(data, options);
72
+ return item.mapDeep(data, config);
63
73
  }
64
74
 
65
75
  /**
@@ -105,14 +115,14 @@ export abstract class CoreModel {
105
115
  options: {
106
116
  cloneDeep?: boolean;
107
117
  funcAllowed?: boolean;
108
- init?: boolean;
118
+ init?: any;
109
119
  mapId?: boolean;
110
120
  } = {}
111
121
  ): this {
112
122
  const config = {
113
123
  cloneDeep: true,
114
124
  funcAllowed: false,
115
- init: false,
125
+ init: undefined,
116
126
  mapId: false,
117
127
  ...options,
118
128
  };
@@ -87,7 +87,7 @@ export abstract class CorePersistenceModel extends CoreModel {
87
87
  updatedAt: Date = undefined;
88
88
 
89
89
  // ===========================================================================
90
- // Properties
90
+ // Methods
91
91
  // ===========================================================================
92
92
 
93
93
  /**
@@ -10,6 +10,7 @@ import { EmailService } from './core/common/services/email.service';
10
10
  import { TemplateService } from './core/common/services/template.service';
11
11
  import { MongooseModule } from '@nestjs/mongoose';
12
12
  import { MailjetService } from './core/common/services/mailjet.service';
13
+ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
13
14
 
14
15
  /**
15
16
  * Core module (dynamic)
@@ -106,7 +107,7 @@ export class CoreModule {
106
107
  module: CoreModule,
107
108
  imports: [
108
109
  MongooseModule.forRoot(config.mongoose.uri, config.mongoose.options),
109
- GraphQLModule.forRoot(config.graphQl),
110
+ GraphQLModule.forRoot<ApolloDriverConfig>(Object.assign({ driver: ApolloDriver }, config.graphQl)),
110
111
  ],
111
112
  providers,
112
113
  exports: [ConfigService, EmailService, TemplateService, MailjetService],
@@ -1,3 +1,4 @@
1
+ import { Types } from 'mongoose';
1
2
  import * as mongoose from 'mongoose';
2
3
  import { Prop } from '@nestjs/mongoose';
3
4
  import { Field, ObjectType } from '@nestjs/graphql';
@@ -24,7 +25,7 @@ export abstract class PersistenceModel extends CorePersistenceModel {
24
25
  nullable: true,
25
26
  })
26
27
  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
27
- createdBy?: User = undefined;
28
+ createdBy?: Types.ObjectId | User = undefined;
28
29
 
29
30
  /**
30
31
  * User who last updated the object
@@ -36,7 +37,7 @@ export abstract class PersistenceModel extends CorePersistenceModel {
36
37
  nullable: true,
37
38
  })
38
39
  @Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User' })
39
- updatedBy?: User = undefined;
40
+ updatedBy?: Types.ObjectId | User = undefined;
40
41
 
41
42
  // ===========================================================================
42
43
  // Properties