@lenne.tech/nest-server 9.0.31 → 9.1.0

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": "9.0.31",
3
+ "version": "9.1.0",
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",
@@ -6,6 +6,7 @@ import { CoreModel } from '../models/core-model.model';
6
6
  import { FieldSelection } from '../types/field-selection.type';
7
7
  import { IdsType } from '../types/ids.type';
8
8
  import { StringOrObjectId } from '../types/string-or-object-id.type';
9
+ import { removePropertiesDeep } from './input.helper';
9
10
 
10
11
  // =====================================================================================================================
11
12
  // Export functions
@@ -350,7 +351,7 @@ export function getPopulatOptionsFromSelections(selectionNodes: readonly Selecti
350
351
  // Check for subfields
351
352
  if (node.selectionSet?.selections?.length) {
352
353
  for (const innerNode of node.selectionSet.selections as FieldNode[]) {
353
- // Subfiled is a primitive
354
+ // Subfield is a primitive
354
355
  if (!innerNode.selectionSet?.selections?.length) {
355
356
  option.select ? option.select.push(innerNode.name.value) : (option.select = [innerNode.name.value]);
356
357
  }
@@ -477,10 +478,14 @@ export async function popAndMap<T extends CoreModel>(
477
478
  queryOrDocument: Query<any, any> | Document | Document[],
478
479
  populate: FieldSelection,
479
480
  modelClass: new (...args: any[]) => T,
480
- mongooseModel?: Model<any>
481
+ mongooseModel?: Model<any>,
482
+ options?: {
483
+ ignoreSelections?: boolean;
484
+ }
481
485
  ): Promise<T | T[]> {
482
486
  let result;
483
487
  let populateOptions: PopulateOptions[] = [];
488
+ const ignoreSelections = options?.ignoreSelections;
484
489
  if (populate) {
485
490
  if (
486
491
  Array.isArray(populate) &&
@@ -511,13 +516,13 @@ export async function popAndMap<T extends CoreModel>(
511
516
  } else {
512
517
  // Process documents
513
518
  if (Array.isArray(queryOrDocument)) {
514
- await setPopulates(queryOrDocument, populateOptions, mongooseModel);
519
+ await setPopulates(queryOrDocument, populateOptions, mongooseModel, { ignoreSelections });
515
520
  result = queryOrDocument.map((item) => (modelClass as any).map(item));
516
521
  }
517
522
 
518
523
  // Process document
519
524
  else {
520
- await setPopulates(queryOrDocument, populateOptions, mongooseModel);
525
+ await setPopulates(queryOrDocument, populateOptions, mongooseModel, { ignoreSelections });
521
526
  result = (modelClass as any).map(queryOrDocument);
522
527
  }
523
528
  }
@@ -562,7 +567,8 @@ export function removeIds(source: any[], ids: StringOrObjectId | StringOrObjectI
562
567
  export async function setPopulates<T = Query<any, any> | Document>(
563
568
  queryOrDocument: T,
564
569
  populateOptions: string[] | PopulateOptions[] | (string | PopulateOptions)[],
565
- mongooseModel: Model<any>
570
+ mongooseModel: Model<any>,
571
+ options?: { ignoreSelections: boolean }
566
572
  ): Promise<T> {
567
573
  // Check parameters
568
574
  if (!populateOptions?.length || !queryOrDocument) {
@@ -580,6 +586,10 @@ export async function setPopulates<T = Query<any, any> | Document>(
580
586
  });
581
587
  }
582
588
 
589
+ if (options?.ignoreSelections) {
590
+ removePropertiesDeep(populateOptions, ['select']);
591
+ }
592
+
583
593
  // Query => Chaining
584
594
  if (queryOrDocument instanceof Query) {
585
595
  for (const options of populateOptions) {
@@ -718,3 +718,52 @@ export function prepareServiceOptionsForCreate(serviceOptions: any) {
718
718
  }
719
719
  return serviceOptions;
720
720
  }
721
+
722
+ /**
723
+ * Remove properties deep
724
+ */
725
+ export function removePropertiesDeep(
726
+ data: any,
727
+ properties: string[],
728
+ options?: {
729
+ processedObjects?: WeakMap<new () => any, boolean>;
730
+ }
731
+ ): any {
732
+ // Set options
733
+ const { processedObjects } = {
734
+ processedObjects: new WeakMap(),
735
+ ...options,
736
+ };
737
+
738
+ // Check for falsifiable values
739
+ if (!data) {
740
+ return data;
741
+ }
742
+
743
+ // Prevent circular processing
744
+ else if (typeof data === 'object') {
745
+ if (processedObjects.get(data)) {
746
+ return data;
747
+ }
748
+ processedObjects.set(data, true);
749
+ }
750
+
751
+ // Process array
752
+ if (Array.isArray(data)) {
753
+ return data.map((item) => removePropertiesDeep(item, properties, { processedObjects }));
754
+ }
755
+
756
+ // Process object
757
+ if (typeof data === 'object') {
758
+ for (const prop of properties) {
759
+ delete data[prop];
760
+ }
761
+ for (const [key, value] of Object.entries(data)) {
762
+ data[key] = removePropertiesDeep(value, properties, { processedObjects });
763
+ }
764
+ return data;
765
+ }
766
+
767
+ // Process others
768
+ return data;
769
+ }
@@ -111,6 +111,14 @@ export interface IServerOptions {
111
111
  options?: GqlModuleAsyncOptions;
112
112
  };
113
113
 
114
+ /**
115
+ * Ignore selections in fieldSelection
116
+ * [ConfigService must be integrated in ModuleService]
117
+ * If falsy (default): select and populate information in fieldSelection will be respected
118
+ * If truly: select fields will be ignored and only populate fields in fieldSelection will be respected
119
+ */
120
+ ignoreSelectionsForPopulate?: boolean;
121
+
114
122
  /**
115
123
  * Configuration of JavaScript Web Token (JWT) module
116
124
  */
@@ -53,6 +53,10 @@ export interface ServiceOptions {
53
53
  processFieldSelection?: {
54
54
  model?: new (...args: any[]) => any;
55
55
  dbModel?: Model<any>;
56
+ // Ignore selections in fieldSelection and populate
57
+ // If falsy (default, if not set in env.config): select and populate information in fieldSelection and populate will be respected
58
+ // If truly: select fields will be ignored and only populate fields in fieldSelection and populate will be respected
59
+ ignoreSelections?: boolean;
56
60
  };
57
61
 
58
62
  // Prepare input configuration:
@@ -7,11 +7,17 @@ import { prepareInput, prepareOutput } from '../helpers/service.helper';
7
7
  import { ServiceOptions } from '../interfaces/service-options.interface';
8
8
  import { CoreModel } from '../models/core-model.model';
9
9
  import { FieldSelection } from '../types/field-selection.type';
10
+ import { ConfigService } from './config.service';
10
11
 
11
12
  /**
12
13
  * Module service class to be extended by concrete module services
13
14
  */
14
15
  export abstract class ModuleService<T extends CoreModel = any> {
16
+ /**
17
+ * Config service, is used to determine certain behavior
18
+ */
19
+ protected configService: ConfigService;
20
+
15
21
  /**
16
22
  * Main model constructor of the service, will be used as default for populate and mapping
17
23
  */
@@ -26,9 +32,11 @@ export abstract class ModuleService<T extends CoreModel = any> {
26
32
  * Set main properties
27
33
  */
28
34
  protected constructor(options?: {
29
- mainDbModel: Model<T & Document>;
35
+ configService?: ConfigService;
36
+ mainDbModel?: Model<T & Document>;
30
37
  mainModelConstructor?: new (...args: any[]) => T;
31
38
  }) {
39
+ this.configService = options?.configService;
32
40
  this.mainDbModel = options?.mainDbModel;
33
41
  this.mainModelConstructor = options?.mainModelConstructor;
34
42
  }
@@ -90,6 +98,14 @@ export abstract class ModuleService<T extends CoreModel = any> {
90
98
  ...options?.serviceOptions,
91
99
  };
92
100
 
101
+ // Set default for ignoreSelections if not set
102
+ const ignoreSelections = this.configService?.getFastButReadOnly('ignoreSelectionsForPopulate', false);
103
+ if (ignoreSelections) {
104
+ if (config.processFieldSelection.ignoreSelections === undefined) {
105
+ config.processFieldSelection.ignoreSelections = ignoreSelections;
106
+ }
107
+ }
108
+
93
109
  // Note force configuration
94
110
  if (config.force) {
95
111
  config.checkRights = false;
@@ -104,6 +120,9 @@ export abstract class ModuleService<T extends CoreModel = any> {
104
120
  // Note populate
105
121
  if (config.populate) {
106
122
  config.fieldSelection = config.populate;
123
+ if (config.processFieldSelection?.ignoreSelections) {
124
+ config.processFieldSelection.ignoreSelections = false;
125
+ }
107
126
  }
108
127
 
109
128
  // Prepare input
@@ -220,6 +239,7 @@ export abstract class ModuleService<T extends CoreModel = any> {
220
239
  options: {
221
240
  model?: new (...args: any[]) => T;
222
241
  dbModel?: Model<T & Document>;
242
+ ignoreSelections?: boolean;
223
243
  } = {}
224
244
  ) {
225
245
  const config = {
@@ -227,6 +247,8 @@ export abstract class ModuleService<T extends CoreModel = any> {
227
247
  dbModel: this.mainDbModel,
228
248
  ...options,
229
249
  };
230
- return popAndMap(data, fieldsSelection, config.model, config.dbModel);
250
+ return popAndMap(data, fieldsSelection, config.model, config.dbModel, {
251
+ ignoreSelections: config.ignoreSelections,
252
+ });
231
253
  }
232
254
  }
@@ -22,10 +22,10 @@ export abstract class CoreUserService<
22
22
  TUserCreateInput extends CoreUserCreateInput
23
23
  > extends CrudService<TUser> {
24
24
  protected constructor(
25
+ protected override readonly configService: ConfigService,
25
26
  protected readonly emailService: EmailService,
26
27
  protected override readonly mainDbModel: Model<TUser & Document>,
27
- protected override readonly mainModelConstructor: CoreModelConstructor<TUser>,
28
- protected readonly configService?: ConfigService
28
+ protected override readonly mainModelConstructor: CoreModelConstructor<TUser>
29
29
  ) {
30
30
  super();
31
31
  }
@@ -31,7 +31,7 @@ export class UserService extends CoreUserService<User, UserInput, UserCreateInpu
31
31
  @InjectModel('User') protected override readonly mainDbModel: Model<UserDocument>,
32
32
  @Inject('PUB_SUB') protected readonly pubSub: PubSub
33
33
  ) {
34
- super(emailService, mainDbModel, mainModelConstructor);
34
+ super(configService, emailService, mainDbModel, mainModelConstructor);
35
35
  }
36
36
 
37
37
  // ===================================================================================================================