@lenne.tech/nest-server 8.6.3 → 8.6.6

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 (72) hide show
  1. package/dist/config.env.js +4 -2
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/decorators/restricted.decorator.js +4 -4
  4. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  5. package/dist/core/common/helpers/file.helper.js +4 -7
  6. package/dist/core/common/helpers/file.helper.js.map +1 -1
  7. package/dist/core/common/helpers/filter.helper.d.ts +7 -0
  8. package/dist/core/common/helpers/filter.helper.js +38 -1
  9. package/dist/core/common/helpers/filter.helper.js.map +1 -1
  10. package/dist/core/common/helpers/input.helper.d.ts +6 -0
  11. package/dist/core/common/helpers/input.helper.js +28 -6
  12. package/dist/core/common/helpers/input.helper.js.map +1 -1
  13. package/dist/core/common/helpers/service.helper.js +3 -5
  14. package/dist/core/common/helpers/service.helper.js.map +1 -1
  15. package/dist/core/common/inputs/filter.input.js.map +1 -1
  16. package/dist/core/common/interfaces/server-options.interface.d.ts +16 -13
  17. package/dist/core/common/models/core-model.model.js +4 -1
  18. package/dist/core/common/models/core-model.model.js.map +1 -1
  19. package/dist/core/common/services/crud.service.js +9 -4
  20. package/dist/core/common/services/crud.service.js.map +1 -1
  21. package/dist/core/common/services/module.service.js +1 -1
  22. package/dist/core/common/services/module.service.js.map +1 -1
  23. package/dist/core/modules/auth/guards/roles.guard.js +2 -1
  24. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  25. package/dist/core/modules/user/core-user.service.js +9 -13
  26. package/dist/core/modules/user/core-user.service.js.map +1 -1
  27. package/dist/core.module.d.ts +3 -2
  28. package/dist/core.module.js +26 -4
  29. package/dist/core.module.js.map +1 -1
  30. package/dist/main.js +13 -0
  31. package/dist/main.js.map +1 -1
  32. package/dist/server/modules/auth/auth.module.js +1 -2
  33. package/dist/server/modules/auth/auth.module.js.map +1 -1
  34. package/dist/server/modules/file/file.controller.js +1 -1
  35. package/dist/server/modules/file/file.controller.js.map +1 -1
  36. package/dist/server/modules/file/file.resolver.d.ts +5 -0
  37. package/dist/server/modules/file/file.resolver.js +51 -0
  38. package/dist/server/modules/file/file.resolver.js.map +1 -0
  39. package/dist/server/modules/user/user.model.d.ts +1 -1
  40. package/dist/server/modules/user/user.resolver.js +2 -1
  41. package/dist/server/modules/user/user.resolver.js.map +1 -1
  42. package/dist/server/modules/user/user.service.js +1 -1
  43. package/dist/server/modules/user/user.service.js.map +1 -1
  44. package/dist/server/server.module.js +3 -1
  45. package/dist/server/server.module.js.map +1 -1
  46. package/dist/test/test.helper.d.ts +4 -1
  47. package/dist/test/test.helper.js +51 -25
  48. package/dist/test/test.helper.js.map +1 -1
  49. package/dist/tsconfig.build.tsbuildinfo +1 -1
  50. package/package.json +33 -28
  51. package/src/config.env.ts +4 -2
  52. package/src/core/common/decorators/restricted.decorator.ts +1 -2
  53. package/src/core/common/helpers/file.helper.ts +9 -11
  54. package/src/core/common/helpers/filter.helper.ts +66 -1
  55. package/src/core/common/helpers/input.helper.ts +61 -2
  56. package/src/core/common/helpers/service.helper.ts +2 -6
  57. package/src/core/common/inputs/filter.input.ts +0 -1
  58. package/src/core/common/interfaces/server-options.interface.ts +68 -52
  59. package/src/core/common/models/core-model.model.ts +4 -1
  60. package/src/core/common/services/crud.service.ts +7 -4
  61. package/src/core/common/services/module.service.ts +0 -1
  62. package/src/core/modules/auth/guards/roles.guard.ts +1 -1
  63. package/src/core/modules/user/core-user.service.ts +13 -22
  64. package/src/core.module.ts +38 -6
  65. package/src/main.ts +16 -0
  66. package/src/server/modules/auth/auth.module.ts +1 -2
  67. package/src/server/modules/file/file.controller.ts +1 -1
  68. package/src/server/modules/file/file.resolver.ts +55 -0
  69. package/src/server/modules/user/user.resolver.ts +1 -1
  70. package/src/server/modules/user/user.service.ts +1 -1
  71. package/src/server/server.module.ts +5 -1
  72. package/src/test/test.helper.ts +92 -34
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "8.6.3",
3
+ "version": "8.6.6",
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",
@@ -16,6 +16,9 @@
16
16
  "scripts": {
17
17
  "build": "rimraf dist && tsc -p tsconfig.build.json",
18
18
  "build:pack": "npm pack && echo 'use file:/ROOT_PATH_TO_TGZ_FILE to integrate the package'",
19
+ "docs": "npm run docs:ci && open ./public/index.html",
20
+ "docs:bootstrap": "node extras/update-spectaql-version.mjs && npx -y spectaql ./spectaql.yml",
21
+ "docs:ci": "ts-node ./scripts/init-server.ts && npm run docs:boostrap",
19
22
  "format": "prettier --write 'src/**/*.ts'",
20
23
  "format:staged": "pretty-quick --staged",
21
24
  "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
@@ -32,9 +35,10 @@
32
35
  "test": "NODE_ENV=local jest",
33
36
  "test:cov": "NODE_ENV=local jest --coverage",
34
37
  "test:debug": "NODE_ENV=local node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
35
- "test:e2e": "NODE_ENV=local jest --config jest-e2e.json --forceExit --detectOpenHandles",
36
- "test:e2e-cov": "NODE_ENV=local jest --config jest-e2e.json --coverage --forceExit --detectOpenHandles",
37
- "test:ci": "NODE_ENV=local jest --config jest-e2e.json --ci --forceExit --detectOpenHandles",
38
+ "test:e2e": "NODE_ENV=local jest --config jest-e2e.json --forceExit",
39
+ "test:e2e-cov": "NODE_ENV=local jest --config jest-e2e.json --coverage --forceExit",
40
+ "test:e2e-doh": "NODE_ENV=local jest --config jest-e2e.json --forceExit --detectOpenHandles",
41
+ "test:ci": "NODE_ENV=local jest --config jest-e2e.json --ci --forceExit",
38
42
  "test:watch": "NODE_ENV=local jest --watch",
39
43
  "prepack": "npm run prestart:prod",
40
44
  "prepare": "husky install",
@@ -53,60 +57,61 @@
53
57
  },
