@lenne.tech/nest-server 8.1.0 → 8.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.
Files changed (61) hide show
  1. package/dist/config.env.js +3 -2
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/decorators/restricted.decorator.d.ts +3 -0
  4. package/dist/core/common/decorators/restricted.decorator.js +14 -8
  5. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  6. package/dist/core/common/enums/role.enum.d.ts +3 -2
  7. package/dist/core/common/enums/role.enum.js +3 -2
  8. package/dist/core/common/enums/role.enum.js.map +1 -1
  9. package/dist/core/common/helpers/db.helper.d.ts +2 -0
  10. package/dist/core/common/helpers/db.helper.js +13 -2
  11. package/dist/core/common/helpers/db.helper.js.map +1 -1
  12. package/dist/core/common/helpers/input.helper.d.ts +14 -2
  13. package/dist/core/common/helpers/input.helper.js +53 -11
  14. package/dist/core/common/helpers/input.helper.js.map +1 -1
  15. package/dist/core/common/interfaces/service-options.interface.d.ts +6 -0
  16. package/dist/core/common/models/core-model.model.d.ts +1 -1
  17. package/dist/core/common/models/core-model.model.js.map +1 -1
  18. package/dist/core/common/pipes/check-input.pipe.js +1 -1
  19. package/dist/core/common/pipes/check-input.pipe.js.map +1 -1
  20. package/dist/core/common/services/crud.service.js +17 -19
  21. package/dist/core/common/services/crud.service.js.map +1 -1
  22. package/dist/core/common/services/module.service.d.ts +14 -1
  23. package/dist/core/common/services/module.service.js +43 -4
  24. package/dist/core/common/services/module.service.js.map +1 -1
  25. package/dist/core/common/types/ids.type.d.ts +8 -0
  26. package/dist/core/common/types/ids.type.js +3 -0
  27. package/dist/core/common/types/ids.type.js.map +1 -0
  28. package/dist/core/modules/auth/guards/roles.guard.js +1 -2
  29. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  30. package/dist/core/modules/user/core-user.service.js +34 -36
  31. package/dist/core/modules/user/core-user.service.js.map +1 -1
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.js +1 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/server/modules/user/avatar.controller.js +1 -1
  36. package/dist/server/modules/user/avatar.controller.js.map +1 -1
  37. package/dist/server/modules/user/user.model.d.ts +1 -1
  38. package/dist/server/modules/user/user.resolver.d.ts +2 -2
  39. package/dist/server/modules/user/user.resolver.js +30 -14
  40. package/dist/server/modules/user/user.resolver.js.map +1 -1
  41. package/dist/server/modules/user/user.service.js +5 -1
  42. package/dist/server/modules/user/user.service.js.map +1 -1
  43. package/dist/tsconfig.build.tsbuildinfo +1 -1
  44. package/package.json +39 -39
  45. package/src/config.env.ts +3 -2
  46. package/src/core/common/decorators/restricted.decorator.ts +24 -12
  47. package/src/core/common/enums/role.enum.ts +24 -6
  48. package/src/core/common/helpers/db.helper.ts +16 -1
  49. package/src/core/common/helpers/input.helper.ts +65 -14
  50. package/src/core/common/interfaces/service-options.interface.ts +19 -1
  51. package/src/core/common/models/core-model.model.ts +1 -1
  52. package/src/core/common/pipes/check-input.pipe.ts +1 -1
  53. package/src/core/common/services/crud.service.ts +17 -22
  54. package/src/core/common/services/module.service.ts +83 -9
  55. package/src/core/common/types/ids.type.ts +7 -0
  56. package/src/core/modules/auth/guards/roles.guard.ts +5 -7
  57. package/src/core/modules/user/core-user.service.ts +38 -45
  58. package/src/index.ts +1 -0
  59. package/src/server/modules/user/avatar.controller.ts +1 -1
  60. package/src/server/modules/user/user.resolver.ts +26 -20
  61. package/src/server/modules/user/user.service.ts +8 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "8.1.0",
3
+ "version": "8.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",
@@ -20,7 +20,7 @@
20
20
  "format:staged": "pretty-quick --staged",
