@lenne.tech/nest-server 9.0.20 → 9.0.22

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 (27) hide show
  1. package/dist/config.env.js +1 -1
  2. package/dist/core/common/decorators/restricted.decorator.js +3 -0
  3. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  4. package/dist/core/common/helpers/input.helper.d.ts +5 -1
  5. package/dist/core/common/helpers/input.helper.js +24 -4
  6. package/dist/core/common/helpers/input.helper.js.map +1 -1
  7. package/dist/core/common/helpers/model.helper.d.ts +10 -0
  8. package/dist/core/common/helpers/model.helper.js +21 -3
  9. package/dist/core/common/helpers/model.helper.js.map +1 -1
  10. package/dist/core/common/helpers/service.helper.d.ts +1 -0
  11. package/dist/core/common/helpers/service.helper.js +10 -0
  12. package/dist/core/common/helpers/service.helper.js.map +1 -1
  13. package/dist/core/common/interfaces/prepare-input-options.interface.d.ts +1 -0
  14. package/dist/core/common/services/crud.service.js +1 -1
  15. package/dist/core/common/services/crud.service.js.map +1 -1
  16. package/dist/core/common/services/module.service.js +2 -2
  17. package/dist/core/common/services/module.service.js.map +1 -1
  18. package/dist/tsconfig.build.tsbuildinfo +1 -1
  19. package/package.json +2 -2
  20. package/src/config.env.ts +1 -1
  21. package/src/core/common/decorators/restricted.decorator.ts +3 -0
  22. package/src/core/common/helpers/input.helper.ts +44 -6
  23. package/src/core/common/helpers/model.helper.ts +40 -10
  24. package/src/core/common/helpers/service.helper.ts +18 -1
  25. package/src/core/common/interfaces/prepare-input-options.interface.ts +1 -0
  26. package/src/core/common/services/crud.service.ts +2 -2
  27. package/src/core/common/services/module.service.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "9.0.20",
3
+ "version": "9.0.22",
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",
@@ -30,7 +30,7 @@
30
30
  "start": "npm run start:local",
31
31
  "stop": "./node_modules/.bin/pm2 delete nest",
32
32
  "start:pm2": "./node_modules/.bin/grunt",
33
- "start:prod": "./node_modules/.bin/grunt productive",
33
+ "start:prod": "./node_modules/.bin/grunt production",
34
34
  "start:nodemon": "ts-node -r tsconfig-paths/register src/main.ts",
35
35
  "start:debug": "nodemon --config nodemon-debug.json",
36
36
  "start:dev": "nodemon",
package/src/config.env.ts CHANGED
@@ -160,7 +160,7 @@ const config: { [env: string]: IServerOptions } = {
160
160
  verificationLink: 'http://localhost:4200/user/verification',
161
161
  passwordResetLink: 'http://localhost:4200/user/password-reset',
162
162
  },
163
- env: 'productive',
163
+ env: 'production',
164
164
  execAfterInit: 'npm run docs:bootstrap',
