@terreno/api 0.0.17 → 0.1.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/.claude/CLAUDE.local.md +204 -0
- package/.cursor/rules/00-root.mdc +338 -0
- package/.github/copilot-instructions.md +333 -0
- package/AGENTS.md +333 -0
- package/README.md +76 -7
- package/biome.jsonc +1 -1
- package/dist/api.d.ts +68 -1
- package/dist/api.js +140 -5
- package/dist/api.query.test.js +1 -1
- package/dist/api.test.js +222 -484
- package/dist/auth.js +3 -1
- package/dist/errors.js +15 -12
- package/dist/example.js +7 -7
- package/dist/expressServer.d.ts +8 -2
- package/dist/expressServer.js +8 -1
- package/dist/githubAuth.d.ts +64 -0
- package/dist/githubAuth.js +293 -0
- package/dist/githubAuth.test.d.ts +1 -0
- package/dist/githubAuth.test.js +351 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/logger.js +1 -1
- package/dist/middleware.js +1 -1
- package/dist/notifiers/googleChatNotifier.js +1 -1
- package/dist/notifiers/googleChatNotifier.test.js +1 -1
- package/dist/notifiers/slackNotifier.js +1 -1
- package/dist/notifiers/slackNotifier.test.js +1 -1
- package/dist/notifiers/zoomNotifier.js +1 -1
- package/dist/notifiers/zoomNotifier.test.js +1 -1
- package/dist/openApi.test.js +8 -5
- package/dist/openApiBuilder.d.ts +69 -1
- package/dist/openApiBuilder.js +109 -5
- package/dist/openApiValidator.d.ts +296 -0
- package/dist/openApiValidator.js +698 -0
- package/dist/openApiValidator.test.d.ts +1 -0
- package/dist/openApiValidator.test.js +346 -0
- package/dist/permissions.js +1 -1
- package/dist/plugins.test.js +3 -3
- package/dist/terrenoPlugin.d.ts +4 -0
- package/dist/terrenoPlugin.js +2 -0
- package/dist/tests/bunSetup.js +2 -2
- package/dist/tests.js +34 -24
- package/package.json +7 -2
- package/src/__snapshots__/openApi.test.ts.snap +399 -0
- package/src/__snapshots__/openApiBuilder.test.ts.snap +108 -0
- package/src/api.query.test.ts +1 -1
- package/src/api.test.ts +161 -374
- package/src/api.ts +210 -4
- package/src/auth.ts +3 -1
- package/src/errors.ts +15 -12
- package/src/example.ts +7 -7
- package/src/expressServer.ts +18 -2
- package/src/githubAuth.test.ts +223 -0
- package/src/githubAuth.ts +335 -0
- package/src/index.ts +3 -0
- package/src/logger.ts +1 -1
- package/src/middleware.ts +1 -1
- package/src/notifiers/googleChatNotifier.test.ts +1 -1
- package/src/notifiers/googleChatNotifier.ts +1 -1
- package/src/notifiers/slackNotifier.test.ts +1 -1
- package/src/notifiers/slackNotifier.ts +1 -1
- package/src/notifiers/zoomNotifier.test.ts +1 -1
- package/src/notifiers/zoomNotifier.ts +1 -1
- package/src/openApi.test.ts +8 -5
- package/src/openApiBuilder.ts +188 -15
- package/src/openApiValidator.test.ts +241 -0
- package/src/openApiValidator.ts +860 -0
- package/src/permissions.ts +1 -1
- package/src/plugins.test.ts +3 -3
- package/src/terrenoPlugin.ts +5 -0
- package/src/tests/bunSetup.ts +2 -2
- package/src/tests.ts +34 -24
- package/CLAUDE.md +0 -107
- package/dist/response.d.ts +0 -0
- package/dist/response.js +0 -1
- package/index.ts +0 -1
- package/src/response.ts +0 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
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);
|
|
24
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
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;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
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
|
+
var bun_test_1 = require("bun:test");
|
|
76
|
+
var api_1 = require("./api");
|
|
77
|
+
var auth_1 = require("./auth");
|
|
78
|
+
var openApiValidator_1 = require("./openApiValidator");
|
|
79
|
+
var permissions_1 = require("./permissions");
|
|
80
|
+
var tests_1 = require("./tests");
|
|
81
|
+
// RequiredModel has a clean schema that AJV can compile (no non-standard types).
|
|
82
|
+
// It has: name (String, required), about (String, optional)
|
|
83
|
+
var requiredRouterOptions = {
|
|
84
|
+
permissions: {
|
|
85
|
+
create: [permissions_1.Permissions.IsAuthenticated],
|
|
86
|
+
delete: [permissions_1.Permissions.IsAdmin],
|
|
87
|
+
list: [permissions_1.Permissions.IsAuthenticated],
|
|
88
|
+
read: [permissions_1.Permissions.IsAuthenticated],
|
|
89
|
+
update: [permissions_1.Permissions.IsAuthenticated],
|
|
90
|
+
},
|
|
91
|
+
queryFields: ["name"],
|
|
92
|
+
sort: "-name",
|
|
93
|
+
};
|
|
94
|
+
var setupFreshApp = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
95
|
+
var freshApp;
|
|
96
|
+
return __generator(this, function (_a) {
|
|
97
|
+
freshApp = (0, tests_1.getBaseServer)();
|
|
98
|
+
(0, auth_1.setupAuth)(freshApp, tests_1.UserModel);
|
|
99
|
+
(0, auth_1.addAuthRoutes)(freshApp, tests_1.UserModel);
|
|
100
|
+
return [2 /*return*/, freshApp];
|
|
101
|
+
});
|
|
102
|
+
}); };
|
|
103
|
+
(0, bun_test_1.describe)("openApiValidator", function () {
|
|
104
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
105
|
+
return __generator(this, function (_a) {
|
|
106
|
+
switch (_a.label) {
|
|
107
|
+
case 0:
|
|
108
|
+
(0, openApiValidator_1.resetOpenApiValidatorConfig)();
|
|
109
|
+
return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
110
|
+
case 1:
|
|
111
|
+
_a.sent();
|
|
112
|
+
return [4 /*yield*/, tests_1.RequiredModel.deleteMany({})];
|
|
113
|
+
case 2:
|
|
114
|
+
_a.sent();
|
|
115
|
+
return [2 /*return*/];
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}); });
|
|
119
|
+
(0, bun_test_1.afterEach)(function () {
|
|
120
|
+
(0, openApiValidator_1.resetOpenApiValidatorConfig)();
|
|
121
|
+
});
|
|
122
|
+
(0, bun_test_1.describe)("isConfigured flag", function () {
|
|
123
|
+
(0, bun_test_1.it)("is false by default", function () {
|
|
124
|
+
(0, bun_test_1.expect)((0, openApiValidator_1.isOpenApiValidatorConfigured)()).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
(0, bun_test_1.it)("becomes true after configureOpenApiValidator()", function () {
|
|
127
|
+
(0, openApiValidator_1.configureOpenApiValidator)();
|
|
128
|
+
(0, bun_test_1.expect)((0, openApiValidator_1.isOpenApiValidatorConfigured)()).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
(0, bun_test_1.it)("resets to false after resetOpenApiValidatorConfig()", function () {
|
|
131
|
+
(0, openApiValidator_1.configureOpenApiValidator)();
|
|
132
|
+
(0, bun_test_1.expect)((0, openApiValidator_1.isOpenApiValidatorConfigured)()).toBe(true);
|
|
133
|
+
(0, openApiValidator_1.resetOpenApiValidatorConfig)();
|
|
134
|
+
(0, bun_test_1.expect)((0, openApiValidator_1.isOpenApiValidatorConfigured)()).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
(0, bun_test_1.describe)("no-op when not configured", function () {
|
|
138
|
+
(0, bun_test_1.it)("does not strip or validate when not configured", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
139
|
+
var freshApp, admin, res;
|
|
140
|
+
return __generator(this, function (_a) {
|
|
141
|
+
switch (_a.label) {
|
|
142
|
+
case 0: return [4 /*yield*/, setupFreshApp()];
|
|
143
|
+
case 1:
|
|
144
|
+
freshApp = _a.sent();
|
|
145
|
+
freshApp.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, requiredRouterOptions));
|
|
146
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(freshApp, "admin")];
|
|
147
|
+
case 2:
|
|
148
|
+
admin = _a.sent();
|
|
149
|
+
return [4 /*yield*/, admin.post("/required").send({ name: "Apple" }).expect(201)];
|
|
150
|
+
case 3:
|
|
151
|
+
res = _a.sent();
|
|
152
|
+
(0, bun_test_1.expect)(res.body.data.name).toBe("Apple");
|
|
153
|
+
return [2 /*return*/];
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}); });
|
|
157
|
+
});
|
|
158
|
+
(0, bun_test_1.describe)("active after configuration", function () {
|
|
159
|
+
(0, bun_test_1.it)("strips extra properties when removeAdditional is true", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
160
|
+
var freshApp, admin, res;
|
|
161
|
+
return __generator(this, function (_a) {
|
|
162
|
+
switch (_a.label) {
|
|
163
|
+
case 0:
|
|
164
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: true });
|
|
165
|
+
return [4 /*yield*/, setupFreshApp()];
|
|
166
|
+
case 1:
|
|
167
|
+
freshApp = _a.sent();
|
|
168
|
+
freshApp.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, requiredRouterOptions));
|
|
169
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(freshApp, "admin")];
|
|
170
|
+
case 2:
|
|
171
|
+
admin = _a.sent();
|
|
172
|
+
return [4 /*yield*/, admin
|
|
173
|
+
.post("/required")
|
|
174
|
+
.send({ fakeField: "this should be stripped", name: "Apple" })
|
|
175
|
+
.expect(201)];
|
|
176
|
+
case 3:
|
|
177
|
+
res = _a.sent();
|
|
178
|
+
(0, bun_test_1.expect)(res.body.data.name).toBe("Apple");
|
|
179
|
+
(0, bun_test_1.expect)(res.body.data.fakeField).toBeUndefined();
|
|
180
|
+
return [2 /*return*/];
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}); });
|
|
184
|
+
(0, bun_test_1.it)("rejects missing required fields", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
185
|
+
var freshApp, admin, res;
|
|
186
|
+
return __generator(this, function (_a) {
|
|
187
|
+
switch (_a.label) {
|
|
188
|
+
case 0:
|
|
189
|
+
(0, openApiValidator_1.configureOpenApiValidator)();
|
|
190
|
+
return [4 /*yield*/, setupFreshApp()];
|
|
191
|
+
case 1:
|
|
192
|
+
freshApp = _a.sent();
|
|
193
|
+
freshApp.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, requiredRouterOptions));
|
|
194
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(freshApp, "admin")];
|
|
195
|
+
case 2:
|
|
196
|
+
admin = _a.sent();
|
|
197
|
+
return [4 /*yield*/, admin.post("/required").send({ about: "no name" }).expect(400)];
|
|
198
|
+
case 3:
|
|
199
|
+
res = _a.sent();
|
|
200
|
+
(0, bun_test_1.expect)(res.body.title).toBe("Request validation failed");
|
|
201
|
+
return [2 /*return*/];
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}); });
|
|
205
|
+
});
|
|
206
|
+
(0, bun_test_1.describe)("onAdditionalPropertiesRemoved hook", function () {
|
|
207
|
+
(0, bun_test_1.it)("fires callback with removed property names", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
208
|
+
var removedProps, freshApp, admin;
|
|
209
|
+
return __generator(this, function (_a) {
|
|
210
|
+
switch (_a.label) {
|
|
211
|
+
case 0:
|
|
212
|
+
removedProps = [];
|
|
213
|
+
(0, openApiValidator_1.configureOpenApiValidator)({
|
|
214
|
+
onAdditionalPropertiesRemoved: function (props) {
|
|
215
|
+
removedProps.push.apply(removedProps, __spreadArray([], __read(props), false));
|
|
216
|
+
},
|
|
217
|
+
removeAdditional: true,
|
|
218
|
+
});
|
|
219
|
+
return [4 /*yield*/, setupFreshApp()];
|
|
220
|
+
case 1:
|
|
221
|
+
freshApp = _a.sent();
|
|
222
|
+
freshApp.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, requiredRouterOptions));
|
|
223
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(freshApp, "admin")];
|
|
224
|
+
case 2:
|
|
225
|
+
admin = _a.sent();
|
|
226
|
+
return [4 /*yield*/, admin
|
|
227
|
+
.post("/required")
|
|
228
|
+
.send({ extraA: "stripped", extraB: "also stripped", name: "Apple" })
|
|
229
|
+
.expect(201)];
|
|
230
|
+
case 3:
|
|
231
|
+
_a.sent();
|
|
232
|
+
(0, bun_test_1.expect)(removedProps).toContain("extraA");
|
|
233
|
+
(0, bun_test_1.expect)(removedProps).toContain("extraB");
|
|
234
|
+
return [2 /*return*/];
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}); });
|
|
238
|
+
});
|
|
239
|
+
(0, bun_test_1.describe)("per-route validation: false override", function () {
|
|
240
|
+
(0, bun_test_1.it)("skips validation when validation is false", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
241
|
+
var freshApp, admin, res;
|
|
242
|
+
return __generator(this, function (_a) {
|
|
243
|
+
switch (_a.label) {
|
|
244
|
+
case 0:
|
|
245
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: true });
|
|
246
|
+
return [4 /*yield*/, setupFreshApp()];
|
|
247
|
+
case 1:
|
|
248
|
+
freshApp = _a.sent();
|
|
249
|
+
freshApp.use("/required", (0, api_1.modelRouter)(tests_1.RequiredModel, __assign(__assign({}, requiredRouterOptions), { validation: false })));
|
|
250
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(freshApp, "admin")];
|
|
251
|
+
case 2:
|
|
252
|
+
admin = _a.sent();
|
|
253
|
+
return [4 /*yield*/, admin
|
|
254
|
+
.post("/required")
|
|
255
|
+
.send({ fakeField: "not stripped", name: "Apple" })
|
|
256
|
+
.expect(201)];
|
|
257
|
+
case 3:
|
|
258
|
+
res = _a.sent();
|
|
259
|
+
(0, bun_test_1.expect)(res.body.data.name).toBe("Apple");
|
|
260
|
+
return [2 /*return*/];
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}); });
|
|
264
|
+
});
|
|
265
|
+
(0, bun_test_1.describe)("sanitization of non-standard mongoose-to-swagger types", function () {
|
|
266
|
+
(0, bun_test_1.it)("validates models with ObjectId and DateOnly fields after sanitization", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
267
|
+
var freshApp, admin, res;
|
|
268
|
+
return __generator(this, function (_a) {
|
|
269
|
+
switch (_a.label) {
|
|
270
|
+
case 0:
|
|
271
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: true });
|
|
272
|
+
return [4 /*yield*/, setupFreshApp()];
|
|
273
|
+
case 1:
|
|
274
|
+
freshApp = _a.sent();
|
|
275
|
+
freshApp.use("/food", (0, api_1.modelRouter)(tests_1.FoodModel, {
|
|
276
|
+
permissions: {
|
|
277
|
+
create: [permissions_1.Permissions.IsAuthenticated],
|
|
278
|
+
delete: [permissions_1.Permissions.IsAdmin],
|
|
279
|
+
list: [permissions_1.Permissions.IsAuthenticated],
|
|
280
|
+
read: [permissions_1.Permissions.IsAuthenticated],
|
|
281
|
+
update: [permissions_1.Permissions.IsAuthenticated],
|
|
282
|
+
},
|
|
283
|
+
queryFields: ["name", "calories", "hidden"],
|
|
284
|
+
sort: "-created",
|
|
285
|
+
}));
|
|
286
|
+
return [4 /*yield*/, (0, tests_1.authAsUser)(freshApp, "admin")];
|
|
287
|
+
case 2:
|
|
288
|
+
admin = _a.sent();
|
|
289
|
+
return [4 /*yield*/, admin
|
|
290
|
+
.post("/food")
|
|
291
|
+
.send({ calories: 100, likesIds: [], name: "Apple", source: { name: "Test" } })
|
|
292
|
+
.expect(201)];
|
|
293
|
+
case 3:
|
|
294
|
+
res = _a.sent();
|
|
295
|
+
(0, bun_test_1.expect)(res.body.data.name).toBe("Apple");
|
|
296
|
+
return [2 /*return*/];
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}); });
|
|
300
|
+
});
|
|
301
|
+
(0, bun_test_1.describe)("buildQuerySchemaFromFields", function () {
|
|
302
|
+
(0, bun_test_1.it)("always includes limit, page, and sort", function () {
|
|
303
|
+
var schema = (0, openApiValidator_1.buildQuerySchemaFromFields)(tests_1.FoodModel, []);
|
|
304
|
+
(0, bun_test_1.expect)(schema.limit).toBeDefined();
|
|
305
|
+
(0, bun_test_1.expect)(schema.page).toBeDefined();
|
|
306
|
+
(0, bun_test_1.expect)(schema.sort).toBeDefined();
|
|
307
|
+
});
|
|
308
|
+
(0, bun_test_1.it)("includes queryFields from model schema", function () {
|
|
309
|
+
var schema = (0, openApiValidator_1.buildQuerySchemaFromFields)(tests_1.FoodModel, ["name", "calories"]);
|
|
310
|
+
(0, bun_test_1.expect)(schema.name).toBeDefined();
|
|
311
|
+
(0, bun_test_1.expect)(schema.calories).toBeDefined();
|
|
312
|
+
(0, bun_test_1.expect)(schema.hidden).toBeUndefined();
|
|
313
|
+
});
|
|
314
|
+
(0, bun_test_1.it)("marks query fields as not required", function () {
|
|
315
|
+
var schema = (0, openApiValidator_1.buildQuerySchemaFromFields)(tests_1.FoodModel, ["name"]);
|
|
316
|
+
(0, bun_test_1.expect)(schema.name.required).toBe(false);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
(0, bun_test_1.describe)("validateRequestBody middleware", function () {
|
|
320
|
+
(0, bun_test_1.it)("is a no-op when not configured", function () {
|
|
321
|
+
(0, openApiValidator_1.resetOpenApiValidatorConfig)();
|
|
322
|
+
var middleware = (0, openApiValidator_1.validateRequestBody)({
|
|
323
|
+
name: { required: true, type: "string" },
|
|
324
|
+
});
|
|
325
|
+
var nextCalled = false;
|
|
326
|
+
var req = { body: {} };
|
|
327
|
+
var res = {};
|
|
328
|
+
var next = function () {
|
|
329
|
+
nextCalled = true;
|
|
330
|
+
};
|
|
331
|
+
middleware(req, res, next);
|
|
332
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
333
|
+
});
|
|
334
|
+
(0, bun_test_1.it)("validates when configured", function () {
|
|
335
|
+
(0, openApiValidator_1.configureOpenApiValidator)();
|
|
336
|
+
var middleware = (0, openApiValidator_1.validateRequestBody)({
|
|
337
|
+
name: { required: true, type: "string" },
|
|
338
|
+
});
|
|
339
|
+
var req = { body: {}, method: "POST", path: "/test" };
|
|
340
|
+
var res = {};
|
|
341
|
+
(0, bun_test_1.expect)(function () {
|
|
342
|
+
middleware(req, res, function () { });
|
|
343
|
+
}).toThrow();
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
});
|
package/dist/permissions.js
CHANGED
|
@@ -87,7 +87,7 @@ exports.Permissions = exports.OwnerQueryFilter = void 0;
|
|
|
87
87
|
exports.checkPermissions = checkPermissions;
|
|
88
88
|
exports.permissionMiddleware = permissionMiddleware;
|
|
89
89
|
// Defaults closed
|
|
90
|
-
var Sentry = __importStar(require("@sentry/
|
|
90
|
+
var Sentry = __importStar(require("@sentry/bun"));
|
|
91
91
|
var mongoose_1 = __importDefault(require("mongoose"));
|
|
92
92
|
var api_1 = require("./api");
|
|
93
93
|
var errors_1 = require("./errors");
|
package/dist/plugins.test.js
CHANGED
|
@@ -64,9 +64,9 @@ var permissions_1 = require("./permissions");
|
|
|
64
64
|
var plugins_1 = require("./plugins");
|
|
65
65
|
var tests_1 = require("./tests");
|
|
66
66
|
var stuffSchema = new mongoose_1.Schema({
|
|
67
|
-
date: plugins_1.DateOnly,
|
|
68
|
-
name: String,
|
|
69
|
-
ownerId: String,
|
|
67
|
+
date: { description: "The date associated with this item", type: plugins_1.DateOnly },
|
|
68
|
+
name: { description: "The name of the item", type: String },
|
|
69
|
+
ownerId: { description: "The user who owns this item", type: String },
|
|
70
70
|
});
|
|
71
71
|
stuffSchema.plugin(plugins_1.isDeletedPlugin);
|
|
72
72
|
stuffSchema.plugin(plugins_1.findOneOrNone);
|
package/dist/tests/bunSetup.js
CHANGED
|
@@ -162,8 +162,8 @@ captureConsoleMethod("debug");
|
|
|
162
162
|
(0, bun_test_1.afterEach)(function () {
|
|
163
163
|
logs = [];
|
|
164
164
|
});
|
|
165
|
-
// Mock @sentry/
|
|
166
|
-
bun_test_1.mock.module("@sentry/
|
|
165
|
+
// Mock @sentry/bun module
|
|
166
|
+
bun_test_1.mock.module("@sentry/bun", function () {
|
|
167
167
|
var mockFn = function () { return (0, bun_test_1.mock)(function () { }); };
|
|
168
168
|
// Mock Scope
|
|
169
169
|
var mockScope = {
|
package/dist/tests.js
CHANGED
|
@@ -99,10 +99,10 @@ var supertest_1 = __importDefault(require("supertest"));
|
|
|
99
99
|
var logger_1 = require("./logger");
|
|
100
100
|
var plugins_1 = require("./plugins");
|
|
101
101
|
var userSchema = new mongoose_1.Schema({
|
|
102
|
-
admin: { default: false, type: Boolean },
|
|
103
|
-
age: Number,
|
|
104
|
-
name: String,
|
|
105
|
-
username: String,
|
|
102
|
+
admin: { default: false, description: "Whether the user has admin privileges", type: Boolean },
|
|
103
|
+
age: { description: "The user's age", type: Number },
|
|
104
|
+
name: { description: "The user's display name", type: String },
|
|
105
|
+
username: { description: "The user's username", type: String },
|
|
106
106
|
});
|
|
107
107
|
userSchema.plugin(passport_local_mongoose_1.default, {
|
|
108
108
|
attemptsField: "attempts",
|
|
@@ -126,55 +126,65 @@ userSchema.methods.postCreate = function (body) {
|
|
|
126
126
|
};
|
|
127
127
|
exports.UserModel = (0, mongoose_1.model)("User", userSchema);
|
|
128
128
|
var superUserSchema = new mongoose_1.Schema({
|
|
129
|
-
superTitle: { required: true, type: String },
|
|
129
|
+
superTitle: { description: "The super user's title", required: true, type: String },
|
|
130
130
|
});
|
|
131
131
|
exports.SuperUserModel = exports.UserModel.discriminator("SuperUser", superUserSchema);
|
|
132
132
|
var staffUserSchema = new mongoose_1.Schema({
|
|
133
|
-
department: {
|
|
133
|
+
department: {
|
|
134
|
+
description: "The department the staff member belongs to",
|
|
135
|
+
required: true,
|
|
136
|
+
type: String,
|
|
137
|
+
},
|
|
134
138
|
});
|
|
135
139
|
exports.StaffUserModel = exports.UserModel.discriminator("Staff", staffUserSchema);
|
|
136
140
|
var foodCategorySchema = new mongoose_1.Schema({
|
|
137
|
-
name: String,
|
|
138
|
-
show: Boolean,
|
|
141
|
+
name: { description: "The name of the food category", type: String },
|
|
142
|
+
show: { description: "Whether this category is visible", type: Boolean },
|
|
139
143
|
}, { timestamps: { createdAt: "created", updatedAt: "updated" } });
|
|
140
144
|
var likesSchema = new mongoose_1.Schema({
|
|
141
|
-
likes: Boolean,
|
|
142
|
-
userId: { ref: "User", type: "ObjectId" },
|
|
145
|
+
likes: { description: "Whether the user liked the item", type: Boolean },
|
|
146
|
+
userId: { description: "The user who liked the item", ref: "User", type: "ObjectId" },
|
|
143
147
|
});
|
|
144
148
|
var foodSchema = new mongoose_1.Schema({
|
|
145
|
-
calories: Number,
|
|
146
|
-
categories: [foodCategorySchema],
|
|
147
|
-
created: Date,
|
|
149
|
+
calories: { description: "Number of calories in the food", type: Number },
|
|
150
|
+
categories: { description: "Categories this food belongs to", type: [foodCategorySchema] },
|
|
151
|
+
created: { description: "When this food was created", type: Date },
|
|
148
152
|
eatenBy: [
|
|
149
153
|
{
|
|
154
|
+
description: "Users who have eaten this food",
|
|
150
155
|
ref: "User",
|
|
151
156
|
required: true,
|
|
152
157
|
type: mongoose_1.Schema.Types.ObjectId,
|
|
153
158
|
},
|
|
154
159
|
],
|
|
155
|
-
expiration: plugins_1.DateOnly,
|
|
156
|
-
hidden: {
|
|
160
|
+
expiration: { description: "Expiration date of the food", type: plugins_1.DateOnly },
|
|
161
|
+
hidden: {
|
|
162
|
+
default: false,
|
|
163
|
+
description: "Whether this food is hidden from listings",
|
|
164
|
+
type: Boolean,
|
|
165
|
+
},
|
|
157
166
|
lastEatenWith: {
|
|
167
|
+
description: "Map of user names to dates they last ate this food with",
|
|
158
168
|
of: Date,
|
|
159
169
|
type: Map,
|
|
160
170
|
},
|
|
161
|
-
likesIds: { required: true, type: [likesSchema] },
|
|
162
|
-
name: String,
|
|
163
|
-
ownerId: { ref: "User", type: "ObjectId" },
|
|
171
|
+
likesIds: { description: "User likes for this food", required: true, type: [likesSchema] },
|
|
172
|
+
name: { description: "The name of the food", type: String },
|
|
173
|
+
ownerId: { description: "The user who owns this food entry", ref: "User", type: "ObjectId" },
|
|
164
174
|
source: {
|
|
165
|
-
dateAdded: String,
|
|
166
|
-
href: String,
|
|
167
|
-
name: String,
|
|
175
|
+
dateAdded: { description: "When the source was added", type: String },
|
|
176
|
+
href: { description: "URL of the source", type: String },
|
|
177
|
+
name: { description: "Name of the source", type: String },
|
|
168
178
|
},
|
|
169
|
-
tags: [String],
|
|
179
|
+
tags: { description: "Tags associated with this food", type: [String] },
|
|
170
180
|
}, { strict: "throw", toJSON: { virtuals: true }, toObject: { virtuals: true } });
|
|
171
181
|
foodSchema.virtual("description").get(function () {
|
|
172
182
|
return "".concat(this.name, " has ").concat(this.calories, " calories");
|
|
173
183
|
});
|
|
174
184
|
exports.FoodModel = (0, mongoose_1.model)("Food", foodSchema);
|
|
175
185
|
var requiredSchema = new mongoose_1.Schema({
|
|
176
|
-
about: String,
|
|
177
|
-
name: { required: true, type: String },
|
|
186
|
+
about: { description: "Information about the item", type: String },
|
|
187
|
+
name: { description: "The name of the item", required: true, type: String },
|
|
178
188
|
});
|
|
179
189
|
exports.RequiredModel = (0, mongoose_1.model)("Required", requiredSchema);
|
|
180
190
|
function getBaseServer() {
|
package/package.json
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
"url": "https://github.com/FlourishHealth/terreno/issues"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@sentry/
|
|
7
|
+
"@sentry/bun": "^10.25.0",
|
|
8
8
|
"@sentry/profiling-node": "^10.25.0",
|
|
9
9
|
"@types/qs": "^6.14.0",
|
|
10
10
|
"@wesleytodd/openapi": "^1.1.0",
|
|
11
|
+
"ajv": "^8.17.1",
|
|
12
|
+
"ajv-formats": "^3.0.1",
|
|
11
13
|
"axios": "^1.13.2",
|
|
12
14
|
"cors": "^2.8.5",
|
|
13
15
|
"cron": "^4.3.4",
|
|
@@ -23,6 +25,7 @@
|
|
|
23
25
|
"on-finished": "^2.3.0",
|
|
24
26
|
"passport": "^0.7.0",
|
|
25
27
|
"passport-anonymous": "^1.0.1",
|
|
28
|
+
"passport-github2": "^0.1.12",
|
|
26
29
|
"passport-jwt": "^4.0.0",
|
|
27
30
|
"passport-local": "^1.0.0",
|
|
28
31
|
"passport-local-mongoose": "^9.0.1",
|
|
@@ -44,6 +47,7 @@
|
|
|
44
47
|
"@types/on-finished": "^2.3.4",
|
|
45
48
|
"@types/passport": "^1.0.17",
|
|
46
49
|
"@types/passport-anonymous": "^1.0.5",
|
|
50
|
+
"@types/passport-github2": "^1.2.9",
|
|
47
51
|
"@types/passport-jwt": "^4.0.1",
|
|
48
52
|
"@types/passport-local": "^1.0.38",
|
|
49
53
|
"@types/sinon": "^17.0.4",
|
|
@@ -75,6 +79,7 @@
|
|
|
75
79
|
},
|
|
76
80
|
"scripts": {
|
|
77
81
|
"compile": "bun tsc",
|
|
82
|
+
"compile:watch": "bun tsc -w",
|
|
78
83
|
"dev": "bun tsc -w",
|
|
79
84
|
"docs": "typedoc --out docs src/index.ts",
|
|
80
85
|
"lint": "biome check ./src",
|
|
@@ -85,5 +90,5 @@
|
|
|
85
90
|
"updateSnapshot": "bun test --update-snapshots"
|
|
86
91
|
},
|
|
87
92
|
"types": "dist/index.d.ts",
|
|
88
|
-
"version": "0.0
|
|
93
|
+
"version": "0.1.0"
|
|
89
94
|
}
|