@lenne.tech/nest-server 8.0.1 → 8.2.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.
Files changed (164) hide show
  1. package/dist/config.env.js +3 -2
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/args/pagination.args.js +1 -1
  4. package/dist/core/common/args/pagination.args.js.map +1 -1
  5. package/dist/core/common/decorators/restricted.decorator.d.ts +2 -0
  6. package/dist/core/common/decorators/restricted.decorator.js +10 -7
  7. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  8. package/dist/core/common/helpers/config.helper.d.ts +2 -1
  9. package/dist/core/common/helpers/config.helper.js +11 -7
  10. package/dist/core/common/helpers/config.helper.js.map +1 -1
  11. package/dist/core/common/helpers/context.helper.d.ts +7 -1
  12. package/dist/core/common/helpers/context.helper.js +33 -29
  13. package/dist/core/common/helpers/context.helper.js.map +1 -1
  14. package/dist/core/common/helpers/db.helper.d.ts +35 -0
  15. package/dist/core/common/helpers/db.helper.js +345 -0
  16. package/dist/core/common/helpers/db.helper.js.map +1 -0
  17. package/dist/core/common/helpers/file.helper.d.ts +8 -1
  18. package/dist/core/common/helpers/file.helper.js +43 -31
  19. package/dist/core/common/helpers/file.helper.js.map +1 -1
  20. package/dist/core/common/helpers/filter.helper.d.ts +3 -0
  21. package/dist/core/common/helpers/filter.helper.js +93 -81
  22. package/dist/core/common/helpers/filter.helper.js.map +1 -1
  23. package/dist/core/common/helpers/graphql.helper.d.ts +24 -1
  24. package/dist/core/common/helpers/graphql.helper.js +144 -96
  25. package/dist/core/common/helpers/graphql.helper.js.map +1 -1
  26. package/dist/core/common/helpers/input.helper.d.ts +40 -4
  27. package/dist/core/common/helpers/input.helper.js +253 -97
  28. package/dist/core/common/helpers/input.helper.js.map +1 -1
  29. package/dist/core/common/helpers/model.helper.d.ts +11 -0
  30. package/dist/core/common/helpers/model.helper.js +41 -29
  31. package/dist/core/common/helpers/model.helper.js.map +1 -1
  32. package/dist/core/common/helpers/service.helper.d.ts +21 -1
  33. package/dist/core/common/helpers/service.helper.js +80 -72
  34. package/dist/core/common/helpers/service.helper.js.map +1 -1
  35. package/dist/core/common/inputs/combined-filter.input.js +1 -1
  36. package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
  37. package/dist/core/common/inputs/core-input.input.js +1 -1
  38. package/dist/core/common/inputs/core-input.input.js.map +1 -1
  39. package/dist/core/common/interceptors/check-response.interceptor.js +1 -1
  40. package/dist/core/common/interceptors/check-response.interceptor.js.map +1 -1
  41. package/dist/core/common/interfaces/resolve-selector.interface.d.ts +5 -0
  42. package/dist/core/common/interfaces/resolve-selector.interface.js +3 -0
  43. package/dist/core/common/interfaces/resolve-selector.interface.js.map +1 -0
  44. package/dist/core/common/interfaces/service-options.interface.d.ts +36 -0
  45. package/dist/core/common/interfaces/service-options.interface.js +3 -0
  46. package/dist/core/common/interfaces/service-options.interface.js.map +1 -0
  47. package/dist/core/common/models/core-model.model.d.ts +5 -1
  48. package/dist/core/common/models/core-model.model.js +1 -1
  49. package/dist/core/common/models/core-model.model.js.map +1 -1
  50. package/dist/core/common/pipes/check-input.pipe.js +2 -2
  51. package/dist/core/common/pipes/check-input.pipe.js.map +1 -1
  52. package/dist/core/common/pipes/map-and-validate.pipe.js +1 -1
  53. package/dist/core/common/pipes/map-and-validate.pipe.js.map +1 -1
  54. package/dist/core/common/plugins/mongoose-id.plugin.js +1 -1
  55. package/dist/core/common/plugins/mongoose-id.plugin.js.map +1 -1
  56. package/dist/core/common/services/crud.service.d.ts +13 -0
  57. package/dist/core/common/services/crud.service.js +57 -0
  58. package/dist/core/common/services/crud.service.js.map +1 -0
  59. package/dist/core/common/services/email.service.js +8 -8
  60. package/dist/core/common/services/email.service.js.map +1 -1
  61. package/dist/core/common/services/module.service.d.ts +39 -0
  62. package/dist/core/common/services/module.service.js +76 -0
  63. package/dist/core/common/services/module.service.js.map +1 -0
  64. package/dist/core/common/types/core-model-constructor.type.d.ts +21 -0
  65. package/dist/core/common/types/core-model-constructor.type.js +3 -0
  66. package/dist/core/common/types/core-model-constructor.type.js.map +1 -0
  67. package/dist/core/common/types/field-selection.type.d.ts +4 -0
  68. package/dist/core/common/types/field-selection.type.js +3 -0
  69. package/dist/core/common/types/field-selection.type.js.map +1 -0
  70. package/dist/core/common/types/ids.type.d.ts +8 -0
  71. package/dist/core/common/types/ids.type.js +3 -0
  72. package/dist/core/common/types/ids.type.js.map +1 -0
  73. package/dist/core/common/types/string-or-object-id.type.d.ts +2 -0
  74. package/dist/core/common/types/string-or-object-id.type.js +3 -0
  75. package/dist/core/common/types/string-or-object-id.type.js.map +1 -0
  76. package/dist/core/modules/auth/core-auth.resolver.d.ts +2 -1
  77. package/dist/core/modules/auth/core-auth.resolver.js +4 -3
  78. package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
  79. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  80. package/dist/core/modules/auth/services/core-auth-user.service.d.ts +3 -1
  81. package/dist/core/modules/auth/services/core-auth-user.service.js.map +1 -1
  82. package/dist/core/modules/auth/services/core-auth.service.d.ts +2 -1
  83. package/dist/core/modules/auth/services/core-auth.service.js +6 -4
  84. package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
  85. package/dist/core/modules/user/core-user.model.js +1 -1
  86. package/dist/core/modules/user/core-user.model.js.map +1 -1
  87. package/dist/core/modules/user/core-user.service.d.ts +16 -25
  88. package/dist/core/modules/user/core-user.service.js +74 -90
  89. package/dist/core/modules/user/core-user.service.js.map +1 -1
  90. package/dist/core.module.js +1 -1
  91. package/dist/core.module.js.map +1 -1
  92. package/dist/index.d.ts +9 -1
  93. package/dist/index.js +9 -1
  94. package/dist/index.js.map +1 -1
  95. package/dist/server/modules/auth/auth.resolver.d.ts +2 -1
  96. package/dist/server/modules/auth/auth.resolver.js +4 -3
  97. package/dist/server/modules/auth/auth.resolver.js.map +1 -1
  98. package/dist/server/modules/file/file.controller.js +1 -1
  99. package/dist/server/modules/file/file.controller.js.map +1 -1
  100. package/dist/server/modules/user/avatar.controller.js +1 -1
  101. package/dist/server/modules/user/avatar.controller.js.map +1 -1
  102. package/dist/server/modules/user/user.model.d.ts +4 -2
  103. package/dist/server/modules/user/user.module.js +7 -3
  104. package/dist/server/modules/user/user.module.js.map +1 -1
  105. package/dist/server/modules/user/user.resolver.d.ts +8 -7
  106. package/dist/server/modules/user/user.resolver.js +85 -49
  107. package/dist/server/modules/user/user.resolver.js.map +1 -1
  108. package/dist/server/modules/user/user.service.d.ts +9 -18
  109. package/dist/server/modules/user/user.service.js +19 -30
  110. package/dist/server/modules/user/user.service.js.map +1 -1
  111. package/dist/test/test.helper.d.ts +1 -2
  112. package/dist/test/test.helper.js +1 -16
  113. package/dist/test/test.helper.js.map +1 -1
  114. package/dist/tsconfig.build.tsbuildinfo +1 -1
  115. package/package.json +58 -60
  116. package/src/config.env.ts +3 -2
  117. package/src/core/common/args/pagination.args.ts +2 -2
  118. package/src/core/common/decorators/restricted.decorator.ts +16 -10
  119. package/src/core/common/helpers/config.helper.ts +26 -6
  120. package/src/core/common/helpers/context.helper.ts +42 -33
  121. package/src/core/common/helpers/db.helper.ts +580 -0
  122. package/src/core/common/helpers/file.helper.ts +76 -49
  123. package/src/core/common/helpers/filter.helper.ts +119 -96
  124. package/src/core/common/helpers/graphql.helper.ts +219 -117
  125. package/src/core/common/helpers/input.helper.ts +347 -108
  126. package/src/core/common/helpers/model.helper.ts +102 -57
  127. package/src/core/common/helpers/service.helper.ts +149 -117
  128. package/src/core/common/inputs/combined-filter.input.ts +2 -2
  129. package/src/core/common/inputs/core-input.input.ts +2 -2
  130. package/src/core/common/interceptors/check-response.interceptor.ts +2 -2
  131. package/src/core/common/interfaces/resolve-selector.interface.ts +9 -0
  132. package/src/core/common/interfaces/service-options.interface.ts +71 -0
  133. package/src/core/common/models/core-model.model.ts +7 -3
  134. package/src/core/common/pipes/check-input.pipe.ts +4 -4
  135. package/src/core/common/pipes/map-and-validate.pipe.ts +2 -2
  136. package/src/core/common/plugins/mongoose-id.plugin.js +1 -1
  137. package/src/core/common/services/crud.service.ts +100 -0
  138. package/src/core/common/services/email.service.ts +9 -9
  139. package/src/core/common/services/module.service.ts +180 -0
  140. package/src/core/common/types/core-model-constructor.type.ts +30 -0
  141. package/src/core/common/types/field-selection.type.ts +8 -0
  142. package/src/core/common/types/ids.type.ts +7 -0
  143. package/src/core/common/types/string-or-object-id.type.ts +3 -0
  144. package/src/core/modules/auth/core-auth.module.ts +1 -1
  145. package/src/core/modules/auth/core-auth.resolver.ts +8 -3
  146. package/src/core/modules/auth/guards/roles.guard.ts +1 -1
  147. package/src/core/modules/auth/services/core-auth-user.service.ts +7 -1
  148. package/src/core/modules/auth/services/core-auth.service.ts +14 -4
  149. package/src/core/modules/user/core-user.model.ts +2 -1
  150. package/src/core/modules/user/core-user.service.ts +120 -185
  151. package/src/core.module.ts +2 -2
  152. package/src/index.ts +9 -1
  153. package/src/main.ts +1 -1
  154. package/src/server/modules/auth/auth.resolver.ts +8 -3
  155. package/src/server/modules/file/file.controller.ts +2 -2
  156. package/src/server/modules/user/avatar.controller.ts +2 -2
  157. package/src/server/modules/user/user.module.ts +7 -3
  158. package/src/server/modules/user/user.resolver.ts +74 -43
  159. package/src/server/modules/user/user.service.ts +23 -53
  160. package/src/test/test.helper.ts +31 -30
  161. package/dist/core/modules/user/core-basic-user.service.d.ts +0 -17
  162. package/dist/core/modules/user/core-basic-user.service.js +0 -73
  163. package/dist/core/modules/user/core-basic-user.service.js.map +0 -1
  164. package/src/core/modules/user/core-basic-user.service.ts +0 -138