165
165
  filter: {
166
166
  maxLimit: null,
@@ -44,6 +44,9 @@ export const Restricted = (...rolesOrMember: RestrictedType): ClassDecorator & P
44
44
  * Get restricted data for (property of) object
45
45
  */
46
46
  export const getRestricted = (object: unknown, propertyKey?: string): RestrictedType => {
47
+ if (!object) {
48
+ return null;
49
+ }
47
50
  if (!propertyKey) {
48
51
  return Reflect.getMetadata(restrictedMetaKey, object);
49
52
  }
@@ -7,6 +7,7 @@ import * as rfdc from 'rfdc';
7
7
  import { checkRestricted } from '../decorators/restricted.decorator';
8
8
  import { ProcessType } from '../enums/process-type.enum';
9
9
  import { RoleEnum } from '../enums/role.enum';
10
+ import { merge } from './config.helper';
10
11
  import { equalIds } from './db.helper';
11
12
 
12
13
  /**
@@ -537,6 +538,24 @@ export function isString(parameter: string, falseFunction: (...params) => any =
537
538
  return typeof parameter === 'string' ? true : falseFunction(isString);
538
539
  }
539
540
 
541
+ /**
542
+ * Merge plain objects deep into target object and ignores undefined
543
+ */
544
+ export function mergePlain(target: Record<any, any>, ...args: Record<any, any>[]): any {
545
+ return merge(
546
+ target,
547
+ ...args.map(
548
+ // Prepare records
549
+ (item) =>
550
+ !item
551
+ ? // Return item if not an object
552
+ item
553
+ : // Return cloned record with undefined properties removed
554
+ filterProperties(clone(item, { circles: false }), (prop) => prop !== undefined)
555
+ )
556
+ );
557
+ }
558
+
540
559
  /**
541
560
  * Alternative for errorFunction
542
561
  */
@@ -629,11 +648,22 @@ export function instanceofArray(arr: any[], strict = false): string {
629
648
 
630
649
  /**
631
650
  * Process data via function deep
632
- * @param data
633
- * @param func
634
- * @param processedObjects
635
651
  */
636
- export function processDeep(data: any, func: (data: any) => any, processedObjects = new WeakMap()): any {
652
+ export function processDeep(
653
+ data: any,
654
+ func: (data: any) => any,
655
+ options?: {
656
+ processedObjects?: WeakMap<new () => any, boolean>;
657
+ specialClasses?: ((new (args: any[]) => any) | string)[];
658
+ }
659
+ ): any {
660
+ // Set options
661
+ const { processedObjects, specialClasses } = {
662
+ processedObjects: new WeakMap(),
663
+ specialClasses: [],
664
+ ...options,
665
+ };
666
+
637
667
  // Prevent circular processing
638
668
  if (typeof data === 'object') {
639
669
  if (processedObjects.get(data)) {
@@ -644,13 +674,21 @@ export function processDeep(data: any, func: (data: any) => any, processedObject
644
674
 
645
675
  // Process array
646
676
  if (Array.isArray(data)) {
647
- return data.map((item) => processDeep(item, func));
677
+ return data.map((item) => processDeep(item, func, { processedObjects, specialClasses }));
648
678
  }
649
679
 
650
680
  // Process object
651
681
  if (typeof data === 'object') {
682
+ for (const specialClass of specialClasses) {
683
+ if (
684
+ (typeof specialClass === 'string' && specialClass === data.constructor.name) ||
685
+ (typeof specialClass !== 'string' && data instanceof specialClass)
686
+ ) {
687
+ return func(data);
688
+ }
689
+ }
652
690
  for (const [key, value] of Object.entries(data)) {
653
- data[key] = processDeep(value, func, processedObjects);
691
+ data[key] = processDeep(value, func, { processedObjects, specialClasses });
654
692
  }
655
693
  return data;
656
694
  }
@@ -9,9 +9,6 @@ import { clone } from './input.helper';
9
9
  export class ModelHelper {
10
10
  /**
11
11
  * Remove all properties from source which are not in target
12
- * @param source
13
- * @param target
14
- * @param options
15
12
  */
16
13
  public static prepareMap<T = Record<string, any>>(
17
14
  source: Partial<T> | Record<string, any>,
@@ -54,9 +51,6 @@ export class ModelHelper {
54
51
 
55
52
  /**
56
53
  * Remove all properties from source which are not in target
57
- * @param source
58
- * @param target
59
- * @param options
60
54
  */
61
55
  export function prepareMap<T = Record<string, any>>(
62
56
  source: Partial<T> | Record<string, any>,
@@ -176,7 +170,7 @@ export function mapClasses<T = Record<string, any>>(
176
170
  input: Record<string, any>,
177
171
  mapping: Record<string, new (...args: any[]) => any>,
178
172
  target?: T,
179
- options?: { objectIdsToString?: boolean }
173
+ options?: { objectIdsToString?: boolean; removeUndefinedProperties?: boolean }
180
174
  ): T {
181
175
  // Check params
182
176
  if (!target) {
@@ -189,6 +183,7 @@ export function mapClasses<T = Record<string, any>>(
189
183
  // Get config
190
184
  const config = {
191
185
  objectIdsToString: true,
186
+ removeUndefinedProperties: false,
192
187
  ...options,
193
188
  };
194
189
 
@@ -238,7 +233,7 @@ export function mapClasses<T = Record<string, any>>(
238
233
  }
239
234
 
240
235
  // Others
241
- else {
236
+ else if (!config.removeUndefinedProperties || value !== undefined) {
242
237
  target[prop] = value;
243
238
  }
244
239
  }
@@ -259,7 +254,7 @@ export async function mapClassesAsync<T = Record<string, any>>(
259
254
  input: Record<string, any>,
260
255
  mapping: Record<string, new (...args: any[]) => any>,
261
256
  target?: T,
262
- options?: { objectIdsToString?: boolean }
257
+ options?: { objectIdsToString?: boolean; removeUndefinedProperties?: boolean }
263
258
  ): Promise<T> {
264
259
  // Check params
265
260
  if (!target) {
@@ -272,6 +267,7 @@ export async function mapClassesAsync<T = Record<string, any>>(
272
267
  // Get config
273
268
  const config = {
274
269
  objectIdsToString: true,
270
+ removeUndefinedProperties: false,
275
271
  ...options,
276
272
  };
277
273
 
@@ -321,7 +317,7 @@ export async function mapClassesAsync<T = Record<string, any>>(
321
317
  }
322
318
 
323
319
  // Others
324
- else {
320
+ else if (!config.removeUndefinedProperties || value !== undefined) {
325
321
  target[prop] = value;
326
322
  }
327
323
  }
@@ -329,3 +325,37 @@ export async function mapClassesAsync<T = Record<string, any>>(
329
325
 
330
326
  return target;
331
327
  }
328
+
329
+ /**
330
+ * Same as mapClasses but with option removeUndefinedProperties = true as default
331
+ */
332
+ export function mapInputClasses<T = Record<string, any>>(
333
+ input: Record<string, any>,
334
+ mapping: Record<string, new (...args: any[]) => any>,
335
+ target?: T,
336
+ options?: { objectIdsToString?: boolean; removeUndefinedProperties?: boolean }
337
+ ) {
338
+ // Get config
339
+ const config = {
340
+ removeUndefinedProperties: true,
341
+ ...options,
342
+ };
343
+ return mapClasses(input, mapping, target, options);
344
+ }
345
+
346
+ /**
347
+ * Same as mapClassesAsync but with option removeUndefinedProperties = true as default
348
+ */
349
+ export function mapInputClassesAsync<T = Record<string, any>>(
350
+ input: Record<string, any>,
351
+ mapping: Record<string, new (...args: any[]) => any>,
352
+ target?: T,
353
+ options?: { objectIdsToString?: boolean; removeUndefinedProperties?: boolean }
354
+ ) {
355
+ // Get config
356
+ const config = {
357
+ removeUndefinedProperties: true,
358
+ ...options,
359
+ };
360
+ return mapClassesAsync(input, mapping, target, options);
361
+ }
@@ -10,7 +10,8 @@ import { PrepareOutputOptions } from '../interfaces/prepare-output-options.inter
10
10
  import { ResolveSelector } from '../interfaces/resolve-selector.interface';
11
11
  import { ServiceOptions } from '../interfaces/service-options.interface';
12
12
  import { ConfigService } from '../services/config.service';
13
- import { clone } from './input.helper';
13
+ import { getStringIds } from './db.helper';
14
+ import { clone, processDeep } from './input.helper';
14
15
 
15
16
  /**
16
17
  * Helper class for services
@@ -66,6 +67,7 @@ export async function prepareInput<T = any>(
66
67
  [key: string]: any;
67
68
  checkRoles?: boolean;
68
69
  circles?: boolean;
70
+ convertObjectIdsToString?: boolean;
69
71
  create?: boolean;
70
72
  clone?: boolean;
71
73
  getNewArray?: boolean;
@@ -80,6 +82,7 @@ export async function prepareInput<T = any>(
80
82
  checkRoles: false,
81
83
  clone: false,
82
84
  circles: false,
85
+ convertObjectIdsToString: true,
83
86
  create: false,
84
87
  getNewArray: false,
85
88
  proto: false,
@@ -114,6 +117,20 @@ export async function prepareInput<T = any>(
114
117
  }
115
118
  }
116
119
 
120
+ // Convert ObjectIds to string
121
+ if (config.convertObjectIdsToString) {
122
+ input = processDeep(
123
+ input,
124
+ (property) => {
125
+ if (property instanceof Types.ObjectId) {
126
+ property = getStringIds(property);
127
+ }
128
+ return property;
129
+ },
130
+ { specialClasses: ['ObjectId'] }
131
+ );
132
+ }
133
+
117
134
  // Map input if target model exist
118
135
  if (config.targetModel && !(input instanceof config.targetModel)) {
119
136
  if ((config.targetModel as any)?.map) {
@@ -4,6 +4,7 @@
4
4
  export interface PrepareInputOptions {
5
5
  [key: string]: any;
6
6
  checkRoles?: boolean;
7
+ convertObjectIdsToString?: boolean;
7
8
  create?: boolean;
8
9
  clone?: boolean;
9
10
  getNewArray?: boolean;
@@ -4,7 +4,7 @@ import { FilterArgs } from '../args/filter.args';
4
4
  import { merge } from '../helpers/config.helper';
5
5
  import { getStringIds } from '../helpers/db.helper';
6
6
  import { convertFilterArgsToQuery } from '../helpers/filter.helper';
7
- import { assignPlain } from '../helpers/input.helper';
7
+ import { mergePlain } from '../helpers/input.helper';
8
8
  import { ServiceOptions } from '../interfaces/service-options.interface';
9
9
  import { CoreModel } from '../models/core-model.model';
10
10
  import { ConfigService } from './config.service';
@@ -388,7 +388,7 @@ export abstract class CrudService<T extends CoreModel = any> extends ModuleServi
388
388
  return this.process(
389
389
  async (data) => {
390
390
  const currentUserId = serviceOptions?.currentUser?.id;
391
- return await assignPlain(dbObject, data.input, { updatedBy: currentUserId }).save();
391
+ return await mergePlain(dbObject, data.input, { updatedBy: currentUserId }).save();
392
392
  },
393
393
  { dbObject, input, serviceOptions }
394
394
  );
@@ -111,7 +111,7 @@ export abstract class ModuleService<T extends CoreModel = any> {
111
111
  if (!opts.targetModel && config.inputType) {
112
112
  opts.targetModel = config.inputType;
113
113
  }
114
- config.input = await this.prepareInput(config.input, opts);
114
+ config.input = await this.prepareInput(config.input, config);
115
115
  }
116
116
 
117
117
  // Get DB object
@@ -156,7 +156,7 @@ export abstract class ModuleService<T extends CoreModel = any> {
156
156
  if (config.outputPath) {
157
157
  _.set(result, config.outputPath, await this.prepareOutput(_.get(result, config.outputPath), opts));
158
158
  } else {
159
- result = await this.prepareOutput(result, opts);
159
+ result = await this.prepareOutput(result, config);
160
160
  }
161
161
  }
162
162