@lenne.tech/nest-server 8.6.5 → 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 (41) hide show
  1. package/dist/core/common/decorators/restricted.decorator.js +4 -4
  2. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  3. package/dist/core/common/helpers/file.helper.js +4 -7
  4. package/dist/core/common/helpers/file.helper.js.map +1 -1
  5. package/dist/core/common/helpers/filter.helper.d.ts +7 -0
  6. package/dist/core/common/helpers/filter.helper.js +38 -1
  7. package/dist/core/common/helpers/filter.helper.js.map +1 -1
  8. package/dist/core/common/helpers/input.helper.d.ts +6 -0
  9. package/dist/core/common/helpers/input.helper.js +28 -6
  10. package/dist/core/common/helpers/input.helper.js.map +1 -1
  11. package/dist/core/common/helpers/service.helper.js +3 -5
  12. package/dist/core/common/helpers/service.helper.js.map +1 -1
  13. package/dist/core/common/models/core-model.model.js +4 -1
  14. package/dist/core/common/models/core-model.model.js.map +1 -1
  15. package/dist/core/common/services/crud.service.js +9 -4
  16. package/dist/core/common/services/crud.service.js.map +1 -1
  17. package/dist/core/common/services/module.service.js +1 -1
  18. package/dist/core/common/services/module.service.js.map +1 -1
  19. package/dist/core/modules/auth/guards/roles.guard.js +2 -1
  20. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  21. package/dist/core/modules/user/core-user.service.js +9 -13
  22. package/dist/core/modules/user/core-user.service.js.map +1 -1
  23. package/dist/server/modules/user/user.model.d.ts +1 -1
  24. package/dist/server/modules/user/user.resolver.js +2 -1
  25. package/dist/server/modules/user/user.resolver.js.map +1 -1
  26. package/dist/test/test.helper.js +11 -25
  27. package/dist/test/test.helper.js.map +1 -1
  28. package/dist/tsconfig.build.tsbuildinfo +1 -1
  29. package/package.json +14 -14
  30. package/src/core/common/decorators/restricted.decorator.ts +1 -2
  31. package/src/core/common/helpers/file.helper.ts +9 -11
  32. package/src/core/common/helpers/filter.helper.ts +66 -1
  33. package/src/core/common/helpers/input.helper.ts +61 -2
  34. package/src/core/common/helpers/service.helper.ts +2 -6
  35. package/src/core/common/models/core-model.model.ts +4 -1
  36. package/src/core/common/services/crud.service.ts +7 -4
  37. package/src/core/common/services/module.service.ts +0 -1
  38. package/src/core/modules/auth/guards/roles.guard.ts +1 -1
  39. package/src/core/modules/user/core-user.service.ts +13 -22
  40. package/src/server/modules/user/user.resolver.ts +1 -1
  41. package/src/test/test.helper.ts +27 -34
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "8.6.5",
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",
@@ -57,12 +57,12 @@
57
57
  },
58
58
  "dependencies": {
59
59
  "@apollo/gateway": "0.50.2",
60
- "@nestjs/apollo": "10.0.11",
60
+ "@nestjs/apollo": "10.0.12",
61
61
  "@nestjs/common": "8.4.5",
62
62
  "@nestjs/core": "8.4.5",
63
- "@nestjs/graphql": "10.0.11",
64
- "@nestjs/jwt": "8.0.0",
65
- "@nestjs/mongoose": "9.0.3",
63
+ "@nestjs/graphql": "10.0.12",
64
+ "@nestjs/jwt": "8.0.1",
65
+ "@nestjs/mongoose": "9.1.0",
66
66
  "@nestjs/passport": "8.2.1",
67
67
  "@nestjs/platform-express": "8.4.5",
68
68
  "apollo-server-core": "3.7.0",
@@ -75,15 +75,15 @@
75
75
  "graphql-subscriptions": "2.0.0",
76
76
  "graphql-upload": "13.0.0",
77
77
  "json-to-graphql-query": "2.2.4",
78
- "light-my-request": "4.10.1",
78
+ "light-my-request": "5.0.0",
79
79
  "lodash": "4.17.21",
80
80
  "mongodb": "4.6.0",
81
- "mongoose": "6.3.3",
81
+ "mongoose": "6.3.4",
82
82
  "multer": "1.4.4",
83
83
  "node-mailjet": "3.4.1",
84
84
  "nodemailer": "6.7.5",
85
85
  "nodemon": "2.0.16",
86
- "passport": "0.5.2",
86
+ "passport": "0.5.3",
87
87
  "passport-jwt": "4.0.0",
88
88
  "reflect-metadata": "0.1.13",
89
89
  "rimraf": "3.0.2",
@@ -95,15 +95,15 @@
95
95
  "@types/jest": "27.5.1",
96
96
  "@types/lodash": "4.14.182",
97
97
  "@types/multer": "1.4.7",
98
- "@types/node": "16.11.35",
99
- "@types/node-mailjet": "3.3.8",
98
+ "@types/node": "16.11.36",
99
+ "@types/node-mailjet": "3.3.9",
100
100
  "@types/nodemailer": "6.4.4",
101
101
  "@types/passport": "1.0.7",
102
102
  "@types/supertest": "2.0.12",
103
- "@typescript-eslint/eslint-plugin": "5.23.0",
104
- "@typescript-eslint/parser": "5.23.0",
103
+ "@typescript-eslint/eslint-plugin": "5.25.0",
104
+ "@typescript-eslint/parser": "5.25.0",
105
105
  "coffeescript": "2.7.0",
106
- "eslint": "8.15.0",
106
+ "eslint": "8.16.0",
107
107
  "eslint-config-prettier": "8.5.0",
108
108
  "find-file-up": "2.0.1",
109
109
  "grunt": "1.5.3",
@@ -119,7 +119,7 @@
119
119
  "supertest": "6.2.3",
120
120
  "ts-jest": "28.0.2",
121
121
  "ts-morph": "14.0.0",
122
- "ts-node": "10.7.0",
122
+ "ts-node": "10.8.0",
123
123
  "tsconfig-paths": "4.0.0",
124
124
  "typescript": "4.6.4"
125
125
  },