54
58
  "dependencies": {
55
59
  "@apollo/gateway": "0.50.2",
56
- "@nestjs/apollo": "10.0.11",
57
- "@nestjs/common": "8.4.4",
58
- "@nestjs/core": "8.4.4",
59
- "@nestjs/graphql": "10.0.11",
60
- "@nestjs/jwt": "8.0.0",
61
- "@nestjs/mongoose": "9.0.3",
60
+ "@nestjs/apollo": "10.0.12",
61
+ "@nestjs/common": "8.4.5",
62
+ "@nestjs/core": "8.4.5",
63
+ "@nestjs/graphql": "10.0.12",
64
+ "@nestjs/jwt": "8.0.1",
65
+ "@nestjs/mongoose": "9.1.0",
62
66
  "@nestjs/passport": "8.2.1",
63
- "@nestjs/platform-express": "8.4.4",
67
+ "@nestjs/platform-express": "8.4.5",
64
68
  "apollo-server-core": "3.7.0",
65
69
  "apollo-server-express": "3.7.0",
66
70
  "bcrypt": "5.0.1",
67
71
  "class-transformer": "0.5.1",
68
72
  "class-validator": "0.13.2",
69
- "ejs": "3.1.7",
70
- "graphql": "16.4.0",
73
+ "ejs": "3.1.8",
74
+ "graphql": "16.5.0",
71
75
  "graphql-subscriptions": "2.0.0",
76
+ "graphql-upload": "13.0.0",
72
77
  "json-to-graphql-query": "2.2.4",
73
- "light-my-request": "4.10.1",
78
+ "light-my-request": "5.0.0",
74
79
  "lodash": "4.17.21",
75
- "mongodb": "4.5.0",
76
- "mongoose": "6.3.2",
80
+ "mongodb": "4.6.0",
81
+ "mongoose": "6.3.4",
77
82
  "multer": "1.4.4",
78
83
  "node-mailjet": "3.4.1",
79
84
  "nodemailer": "6.7.5",
80
85
  "nodemon": "2.0.16",
81
- "passport": "0.5.2",
86
+ "passport": "0.5.3",
82
87
  "passport-jwt": "4.0.0",
83
88
  "reflect-metadata": "0.1.13",
84
89
  "rimraf": "3.0.2",
85
90
  "rxjs": "7.5.5"
86
91
  },