21
21
  "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
22
22
  "prestart:prod": "npm run build",
23
- "reinit": "rimraf package-lock.json && rimraf node_modules && npm cache clean --force && npm i && npm run test:e2e",
23
+ "reinit": "rimraf package-lock.json && rimraf node_modules && npm cache clean --force && npm i && npm run test:e2e && npm run build",
24
24
  "reinit:force": "rimraf package-lock.json && rimraf node_modules && npm cache clean --force && npm i --force && npm run test:e2e",
25
25
  "reinit:legacy": "rimraf package-lock.json && rimraf node_modules && npm cache clean --force && npm i --legacy-peer-deps && npm run test:e2e",
26
26
  "start": "./node_modules/.bin/grunt",
@@ -29,13 +29,13 @@
29
29
  "start:nodemon": "ts-node -r tsconfig-paths/register src/main.ts",
30
30
  "start:debug": "nodemon --config nodemon-debug.json",
31
31
  "start:dev": "nodemon",
32
- "test": "jest",
33
- "test:cov": "jest --coverage",
34
- "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
35
- "test:e2e": "jest --config jest-e2e.json",
36
- "test:e2e-cov": "jest --config jest-e2e.json --coverage",
37
- "test:ci": "jest --config jest-e2e.json --ci",
38
- "test:watch": "jest --watch",
32
+ "test": "NODE_ENV=local jest",
33
+ "test:cov": "NODE_ENV=local jest --coverage",
34
+ "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",
36
+ "test:e2e-cov": "NODE_ENV=local jest --config jest-e2e.json --coverage",
37
+ "test:ci": "NODE_ENV=local jest --config jest-e2e.json --ci",
38
+ "test:watch": "NODE_ENV=local jest --watch",
39
39
  "prepack": "npm run prestart:prod",
40
40
  "prepare": "husky install",
41
41
  "prepublishOnly": "npm run format && npm run lint && npm run test:ci",
@@ -52,39 +52,32 @@
52
52
  "node": ">= 16.13.0"
53
53
  },
54
54
  "dependencies": {
55
- "@apollo/gateway": "0.50.1",
56
- "@nestjs/apollo": "10.0.9",
55
+ "@apollo/gateway": "0.50.2",
56
+ "@nestjs/apollo": "10.0.11",
57
57
  "@nestjs/common": "8.4.4",
58
58
  "@nestjs/core": "8.4.4",
59
- "@nestjs/graphql": "10.0.9",
59
+ "@nestjs/graphql": "10.0.11",
60
60
  "@nestjs/jwt": "8.0.0",
61
61
  "@nestjs/mongoose": "9.0.3",
62
62
  "@nestjs/passport": "8.2.1",
63
63
  "@nestjs/platform-express": "8.4.4",
64
- "@types/ejs": "3.1.0",
65
- "@types/lodash": "4.14.182",
66
- "@types/multer": "1.4.7",
67
- "@types/node": "17.0.25",
68
- "@types/node-mailjet": "3.3.8",
69
- "@types/nodemailer": "6.4.4",
70
- "@types/passport": "1.0.7",
71
- "apollo-server-core": "3.6.7",
72
- "apollo-server-express": "3.6.7",
64
+ "apollo-server-core": "3.7.0",
65
+ "apollo-server-express": "3.7.0",
73
66
  "bcrypt": "5.0.1",
74
67
  "class-transformer": "0.5.1",
75
68
  "class-validator": "0.13.2",
76
- "ejs": "3.0.2",
77
- "graphql": "16.3.0",
69
+ "ejs": "3.1.7",
70
+ "graphql": "16.4.0",
78
71
  "graphql-subscriptions": "2.0.0",
79
- "json-to-graphql-query": "2.2.3",
80
- "light-my-request": "4.9.0",
72
+ "json-to-graphql-query": "2.2.4",
73
+ "light-my-request": "4.10.1",
81
74
  "lodash": "4.17.21",
82
75
  "mongodb": "4.5.0",
83
- "mongoose": "6.3.0",
76
+ "mongoose": "6.3.2",
84
77
  "multer": "1.4.4",
85
- "node-mailjet": "3.3.10",
86
- "nodemailer": "6.7.3",
87
- "nodemon": "2.0.15",
78
+ "node-mailjet": "3.4.1",
79
+ "nodemailer": "6.7.5",
80
+ "nodemon": "2.0.16",
88
81
  "passport": "0.5.2",
89
82
  "passport-jwt": "4.0.0",
90
83
  "reflect-metadata": "0.1.13",
@@ -93,12 +86,19 @@
93
86
  },
