@terreno/api 0.0.1
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/LICENSE +202 -0
- package/README.md +170 -0
- package/biome.jsonc +22 -0
- package/bunfig.toml +4 -0
- package/dist/api.d.ts +227 -0
- package/dist/api.js +1024 -0
- package/dist/api.test.d.ts +1 -0
- package/dist/api.test.js +2143 -0
- package/dist/auth.d.ts +50 -0
- package/dist/auth.js +512 -0
- package/dist/auth.test.d.ts +1 -0
- package/dist/auth.test.js +778 -0
- package/dist/errors.d.ts +75 -0
- package/dist/errors.js +216 -0
- package/dist/example.d.ts +1 -0
- package/dist/example.js +118 -0
- package/dist/expressServer.d.ts +35 -0
- package/dist/expressServer.js +436 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +30 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.js +249 -0
- package/dist/middleware.d.ts +10 -0
- package/dist/middleware.js +52 -0
- package/dist/notifiers/googleChatNotifier.d.ts +5 -0
- package/dist/notifiers/googleChatNotifier.js +130 -0
- package/dist/notifiers/googleChatNotifier.test.d.ts +1 -0
- package/dist/notifiers/googleChatNotifier.test.js +260 -0
- package/dist/notifiers/index.d.ts +3 -0
- package/dist/notifiers/index.js +19 -0
- package/dist/notifiers/slackNotifier.d.ts +5 -0
- package/dist/notifiers/slackNotifier.js +130 -0
- package/dist/notifiers/slackNotifier.test.d.ts +1 -0
- package/dist/notifiers/slackNotifier.test.js +259 -0
- package/dist/notifiers/zoomNotifier.d.ts +34 -0
- package/dist/notifiers/zoomNotifier.js +181 -0
- package/dist/notifiers/zoomNotifier.test.d.ts +1 -0
- package/dist/notifiers/zoomNotifier.test.js +370 -0
- package/dist/openApi.d.ts +60 -0
- package/dist/openApi.js +441 -0
- package/dist/openApi.test.d.ts +1 -0
- package/dist/openApi.test.js +445 -0
- package/dist/openApiBuilder.d.ts +419 -0
- package/dist/openApiBuilder.js +424 -0
- package/dist/openApiBuilder.test.d.ts +1 -0
- package/dist/openApiBuilder.test.js +509 -0
- package/dist/openApiEtag.d.ts +7 -0
- package/dist/openApiEtag.js +38 -0
- package/dist/permissions.d.ts +26 -0
- package/dist/permissions.js +331 -0
- package/dist/permissions.test.d.ts +1 -0
- package/dist/permissions.test.js +413 -0
- package/dist/plugins.d.ts +67 -0
- package/dist/plugins.js +315 -0
- package/dist/plugins.test.d.ts +1 -0
- package/dist/plugins.test.js +639 -0
- package/dist/populate.d.ts +14 -0
- package/dist/populate.js +315 -0
- package/dist/populate.test.d.ts +1 -0
- package/dist/populate.test.js +133 -0
- package/dist/response.d.ts +0 -0
- package/dist/response.js +1 -0
- package/dist/tests/bunSetup.d.ts +1 -0
- package/dist/tests/bunSetup.js +297 -0
- package/dist/tests/index.d.ts +1 -0
- package/dist/tests/index.js +17 -0
- package/dist/tests.d.ts +99 -0
- package/dist/tests.js +273 -0
- package/dist/transformers.d.ts +25 -0
- package/dist/transformers.js +217 -0
- package/dist/transformers.test.d.ts +1 -0
- package/dist/transformers.test.js +370 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +143 -0
- package/dist/utils.test.d.ts +1 -0
- package/dist/utils.test.js +14 -0
- package/index.ts +1 -0
- package/package.json +88 -0
- package/src/__snapshots__/openApi.test.ts.snap +4814 -0
- package/src/__snapshots__/openApiBuilder.test.ts.snap +1485 -0
- package/src/api.test.ts +1661 -0
- package/src/api.ts +1036 -0
- package/src/auth.test.ts +550 -0
- package/src/auth.ts +408 -0
- package/src/errors.ts +225 -0
- package/src/example.ts +99 -0
- package/src/express.d.ts +5 -0
- package/src/expressServer.ts +387 -0
- package/src/index.ts +14 -0
- package/src/logger.ts +190 -0
- package/src/middleware.ts +18 -0
- package/src/notifiers/googleChatNotifier.test.ts +114 -0
- package/src/notifiers/googleChatNotifier.ts +47 -0
- package/src/notifiers/index.ts +3 -0
- package/src/notifiers/slackNotifier.test.ts +113 -0
- package/src/notifiers/slackNotifier.ts +55 -0
- package/src/notifiers/zoomNotifier.test.ts +207 -0
- package/src/notifiers/zoomNotifier.ts +111 -0
- package/src/openApi.test.ts +331 -0
- package/src/openApi.ts +494 -0
- package/src/openApiBuilder.test.ts +442 -0
- package/src/openApiBuilder.ts +636 -0
- package/src/openApiEtag.ts +40 -0
- package/src/permissions.test.ts +219 -0
- package/src/permissions.ts +228 -0
- package/src/plugins.test.ts +390 -0
- package/src/plugins.ts +289 -0
- package/src/populate.test.ts +65 -0
- package/src/populate.ts +258 -0
- package/src/response.ts +0 -0
- package/src/tests/bunSetup.ts +234 -0
- package/src/tests/index.ts +1 -0
- package/src/tests.ts +218 -0
- package/src/transformers.test.ts +202 -0
- package/src/transformers.ts +170 -0
- package/src/utils.test.ts +14 -0
- package/src/utils.ts +47 -0
- package/tsconfig.json +60 -0
- package/types.d.ts +17 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type express from "express";
|
|
2
|
+
import type { Document } from "mongoose";
|
|
3
|
+
import type { modelRouterOptions } from "./api";
|
|
4
|
+
import type { User } from "./auth";
|
|
5
|
+
export interface TerrenoTransformer<T> {
|
|
6
|
+
transform?: (obj: Partial<T>, method: "create" | "update", user?: User) => Partial<T> | undefined;
|
|
7
|
+
serialize?: (obj: T, user?: User) => Partial<T> | undefined;
|
|
8
|
+
}
|
|
9
|
+
export declare function AdminOwnerTransformer<T>(options: {
|
|
10
|
+
anonReadFields?: string[];
|
|
11
|
+
authReadFields?: string[];
|
|
12
|
+
ownerReadFields?: string[];
|
|
13
|
+
adminReadFields?: string[];
|
|
14
|
+
anonWriteFields?: string[];
|
|
15
|
+
authWriteFields?: string[];
|
|
16
|
+
ownerWriteFields?: string[];
|
|
17
|
+
adminWriteFields?: string[];
|
|
18
|
+
}): TerrenoTransformer<T>;
|
|
19
|
+
export declare function transform<T>(options: modelRouterOptions<T>, data: Partial<T> | Partial<T>[], method: "create" | "update", user?: User): Partial<T> | (Partial<T> | undefined)[] | undefined;
|
|
20
|
+
export declare function serialize<T>(req: express.Request, options: modelRouterOptions<T>, data: (Document<any, any, any> & T) | (Document<any, any, any> & T)[]): Partial<T> | (Partial<T> | undefined)[] | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Default response handler for modelRouter. Calls toObject on each doc and returns the result,
|
|
23
|
+
* using transformers.serializer if provided.
|
|
24
|
+
*/
|
|
25
|
+
export declare function defaultResponseHandler<T>(doc: (Document<any, any, any> & T) | (Document<any, any, any> & T)[] | null, method: "list" | "create" | "read" | "update", request: express.Request, options: modelRouterOptions<T>): Promise<Partial<T> | (Partial<T> | undefined)[] | null | undefined>;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var __values = (this && this.__values) || function(o) {
|
|
39
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
40
|
+
if (m) return m.call(o);
|
|
41
|
+
if (o && typeof o.length === "number") return {
|
|
42
|
+
next: function () {
|
|
43
|
+
if (o && i >= o.length) o = void 0;
|
|
44
|
+
return { value: o && o[i++], done: !o };
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
48
|
+
};
|
|
49
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
50
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
51
|
+
if (!m) return o;
|
|
52
|
+
var i = m.call(o), r, ar = [], e;
|
|
53
|
+
try {
|
|
54
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
55
|
+
}
|
|
56
|
+
catch (error) { e = { error: error }; }
|
|
57
|
+
finally {
|
|
58
|
+
try {
|
|
59
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
60
|
+
}
|
|
61
|
+
finally { if (e) throw e.error; }
|
|
62
|
+
}
|
|
63
|
+
return ar;
|
|
64
|
+
};
|
|
65
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
66
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
67
|
+
if (ar || !(i in from)) {
|
|
68
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
69
|
+
ar[i] = from[i];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
73
|
+
};
|
|
74
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
75
|
+
exports.AdminOwnerTransformer = AdminOwnerTransformer;
|
|
76
|
+
exports.transform = transform;
|
|
77
|
+
exports.serialize = serialize;
|
|
78
|
+
exports.defaultResponseHandler = defaultResponseHandler;
|
|
79
|
+
var errors_1 = require("./errors");
|
|
80
|
+
var logger_1 = require("./logger");
|
|
81
|
+
function getUserType(user, obj) {
|
|
82
|
+
if (user === null || user === void 0 ? void 0 : user.admin) {
|
|
83
|
+
return "admin";
|
|
84
|
+
}
|
|
85
|
+
if (obj && user && String(obj === null || obj === void 0 ? void 0 : obj.ownerId) === String(user === null || user === void 0 ? void 0 : user.id)) {
|
|
86
|
+
return "owner";
|
|
87
|
+
}
|
|
88
|
+
if (user === null || user === void 0 ? void 0 : user.id) {
|
|
89
|
+
return "auth";
|
|
90
|
+
}
|
|
91
|
+
return "anon";
|
|
92
|
+
}
|
|
93
|
+
function AdminOwnerTransformer(options) {
|
|
94
|
+
function pickFields(obj, fields) {
|
|
95
|
+
var e_1, _a;
|
|
96
|
+
var newData = {};
|
|
97
|
+
try {
|
|
98
|
+
for (var fields_1 = __values(fields), fields_1_1 = fields_1.next(); !fields_1_1.done; fields_1_1 = fields_1.next()) {
|
|
99
|
+
var field = fields_1_1.value;
|
|
100
|
+
if (obj[field] !== undefined) {
|
|
101
|
+
newData[field] = obj[field];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
106
|
+
finally {
|
|
107
|
+
try {
|
|
108
|
+
if (fields_1_1 && !fields_1_1.done && (_a = fields_1.return)) _a.call(fields_1);
|
|
109
|
+
}
|
|
110
|
+
finally { if (e_1) throw e_1.error; }
|
|
111
|
+
}
|
|
112
|
+
return newData;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
serialize: function (obj, user) {
|
|
116
|
+
var _a, _b, _c, _d;
|
|
117
|
+
var userType = getUserType(user, obj);
|
|
118
|
+
if (userType === "admin") {
|
|
119
|
+
return pickFields(obj, __spreadArray(__spreadArray([], __read(((_a = options.adminReadFields) !== null && _a !== void 0 ? _a : [])), false), ["id"], false));
|
|
120
|
+
}
|
|
121
|
+
if (userType === "owner") {
|
|
122
|
+
return pickFields(obj, __spreadArray(__spreadArray([], __read(((_b = options.ownerReadFields) !== null && _b !== void 0 ? _b : [])), false), ["id"], false));
|
|
123
|
+
}
|
|
124
|
+
if (userType === "auth") {
|
|
125
|
+
return pickFields(obj, __spreadArray(__spreadArray([], __read(((_c = options.authReadFields) !== null && _c !== void 0 ? _c : [])), false), ["id"], false));
|
|
126
|
+
}
|
|
127
|
+
return pickFields(obj, __spreadArray(__spreadArray([], __read(((_d = options.anonReadFields) !== null && _d !== void 0 ? _d : [])), false), ["id"], false));
|
|
128
|
+
},
|
|
129
|
+
// TODO: Migrate AdminOwnerTransform to use pre-hooks.
|
|
130
|
+
transform: function (obj, _method, user) {
|
|
131
|
+
var _a, _b, _c, _d;
|
|
132
|
+
var userType = getUserType(user, obj);
|
|
133
|
+
var allowedFields;
|
|
134
|
+
if (userType === "admin") {
|
|
135
|
+
allowedFields = (_a = options.adminWriteFields) !== null && _a !== void 0 ? _a : [];
|
|
136
|
+
}
|
|
137
|
+
else if (userType === "owner") {
|
|
138
|
+
allowedFields = (_b = options.ownerWriteFields) !== null && _b !== void 0 ? _b : [];
|
|
139
|
+
}
|
|
140
|
+
else if (userType === "auth") {
|
|
141
|
+
allowedFields = (_c = options.authWriteFields) !== null && _c !== void 0 ? _c : [];
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
allowedFields = (_d = options.anonWriteFields) !== null && _d !== void 0 ? _d : [];
|
|
145
|
+
}
|
|
146
|
+
var unallowedFields = Object.keys(obj).filter(function (k) { return !allowedFields.includes(k); });
|
|
147
|
+
if (unallowedFields.length) {
|
|
148
|
+
throw new Error("User of type ".concat(userType, " cannot write fields: ").concat(unallowedFields.join(", ")));
|
|
149
|
+
}
|
|
150
|
+
return obj;
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function transform(options, data, method, user) {
|
|
155
|
+
var _a, _b;
|
|
156
|
+
if (!((_a = options.transformer) === null || _a === void 0 ? void 0 : _a.transform)) {
|
|
157
|
+
return data;
|
|
158
|
+
}
|
|
159
|
+
logger_1.logger.warn("transform functions are deprecated, use preCreate/preUpdate/preDelete hooks instead");
|
|
160
|
+
// TS doesn't realize this is defined otherwise...
|
|
161
|
+
var transformFn = (_b = options.transformer) === null || _b === void 0 ? void 0 : _b.transform;
|
|
162
|
+
if (!Array.isArray(data)) {
|
|
163
|
+
return transformFn(data, method, user);
|
|
164
|
+
}
|
|
165
|
+
return data.map(function (d) { return transformFn(d, method, user); });
|
|
166
|
+
}
|
|
167
|
+
function serialize(req, options, data) {
|
|
168
|
+
var _a;
|
|
169
|
+
var serializeFn = function (serializeData, serializeUser) {
|
|
170
|
+
var _a, _b;
|
|
171
|
+
var dataObject = serializeData.toObject();
|
|
172
|
+
dataObject.id = serializeData._id;
|
|
173
|
+
// Search for any value that is a Map and transform it to a plain object.
|
|
174
|
+
// Otherwise Express drops the contents.
|
|
175
|
+
for (var key in dataObject) {
|
|
176
|
+
var value = dataObject[key];
|
|
177
|
+
if (value instanceof Map) {
|
|
178
|
+
dataObject[key] = Object.fromEntries(value);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if ((_a = options.transformer) === null || _a === void 0 ? void 0 : _a.serialize) {
|
|
182
|
+
return (_b = options.transformer) === null || _b === void 0 ? void 0 : _b.serialize(dataObject, serializeUser);
|
|
183
|
+
}
|
|
184
|
+
return dataObject;
|
|
185
|
+
};
|
|
186
|
+
if ((_a = options.transformer) === null || _a === void 0 ? void 0 : _a.serialize) {
|
|
187
|
+
logger_1.logger.warn("transform.serialize functions are deprecated, use post* hooks and serialize instead");
|
|
188
|
+
}
|
|
189
|
+
if (!Array.isArray(data)) {
|
|
190
|
+
return serializeFn(data, req.user);
|
|
191
|
+
}
|
|
192
|
+
return data.map(function (d) { return serializeFn(d, req.user); });
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Default response handler for modelRouter. Calls toObject on each doc and returns the result,
|
|
196
|
+
* using transformers.serializer if provided.
|
|
197
|
+
*/
|
|
198
|
+
function defaultResponseHandler(doc, method, request, options) {
|
|
199
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
200
|
+
return __generator(this, function (_a) {
|
|
201
|
+
if (!doc) {
|
|
202
|
+
return [2 /*return*/, null];
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
return [2 /*return*/, serialize(request, options, doc)];
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
throw new errors_1.APIError({
|
|
209
|
+
error: error,
|
|
210
|
+
status: 400,
|
|
211
|
+
title: "Error serializing ".concat(method, " response: ").concat(error.message),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return [2 /*return*/];
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
39
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
40
|
+
if (!m) return o;
|
|
41
|
+
var i = m.call(o), r, ar = [], e;
|
|
42
|
+
try {
|
|
43
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
44
|
+
}
|
|
45
|
+
catch (error) { e = { error: error }; }
|
|
46
|
+
finally {
|
|
47
|
+
try {
|
|
48
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
49
|
+
}
|
|
50
|
+
finally { if (e) throw e.error; }
|
|
51
|
+
}
|
|
52
|
+
return ar;
|
|
53
|
+
};
|
|
54
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
55
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
56
|
+
};
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
var bun_test_1 = require("bun:test");
|
|
59
|
+
var supertest_1 = __importDefault(require("supertest"));
|
|
60
|
+
var api_1 = require("./api");
|
|
61
|
+
var auth_1 = require("./auth");
|
|
62
|
+
var permissions_1 = require("./permissions");
|
|
63
|
+
var tests_1 = require("./tests");
|
|
64
|
+
var transformers_1 = require("./transformers");
|
|
65
|
+
(0, bun_test_1.describe)("query and transform", function () {
|
|
66
|
+
var notAdmin;
|
|
67
|
+
var admin;
|
|
68
|
+
var server;
|
|
69
|
+
var app;
|
|
70
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
71
|
+
var _a;
|
|
72
|
+
return __generator(this, function (_b) {
|
|
73
|
+
switch (_b.label) {
|
|
74
|
+
case 0:
|
|
75
|
+
process.env.REFRESH_TOKEN_SECRET = "testsecret1234";
|
|
76
|
+
return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
77
|
+
case 1:
|
|
78
|
+
_a = __read.apply(void 0, [_b.sent(), 2]), admin = _a[0], notAdmin = _a[1];
|
|
79
|
+
return [4 /*yield*/, Promise.all([
|
|
80
|
+
tests_1.FoodModel.create({
|
|
81
|
+
calories: 1,
|
|
82
|
+
created: new Date(),
|
|
83
|
+
name: "Spinach",
|
|
84
|
+
ownerId: notAdmin._id,
|
|
85
|
+
}),
|
|
86
|
+
tests_1.FoodModel.create({
|
|
87
|
+
calories: 100,
|
|
88
|
+
created: Date.now() - 10,
|
|
89
|
+
hidden: true,
|
|
90
|
+
name: "Apple",
|
|
91
|
+
ownerId: admin._id,
|
|
92
|
+
}),
|
|
93
|
+
tests_1.FoodModel.create({
|
|
94
|
+
calories: 100,
|
|
95
|
+
created: Date.now() - 10,
|
|
96
|
+
name: "Carrots",
|
|
97
|
+
ownerId: admin._id,
|
|
98
|
+
}),
|
|
99
|
+
])];
|
|
100
|
+
case 2:
|
|
101
|
+
_b.sent();
|
|
102
|
+
app = (0, tests_1.getBaseServer)();
|
|
103
|
+
(0, auth_1.setupAuth)(app, tests_1.UserModel);
|
|
104
|
+
(0, auth_1.addAuthRoutes)(app, tests_1.UserModel);
|
|
105
|
+
app.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
106
|
+
allowAnonymous: true,
|
|
107
|
+
permissions: {
|
|
108
|
+
create: [permissions_1.Permissions.IsAny],
|
|
109
|
+
delete: [permissions_1.Permissions.IsAny],
|
|
110
|
+
list: [permissions_1.Permissions.IsAny],
|
|
111
|
+
read: [permissions_1.Permissions.IsAny],
|
|
112
|
+
update: [permissions_1.Permissions.IsAny],
|
|
113
|
+
},
|
|
114
|
+
queryFilter: function (user) {
|
|
115
|
+
if (!(user === null || user === void 0 ? void 0 : user.admin)) {
|
|
116
|
+
return { hidden: { $ne: true } };
|
|
117
|
+
}
|
|
118
|
+
return {};
|
|
119
|
+
},
|
|
120
|
+
transformer: (0, transformers_1.AdminOwnerTransformer)({
|
|
121
|
+
adminReadFields: ["name", "calories", "created", "ownerId"],
|
|
122
|
+
adminWriteFields: ["name", "calories", "created", "ownerId"],
|
|
123
|
+
anonReadFields: ["name"],
|
|
124
|
+
anonWriteFields: [],
|
|
125
|
+
authReadFields: ["name", "calories", "created"],
|
|
126
|
+
authWriteFields: ["name", "calories"],
|
|
127
|
+
ownerReadFields: ["name", "calories", "created", "ownerId"],
|
|
128
|
+
ownerWriteFields: ["name", "calories", "created"],
|
|
129
|
+
}),
|
|
130
|
+
}));
|
|
131
|
+
server = (0, supertest_1.default)(app);
|
|
132
|
+
return [2 /*return*/];
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}); });
|
|
136
|
+
(0, bun_test_1.it)("filters list for non-admin", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
137
|
+
var agent, foodRes;
|
|
138
|
+
return __generator(this, function (_a) {
|
|
139
|
+
switch (_a.label) {
|
|
140
|
+
case 0: return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
141
|
+
case 1:
|
|
142
|
+
agent = _a.sent();
|
|
143
|
+
return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
144
|
+
case 2:
|
|
145
|
+
foodRes = _a.sent();
|
|
146
|
+
(0, bun_test_1.expect)(foodRes.body.data).toHaveLength(2);
|
|
147
|
+
return [2 /*return*/];
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}); });
|
|
151
|
+
(0, bun_test_1.it)("does not filter list for admin", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
152
|
+
var agent, foodRes;
|
|
153
|
+
return __generator(this, function (_a) {
|
|
154
|
+
switch (_a.label) {
|
|
155
|
+
case 0: return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
|
|
156
|
+
case 1:
|
|
157
|
+
agent = _a.sent();
|
|
158
|
+
return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
159
|
+
case 2:
|
|
160
|
+
foodRes = _a.sent();
|
|
161
|
+
(0, bun_test_1.expect)(foodRes.body.data).toHaveLength(3);
|
|
162
|
+
return [2 /*return*/];
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}); });
|
|
166
|
+
(0, bun_test_1.it)("admin read transform", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
167
|
+
var agent, foodRes, spinach;
|
|
168
|
+
return __generator(this, function (_a) {
|
|
169
|
+
switch (_a.label) {
|
|
170
|
+
case 0: return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
|
|
171
|
+
case 1:
|
|
172
|
+
agent = _a.sent();
|
|
173
|
+
return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
174
|
+
case 2:
|
|
175
|
+
foodRes = _a.sent();
|
|
176
|
+
(0, bun_test_1.expect)(foodRes.body.data).toHaveLength(3);
|
|
177
|
+
spinach = foodRes.body.data.find(function (food) { return food.name === "Spinach"; });
|
|
178
|
+
(0, bun_test_1.expect)(spinach.created).toBeDefined();
|
|
179
|
+
(0, bun_test_1.expect)(spinach.id).toBeDefined();
|
|
180
|
+
(0, bun_test_1.expect)(spinach.ownerId).toBeDefined();
|
|
181
|
+
(0, bun_test_1.expect)(spinach.name).toBe("Spinach");
|
|
182
|
+
(0, bun_test_1.expect)(spinach.calories).toBe(1);
|
|
183
|
+
(0, bun_test_1.expect)(spinach.hidden).toBeUndefined();
|
|
184
|
+
return [2 /*return*/];
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}); });
|
|
188
|
+
(0, bun_test_1.it)("admin write transform", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
189
|
+
var agent, foodRes, spinach, spinachRes;
|
|
190
|
+
return __generator(this, function (_a) {
|
|
191
|
+
switch (_a.label) {
|
|
192
|
+
case 0: return [4 /*yield*/, (0, tests_1.authAsUser)(app, "admin")];
|
|
193
|
+
case 1:
|
|
194
|
+
agent = _a.sent();
|
|
195
|
+
return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
196
|
+
case 2:
|
|
197
|
+
foodRes = _a.sent();
|
|
198
|
+
spinach = foodRes.body.data.find(function (food) { return food.name === "Spinach"; });
|
|
199
|
+
return [4 /*yield*/, agent.patch("/food/".concat(spinach.id)).send({ name: "Lettuce" }).expect(200)];
|
|
200
|
+
case 3:
|
|
201
|
+
spinachRes = _a.sent();
|
|
202
|
+
(0, bun_test_1.expect)(spinachRes.body.data.name).toBe("Lettuce");
|
|
203
|
+
return [2 /*return*/];
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}); });
|
|
207
|
+
(0, bun_test_1.it)("owner read transform", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
208
|
+
var agent, foodRes, spinach;
|
|
209
|
+
return __generator(this, function (_a) {
|
|
210
|
+
switch (_a.label) {
|
|
211
|
+
case 0: return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
212
|
+
case 1:
|
|
213
|
+
agent = _a.sent();
|
|
214
|
+
return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
215
|
+
case 2:
|
|
216
|
+
foodRes = _a.sent();
|
|
217
|
+
(0, bun_test_1.expect)(foodRes.body.data).toHaveLength(2);
|
|
218
|
+
spinach = foodRes.body.data.find(function (food) { return food.name === "Spinach"; });
|
|
219
|
+
(0, bun_test_1.expect)(spinach.id).toBeDefined();
|
|
220
|
+
(0, bun_test_1.expect)(spinach.name).toBe("Spinach");
|
|
221
|
+
(0, bun_test_1.expect)(spinach.calories).toBe(1);
|
|
222
|
+
(0, bun_test_1.expect)(spinach.created).toBeDefined();
|
|
223
|
+
(0, bun_test_1.expect)(spinach.ownerId).toBeDefined();
|
|
224
|
+
(0, bun_test_1.expect)(spinach.hidden).toBeUndefined();
|
|
225
|
+
return [2 /*return*/];
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}); });
|
|
229
|
+
(0, bun_test_1.it)("owner write transform", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
230
|
+
var agent, foodRes, spinach;
|
|
231
|
+
return __generator(this, function (_a) {
|
|
232
|
+
switch (_a.label) {
|
|
233
|
+
case 0: return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
234
|
+
case 1:
|
|
235
|
+
agent = _a.sent();
|
|
236
|
+
return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
237
|
+
case 2:
|
|
238
|
+
foodRes = _a.sent();
|
|
239
|
+
spinach = foodRes.body.data.find(function (food) { return food.name === "Spinach"; });
|
|
240
|
+
return [4 /*yield*/, agent.patch("/food/".concat(spinach.id)).send({ ownerId: admin.id }).expect(403)];
|
|
241
|
+
case 3:
|
|
242
|
+
_a.sent();
|
|
243
|
+
return [2 /*return*/];
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}); });
|
|
247
|
+
(0, bun_test_1.it)("owner write transform fails", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
248
|
+
var agent, foodRes, spinach, spinachRes;
|
|
249
|
+
return __generator(this, function (_a) {
|
|
250
|
+
switch (_a.label) {
|
|
251
|
+
case 0: return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
252
|
+
case 1:
|
|
253
|
+
agent = _a.sent();
|
|
254
|
+
return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
255
|
+
case 2:
|
|
256
|
+
foodRes = _a.sent();
|
|
257
|
+
spinach = foodRes.body.data.find(function (food) { return food.name === "Spinach"; });
|
|
258
|
+
return [4 /*yield*/, agent
|
|
259
|
+
.patch("/food/".concat(spinach.id))
|
|
260
|
+
.send({ ownerId: notAdmin.id })
|
|
261
|
+
.expect(403)];
|
|
262
|
+
case 3:
|
|
263
|
+
spinachRes = _a.sent();
|
|
264
|
+
(0, bun_test_1.expect)(spinachRes.body.title.includes("User of type owner cannot write fields: ownerId")).toBe(true);
|
|
265
|
+
return [2 /*return*/];
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}); });
|
|
269
|
+
(0, bun_test_1.it)("auth read transform", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
270
|
+
var agent, foodRes, spinach, carrots;
|
|
271
|
+
return __generator(this, function (_a) {
|
|
272
|
+
switch (_a.label) {
|
|
273
|
+
case 0: return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
274
|
+
case 1:
|
|
275
|
+
agent = _a.sent();
|
|
276
|
+
return [4 /*yield*/, agent.get("/food").expect(200)];
|
|
277
|
+
case 2:
|
|
278
|
+
foodRes = _a.sent();
|
|
279
|
+
(0, bun_test_1.expect)(foodRes.body.data).toHaveLength(2);
|
|
280
|
+
spinach = foodRes.body.data.find(function (food) { return food.name === "Spinach"; });
|
|
281
|
+
(0, bun_test_1.expect)(spinach.id).toBeDefined();
|
|
282
|
+
(0, bun_test_1.expect)(spinach.name).toBe("Spinach");
|
|
283
|
+
(0, bun_test_1.expect)(spinach.calories).toBe(1);
|
|
284
|
+
(0, bun_test_1.expect)(spinach.created).toBeDefined();
|
|
285
|
+
// Owner, so this is defined.
|
|
286
|
+
(0, bun_test_1.expect)(spinach.ownerId).toBeDefined();
|
|
287
|
+
(0, bun_test_1.expect)(spinach.hidden).toBeUndefined();
|
|
288
|
+
carrots = foodRes.body.data.find(function (food) { return food.name === "Carrots"; });
|
|
289
|
+
(0, bun_test_1.expect)(carrots.id).toBeDefined();
|
|
290
|
+
(0, bun_test_1.expect)(carrots.name).toBe("Carrots");
|
|
291
|
+
(0, bun_test_1.expect)(carrots.calories).toBe(100);
|
|
292
|
+
(0, bun_test_1.expect)(carrots.created).toBeDefined();
|
|
293
|
+
// Not owner, so undefined.
|
|
294
|
+
(0, bun_test_1.expect)(carrots.ownerId).toBeUndefined();
|
|
295
|
+
(0, bun_test_1.expect)(spinach.hidden).toBeUndefined();
|
|
296
|
+
return [2 /*return*/];
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}); });
|
|
300
|
+
(0, bun_test_1.it)("auth write transform", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
301
|
+
var agent, foodRes, carrots, carrotRes;
|
|
302
|
+
return __generator(this, function (_a) {
|
|
303
|
+
switch (_a.label) {
|
|
304
|
+
case 0: return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
305
|
+
case 1:
|
|
306
|
+
agent = _a.sent();
|
|
307
|
+
return [4 /*yield*/, agent.get("/food")];
|
|
308
|
+
case 2:
|
|
309
|
+
foodRes = _a.sent();
|
|
310
|
+
carrots = foodRes.body.data.find(function (food) { return food.name === "Carrots"; });
|
|
311
|
+
return [4 /*yield*/, agent.patch("/food/".concat(carrots.id)).send({ calories: 2000 }).expect(200)];
|
|
312
|
+
case 3:
|
|
313
|
+
carrotRes = _a.sent();
|
|
314
|
+
(0, bun_test_1.expect)(carrotRes.body.data.calories).toBe(2000);
|
|
315
|
+
return [2 /*return*/];
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}); });
|
|
319
|
+
(0, bun_test_1.it)("auth write transform fail", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
320
|
+
var agent, foodRes, carrots, writeRes;
|
|
321
|
+
return __generator(this, function (_a) {
|
|
322
|
+
switch (_a.label) {
|
|
323
|
+
case 0: return [4 /*yield*/, (0, tests_1.authAsUser)(app, "notAdmin")];
|
|
324
|
+
case 1:
|
|
325
|
+
agent = _a.sent();
|
|
326
|
+
return [4 /*yield*/, agent.get("/food")];
|
|
327
|
+
case 2:
|
|
328
|
+
foodRes = _a.sent();
|
|
329
|
+
carrots = foodRes.body.data.find(function (food) { return food.name === "Carrots"; });
|
|
330
|
+
return [4 /*yield*/, agent
|
|
331
|
+
.patch("/food/".concat(carrots.id))
|
|
332
|
+
.send({ created: "2020-01-01T00:00:00Z" })
|
|
333
|
+
.expect(403)];
|
|
334
|
+
case 3:
|
|
335
|
+
writeRes = _a.sent();
|
|
336
|
+
(0, bun_test_1.expect)(writeRes.body.title.includes("User of type auth cannot write fields: created")).toBe(true);
|
|
337
|
+
return [2 /*return*/];
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}); });
|
|
341
|
+
(0, bun_test_1.it)("anon read transform", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
342
|
+
var res;
|
|
343
|
+
return __generator(this, function (_a) {
|
|
344
|
+
switch (_a.label) {
|
|
345
|
+
case 0: return [4 /*yield*/, server.get("/food")];
|
|
346
|
+
case 1:
|
|
347
|
+
res = _a.sent();
|
|
348
|
+
(0, bun_test_1.expect)(res.body.data).toHaveLength(2);
|
|
349
|
+
(0, bun_test_1.expect)(res.body.data.find(function (f) { return f.name === "Spinach"; })).toBeDefined();
|
|
350
|
+
(0, bun_test_1.expect)(res.body.data.find(function (f) { return f.name === "Carrots"; })).toBeDefined();
|
|
351
|
+
return [2 /*return*/];
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
}); });
|
|
355
|
+
(0, bun_test_1.it)("anon write transform fails", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
356
|
+
var foodRes, carrots;
|
|
357
|
+
return __generator(this, function (_a) {
|
|
358
|
+
switch (_a.label) {
|
|
359
|
+
case 0: return [4 /*yield*/, server.get("/food")];
|
|
360
|
+
case 1:
|
|
361
|
+
foodRes = _a.sent();
|
|
362
|
+
carrots = foodRes.body.data.find(function (food) { return food.name === "Carrots"; });
|
|
363
|
+
return [4 /*yield*/, server.patch("/food/".concat(carrots.id)).send({ calories: 10 }).expect(403)];
|
|
364
|
+
case 2:
|
|
365
|
+
_a.sent();
|
|
366
|
+
return [2 /*return*/];
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
}); });
|
|
370
|
+
});
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function isValidObjectId(id: string): boolean;
|
|
2
|
+
export declare const timeout: (ms: number) => Promise<NodeJS.Timeout>;
|
|
3
|
+
/**
|
|
4
|
+
* Ensure that all mongoose models are set to strict mode.
|
|
5
|
+
* This validates that models will throw errors when attempting to set
|
|
6
|
+
* properties that aren't defined in the schema.
|
|
7
|
+
*
|
|
8
|
+
* @param ignoredModels - Array of model names to skip validation for
|
|
9
|
+
* @throws Error if any model is not set to strict mode or missing virtual settings
|
|
10
|
+
*/
|
|
11
|
+
export declare function checkModelsStrict(ignoredModels?: string[]): void;
|