87
92
  "devDependencies": {
88
- "@nestjs/testing": "8.4.4",
89
- "@types/ejs": "3.1.0",
90
- "@types/jest": "27.5.0",
93
+ "@nestjs/testing": "8.4.5",
94
+ "@types/ejs": "3.1.1",
95
+ "@types/jest": "27.5.1",
91
96
  "@types/lodash": "4.14.182",
92
97
  "@types/multer": "1.4.7",
93
- "@types/node": "16.11.33",
94
- "@types/node-mailjet": "3.3.8",
98
+ "@types/node": "16.11.36",
99
+ "@types/node-mailjet": "3.3.9",
95
100
  "@types/nodemailer": "6.4.4",
96
101
  "@types/passport": "1.0.7",
97
102
  "@types/supertest": "2.0.12",
98
- "@typescript-eslint/eslint-plugin": "5.22.0",
99
- "@typescript-eslint/parser": "5.22.0",
103
+ "@typescript-eslint/eslint-plugin": "5.25.0",
104
+ "@typescript-eslint/parser": "5.25.0",
100
105
  "coffeescript": "2.7.0",
101
- "eslint": "8.15.0",
106
+ "eslint": "8.16.0",
102
107
  "eslint-config-prettier": "8.5.0",
103
108
  "find-file-up": "2.0.1",
104
- "grunt": "1.5.2",
109
+ "grunt": "1.5.3",
105
110
  "grunt-bg-shell": "2.3.3",
106
111
  "grunt-contrib-clean": "2.0.1",
107
112
  "grunt-contrib-watch": "1.1.0",
108
113
  "grunt-sync": "0.8.2",
109
- "husky": "7.0.4",
114
+ "husky": "8.0.1",
110
115
  "jest": "28.1.0",
111
116
  "pm2": "5.2.0",
112
117
  "prettier": "2.6.2",
@@ -114,7 +119,7 @@
114
119
  "supertest": "6.2.3",
115
120
  "ts-jest": "28.0.2",
116
121
  "ts-morph": "14.0.0",
117
- "ts-node": "10.7.0",
122
+ "ts-node": "10.8.0",
118
123
  "tsconfig-paths": "4.0.0",
119
124
  "typescript": "4.6.4"
120
125
  },
package/src/config.env.ts CHANGED
@@ -31,6 +31,7 @@ const config: { [env: string]: IServerOptions } = {
31
31
  passwordResetLink: 'http://localhost:4200/user/password-reset',
32
32
  },
33
33
  env: 'development',