94
87
  "devDependencies": {
95
88
  "@nestjs/testing": "8.4.4",
96
- "@types/jest": "27.4.1",
89
+ "@types/ejs": "3.1.0",
90
+ "@types/jest": "27.5.0",
91
+ "@types/lodash": "4.14.182",
92
+ "@types/multer": "1.4.7",
93
+ "@types/node": "16.11.33",
94
+ "@types/node-mailjet": "3.3.8",
95
+ "@types/nodemailer": "6.4.4",
96
+ "@types/passport": "1.0.7",
97
97
  "@types/supertest": "2.0.12",
98
- "@typescript-eslint/eslint-plugin": "5.20.0",
99
- "@typescript-eslint/parser": "5.20.0",
100
- "coffeescript": "2.6.1",
101
- "eslint": "8.13.0",
98
+ "@typescript-eslint/eslint-plugin": "5.22.0",
99
+ "@typescript-eslint/parser": "5.22.0",
100
+ "coffeescript": "2.7.0",
101
+ "eslint": "8.14.0",
102
102
  "eslint-config-prettier": "8.5.0",
103
103
  "find-file-up": "2.0.1",
104
104
  "grunt": "1.5.2",
@@ -107,16 +107,16 @@
107
107
  "grunt-contrib-watch": "1.1.0",
108
108
  "grunt-sync": "0.8.2",
109
109
  "husky": "7.0.4",
110
- "jest": "27.5.1",
110
+ "jest": "28.0.3",
111
111
  "pm2": "5.2.0",
112
112
  "prettier": "2.6.2",
113
113
  "pretty-quick": "3.1.3",
114
- "supertest": "6.2.2",
115
- "ts-jest": "27.1.4",
114
+ "supertest": "6.2.3",
115
+ "ts-jest": "28.0.1",
116
116
  "ts-morph": "14.0.0",
117
117
  "ts-node": "10.7.0",
118
- "tsconfig-paths": "3.14.1",
119
- "typescript": "4.6.3"
118
+ "tsconfig-paths": "4.0.0",
119
+ "typescript": "4.6.4"
120
120
  },
