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