@@ -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 {
@@ -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);
@@ -38,7 +38,7 @@ export class RolesGuard extends AuthGuard('jwt') {
38
38
  }
39
39
 
40
40
  // Check user and user roles
41
- if (!user || !user.hasRole(roles)) {
41
+ if (!user?.hasRole?.(roles)) {
42
42
  // Get args
43
43
  const args: any = GqlExecutionContext.create(context).getArgs();
44
44
 
@@ -3,6 +3,7 @@ import * as bcrypt from 'bcrypt';
3
3
  import * as crypto from 'crypto';
4
4
  import { Document, Model } from 'mongoose';
5
5
  import { merge } from '../../common/helpers/config.helper';
6
+ import { assignPlain } from '../../common/helpers/input.helper';
6
7
  import { ServiceOptions } from '../../common/interfaces/service-options.interface';
7
8
  import { CrudService } from '../../common/services/crud.service';
8
9
  import { EmailService } from '../../common/services/email.service';
@@ -39,9 +40,12 @@ export abstract class CoreUserService<
39
40
  return this.process(
40
41
  async (data) => {
41
42
  // Create user with verification token
43
+ const currentUserId = serviceOptions?.currentUser?._id;
42
44
  const createdUser = new this.mainDbModel({
43
45
  ...data.input,
44
46
  verificationToken: crypto.randomBytes(32).toString('hex'),
47
+ createdBy: currentUserId,
48
+ updatedBy: currentUserId,
45
49
  });
46
50
 
47
51
  // Distinguish between different error messages when saving
@@ -105,21 +109,15 @@ export abstract class CoreUserService<
105
109
  }
106
110
  return this.process(
107
111
  async () => {
108
- // Update user
109
- await Object.assign(dbObject, {
110
- verified: true,
111
- verificationToken: null,
112
- }).save();
113
-
114
- // Return prepared user
115
- return dbObject;
112
+ // Update and return user
113
+ return await assignPlain(dbObject, { verified: true, verificationToken: null }).save();
116
114
  },
117
115
  { dbObject, serviceOptions }
118
116
  );
119
117
  }
120
118
 
121
119
  /**
122
- * Set newpassword for user with token
120
+ * Set new password for user with token
123
121
  */
124
122
  async resetPassword(token: string, newPassword: string, serviceOptions?: ServiceOptions): Promise<TUser> {
125
123
  // Get user
@@ -130,14 +128,11 @@ export abstract class CoreUserService<
130
128
 
131
129
  return this.process(
132
130
  async () => {
133
- // Update user
134
- await Object.assign(dbObject, {
131
+ // Update and return user
132
+ return await assignPlain(dbObject, {
135
133
  password: await bcrypt.hash(newPassword, 10),
136
134
  passwordResetToken: null,
137
135
  }).save();
138
-
139
- // Return user
140
- return dbObject;
141
136
  },
142
137
  { dbObject, serviceOptions }
143
138
  );
@@ -154,13 +149,9 @@ export abstract class CoreUserService<
154
149
  }
155
150
  return this.process(
156
151
  async () => {
157
- // Set reset token
158
- const resetToken = crypto.randomBytes(32).toString('hex');
159
- dbObject.passwordResetToken = resetToken;
160
- await dbObject.save();
161
-
162
- // Return user
163
- return dbObject;
152
+ // Set reset token and return
153
+ dbObject.passwordResetToken = crypto.randomBytes(32).toString('hex');
154
+ return await dbObject.save();
164
155
  },
165
156
  { dbObject, serviceOptions }
166
157
  );
@@ -182,7 +173,7 @@ export abstract class CoreUserService<
182
173
 
183
174
  // Update and return user
184
175
  return this.process(
185
- async (data) => {
176
+ async () => {
186
177
  return await this.mainDbModel.findByIdAndUpdate(userId, { roles }).exec();
187
178
  },
188
179
  { serviceOptions }
@@ -145,7 +145,7 @@ export class UserResolver {
145
145
  */
146
146
  @Subscription(() => User, {
147
147
  filter(this: UserResolver, payload, variables, context) {
148
- return context.user.hasRole(RoleEnum.ADMIN);
148
+ return context?.user?.hasRole?.(RoleEnum.ADMIN);
149
149
  },
150
150
  resolve: (user) => user,
151
151
  })
@@ -1,6 +1,5 @@
1
1
  import { INestApplication } from '@nestjs/common';
2
2
  import { createClient } from 'graphql-ws';
3
- // import { FastifyInstance } from 'fastify';
4
3
  import { jsonToGraphQLQuery } from 'json-to-graphql-query';
5
4
  import * as LightMyRequest from 'light-my-request';
6
5
  import * as supertest from 'supertest';
@@ -125,25 +124,21 @@ export class TestHelper {
125
124
 
126
125
  /**
127
126
  * GraphQL request
128
- * @param graphql
129
- * @param options
130
127
  */
131
128
  async graphQl(graphql: string | TestGraphQLConfig, options: TestGraphQLOptions = {}): Promise<any> {
132
129
  // Default options
133
- options = Object.assign(
134
- {
135
- convertEnums: true,
136
- countOfSubscriptionMessages: 1,
137
- token: null,
138
- statusCode: 200,
139
- log: false,
140
- logError: false,
141
- },
142
- options
143
- );
130
+ const config = {
131
+ convertEnums: true,
132
+ countOfSubscriptionMessages: 1,
133
+ token: null,
134
+ statusCode: 200,
135
+ log: false,
136
+ logError: false,
137
+ ...options,
138
+ };
144
139
 
145
140
  // Init vars
146
- const { token, statusCode, log, logError } = options;
141
+ const { token, statusCode, log, logError } = config;
147
142
 
148
143
  // Init
149
144
  let query = '';
@@ -188,13 +183,13 @@ export class TestHelper {
188
183
  }
189
184
 
190
185
  if ((graphql as TestGraphQLConfig).type === TestGraphQLType.SUBSCRIPTION) {
191
- return this.getSubscription(graphql as TestGraphQLConfig, query, options);
186
+ return this.getSubscription(graphql as TestGraphQLConfig, query, config);
192
187
  }
193
188
 
194
189
  // Convert uppercase strings in arguments of query to enums
195
- if (options.convertEnums) {
196
- if (Array.isArray(options.convertEnums)) {
197
- for (const key of Object.values(options.convertEnums)) {
190
+ if (config.convertEnums) {
191
+ if (Array.isArray(config.convertEnums)) {
192
+ for (const key of Object.values(config.convertEnums)) {
198
193
  const regExpStr = '(' + key + ': )\\"([_A-Z][_0-9A-Z]*)\\"';
199
194
  const regExp = new RegExp(regExpStr, 'g');
200
195
  query = query.replace(regExp, '$1$2');
@@ -242,28 +237,26 @@ export class TestHelper {
242
237
  */
243
238
  async rest(url: string, options: TestRestOptions = {}): Promise<any> {
244
239
  // Default options
245
- options = Object.assign(
246
- {
247
- token: null,
248
- statusCode: 200,
249
- log: false,
250
- logError: false,
251
- payload: null,
252
- method: 'GET',
253
- },
254
- options
255
- );
240
+ const config: TestRestOptions = {
241
+ token: null,
242
+ statusCode: 200,
243
+ log: false,
244
+ logError: false,
245
+ payload: null,
246
+ method: 'GET',
247
+ ...options,
248
+ };
256
249
 
257
250
  // Init vars
258
- const { token, statusCode, log, logError } = options;
251
+ const { token, statusCode, log, logError } = config;
259
252
 
260
253
  // Request configuration
261
254
  const requestConfig: LightMyRequest.InjectOptions = {
262
- method: options.method,
255
+ method: config.method,
263
256
  url,
264
257
  };
265
- if (options.payload) {
266
- requestConfig.payload = options.payload;
258
+ if (config.payload) {
259
+ requestConfig.payload = config.payload;
267
260
  }
268
261
 
269
262
  // Process response