@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.
- package/dist/config.env.js +3 -2
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/args/pagination.args.js +1 -1
- package/dist/core/common/args/pagination.args.js.map +1 -1
- package/dist/core/common/decorators/restricted.decorator.d.ts +3 -0
- package/dist/core/common/decorators/restricted.decorator.js +14 -8
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/enums/role.enum.d.ts +3 -2
- package/dist/core/common/enums/role.enum.js +3 -2
- package/dist/core/common/enums/role.enum.js.map +1 -1
- package/dist/core/common/helpers/config.helper.d.ts +2 -1
- package/dist/core/common/helpers/config.helper.js +11 -7
- package/dist/core/common/helpers/config.helper.js.map +1 -1
- package/dist/core/common/helpers/context.helper.d.ts +7 -1
- package/dist/core/common/helpers/context.helper.js +33 -29
- package/dist/core/common/helpers/context.helper.js.map +1 -1
- package/dist/core/common/helpers/db.helper.d.ts +37 -0
- package/dist/core/common/helpers/db.helper.js +356 -0
- package/dist/core/common/helpers/db.helper.js.map +1 -0
- package/dist/core/common/helpers/file.helper.d.ts +8 -1
- package/dist/core/common/helpers/file.helper.js +43 -31
- package/dist/core/common/helpers/file.helper.js.map +1 -1
- package/dist/core/common/helpers/filter.helper.d.ts +3 -0
- package/dist/core/common/helpers/filter.helper.js +93 -81
- package/dist/core/common/helpers/filter.helper.js.map +1 -1
- package/dist/core/common/helpers/graphql.helper.d.ts +24 -1
- package/dist/core/common/helpers/graphql.helper.js +144 -96
- package/dist/core/common/helpers/graphql.helper.js.map +1 -1
- package/dist/core/common/helpers/input.helper.d.ts +42 -4
- package/dist/core/common/helpers/input.helper.js +256 -97
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/helpers/model.helper.d.ts +11 -0
- package/dist/core/common/helpers/model.helper.js +41 -29
- package/dist/core/common/helpers/model.helper.js.map +1 -1
- package/dist/core/common/helpers/service.helper.d.ts +21 -1
- package/dist/core/common/helpers/service.helper.js +80 -72
- package/dist/core/common/helpers/service.helper.js.map +1 -1
- package/dist/core/common/inputs/combined-filter.input.js +1 -1
- package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
- package/dist/core/common/inputs/core-input.input.js +1 -1
- package/dist/core/common/inputs/core-input.input.js.map +1 -1
- package/dist/core/common/interceptors/check-response.interceptor.js +1 -1
- package/dist/core/common/interceptors/check-response.interceptor.js.map +1 -1
- package/dist/core/common/interfaces/resolve-selector.interface.d.ts +5 -0
- package/dist/core/common/interfaces/resolve-selector.interface.js +3 -0
- package/dist/core/common/interfaces/resolve-selector.interface.js.map +1 -0
- package/dist/core/common/interfaces/service-options.interface.d.ts +36 -0
- package/dist/core/common/interfaces/service-options.interface.js +3 -0
- package/dist/core/common/interfaces/service-options.interface.js.map +1 -0
- package/dist/core/common/models/core-model.model.d.ts +5 -1
- package/dist/core/common/models/core-model.model.js +1 -1
- package/dist/core/common/models/core-model.model.js.map +1 -1
- package/dist/core/common/pipes/check-input.pipe.js +2 -2
- package/dist/core/common/pipes/check-input.pipe.js.map +1 -1
- package/dist/core/common/pipes/map-and-validate.pipe.js +1 -1
- package/dist/core/common/pipes/map-and-validate.pipe.js.map +1 -1
- package/dist/core/common/services/crud.service.d.ts +13 -0
- package/dist/core/common/services/crud.service.js +57 -0
- package/dist/core/common/services/crud.service.js.map +1 -0
- package/dist/core/common/services/email.service.js +8 -8
- package/dist/core/common/services/email.service.js.map +1 -1
- package/dist/core/common/services/module.service.d.ts +40 -0
- package/dist/core/common/services/module.service.js +80 -0
- package/dist/core/common/services/module.service.js.map +1 -0
- package/dist/core/common/types/core-model-constructor.type.d.ts +21 -0
- package/dist/core/common/types/core-model-constructor.type.js +3 -0
- package/dist/core/common/types/core-model-constructor.type.js.map +1 -0
- package/dist/core/common/types/field-selection.type.d.ts +4 -0
- package/dist/core/common/types/field-selection.type.js +3 -0
- package/dist/core/common/types/field-selection.type.js.map +1 -0
- package/dist/core/common/types/ids.type.d.ts +8 -0
- package/dist/core/common/types/ids.type.js +3 -0
- package/dist/core/common/types/ids.type.js.map +1 -0
- package/dist/core/common/types/string-or-object-id.type.d.ts +2 -0
- package/dist/core/common/types/string-or-object-id.type.js +3 -0
- package/dist/core/common/types/string-or-object-id.type.js.map +1 -0
- package/dist/core/modules/auth/core-auth.resolver.d.ts +2 -1
- package/dist/core/modules/auth/core-auth.resolver.js +4 -3
- package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
- package/dist/core/modules/auth/guards/roles.guard.js +1 -2
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/auth/services/core-auth-user.service.d.ts +3 -1
- package/dist/core/modules/auth/services/core-auth-user.service.js.map +1 -1
- package/dist/core/modules/auth/services/core-auth.service.d.ts +2 -1
- package/dist/core/modules/auth/services/core-auth.service.js +6 -4
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/user/core-user.model.js +1 -1
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/core/modules/user/core-user.service.d.ts +16 -25
- package/dist/core/modules/user/core-user.service.js +69 -90
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/core.module.js +1 -1
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/server/modules/auth/auth.resolver.d.ts +2 -1
- package/dist/server/modules/auth/auth.resolver.js +4 -3
- package/dist/server/modules/auth/auth.resolver.js.map +1 -1
- package/dist/server/modules/file/file.controller.js +1 -1
- package/dist/server/modules/file/file.controller.js.map +1 -1
- package/dist/server/modules/user/avatar.controller.js +2 -2
- package/dist/server/modules/user/avatar.controller.js.map +1 -1
- package/dist/server/modules/user/user.model.d.ts +2 -1
- package/dist/server/modules/user/user.module.js +7 -3
- package/dist/server/modules/user/user.module.js.map +1 -1
- package/dist/server/modules/user/user.resolver.d.ts +8 -7
- package/dist/server/modules/user/user.resolver.js +85 -49
- package/dist/server/modules/user/user.resolver.js.map +1 -1
- package/dist/server/modules/user/user.service.d.ts +9 -18
- package/dist/server/modules/user/user.service.js +23 -30
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/test/test.helper.d.ts +1 -2
- package/dist/test/test.helper.js +1 -16
- package/dist/test/test.helper.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +58 -59
- package/src/config.env.ts +3 -2
- package/src/core/common/args/pagination.args.ts +2 -2
- package/src/core/common/decorators/restricted.decorator.ts +24 -12
- package/src/core/common/enums/role.enum.ts +23 -5
- package/src/core/common/helpers/config.helper.ts +26 -6
- package/src/core/common/helpers/context.helper.ts +42 -33
- package/src/core/common/helpers/db.helper.ts +595 -0
- package/src/core/common/helpers/file.helper.ts +76 -49
- package/src/core/common/helpers/filter.helper.ts +119 -96
- package/src/core/common/helpers/graphql.helper.ts +219 -117
- package/src/core/common/helpers/input.helper.ts +349 -108
- package/src/core/common/helpers/model.helper.ts +102 -57
- package/src/core/common/helpers/service.helper.ts +149 -117
- package/src/core/common/inputs/combined-filter.input.ts +2 -2
- package/src/core/common/inputs/core-input.input.ts +2 -2
- package/src/core/common/interceptors/check-response.interceptor.ts +2 -2
- package/src/core/common/interfaces/resolve-selector.interface.ts +9 -0
- package/src/core/common/interfaces/service-options.interface.ts +71 -0
- package/src/core/common/models/core-model.model.ts +7 -3
- package/src/core/common/pipes/check-input.pipe.ts +4 -4
- package/src/core/common/pipes/map-and-validate.pipe.ts +2 -2
- package/src/core/common/services/crud.service.ts +100 -0
- package/src/core/common/services/email.service.ts +9 -9
- package/src/core/common/services/module.service.ts +188 -0
- package/src/core/common/types/core-model-constructor.type.ts +30 -0
- package/src/core/common/types/field-selection.type.ts +8 -0
- package/src/core/common/types/ids.type.ts +7 -0
- package/src/core/common/types/string-or-object-id.type.ts +3 -0
- package/src/core/modules/auth/core-auth.module.ts +1 -1
- package/src/core/modules/auth/core-auth.resolver.ts +8 -3
- package/src/core/modules/auth/guards/roles.guard.ts +5 -7
- package/src/core/modules/auth/services/core-auth-user.service.ts +7 -1
- package/src/core/modules/auth/services/core-auth.service.ts +14 -4
- package/src/core/modules/user/core-user.model.ts +2 -1
- package/src/core/modules/user/core-user.service.ts +115 -185
- package/src/core.module.ts +2 -2
- package/src/index.ts +9 -1
- package/src/main.ts +1 -1
- package/src/server/modules/auth/auth.resolver.ts +8 -3
- package/src/server/modules/file/file.controller.ts +2 -2
- package/src/server/modules/user/avatar.controller.ts +3 -3
- package/src/server/modules/user/user.module.ts +7 -3
- package/src/server/modules/user/user.resolver.ts +74 -43
- package/src/server/modules/user/user.service.ts +30 -53
- package/src/test/test.helper.ts +31 -30
- package/dist/core/modules/user/core-basic-user.service.d.ts +0 -17
- package/dist/core/modules/user/core-basic-user.service.js +0 -73
- package/dist/core/modules/user/core-basic-user.service.js.map +0 -1
- 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
|
+
}
|