vanta-api 1.2.6 → 1.2.8
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 +1 -1
- package/src/api-features.js +97 -68
package/package.json
CHANGED
package/src/api-features.js
CHANGED
|
@@ -62,21 +62,46 @@ export class ApiFeatures {
|
|
|
62
62
|
return this;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
limitFields(input = "") {
|
|
66
|
+
const rawFields = [input, this.query.fields].filter(Boolean).join(",");
|
|
67
|
+
if (!rawFields) return this;
|
|
68
|
+
|
|
69
|
+
const validFields = Object.keys(this.model.schema.paths).filter(
|
|
70
|
+
(f) => !securityConfig.forbiddenFields.includes(f)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const fieldsArray = rawFields
|
|
74
|
+
.split(",")
|
|
75
|
+
.map((f) => f.trim())
|
|
76
|
+
.filter(Boolean);
|
|
77
|
+
|
|
78
|
+
const includeFields = new Set();
|
|
79
|
+
const excludeFields = new Set();
|
|
80
|
+
|
|
81
|
+
fieldsArray.forEach((f) => {
|
|
82
|
+
if (f.startsWith("-")) excludeFields.add(f.slice(1));
|
|
83
|
+
else includeFields.add(f);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const project = {};
|
|
71
87
|
|
|
72
|
-
|
|
88
|
+
if (includeFields.size > 0) {
|
|
89
|
+
includeFields.forEach((f) => {
|
|
73
90
|
if (validFields.includes(f)) project[f] = 1;
|
|
74
91
|
});
|
|
92
|
+
} else if (excludeFields.size > 0) {
|
|
93
|
+
validFields.forEach((f) => {
|
|
94
|
+
if (!excludeFields.has(f)) project[f] = 1;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
75
97
|
|
|
76
|
-
|
|
77
|
-
|
|
98
|
+
if (Object.keys(project).length) {
|
|
99
|
+
this.pipeline.push({ $project: project });
|
|
78
100
|
}
|
|
79
101
|
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
80
105
|
paginate() {
|
|
81
106
|
const { maxLimit } = securityConfig.accessLevels[this.userRole] || {
|
|
82
107
|
maxLimit: 100,
|
|
@@ -91,70 +116,74 @@ export class ApiFeatures {
|
|
|
91
116
|
return this;
|
|
92
117
|
}
|
|
93
118
|
|
|
94
|
-
populate(input = "") {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
119
|
+
populate(input = "") {
|
|
120
|
+
let list = [];
|
|
121
|
+
const raw = Array.isArray(input) ? input : [input];
|
|
122
|
+
if (this.query.populate) raw.push(...this.query.populate.split(","));
|
|
98
123
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
124
|
+
raw.forEach((item) => {
|
|
125
|
+
if (typeof item === "string" && item.trim()) list.push(item.trim());
|
|
126
|
+
else if (item?.path) list.push(item);
|
|
127
|
+
});
|
|
103
128
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
129
|
+
const map = new Map();
|
|
130
|
+
list.forEach((opt) => {
|
|
131
|
+
const key = typeof opt === "string" ? opt : opt.path;
|
|
132
|
+
map.set(key, opt);
|
|
133
|
+
});
|
|
109
134
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
135
|
+
const allowed =
|
|
136
|
+
securityConfig.accessLevels[this.userRole]?.allowedPopulate || [];
|
|
137
|
+
const final = [];
|
|
138
|
+
map.forEach((opt, key) => {
|
|
139
|
+
if (allowed.includes("*") || allowed.includes(key)) final.push(opt);
|
|
140
|
+
});
|
|
116
141
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
142
|
+
for (const opt of final) {
|
|
143
|
+
const field = typeof opt === "string" ? opt : opt.path;
|
|
144
|
+
const proj =
|
|
145
|
+
typeof opt === "object" && opt.select
|
|
146
|
+
? opt.select.split(" ").reduce(
|
|
147
|
+
(a, f) => {
|
|
148
|
+
a[f] = 1;
|
|
149
|
+
return a;
|
|
150
|
+
},
|
|
151
|
+
{ _id: 1 }
|
|
152
|
+
)
|
|
153
|
+
: {};
|
|
154
|
+
|
|
155
|
+
const { collection, isArray } = this._getCollectionInfo(field);
|
|
156
|
+
|
|
157
|
+
const matchStage = isArray
|
|
158
|
+
? { $match: { $expr: { $in: ["$_id", "$$id"] } } }
|
|
159
|
+
: { $match: { $expr: { $eq: ["$_id", "$$id"] } } };
|
|
160
|
+
|
|
161
|
+
const lookup =
|
|
162
|
+
proj && Object.keys(proj).length
|
|
163
|
+
? {
|
|
164
|
+
from: collection,
|
|
165
|
+
let: { id: `$${field}` },
|
|
166
|
+
pipeline: [matchStage, { $project: proj }],
|
|
167
|
+
as: field,
|
|
168
|
+
}
|
|
169
|
+
: {
|
|
170
|
+
from: collection,
|
|
171
|
+
localField: field,
|
|
172
|
+
foreignField: "_id",
|
|
173
|
+
as: field,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
this.pipeline.push({ $lookup: lookup });
|
|
177
|
+
|
|
178
|
+
if (!isArray) {
|
|
179
|
+
this.pipeline.push({
|
|
180
|
+
$unwind: { path: `$${field}`, preserveNullAndEmptyArrays: true },
|
|
181
|
+
});
|
|
182
|
+
}
|
|
153
183
|
}
|
|
154
|
-
}
|
|
155
184
|
|
|
156
|
-
|
|
157
|
-
}
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
158
187
|
|
|
159
188
|
addManualFilters(filters) {
|
|
160
189
|
if (filters) this.manualFilters = { ...this.manualFilters, ...filters };
|
|
@@ -285,7 +314,7 @@ populate(input = "") {
|
|
|
285
314
|
resultObj[keyObj] = false;
|
|
286
315
|
return;
|
|
287
316
|
}
|
|
288
|
-
|
|
317
|
+
|
|
289
318
|
if (typeof val === "string" && /^[0-9]+$/.test(val)) {
|
|
290
319
|
resultObj[keyObj] = parseInt(val, 10);
|
|
291
320
|
return;
|