34
+ execAfterInit: 'npm run docs:bootstrap',
34
35
  graphQl: {
35
36
  driver: {
36
37
  debug: true,
@@ -46,7 +47,7 @@ const config: { [env: string]: IServerOptions } = {
46
47
  port: 3000,
47
48
  staticAssets: {
48
49
  path: join(__dirname, '..', 'public'),
49
- options: { prefix: '/public/' },
50
+ options: { prefix: '' },
50
51
  },
51
52
  templates: {
52
53
  path: join(__dirname, 'templates'),
@@ -80,6 +81,7 @@ const config: { [env: string]: IServerOptions } = {
80
81
  passwordResetLink: 'http://localhost:4200/user/password-reset',
81
82
  },
82
83
  env: 'productive',
84
+ execAfterInit: 'npm run docs:bootstrap',
83
85
  graphQl: {
84
86
  driver: {
85
87
  debug: false,
@@ -95,7 +97,7 @@ const config: { [env: string]: IServerOptions } = {
95
97
  port: 3000,
96
98
  staticAssets: {
97
99
  path: join(__dirname, '..', 'public'),
98
- options: { prefix: '/public/' },
100
+ options: { prefix: '' },
99
101
  },
100
102
  templates: {
101
103
  path: join(__dirname, 'templates'),
@@ -118,9 +118,8 @@ export const checkRestricted = (
118
118
 
119
119
  // Check roles
120
120
  if (roles.length) {
121
- // Check roles
122
121
  if (
123
- user?.hasRole(roles) ||
122
+ user?.hasRole?.(roles) ||
124
123
  (user?.id && roles.includes(RoleEnum.S_USER)) ||
125
124
  (roles.includes(RoleEnum.S_CREATOR) && getIncludedIds(config.dbObject?.createdBy, user))
126
125
  ) {
@@ -75,23 +75,21 @@ export function multerOptionsForImageUpload(options: {
75
75
  fileSize?: number;
76
76
  fileTypeRegex?: RegExp;
77
77
  }): MulterOptions {
78
- // Default options
79
- options = Object.assign(
80
- {
81
- fileSize: 1024 * 1024, // 1MB
82
- fileTypeRegex: /jpeg|jpg|png/, // Images only
83
- },
84
- options
85
- );
78
+ // Set config
79
+ const config = {
80
+ fileSize: 1024 * 1024, // 1MB
81
+ fileTypeRegex: /jpeg|jpg|png/, // Images only
82
+ ...options,
83
+ };
86
84
 
87
85
  return {
88
86
  // File filter
89
- fileFilter: options.fileTypeRegex ? multerFileFilter(options.fileTypeRegex) : undefined,
87
+ fileFilter: config.fileTypeRegex ? multerFileFilter(config.fileTypeRegex) : undefined,
90
88
 
91
89
  // Limits
92
90
  limits: {
93
91
  // Limit of file size
94
- fileSize: options.fileSize ? options.fileSize : undefined,
92
+ fileSize: config.fileSize ? config.fileSize : undefined,
95
93
  },
96
94
 
97
95
  // Automatic storage handling
@@ -100,7 +98,7 @@ export function multerOptionsForImageUpload(options: {
100
98
  // Destination for uploaded file
101
99
  // If destination is not set file will be buffered and can be processed
102
100
  // in the method
103
- destination: options.destination ? options.destination : undefined,
101
+ destination: config.destination ? config.destination : undefined,
104
102
 
105
103
  // Generated random file name
106
104
  filename: multerRandomFileName(),
@@ -4,6 +4,8 @@ import { ComparisonOperatorEnum } from '../enums/comparison-operator.enum';
4
4
  import { LogicalOperatorEnum } from '../enums/logical-operator.enum';
5
5
  import { FilterInput } from '../inputs/filter.input';
6
6
  import { SortInput } from '../inputs/sort.input';
7
+ import { getObjectIds } from './db.helper';
8
+ import { assignPlain } from './input.helper';
7
9
 
8
10
  /**
9
11
  * Helper for filter handling
@@ -33,9 +35,72 @@ export class Filter {
33
35
  }
34
36
  }
35
37
 
38
+ /**
39
+ * Helper function to create $and, $or or $nor filter
40
+ */
41
+ export function findFilter(options?: {
42
+ conditions?: Record<string, any>[];
43
+ filterOptions?: FilterQuery<any>;
44
+ id?: any;
45
+ ids?: any[];
46
+ type?: '$and' | '$or' | '$nor';
47
+ }): FilterQuery<any> {
48
+ const config = {
49
+ type: '$and',
50
+ ...options,
51
+ };
52
+
53
+ // Init filter Option
54
+ let filterOptions: FilterQuery<any> = config?.filterOptions;
55
+
56
+ // Check where condition
57
+ if (!filterOptions) {
58
+ filterOptions = {};
59
+ filterOptions[config.type] = [];
60
+ }
61
+
62
+ // Convert where condition to array
63
+ if (!Array.isArray(filterOptions?.[config.type])) {
64
+ filterOptions = {};
65
+ filterOptions[config.type] = [config?.filterOptions];
66
+ }
67
+
68
+ // ObjectId
69
+ if (config?.id) {
70
+ filterOptions[config.type].push({ _id: getObjectIds(config.id) });
71
+ }
72
+
73
+ // ObjectIds
74
+ if (config?.ids) {
75
+ if (!Array.isArray(config.ids)) {
76
+ config.ids = [config.ids];
77
+ }
78
+ filterOptions[config.type].push({ _id: { $in: getObjectIds(config.ids) } });
79
+ }
80
+
81
+ // Integrate conditions
82
+ if (config?.conditions) {
83
+ filterOptions[config.type] = [...filterOptions[config.type], ...config.conditions];
84
+ }
85
+
86
+ // Filter falsy values
87
+ filterOptions[config.type] = filterOptions[config.type].filter((value) => value);
88
+
89
+ // Optimizations
90
+ if (!filterOptions[config.type].length) {
91
+ filterOptions = {};
92
+ } else if (filterOptions[config.type].length === 1) {
93
+ const additionalProperties = filterOptions[config.type][0];
94
+ delete filterOptions[config.type];
95
+ assignPlain(filterOptions, additionalProperties);
96
+ }
97
+
98
+ // Return filter config
99
+ return filterOptions;
100
+ }
101
+
36
102
  /**
37
103
  * Convert filter arguments to a query array
38
- * @param filterArgs
39
104
  */
40
105
  export function convertFilterArgsToQuery<T = any>(filterArgs: Partial<FilterArgs>): [FilterQuery<T>, QueryOptions] {
41
106
  return [generateFilterQuery(filterArgs?.filter), generateFindOptions(filterArgs)];
@@ -1,6 +1,7 @@
1
1
  import { BadRequestException, UnauthorizedException } from '@nestjs/common';
2
2
  import { plainToInstance } from 'class-transformer';
3
3
  import { validate } from 'class-validator';
4
+ import { ValidatorOptions } from 'class-validator/types/validation/ValidatorOptions';
4
5
  import * as _ from 'lodash';
5
6
  import { checkRestricted } from '../decorators/restricted.decorator';
6
7
  import { ProcessType } from '../enums/process-type.enum';
@@ -182,6 +183,24 @@ export default class InputHelper {
182
183
  }
183
184
  }
184
185
 
186
+ /**
187
+ * Assign plain objects to the target object and ignores undefined
188
+ */
189
+ export function assignPlain(target: Record<any, any>, ...args: Record<any, any>[]): any {
190
+ return Object.assign(
191
+ target,
192
+ ...args.map(
193
+ // Prepare records
194
+ (item) =>
195
+ !item
196
+ ? // Return item if not an object
197
+ item
198
+ : // Return cloned record with undefined properties removed
199
+ filterProperties(JSON.parse(JSON.stringify(item)), (prop) => prop !== undefined)
200
+ )
201
+ );
202
+ }
203
+
185
204
  /**
186
205
  * Check input
187
206
  */
@@ -194,11 +213,16 @@ export async function check(
194
213
  processType?: ProcessType;
195
214
  roles?: string | string[];
196
215
  throwError?: boolean;
216
+ validatorOptions?: ValidatorOptions;
197
217
  }
198
218
  ): Promise<any> {
199
219
  const config = {
200
220
  throwError: true,
201
221
  ...options,
222
+ validatorOptions: {
223
+ skipUndefinedProperties: true,
224
+ ...options?.validatorOptions,
225
+ },
202
226
  };
203
227
 
204
228
  // Check roles
@@ -212,7 +236,7 @@ export async function check(
212
236
  // check if user is logged in
213
237
  (roles.includes(RoleEnum.S_USER) && user?.id) ||
214
238
  // check if the user has at least one of the required roles
215
- user.hasRole(roles) ||
239
+ user?.hasRole?.(roles) ||
216
240
  // check if the user is the creator
217
241
  (roles.includes(RoleEnum.S_CREATOR) && equalIds(config.dbObject?.createdBy, user))
218
242
  ) {
@@ -254,7 +278,7 @@ export async function check(
254
278
  }
255
279
 
256
280
  // Validate
257
- const errors = await validate(value);
281
+ const errors = await validate(value, config.validatorOptions);
258
282
  if (errors.length > 0 && config.throwError) {
259
283
  throw new BadRequestException('Validation failed');
260
284
  }
@@ -264,6 +288,13 @@ export async function check(
264
288
  return value;
265
289
  }
266
290
 
291
+ /**
292
+ * Combines objects to a new single plain object and ignores undefined
293
+ */
294
+ export function combinePlain(...args: Record<any, any>[]): any {
295
+ return assignPlain({}, ...args);
296
+ }
297
+
267
298
  /**
268
299
  * Standard error function
269
300
  */
@@ -273,6 +304,18 @@ export function errorFunction(caller: (...params) => any, message = 'Required pa
273
304
  throw err;
274
305
  }
275
306
 
307
+ /**
308
+ * Filter function for objects
309
+ */
310
+ export function filterProperties<T = Record<string, any>>(
311
+ obj: T,
312
+ filterFunction: (value?: any, key?: string, obj?: T) => boolean
313
+ ): Partial<T> {
314
+ return Object.keys(obj)
315
+ .filter((key) => filterFunction(obj[key], key, obj))
316
+ .reduce((res, key) => Object.assign(res, { [key]: obj[key] }), {});
317
+ }
318
+
276
319
  /**
277
320
  * Check if parameter is an array
278
321
  */
@@ -433,6 +476,22 @@ export function returnFalse(): boolean {
433
476
  return false;
434
477
  }
435
478
 
479
+ /**
480
+ * Match function to use instead of switch case
481
+ * Inspired by https://yusfuu.medium.com/dont-use-switch-or-if-else-in-javascript-instead-try-this-82f32616c269
482
+ *
483
+ * Example:
484
+ * const matched = match(expr, {
485
+ * Oranges: 'Oranges are $0.59 a pound.',
486
+ * Mangoes: 'Mangoes and papayas are $2.79 a pound.',
487
+ * Papayas: 'Mangoes and papayas are $2.79 a pound.',
488
+ * default: `Sorry, we are out of ${expr}.`,
489
+ * });
490
+ */
491
+ export function match(expression: any, cases: Record<any, any>): any {
492
+ return cases[expression] || cases?.default;
493
+ }
494
+
436
495
  /**
437
496
  * Map values into specific type
438
497
  */
@@ -68,7 +68,7 @@ export async function prepareInput<T = any>(
68
68
  clone: false,
69
69
  create: false,
70
70
  getNewArray: false,
71
- removeUndefined: false,
71
+ removeUndefined: true,
72
72
  ...options,
73
73
  };
74
74
 
@@ -107,11 +107,7 @@ export async function prepareInput<T = any>(
107
107
  }
108
108
 
109
109
  // Process roles
110
- if (
111
- config.checkRoles &&
112
- (input as Record<string, any>).roles &&
113
- (!currentUser?.hasRole || !currentUser.hasRole(RoleEnum.ADMIN))
114
- ) {
110
+ if (config.checkRoles && (input as Record<string, any>).roles && !currentUser?.hasRole?.(RoleEnum.ADMIN)) {
115
111
  if (!(currentUser as any)?.roles) {
116
112
  throw new UnauthorizedException('Missing roles of current user');
117
113
  } else {
@@ -1,5 +1,4 @@
1
1
  import { Field, InputType } from '@nestjs/graphql';
2
- import { ModelHelper } from '../helpers/model.helper';
3
2
  import { CombinedFilterInput } from './combined-filter.input';
4
3
  import { CoreInput } from './core-input.input';
5
4
  import { SingleFilterInput } from './single-filter.input';
@@ -1,4 +1,5 @@
1
1
  import { ApolloDriverConfig } from '@nestjs/apollo';
2
+ import { GqlModuleAsyncOptions } from '@nestjs/graphql';
2
3
  import { JwtModuleOptions } from '@nestjs/jwt';
3
4
  import { MongooseModuleOptions } from '@nestjs/mongoose/dist/interfaces/mongoose-options.interface';
4
5
  import { ServeStaticOptions } from '@nestjs/platform-express/interfaces/serve-static-options.interface';
@@ -9,12 +10,58 @@ import { MailjetOptions } from './mailjet-options.interface';
9
10
  * Options for the server
10
11
  */
11
12
  export interface IServerOptions {
13
+ /**
14
+ * SMTP and template configuration for sending emails
15
+ */
16
+ email?: {
17
+ /**
18
+ * Data for default sender
19
+ */
20
+ defaultSender?: {
21
+ /**
22
+ * Default email for sending emails
23
+ */
24
+ email?: string;
25
+
26
+ /**
27
+ * Default name for sending emails
28
+ */
29
+ name?: string;
30
+ };
31
+
32
+ /**
33
+ * Options for Mailjet
34
+ */
35
+ mailjet?: MailjetOptions;
36
+
37
+ /**
38
+ * Password reset link for email
39
+ */
40
+ passwordResetLink?: string;
41
+
42
+ /**
43
+ * SMTP configuration for nodemailer
44
+ */
45
+ smtp?: SMTPTransport | SMTPTransport.Options | string;
46
+
47
+ /**
48
+ * Verification link for email
49
+ */
50
+ verificationLink?: string;
51
+ };
52
+
12
53
  /**
13
54
  * Environment
14
55
  * e.g. 'development'
15
56
  */
16
57
  env?: string;
17
58
 
59
+ /**
60
+ * Exec a command after server is initialized
61
+ * e.g. 'npm run docs:bootstrap'
62
+ */
63
+ execAfterInit?: string;
64
+
18
65
  /**
19
66
  * Configuration of the GraphQL module
20
67
  * see https://docs.nestjs.com/graphql/quick-start
@@ -30,6 +77,11 @@ export interface IServerOptions {
30
77
  * Subscription authentication
31
78
  */
32
79
  enableSubscriptionAuth?: boolean;
80
+
81
+ /**
82
+ * Module options (forRootAsync)
83
+ */
84
+ options?: GqlModuleAsyncOptions;
33
85
  };
34
86
 
35
87
  /**
@@ -67,6 +119,11 @@ export interface IServerOptions {
67
119
  secretOrPrivateKey?: string;
68
120
  } & JwtModuleOptions;
69
121
 
122
+ /**
123
+ * Configuration for Mongoose
124
+ */
125
+ mongoose?: { uri: string; options?: MongooseModuleOptions };
126
+
70
127
  /**
71
128
  * Port number of the server
72
129
  * e.g. 8080
@@ -74,77 +131,36 @@ export interface IServerOptions {
74
131
  port?: number;
75
132
 
76
133
  /**
77
- * SMTP and template configuration for sending emails
134
+ * Configuration for useStaticAssets
78
135
  */
79
- email?: {
80
- /**
81
- * SMTP configuration for nodemailer
82
- */
83
- smtp?: SMTPTransport | SMTPTransport.Options | string;
84
-
85
- mailjet?: MailjetOptions;
86
- /**
87
- * Verification link for email
88
- */
89
- verificationLink?: string;
90
-
91
- /**
92
- * Password reset link for email
93
- */
94
- passwordResetLink?: string;
95
-
136
+ staticAssets?: {
96
137
  /**
97
- * Data for default sender
138
+ * Additional options for useStaticAssets
139
+ * e.g. {prefix: '/public/'}
98
140
  */
99
- defaultSender?: {
100
- /**
101
- * Default email for sending emails
102
- */
103
- email?: string;
104
-
105
- /**
106
- * Default name for sending emails
107
- */
108
- name?: string;
109
- };
110
- };
111
-
112
- /**
113
- * Configuration for Mongoose
114
- */
115
- mongoose?: { uri: string; options?: MongooseModuleOptions };
141
+ options?: ServeStaticOptions;
116
142
 
117
- /**
118
- * Configuration for useStaticAssets
119
- */
120
- staticAssets?: {
121
143
  /**
122
144
  * Root directory for static assets
123
145
  * e.g. join(__dirname, '..', 'public')
124
146
  */
125
147
  path?: string;
126
-
127
- /**
128
- * Additional options for useStaticAssets
129
- * e.g. {prefix: '/public/'}
130
- */
131
- options?: ServeStaticOptions;
132
148
  };
133
149
 
134
150
  /**
135
151
  * Templates
136
152
  */
137
153
  templates?: {
138
- /**
139
- * Directory for templates
140
- * e.g. join(__dirname, '..', 'templates')
141
- */
142
- path?: string;
143
-
144
154
  /**
145
155
  * View engine
146
156
  * e.g. 'ejs'
147
157
  */
148
158
  engine?: string;
159
+
160
+ /**
161
+ * Directory for templates
162
+ * e.g. join(__dirname, '..', 'templates')
163
+ */
164
+ path?: string;
149
165
  };
150
166
  }
@@ -92,7 +92,10 @@ export abstract class CoreModel {
92
92
  mapId: false,
93
93
  ...options,
94
94
  };
95
- return config.init ? map(data, this, config).init(config.init) : map(data, this, config);
95
+ if (config.init) {
96
+ this.init(config.init);
97
+ }
98
+ return map(data, this, config);
96
99
  }
97
100
 
98
101
  /**
@@ -2,6 +2,7 @@ import { NotFoundException } from '@nestjs/common';
2
2
  import { FilterArgs } from '../args/filter.args';
3
3
  import { merge } from '../helpers/config.helper';
4
4
  import { convertFilterArgsToQuery } from '../helpers/filter.helper';
5
+ import { assignPlain } from '../helpers/input.helper';
5
6
  import { ServiceOptions } from '../interfaces/service-options.interface';
6
7
  import { CoreModel } from '../models/core-model.model';
7
8
  import { ModuleService } from './module.service';
@@ -14,7 +15,8 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
14
15
  merge({ prepareInput: { create: true } }, serviceOptions);
15
16
  return this.process(
16
17
  async (data) => {
17
- return new this.mainDbModel({ ...data.input }).save();
18
+ const currentUserId = serviceOptions?.currentUser?.id;
19
+ return new this.mainDbModel({ ...data.input, createdBy: currentUserId, updatedBy: currentUserId }).save();
18
20
  },
19
21
  { input, serviceOptions }
20
22
  );
@@ -75,7 +77,8 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
75
77
  }
76
78
  return this.process(
77
79
  async (data) => {
78
- return await Object.assign(dbObject, data.input).save();
80
+ const currentUserId = serviceOptions?.currentUser?.id;
81
+ return await assignPlain(dbObject, data.input, { updatedBy: currentUserId }).save();
79
82
  },
80
83
  { dbObject, input, serviceOptions }
81
84
  );
@@ -90,11 +93,11 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
90
93
  throw new NotFoundException(`No ${this.mainModelConstructor.name} found with ID: ${id}`);
91
94
  }
92
95
  return this.process(
93
- async (data) => {
96
+ async () => {
94
97
  await this.mainDbModel.findByIdAndDelete(id).exec();
95
98
  return dbObject;
96
99
  },
97
- { dbObject, input: id, serviceOptions }
100
+ { dbObject, serviceOptions }
98
101
  );
99
102
  }
100
103
  }
@@ -150,7 +150,6 @@ export abstract class ModuleService<T extends CoreModel = any> {
150
150
  */
151
151
  async prepareInput(input: Record<string, any>, options: ServiceOptions = {}) {
152
152
  const config = {
153
- targetModel: this.mainModelConstructor,
154
153
  ...options?.prepareInput,
155
154
  };
156
155
  return prepareInput(input, options.currentUser, config);