@@ -0,0 +1,580 @@
1
+ import { FieldNode, GraphQLResolveInfo, SelectionNode } from 'graphql';
2
+ import * as _ from 'lodash';
3
+ import { Document, Model, PopulateOptions, Query, SchemaType, Types } from 'mongoose';
4
+ import { ResolveSelector } from '../interfaces/resolve-selector.interface';
5
+ import { CoreModel } from '../models/core-model.model';
6
+ import { FieldSelection } from '../types/field-selection.type';
7
+ import { StringOrObjectId } from '../types/string-or-object-id.type';
8
+
9
+ // =====================================================================================================================
10
+ // Export functions
11
+ // =====================================================================================================================
12
+
13
+ /**
14
+ * Add IDs to array
15
+ * @param target Array will be changed
16
+ * @param ids ID or IDs to add
17
+ * @param convert Convert ID and / or array values
18
+ * 'auto': ID will be converted to type of first element in array
19
+ * 'string': ID and other array values will be converted to string IDs
20
+ * 'object': ID and other array values will be converted to ObjectIds
21
+ * false: no conversion
22
+ * @param options
23
+ * uniqui: Add only non-existing IDs, if true
24
+ */
25
+ export function addIds(
26
+ target: any,
27
+ ids: StringOrObjectId | StringOrObjectId[],
28
+ convert: 'string' | 'object' | 'auto' | false = 'auto',
29
+ options?: { unique?: boolean }
30
+ ): any[] {
31
+ // Set config
32
+ const config = {
33
+ unique: true,
34
+ ...options,
35
+ };
36
+
37
+ // Check and convert parameters
38
+ let result = target as any;
39
+ if (!Array.isArray(result)) {
40
+ result = [];
41
+ }
42
+ if (!Array.isArray(ids)) {
43
+ ids = [ids];
44
+ }
45
+
46
+ // Unique
47
+ if (config.unique) {
48
+ removeIds(ids, target as any);
49
+ }
50
+
51
+ // Process ids
52
+ if (ids.length) {
53
+ // Add autoconverted ID
54
+ if (
55
+ result.length &&
56
+ convert === 'auto' &&
57
+ ((result[0] instanceof Types.ObjectId && !(ids instanceof Types.ObjectId)) ||
58
+ (typeof result[0] === 'string' && typeof ids !== 'string'))
59
+ ) {
60
+ const converted = result[0] instanceof Types.ObjectId ? getObjectIds(ids) : getStringIds(ids);
61
+ result.push(...(converted as any));
62
+ }
63
+
64
+ // Add ID
65
+ else {
66
+ result.push(...ids);
67
+ }
68
+ }
69
+
70
+ // Convert array
71
+ if (['string', 'object'].includes(convert as string)) {
72
+ for (let i = 0; i < result.length; i++) {
73
+ result[i] = convert === 'string' ? getStringId(result[i]) : getObjectIds(result[i]);
74
+ }
75
+ }
76
+
77
+ // Return result
78
+ return result;
79
+ }
80
+
81
+ /**
82
+ * Get indexes of IDs in an array
83
+ */
84
+ export function getIndexesViaIds(ids: any | any[], array: any[]): number[] {
85
+ // Check and prepare parameters
86
+ if (!ids) {
87
+ return [];
88
+ }
89
+ if (!Array.isArray(ids)) {
90
+ ids = [ids];
91
+ }
92
+ if (!Array.isArray(array)) {
93
+ return [];
94
+ }
95
+
96
+ // Get indexes
97
+ const indexes: number[] = [];
98
+ ids.forEach((id) => {
99
+ array.forEach((element, index) => {
100
+ if (getStringIds(id) === getStringIds(element)) {
101
+ indexes.push(index);
102
+ }
103
+ });
104
+ });
105
+
106
+ // Return indexes
107
+ return indexes;
108
+ }
109
+
110
+ /**
111
+ * Get IDs from string of ObjectId array in a flat string array
112
+ */
113
+ export function getStringIds(elements: any[], options?: { deep?: boolean; unique?: boolean }): string[];
114
+ export function getStringIds(elements: any, options?: { deep?: boolean; unique?: boolean }): string;
115
+ export function getStringIds<T extends any | any[]>(
116
+ elements: T,
117
+ options?: { deep?: boolean; unique?: boolean }
118
+ ): string | string[] {
119
+ // Process options
120
+ const { deep, unique } = {
121
+ deep: false,
122
+ unique: false,
123
+ ...options,
124
+ };
125
+
126
+ // Check elements
127
+ if (!elements) {
128
+ return elements as any;
129
+ }
130
+
131
+ // Init ids
132
+ let ids = [];
133
+
134
+ // Process non array
135
+ if (!Array.isArray(elements)) {
136
+ return getStringId(elements);
137
+ }
138
+
139
+ // Process array
140
+ for (const element of elements) {
141
+ if (Array.isArray(element)) {
142
+ if (deep) {
143
+ ids = ids.concat(getStringIds(element, { deep }));
144
+ }
145
+ } else {
146
+ const id = getStringId(element);
147
+ if (id) {
148
+ ids.push(id);
149
+ }
150
+ }
151
+ }
152
+
153
+ // Return (unique) ID array
154
+ return unique ? _.uniq(ids) : ids;
155
+ }
156
+
157
+ /**
158
+ * Convert string(s) to ObjectId(s)
159
+ */
160
+ export function getObjectIds(ids: any[]): Types.ObjectId[];
161
+ export function getObjectIds(ids: any): Types.ObjectId;
162
+ export function getObjectIds<T extends any | any[]>(ids: T): Types.ObjectId | Types.ObjectId[] {
163
+ if (Array.isArray(ids)) {
164
+ return ids.map((id) => new Types.ObjectId(getStringId(id)));
165
+ }
166
+ return new Types.ObjectId(getStringId(ids));
167
+ }
168
+
169
+ /**
170
+ * Get (and remove) elements with specific IDs from array
171
+ */
172
+ export function getElementsViaIds<T = any>(
173
+ ids: any | any[],
174
+ array: T[],
175
+ options: {
176
+ splice?: boolean;
177
+ unique?: boolean;
178
+ } = {}
179
+ ): T[] {
180
+ // Config
181
+ const config = {
182
+ // Remove found elements from array
183
+ splice: false,
184
+
185
+ // Return unique elements
186
+ unique: false,
187
+
188
+ // Overwrite with options from parameters
189
+ ...options,
190
+ };
191
+
192
+ // Get and check indexes
193
+ const indexes = getIndexesViaIds(ids, array);
194
+ if (!indexes?.length) {
195
+ return [];
196
+ }
197
+
198
+ // Get elements
199
+ const elements = [];
200
+ indexes.forEach((index) => {
201
+ if (config.splice) {
202
+ elements.push(array.splice(index, 1)[0]);
203
+ } else {
204
+ elements.push(array[index]);
205
+ }
206
+ });
207
+
208
+ // Unique elements
209
+ if (config.unique) {
210
+ return elements.filter((value, index, self) => {
211
+ return self.findIndex((e) => getStringIds(e)) === index;
212
+ });
213
+ }
214
+
215
+ // Return elements
216
+ return elements;
217
+ }
218
+
219
+ /**
220
+ * Get populate options from GraphQL resolve info
221
+ */
222
+ export function getPopulateOptions(info: GraphQLResolveInfo, select?: string): PopulateOptions[] {
223
+ const result = [];
224
+
225
+ if (!info?.fieldNodes?.length) {
226
+ return result;
227
+ }
228
+
229
+ for (const fieldNode of info.fieldNodes) {
230
+ if ((select || fieldNode?.name?.value === select) && fieldNode?.selectionSet?.selections?.length) {
231
+ return getPopulatOptionsFromSelections(fieldNode.selectionSet.selections);
232
+ }
233
+ }
234
+
235
+ return result;
236
+ }
237
+
238
+ /**
239
+ * Get populate options from selections
240
+ */
241
+ export function getPopulatOptionsFromSelections(selectionNodes: readonly SelectionNode[]): PopulateOptions[] {
242
+ const populateOptions = [];
243
+
244
+ if (!selectionNodes || !selectionNodes.length) {
245
+ return populateOptions;
246
+ }
247
+
248
+ for (const node of selectionNodes as FieldNode[]) {
249
+ // Set main path
250
+ const option: PopulateOptions = {
251
+ path: node.name.value,
252
+ };
253
+
254
+ // Check for subfields
255
+ if (node.selectionSet?.selections?.length) {
256
+ for (const innerNode of node.selectionSet.selections as FieldNode[]) {
257
+ // Subfiled is a primitive
258
+ if (!innerNode.selectionSet?.selections?.length) {
259
+ option.select ? option.select.push(innerNode.name.value) : (option.select = [innerNode.name.value]);
260
+ }
261
+
262
+ // Subfield ist an object
263
+ else {
264
+ const innerPopulate = getPopulatOptionsFromSelections([innerNode]);
265
+ option.populate = option.populate
266
+ ? (option.populate as PopulateOptions[]).concat(innerPopulate)
267
+ : innerPopulate;
268
+ }
269
+ }
270
+ }
271
+
272
+ // Add option to populate options
273
+ if (option.select || option.populate) {
274
+ populateOptions.push(option);
275
+ }
276
+ }
277
+
278
+ return populateOptions;
279
+ }
280
+
281
+ /**
282
+ * Get simple clone of object via JSON.stringify and JSON.parse
283
+ * @param obj
284
+ */
285
+ export function getJSONClone<T = any>(obj: T): Partial<T> {
286
+ return JSON.parse(JSON.stringify(obj));
287
+ }
288
+
289
+ /**
290
+ * Check if ID is included
291
+ * @param includes String, ObjectId or array with both, which should be checked if it contains the ID
292
+ * @param ids String, ObjectId or array with both, which should be included
293
+ * @param convert If set the result array will be converted to pure type String array or ObjectId array
294
+ * @return StringOrObjectId[] Array with IDs which are included or null if none is included
295
+ */
296
+ export function includesIds(
297
+ includes: StringOrObjectId | StringOrObjectId[],
298
+ ids: StringOrObjectId | StringOrObjectId[],
299
+ convert?: 'string'
300
+ ): string[];
301
+ export function includesIds(
302
+ includes: StringOrObjectId | StringOrObjectId[],
303
+ ids: StringOrObjectId | StringOrObjectId[],
304
+ convert?: 'object'
305
+ ): Types.ObjectId[];
306
+ export function includesIds<T = StringOrObjectId>(
307
+ includes: StringOrObjectId | StringOrObjectId[] | any | any[],
308
+ ids: T | StringOrObjectId[],
309
+ convert?: 'string' | 'object'
310
+ ): T[] | null {
311
+ if (!includes || !ids) {
312
+ return null;
313
+ }
314
+
315
+ if (!Array.isArray(includes)) {
316
+ includes = [includes];
317
+ }
318
+
319
+ if (!Array.isArray(ids)) {
320
+ ids = [ids] as any;
321
+ }
322
+
323
+ let result = [];
324
+ const includesStrings = getStringIds(includes);
325
+ for (const id of ids as StringOrObjectId[]) {
326
+ if (includesStrings.includes(getStringIds(id))) {
327
+ result.push(id);
328
+ }
329
+ }
330
+
331
+ if (convert) {
332
+ result = convert === 'string' ? getStringIds(result) : getObjectIds(result);
333
+ }
334
+
335
+ return result.length ? result : null;
336
+ }
337
+
338
+ /**
339
+ * Convert all ObjectIds to strings
340
+ */
341
+ export function objectIdsToStrings(element: any, prepared: WeakMap<any, any> = new WeakMap()) {
342
+ // Check element
343
+ if (!element) {
344
+ return element;
345
+ }
346
+
347
+ // ObjectId to string
348
+ if (element instanceof Types.ObjectId) {
349
+ return element.toHexString();
350
+ }
351
+
352
+ // Process array
353
+ if (Array.isArray(element)) {
354
+ return element.map((e) => objectIdsToStrings(e, prepared));
355
+ }
356
+
357
+ // Process object
358
+ if (typeof element === 'object') {
359
+ // Avoid infinite regress
360
+ if (prepared.has(element)) {
361
+ return prepared.get(element);
362
+ }
363
+ const preparedObject = element;
364
+ prepared.set(element, preparedObject);
365
+ for (const [key, val] of Object.entries(element)) {
366
+ preparedObject[key] = objectIdsToStrings(val, prepared);
367
+ }
368
+ }
369
+
370
+ // Process others
371
+ return element;
372
+ }
373
+
374
+ /**
375
+ * Remove unresolved references: ObjectId => null
376
+ */
377
+ export function removeUnresolvedReferences<T = any>(
378
+ populated: T,
379
+ populatedOptions: string | PopulateOptions | PopulateOptions[] | (string | PopulateOptions)[],
380
+ ignoreFirst = true
381
+ ): T {
382
+ // Check parameter
383
+ if (!populated || !populatedOptions) {
384
+ return populated;
385
+ }
386
+
387
+ // Process array
388
+ if (Array.isArray(populated)) {
389
+ populated.forEach((p) => removeUnresolvedReferences(p, populatedOptions, false));
390
+ return populated;
391
+ }
392
+
393
+ // Process object
394
+ if (typeof populated === 'object') {
395
+ // populatedOptions is an array
396
+ if (Array.isArray(populatedOptions)) {
397
+ populatedOptions.forEach((po) => removeUnresolvedReferences(populated, ignoreFirst ? po.populate : po, false));
398
+ return populated;
399
+ }
400
+
401
+ // populatedOptions is a string
402
+ if (typeof populatedOptions === 'string') {
403
+ if (!['id', '_id'].includes(populatedOptions) && populated[populatedOptions] instanceof Types.ObjectId) {
404
+ populated[populatedOptions] = null;
405
+ }
406
+ return populated;
407
+ }
408
+
409
+ // populatedOptions is an PopulateOptions object
410
+ if (populatedOptions.path) {
411
+ const key = populatedOptions.path;
412
+ if (!['id', '_id'].includes(key) && populated[key] instanceof Types.ObjectId) {
413
+ populated[key] = null;
414
+ } else if (populatedOptions.populate) {
415
+ removeUnresolvedReferences(populated[key], populatedOptions.populate, false);
416
+ }
417
+ }
418
+ }
419
+
420
+ // Process others
421
+ return populated;
422
+ }
423
+
424
+ /**
425
+ * Set populates, execute and map result
426
+ */
427
+ export async function popAndMap<T extends CoreModel>(
428
+ queryOrDocument: Query<any, any> | Document | Document[],
429
+ populate: FieldSelection,
430
+ modelClass: new (...args: any[]) => T,
431
+ mongooseModel?: Model<any>
432
+ ): Promise<T | T[]> {
433
+ let result;
434
+ let populateOptions: PopulateOptions[] = [];
435
+ if (populate) {
436
+ if (Array.isArray(populate) && typeof (populate as PopulateOptions[])[0]?.path === 'string') {
437
+ populateOptions = populate as PopulateOptions[];
438
+ } else if (Array.isArray(populate) && typeof (populate as SelectionNode[])[0]?.kind === 'string') {
439
+ populateOptions = getPopulatOptionsFromSelections(populate as SelectionNode[]);
440
+ } else if ((populate as ResolveSelector).info) {
441
+ populateOptions = getPopulateOptions((populate as ResolveSelector).info, (populate as ResolveSelector).select);
442
+ }
443
+ }
444
+ if (queryOrDocument instanceof Query) {
445
+ // Get result
446
+ result = await setPopulates(queryOrDocument, populateOptions, mongooseModel?.schema?.paths);
447
+ if (result instanceof Query) {
448
+ result = await result.exec();
449
+ }
450
+
451
+ // Map result
452
+ if (Array.isArray(result)) {
453
+ result = result.map((item) => (modelClass as any).map(item));
454
+ } else {
455
+ result = (modelClass as any).map(result);
456
+ }
457
+ } else {
458
+ // Process documents
459
+ if (Array.isArray(queryOrDocument)) {
460
+ await setPopulates(queryOrDocument, populateOptions, mongooseModel?.schema?.paths);
461
+ result = queryOrDocument.map((item) => (modelClass as any).map(item));
462
+ }
463
+
464
+ // Process document
465
+ else {
466
+ await setPopulates(queryOrDocument, populateOptions, mongooseModel?.schema?.paths);
467
+ result = (modelClass as any).map(queryOrDocument);
468
+ }
469
+ }
470
+
471
+ // Check for unresolved references
472
+ return removeUnresolvedReferences(result, populateOptions);
473
+ }
474
+
475
+ /**
476
+ * Remove ID from array
477
+ * @param source Array with IDs which will be reduced
478
+ * @param ids ID(s) to remove
479
+ */
480
+ export function removeIds(source: any[], ids: StringOrObjectId | StringOrObjectId[]): any[] {
481
+ // Check params and convert if necessary
482
+ if (!ids) {
483
+ return source;
484
+ }
485
+ if (!Array.isArray(ids)) {
486
+ ids = [ids];
487
+ }
488
+ if (!Array.isArray(source)) {
489
+ return [];
490
+ }
491
+
492
+ // Remove IDs from array
493
+ const stringIds = getStringIds(source);
494
+ ids.forEach((id) => {
495
+ const position = stringIds.indexOf(getStringIds(id));
496
+ if (position !== -1) {
497
+ source.splice(position, 1);
498
+ }
499
+ });
500
+
501
+ // Return array
502
+ return source;
503
+ }
504
+
505
+ /**
506
+ * Set populates via populates options array
507
+ */
508
+ export async function setPopulates<T = Query<any, any> | Document>(
509
+ queryOrDocument: T,
510
+ populateOptions: PopulateOptions[],
511
+ modelSchemaPaths?: { [key: string]: SchemaType }
512
+ ): Promise<T> {
513
+ // Check parameters
514
+ if (!populateOptions?.length || !queryOrDocument) {
515
+ return queryOrDocument;
516
+ }
517
+
518
+ // Filter populate options via model schema paths
519
+ if (modelSchemaPaths) {
520
+ populateOptions = populateOptions.filter((options) => {
521
+ return Object.keys(modelSchemaPaths).includes(options.path);
522
+ });
523
+ }
524
+
525
+ // Query => Chaining
526
+ if (queryOrDocument instanceof Query) {
527
+ for (const options of populateOptions) {
528
+ queryOrDocument = (queryOrDocument as any).populate(options);
529
+ }
530
+ }
531
+
532
+ // Document => Non chaining
533
+ // Array with documents
534
+ else if (Array.isArray(queryOrDocument)) {
535
+ const promises = [];
536
+ queryOrDocument.forEach((item) => promises.push(item.populate(populateOptions)));
537
+ await Promise.all(promises);
538
+ }
539
+ // Single document
540
+ else {
541
+ await (queryOrDocument as any).populate(populateOptions);
542
+ }
543
+
544
+ // Return populated
545
+ return queryOrDocument;
546
+ }
547
+
548
+ // =====================================================================================================================
549
+ // Not exported helper functions
550
+ // =====================================================================================================================
551
+ /**
552
+ * Get ID of element as string
553
+ */
554
+ function getStringId(element: any): string {
555
+ // Check element
556
+ if (!element) {
557
+ return element;
558
+ }
559
+
560
+ // Sring handling
561
+ if (typeof element === 'string') {
562
+ return element;
563
+ }
564
+
565
+ // Object handling
566
+ if (typeof element === 'object') {
567
+ if (element instanceof Types.ObjectId) {
568
+ return element.toHexString();
569
+ }
570
+
571
+ if (element.id) {
572
+ return getStringId(element.id);
573
+ } else if (element._id) {
574
+ return getStringId(element._id);
575
+ }
576
+ }
577
+
578
+ // Other types
579
+ return element.toString();
580
+ }