121
121
  "jest": {
122
122
  "collectCoverage": true,
package/src/config.env.ts CHANGED
@@ -109,8 +109,9 @@ const config: { [env: string]: IServerOptions } = {
109
109
  *
110
110
  * default: development
111
111
  */
112
- const envConfig = config[process.env['NODE' + '_ENV'] || 'development'] || config.development;
113
- console.log('Server starts in mode: ', process.env['NODE' + '_ENV'] || 'development');
112
+ const env = process.env['NODE' + '_ENV'] || 'development';
113
+ const envConfig = config[env] || config.development;
114
+ console.log('Configured for: ' + envConfig.env + (env !== envConfig.env ? ' (requested: ' + env + ')' : ''));
114
115
 
115
116
  /**
116
117
  * Export envConfig as default
@@ -1,6 +1,8 @@
1
1
  import 'reflect-metadata';
2
2
  import { UnauthorizedException } from '@nestjs/common';
3
3
  import { RoleEnum } from '../enums/role.enum';
4
+ import { equalIds, getStringIds } from '../helpers/db.helper';
5
+ import { IdsType } from '../types/ids.type';
4
6
 
5
7
  /**
6
8
  * Restricted meta key
@@ -28,11 +30,14 @@ export const getRestricted = (object: unknown, propertyKey: string) => {
28
30
 
29
31
  /**
30
32
  * Check data for restricted properties (properties with `Restricted` decorator)
33
+ *
34
+ * If restricted roles includes RoleEnum.S_CREATOR, `creator` (createdBy) from current (DB) data must be set in options
35
+ * If restricted roles includes RoleEnum.S_OWNER, `ownerIds` from current (DB) data must be set in options
31
36
  */
32
37
  export const checkRestricted = (
33
38
  data: any,
34
39
  user: { id: any; hasRole: (roles: string[]) => boolean },
35
- options: { ignoreUndefined?: boolean; throwError?: boolean } = {},
40
+ options: { creator?: IdsType; ignoreUndefined?: boolean; ownerIds?: IdsType; throwError?: boolean } = {},
36
41
  processedObjects: any[] = []
37
42
  ) => {
38
43
  const config = {
@@ -55,7 +60,7 @@ export const checkRestricted = (
55
60
  // Array
56
61
  if (Array.isArray(data)) {
57
62
  // Check array items
58
- return data.map((item) => checkRestricted(item, user, options, processedObjects));
63
+ return data.map((item) => checkRestricted(item, user, config, processedObjects));
59
64
  }
60
65
 
61
66
  // Object
@@ -72,20 +77,27 @@ export const checkRestricted = (
72
77
  if (roles && roles.some((value) => !!value)) {
73
78
  // Check user and user roles
74
79
  if (!user || !user.hasRole(roles)) {
75
- // Check special role for owner
76
- if (user && roles.includes(RoleEnum.OWNER)) {
77
- const userId = user.id.toString();
80
+ // Check special creator role
81
+ if (user?.id && roles.includes(RoleEnum.S_CREATOR) && equalIds(user.id, config.creator)) {
82
+ continue;
83
+ }
84
+
85
+ // Check special owner role
86
+ else if (user && roles.includes(RoleEnum.S_OWNER)) {
87
+ const userId = getStringIds(user);
88
+ const ownerIds = config.ownerIds ? getStringIds(config.ownerIds) : null;
78
89
 
79
90
  if (
80
- !data.ownerIds ||
81
- !(
82
- data.ownerIds === userId ||
83
- (Array.isArray(data.ownerIds) &&
84
- data.ownerIds.some((item) => (item.id ? item.id.toString() === userId : item.toString() === userId)))
85
- )
91
+ // No owner IDs
92
+ !ownerIds ||
93
+ // User is not the owner
94
+ !(ownerIds === userId || (Array.isArray(ownerIds) && ownerIds.includes(userId)))
86
95
  ) {
87
96
  // The user does not have the required rights and is not the owner
88
97
  if (config.throwError) {
98
+ if (!config.ownerIds) {
99
+ throw new UnauthorizedException('Lack of ownerIds to verify ownership of ' + propertyKey);
100
+ }
89
101
  throw new UnauthorizedException('Current user is not allowed to set ' + propertyKey);
90
102
  }
91
103
  continue;
@@ -101,7 +113,7 @@ export const checkRestricted = (
101
113
  }
102
114
 
103
115
  // Check property data
104
- data[propertyKey] = checkRestricted(data[propertyKey], user, options, processedObjects);
116
+ data[propertyKey] = checkRestricted(data[propertyKey], user, config, processedObjects);
105
117
  }
106
118
 
107
119
  // Return processed data
@@ -1,13 +1,31 @@
1
1
  /**
2
- * Enums for role decorator
2
+ * Enums for Resolver @Role and Model @Restricted decorator and for roles property in ServiceOptions
3
3
  */
4
4
  export enum RoleEnum {
5
- // User must be an administrator
5
+ // ===================================================================================================================
6
+ // Real roles (integrated into user.roles), which can be used via @Restricted for Models (properties),
7
+ // via @Roles for Resolvers (methods) and via ServiceOptions for Resolver methods.
8
+ // ===================================================================================================================
9
+
10
+ // User must be an administrator (see roles of user)
6
11
  ADMIN = 'admin',
7
12
 
8
- // User must be the owner of the processed object(s)
9
- OWNER = 'owner',
13
+ // ===================================================================================================================
14
+ // Special system roles, which can be used via @Restricted for Models (properties), via @Roles for Resolvers (methods)
15
+ // and via ServiceOptions for Resolver methods. This roles should not be integrated into user.roles!
16
+ // ===================================================================================================================
17
+
18
+ // User must be signed in (see context user, e.g. @GraphQLUser)
19
+ S_USER = 's_user',
20
+
21
+ // ===================================================================================================================
22
+ // Special system roles that check rights for DB objects and can be used via @Restricted for Models (properties)
23
+ // and via ServiceOptions for Resolver methods. These roles should not be integrated in user.roles!
24
+ // ===================================================================================================================
25
+
26
+ // User must be the creator of the processed object(s) (see createdBy property of object(s))
27
+ S_CREATOR = 's_creator',
10
28
 
11
- // User must be signed in
12
- USER = 'user',
29
+ // User must be an owner of the processed object(s) (see owners property of object(s))
30
+ S_OWNER = 's_owner',
13
31
  }
@@ -4,6 +4,7 @@ import { Document, Model, PopulateOptions, Query, SchemaType, Types } from 'mong
4
4
  import { ResolveSelector } from '../interfaces/resolve-selector.interface';
5
5
  import { CoreModel } from '../models/core-model.model';
6
6
  import { FieldSelection } from '../types/field-selection.type';
7
+ import { IdsType } from '../types/ids.type';
7
8
  import { StringOrObjectId } from '../types/string-or-object-id.type';
8
9
 
9
10
  // =====================================================================================================================
@@ -78,6 +79,20 @@ export function addIds(
78
79
  return result;
79
80
  }
80
81
 
82
+ /**
83
+ * Checks if all IDs are equal
84
+ */
85
+ export function equalIds(...ids: IdsType[]): boolean {
86
+ if (!ids) {
87
+ return true;
88
+ }
89
+ const compare = getStringIds(ids[0]);
90
+ if (!compare) {
91
+ return false;
92
+ }
93
+ return ids.every((id) => getStringIds(id) === compare);
94
+ }
95
+
81
96
  /**
82
97
  * Get indexes of IDs in an array
83
98
  */
@@ -97,7 +112,7 @@ export function getIndexesViaIds(ids: any | any[], array: any[]): number[] {
97
112
  const indexes: number[] = [];
98
113
  ids.forEach((id) => {
99
114
  array.forEach((element, index) => {
100
- if (getStringIds(id) === getStringIds(element)) {
115
+ if (equalIds(id, element)) {
101
116
  indexes.push(index);
102
117
  }
103
118
  });
@@ -1,8 +1,11 @@
1
- import { BadRequestException } from '@nestjs/common';
1
+ import { BadRequestException, UnauthorizedException } from '@nestjs/common';
2
2
  import { plainToInstance } from 'class-transformer';
3
3
  import { validate } from 'class-validator';
4
4
  import * as _ from 'lodash';
5
5
  import { checkRestricted } from '../decorators/restricted.decorator';
6
+ import { RoleEnum } from '../enums/role.enum';
7
+ import { IdsType } from '../types/ids.type';
8
+ import { equalIds, getStringIds } from './db.helper';
6
9
 
7
10
  /**
8
11
  * Helper class for inputs
@@ -15,9 +18,9 @@ export default class InputHelper {
15
18
  public static async check(
16
19
  value: any,
17
20
  user: { id: any; hasRole: (roles: string[]) => boolean },
18
- metatype?
21
+ options?: { creator?: IdsType; metatype?: any; ownerIds?: IdsType; roles?: string | string[] }
19
22
  ): Promise<any> {
20
- return check(value, user, metatype);
23
+ return check(value, user, options);
21
24
  }
22
25
 
23
26
  // Standard error function
@@ -179,34 +182,82 @@ export default class InputHelper {
179
182
  export async function check(
180
183
  value: any,
181
184
  user: { id: any; hasRole: (roles: string[]) => boolean },
182
- metatype?
185
+ options?: { creator?: IdsType; metatype?: any; ownerIds?: IdsType; roles?: string | string[]; throwError?: boolean }
183
186
  ): Promise<any> {
187
+ const config = {
188
+ throwError: true,
189
+ ...options,
190
+ };
191
+
192
+ // Check roles
193
+ if (config.roles?.length && config.throwError) {
194
+ let roles = config.roles;
195
+ if (!Array.isArray(roles)) {
196
+ roles = [roles];
197
+ }
198
+ let valid = false;
199
+ if (roles.includes(RoleEnum.S_USER) && user?.id) {
200
+ valid = true;
201
+ } else if (user.hasRole(roles)) {
202
+ valid = true;
203
+ } else if (roles.includes(RoleEnum.S_CREATOR) && user?.id && equalIds(user.id, config.creator)) {
204
+ valid = true;
205
+ } else if (roles.includes(RoleEnum.S_OWNER) && user?.id && config.ownerIds) {
206
+ let ownerIds: string | string[] = getStringIds(config.ownerIds);
207
+ if (!Array.isArray(ownerIds)) {
208
+ ownerIds = [ownerIds];
209
+ }
210
+ valid = ownerIds.includes(getStringIds(user.id));
211
+ }
212
+ if (!valid) {
213
+ throw new UnauthorizedException('Missing rights');
214
+ }
215
+ }
216
+
184
217
  // Return value if it is only a basic type
185
- if (typeof value !== 'object' || !metatype || isBasicType(metatype)) {
218
+ if (typeof value !== 'object') {
186
219
  return value;
187
220
  }
188
221
 
189
- // Convert to metatype
190
- if (!(value instanceof metatype)) {
191
- if ((metatype as any)?.map) {
192
- value = (metatype as any)?.map(value);
193
- } else {
194
- value = plainToInstance(metatype, value);
222
+ // Check array
223
+ if (Array.isArray(value)) {
224
+ for (const [key, item] of Object.entries(value)) {
225
+ value[key] = await check(item, user, config);
226
+ }
227
+ return value;
228
+ }
229
+
230
+ const metatype = config.metatype;
231
+ if (metatype) {
232
+ // Check metatype
233
+ if (isBasicType(metatype)) {
234
+ return value;
235
+ }
236
+
237
+ // Convert to metatype
238
+ if (!(value instanceof metatype)) {
239
+ if ((metatype as any)?.map) {
240
+ value = (metatype as any)?.map(value);
241
+ } else {
242
+ value = plainToInstance(metatype, value);
243
+ }
195
244
  }
196
245
  }
197
246
 
198
247
  // Validate
199
248
  const errors = await validate(value);
200
- if (errors.length > 0) {
249
+ if (errors.length > 0 && config.throwError) {
201
250
  throw new BadRequestException('Validation failed');
202
251
  }
203
252
 
204
253
  // Remove restricted values if roles are missing
205
- value = checkRestricted(value, user);
254
+ value = checkRestricted(value, user, config);
206
255
  return value;
207
256
  }
208
257
 
209
- // Standard error function
258
+ /**
259
+ * Standard error function
260
+ */
210
261
  export function errorFunction(caller: (...params) => any, message = 'Required parameter is missing or invalid') {
211
262
  const err = new Error(message);
212
263
  Error.captureStackTrace(err, caller);
@@ -1,5 +1,6 @@
1
- import { Model } from 'mongoose';
1
+ import { Model, Types } from 'mongoose';
2
2
  import { FieldSelection } from '../types/field-selection.type';
3
+ import { IdsType } from '../types/ids.type';
3
4
 
4
5
  /**
5
6
  * General service options
@@ -8,6 +9,11 @@ export interface ServiceOptions {
8
9
  // All fields are allowed to be compatible as far as possible
9
10
  [key: string]: any;
10
11
 
12
+ // Check rights for input data (see check function in InputHelper)
13
+ // If falsy: input data will not be checked
14
+ // If truly (default): input data will be checked
15
+ checkRights?: boolean;
16
+
11
17
  // Current user to set ownership, check roles and other things
12
18
  currentUser?: {
13
19
  [key: string]: any;
@@ -18,6 +24,12 @@ export interface ServiceOptions {
18
24
  // Field selection for results
19
25
  fieldSelection?: FieldSelection;
20
26
 
27
+ // Overwrites type of input (array items)
28
+ inputType?: new (...params: any[]) => any;
29
+
30
+ // Owner IDs
31
+ ownerIds?: IdsType;
32
+
21
33
  // Process field selection
22
34
  // If {} or not set, then the field selection runs with defaults
23
35
  // If falsy, then the field selection will not be automatically executed
@@ -50,4 +62,10 @@ export interface ServiceOptions {
50
62
 
51
63
  // Whether to publish action via GraphQL subscription
52
64
  pubSub?: boolean;
65
+
66
+ // Overwrites type of result (array items)
67
+ resultType?: new (...params: any[]) => any;
68
+
69
+ // Roles (as string) to check
70
+ roles?: string | string[];
53
71
  }
@@ -78,7 +78,7 @@ export abstract class CoreModel {
78
78
  * Initialize instance with default values instead of undefined
79
79
  * Should be overwritten in child class to organize the defaults
80
80
  */
81
- public init<T extends CoreModel>(...args: any[]): this {
81
+ public init(...args: any[]): this {
82
82
  return this;
83
83
  }
84
84
 
@@ -28,6 +28,6 @@ export class CheckInputPipe implements PipeTransform {
28
28
  const { user }: any = getContextData(this.context);
29
29
 
30
30
  // Check and return
31
- return check(value, user, metatype);
31
+ return check(value, user, { metatype });
32
32
  }
33
33
  }
@@ -24,16 +24,11 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
24
24
  * Get item by ID
25
25
  */
26
26
  async get(id: string, serviceOptions?: ServiceOptions): Promise<T> {
27
- return this.process(
28
- async (data) => {
29
- const item = await this.mainDbModel.findById(data.input).exec();
30
- if (!item) {
31
- throw new NotFoundException(`No ${this.mainModelConstructor.name} found with ID: ${id}`);
32
- }
33
- return item;
34
- },
35
- { input: id, serviceOptions }
36
- );
27
+ const dbObject = await this.mainDbModel.findById(id).exec();
28
+ if (!dbObject) {
29
+ throw new NotFoundException(`No ${this.mainModelConstructor.name} found with ID: ${id}`);
30
+ }
31
+ return this.process(async () => dbObject, { dbObject, serviceOptions });
37
32
  }
38
33
 
39
34
  /**
@@ -74,15 +69,15 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
74
69
  * Update item via ID
75
70
  */
76
71
  async update(id: string, input: any, serviceOptions?: ServiceOptions): Promise<T> {
72
+ const dbObject = await this.mainDbModel.findById(id).exec();
73
+ if (!dbObject) {
74
+ throw new NotFoundException(`No ${this.mainModelConstructor.name} found with ID: ${id}`);
75
+ }
77
76
  return this.process(
78
77
  async (data) => {
79
- const item = await this.mainDbModel.findById(id).exec();
80
- if (!item) {
81
- throw new NotFoundException(`No ${this.mainModelConstructor.name} found with ID: ${id}`);
82
- }
83
- return await Object.assign(item, data.input).save();
78
+ return await Object.assign(dbObject, data.input).save();
84
79
  },
85
- { input, serviceOptions }
80
+ { dbObject, input, serviceOptions }
86
81
  );
87
82
  }
88
83
 
@@ -90,16 +85,16 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
90
85
  * Delete item via ID
91
86
  */
92
87
  async delete(id: string, serviceOptions?: ServiceOptions): Promise<T> {
88
+ const dbObject = await this.mainDbModel.findById(id).exec();
89
+ if (!dbObject) {
90
+ throw new NotFoundException(`No ${this.mainModelConstructor.name} found with ID: ${id}`);
91
+ }
93
92
  return this.process(
94
93
  async (data) => {
95
- const item = await this.mainDbModel.findById(id).exec();
96
- if (!item) {
97
- throw new NotFoundException(`No ${this.mainModelConstructor.name} found with ID: ${id}`);
98
- }
99
94
  await this.mainDbModel.findByIdAndDelete(id).exec();
100
- return item;
95
+ return dbObject;
101
96
  },
102
- { input: id, serviceOptions }
97
+ { dbObject, input: id, serviceOptions }
103
98
  );
104
99
  }
105
100
  }