@terreno/api 0.20.1 → 0.21.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/.ai/guidelines/core.md +71 -0
- package/.ai/skills/mongoose-schema-safety/SKILL.md +143 -0
- package/README.md +54 -1
- package/dist/__tests__/versionCheckPlugin.test.js +29 -7
- package/dist/actions.openApi.test.js +13 -11
- package/dist/api.js +98 -11
- package/dist/api.query.test.js +31 -1
- package/dist/api.test.js +211 -0
- package/dist/auth.test.js +10 -10
- package/dist/betterAuth.d.ts +1 -1
- package/dist/consentApp.test.js +1 -0
- package/dist/example.js +4 -4
- package/dist/expressServer.d.ts +0 -22
- package/dist/expressServer.js +1 -125
- package/dist/expressServer.test.js +90 -91
- package/dist/githubAuth.test.js +22 -22
- package/dist/logger.d.ts +154 -0
- package/dist/logger.js +445 -26
- package/dist/logger.test.js +435 -0
- package/dist/middleware.d.ts +7 -0
- package/dist/middleware.js +58 -1
- package/dist/middleware.test.js +159 -0
- package/dist/openApi.test.js +10 -17
- package/dist/openApiBuilder.test.js +18 -10
- package/dist/realtime/changeStreamWatcher.d.ts +4 -4
- package/dist/realtime/changeStreamWatcher.js +2 -4
- package/dist/realtime/queryMatcher.d.ts +1 -1
- package/dist/realtime/queryMatcher.js +39 -14
- package/dist/realtime/types.d.ts +3 -3
- package/dist/requestContext.d.ts +61 -0
- package/dist/requestContext.js +74 -0
- package/dist/secretProviders.test.js +335 -0
- package/dist/terrenoApp.d.ts +27 -15
- package/dist/terrenoApp.js +24 -14
- package/dist/terrenoApp.test.js +52 -0
- package/dist/tests/bunSetup.js +61 -7
- package/dist/tests.js +27 -4
- package/package.json +1 -1
- package/src/__tests__/versionCheckPlugin.test.ts +43 -15
- package/src/actions.openApi.test.ts +12 -10
- package/src/api.query.test.ts +24 -1
- package/src/api.test.ts +169 -0
- package/src/api.ts +71 -0
- package/src/auth.test.ts +10 -10
- package/src/betterAuth.ts +1 -1
- package/src/consentApp.test.ts +1 -0
- package/src/example.ts +4 -4
- package/src/expressServer.test.ts +82 -85
- package/src/expressServer.ts +1 -213
- package/src/githubAuth.test.ts +22 -22
- package/src/logger.test.ts +466 -1
- package/src/logger.ts +477 -14
- package/src/middleware.test.ts +74 -2
- package/src/middleware.ts +57 -0
- package/src/openApi.test.ts +10 -17
- package/src/openApiBuilder.test.ts +18 -10
- package/src/realtime/changeStreamWatcher.ts +15 -10
- package/src/realtime/queryMatcher.ts +54 -27
- package/src/realtime/types.ts +4 -4
- package/src/requestContext.ts +86 -0
- package/src/secretProviders.test.ts +219 -1
- package/src/terrenoApp.test.ts +38 -0
- package/src/terrenoApp.ts +37 -15
- package/src/tests/bunSetup.ts +16 -3
- package/src/tests.ts +17 -4
package/dist/terrenoApp.test.js
CHANGED
|
@@ -104,6 +104,7 @@ var mongoose_1 = __importStar(require("mongoose"));
|
|
|
104
104
|
var supertest_1 = __importDefault(require("supertest"));
|
|
105
105
|
var api_1 = require("./api");
|
|
106
106
|
var configurationPlugin_1 = require("./configurationPlugin");
|
|
107
|
+
var errors_1 = require("./errors");
|
|
107
108
|
var permissions_1 = require("./permissions");
|
|
108
109
|
var plugins_1 = require("./plugins");
|
|
109
110
|
var terrenoApp_1 = require("./terrenoApp");
|
|
@@ -125,6 +126,24 @@ var typedUserModel = tests_1.UserModel;
|
|
|
125
126
|
}).build();
|
|
126
127
|
(0, bun_test_1.expect)(app).toBeDefined();
|
|
127
128
|
});
|
|
129
|
+
(0, bun_test_1.it)("does not add requestId to GET /openapi.json document bodies", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
130
|
+
var app, res;
|
|
131
|
+
return __generator(this, function (_d) {
|
|
132
|
+
switch (_d.label) {
|
|
133
|
+
case 0:
|
|
134
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
135
|
+
skipListen: true,
|
|
136
|
+
userModel: typedUserModel,
|
|
137
|
+
}).build();
|
|
138
|
+
return [4 /*yield*/, (0, supertest_1.default)(app).get("/openapi.json").expect(200)];
|
|
139
|
+
case 1:
|
|
140
|
+
res = _d.sent();
|
|
141
|
+
(0, bun_test_1.expect)(res.body.openapi).toBe("3.0.0");
|
|
142
|
+
(0, bun_test_1.expect)(res.body.requestId).toBeUndefined();
|
|
143
|
+
return [2 /*return*/];
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}); });
|
|
128
147
|
(0, bun_test_1.it)("creates server with custom corsOrigin", function () {
|
|
129
148
|
var app = new terrenoApp_1.TerrenoApp({
|
|
130
149
|
corsOrigin: "https://example.com",
|
|
@@ -196,6 +215,7 @@ var typedUserModel = tests_1.UserModel;
|
|
|
196
215
|
res = _d.sent();
|
|
197
216
|
(0, bun_test_1.expect)(res.body.data).toHaveLength(1);
|
|
198
217
|
(0, bun_test_1.expect)(res.body.data[0].name).toBe("Apple");
|
|
218
|
+
(0, bun_test_1.expect)(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
199
219
|
return [2 /*return*/];
|
|
200
220
|
}
|
|
201
221
|
});
|
|
@@ -301,6 +321,7 @@ var typedUserModel = tests_1.UserModel;
|
|
|
301
321
|
case 2:
|
|
302
322
|
res = _d.sent();
|
|
303
323
|
(0, bun_test_1.expect)(res.status).toBe(200);
|
|
324
|
+
(0, bun_test_1.expect)(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
304
325
|
return [2 /*return*/];
|
|
305
326
|
}
|
|
306
327
|
});
|
|
@@ -356,6 +377,37 @@ var typedUserModel = tests_1.UserModel;
|
|
|
356
377
|
case 1:
|
|
357
378
|
res = _d.sent();
|
|
358
379
|
(0, bun_test_1.expect)(res.status).toBe(500);
|
|
380
|
+
(0, bun_test_1.expect)(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
381
|
+
(0, bun_test_1.expect)(res.body.status).toBe(500);
|
|
382
|
+
(0, bun_test_1.expect)(res.body.title).toBe("Internal server error");
|
|
383
|
+
return [2 /*return*/];
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}); });
|
|
387
|
+
(0, bun_test_1.it)("adds requestId to APIError JSON responses", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
388
|
+
var plugin, app, res;
|
|
389
|
+
return __generator(this, function (_d) {
|
|
390
|
+
switch (_d.label) {
|
|
391
|
+
case 0:
|
|
392
|
+
plugin = {
|
|
393
|
+
register: function (pluginApp) {
|
|
394
|
+
pluginApp.get("/api-error-route", function () {
|
|
395
|
+
throw new errors_1.APIError({ status: 400, title: "Bad request test" });
|
|
396
|
+
});
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
app = new terrenoApp_1.TerrenoApp({
|
|
400
|
+
skipListen: true,
|
|
401
|
+
userModel: typedUserModel,
|
|
402
|
+
})
|
|
403
|
+
.register(plugin)
|
|
404
|
+
.build();
|
|
405
|
+
return [4 /*yield*/, (0, supertest_1.default)(app).get("/api-error-route").set("X-Request-ID", "api-err-rid")];
|
|
406
|
+
case 1:
|
|
407
|
+
res = _d.sent();
|
|
408
|
+
(0, bun_test_1.expect)(res.status).toBe(400);
|
|
409
|
+
(0, bun_test_1.expect)(res.body.requestId).toBe("api-err-rid");
|
|
410
|
+
(0, bun_test_1.expect)(res.body.title).toBe("Bad request test");
|
|
359
411
|
return [2 /*return*/];
|
|
360
412
|
}
|
|
361
413
|
});
|
package/dist/tests/bunSetup.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
36
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
37
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -72,16 +105,32 @@ var winston_1 = __importDefault(require("winston"));
|
|
|
72
105
|
var expressServer_1 = require("../expressServer");
|
|
73
106
|
var logger_1 = require("../logger");
|
|
74
107
|
var shouldConnectToTestDb = process.env.BUN_TEST_DISABLE_DB !== "true";
|
|
108
|
+
var defaultLocalMongoUri = "mongodb://127.0.0.1/terreno?&connectTimeoutMS=360000";
|
|
109
|
+
/** When set by {@link TERRENO_TEST_USE_MEMORY_MONGO}, holds the server to stop in afterAll. */
|
|
110
|
+
var memoryMongo;
|
|
75
111
|
// Connect to MongoDB once for all tests
|
|
76
112
|
if (shouldConnectToTestDb) {
|
|
77
113
|
(0, bun_test_1.beforeAll)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
114
|
+
var uri, MongoMemoryServer, connectUri;
|
|
115
|
+
var _a;
|
|
116
|
+
return __generator(this, function (_b) {
|
|
117
|
+
switch (_b.label) {
|
|
118
|
+
case 0:
|
|
119
|
+
uri = (_a = process.env.TERRENO_TEST_MONGODB_URI) === null || _a === void 0 ? void 0 : _a.trim();
|
|
120
|
+
if (!(!uri && process.env.TERRENO_TEST_USE_MEMORY_MONGO === "true")) return [3 /*break*/, 3];
|
|
121
|
+
return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("mongodb-memory-server")); })];
|
|
83
122
|
case 1:
|
|
84
|
-
|
|
123
|
+
MongoMemoryServer = (_b.sent()).MongoMemoryServer;
|
|
124
|
+
return [4 /*yield*/, MongoMemoryServer.create()];
|
|
125
|
+
case 2:
|
|
126
|
+
memoryMongo = _b.sent();
|
|
127
|
+
uri = memoryMongo.getUri();
|
|
128
|
+
_b.label = 3;
|
|
129
|
+
case 3:
|
|
130
|
+
connectUri = uri !== null && uri !== void 0 ? uri : defaultLocalMongoUri;
|
|
131
|
+
return [4 /*yield*/, mongoose_1.default.connect(connectUri).catch(logger_1.logger.catch)];
|
|
132
|
+
case 4:
|
|
133
|
+
_b.sent();
|
|
85
134
|
return [2 /*return*/];
|
|
86
135
|
}
|
|
87
136
|
});
|
|
@@ -95,7 +144,12 @@ if (shouldConnectToTestDb) {
|
|
|
95
144
|
case 0: return [4 /*yield*/, mongoose_1.default.connection.close()];
|
|
96
145
|
case 1:
|
|
97
146
|
_a.sent();
|
|
98
|
-
return [
|
|
147
|
+
if (!memoryMongo) return [3 /*break*/, 3];
|
|
148
|
+
return [4 /*yield*/, memoryMongo.stop()];
|
|
149
|
+
case 2:
|
|
150
|
+
_a.sent();
|
|
151
|
+
_a.label = 3;
|
|
152
|
+
case 3: return [2 /*return*/];
|
|
99
153
|
}
|
|
100
154
|
});
|
|
101
155
|
}); });
|
package/dist/tests.js
CHANGED
|
@@ -192,7 +192,7 @@ var getBaseServer = function () {
|
|
|
192
192
|
app.set("query parser", function (str) { return qs_1.default.parse(str, { arrayLimit: 200 }); });
|
|
193
193
|
// Express 5 defaults to 'simple' query parser (Node querystring) which doesn't
|
|
194
194
|
// support nested bracket notation like name[$regex]=Green. Use qs to match
|
|
195
|
-
// what
|
|
195
|
+
// what TerrenoApp.build() configures.
|
|
196
196
|
app.set("query parser", function (str) { return qs_1.default.parse(str, { arrayLimit: 200 }); });
|
|
197
197
|
// Record mount paths on layers for Express 5 → OpenAPI compat
|
|
198
198
|
(0, openApiCompat_1.patchAppUse)(app);
|
|
@@ -230,13 +230,36 @@ var authAsUser = function (app, type) { return __awaiter(void 0, void 0, void 0,
|
|
|
230
230
|
});
|
|
231
231
|
}); };
|
|
232
232
|
exports.authAsUser = authAsUser;
|
|
233
|
+
var defaultTestMongoUri = "mongodb://127.0.0.1/terreno?&connectTimeoutMS=360000";
|
|
234
|
+
/** Ensures Mongoose is connected without replacing an existing test connection (e.g. MongoMemoryServer from bunSetup). */
|
|
235
|
+
var ensureTestMongooseConnected = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
236
|
+
var uri;
|
|
237
|
+
var _a;
|
|
238
|
+
return __generator(this, function (_b) {
|
|
239
|
+
switch (_b.label) {
|
|
240
|
+
case 0:
|
|
241
|
+
if (mongoose_1.default.connection.readyState === 1) {
|
|
242
|
+
return [2 /*return*/];
|
|
243
|
+
}
|
|
244
|
+
if (!(mongoose_1.default.connection.readyState === 2)) return [3 /*break*/, 2];
|
|
245
|
+
return [4 /*yield*/, mongoose_1.default.connection.asPromise()];
|
|
246
|
+
case 1:
|
|
247
|
+
_b.sent();
|
|
248
|
+
return [2 /*return*/];
|
|
249
|
+
case 2:
|
|
250
|
+
uri = ((_a = process.env.TERRENO_TEST_MONGODB_URI) === null || _a === void 0 ? void 0 : _a.trim()) || defaultTestMongoUri;
|
|
251
|
+
return [4 /*yield*/, mongoose_1.default.connect(uri).catch(logger_1.logger.catch)];
|
|
252
|
+
case 3:
|
|
253
|
+
_b.sent();
|
|
254
|
+
return [2 /*return*/];
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}); };
|
|
233
258
|
var setupDb = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
234
259
|
var _a, notAdmin, admin, adminOther, error_1;
|
|
235
260
|
return __generator(this, function (_b) {
|
|
236
261
|
switch (_b.label) {
|
|
237
|
-
case 0: return [4 /*yield*/,
|
|
238
|
-
.connect("mongodb://127.0.0.1/terreno?&connectTimeoutMS=360000")
|
|
239
|
-
.catch(logger_1.logger.catch)];
|
|
262
|
+
case 0: return [4 /*yield*/, ensureTestMongooseConnected()];
|
|
240
263
|
case 1:
|
|
241
264
|
_b.sent();
|
|
242
265
|
process.env.REFRESH_TOKEN_SECRET = "refresh_secret";
|
package/package.json
CHANGED
|
@@ -30,19 +30,27 @@ describe("VersionCheckPlugin", () => {
|
|
|
30
30
|
it("returns ok when no VersionConfig exists", async () => {
|
|
31
31
|
const res = await app.get("/version-check").query({platform: "web", version: 100});
|
|
32
32
|
expect(res.status).toBe(200);
|
|
33
|
-
expect(res.body).
|
|
33
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
34
|
+
expect(res.body).toEqual(
|
|
35
|
+
expect.objectContaining({
|
|
36
|
+
pollingIntervalMs: 86400000,
|
|
37
|
+
status: "ok",
|
|
38
|
+
})
|
|
39
|
+
);
|
|
34
40
|
});
|
|
35
41
|
|
|
36
42
|
it("returns ok when version param is missing", async () => {
|
|
37
43
|
const res = await app.get("/version-check");
|
|
38
44
|
expect(res.status).toBe(200);
|
|
39
|
-
expect(res.body).
|
|
45
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
46
|
+
expect(res.body).toEqual(expect.objectContaining({status: "ok"}));
|
|
40
47
|
});
|
|
41
48
|
|
|
42
49
|
it("returns ok when version param is invalid", async () => {
|
|
43
50
|
const res = await app.get("/version-check").query({platform: "web", version: "invalid"});
|
|
44
51
|
expect(res.status).toBe(200);
|
|
45
|
-
expect(res.body).
|
|
52
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
53
|
+
expect(res.body).toEqual(expect.objectContaining({status: "ok"}));
|
|
46
54
|
});
|
|
47
55
|
|
|
48
56
|
it("returns ok when client version >= warning and required (web)", async () => {
|
|
@@ -53,12 +61,15 @@ describe("VersionCheckPlugin", () => {
|
|
|
53
61
|
|
|
54
62
|
const res = await app.get("/version-check").query({platform: "web", version: 150});
|
|
55
63
|
expect(res.status).toBe(200);
|
|
56
|
-
expect(res.body).
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
65
|
+
expect(res.body).toEqual(
|
|
66
|
+
expect.objectContaining({
|
|
67
|
+
pollingIntervalMs: 86400000,
|
|
68
|
+
requiredVersion: 50,
|
|
69
|
+
status: "ok",
|
|
70
|
+
warningVersion: 100,
|
|
71
|
+
})
|
|
72
|
+
);
|
|
62
73
|
});
|
|
63
74
|
|
|
64
75
|
it("returns warning when client version < warning (web)", async () => {
|
|
@@ -70,6 +81,7 @@ describe("VersionCheckPlugin", () => {
|
|
|
70
81
|
|
|
71
82
|
const res = await app.get("/version-check").query({platform: "web", version: 80});
|
|
72
83
|
expect(res.status).toBe(200);
|
|
84
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
73
85
|
expect(res.body.status).toBe("warning");
|
|
74
86
|
expect(res.body.message).toBe("Please update!");
|
|
75
87
|
});
|
|
@@ -84,6 +96,7 @@ describe("VersionCheckPlugin", () => {
|
|
|
84
96
|
|
|
85
97
|
const res = await app.get("/version-check").query({platform: "web", version: 50});
|
|
86
98
|
expect(res.status).toBe(200);
|
|
99
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
87
100
|
expect(res.body.status).toBe("required");
|
|
88
101
|
expect(res.body.message).toBe("Update required");
|
|
89
102
|
expect(res.body.updateUrl).toBe("https://example.com/update");
|
|
@@ -98,9 +111,11 @@ describe("VersionCheckPlugin", () => {
|
|
|
98
111
|
});
|
|
99
112
|
|
|
100
113
|
const webRes = await app.get("/version-check").query({platform: "web", version: 100});
|
|
114
|
+
expect(webRes.body.requestId).toBe(webRes.headers["x-request-id"]);
|
|
101
115
|
expect(webRes.body.status).toBe("ok");
|
|
102
116
|
|
|
103
117
|
const mobileRes = await app.get("/version-check").query({platform: "mobile", version: 100});
|
|
118
|
+
expect(mobileRes.body.requestId).toBe(mobileRes.headers["x-request-id"]);
|
|
104
119
|
expect(mobileRes.body.status).toBe("required");
|
|
105
120
|
});
|
|
106
121
|
|
|
@@ -111,6 +126,7 @@ describe("VersionCheckPlugin", () => {
|
|
|
111
126
|
});
|
|
112
127
|
|
|
113
128
|
const res = await app.get("/version-check").query({platform: "invalid", version: 50});
|
|
129
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
114
130
|
expect(res.body.status).toBe("required");
|
|
115
131
|
});
|
|
116
132
|
|
|
@@ -122,12 +138,15 @@ describe("VersionCheckPlugin", () => {
|
|
|
122
138
|
|
|
123
139
|
const res = await app.get("/version-check").query({platform: "web", version: 100});
|
|
124
140
|
expect(res.status).toBe(200);
|
|
125
|
-
expect(res.body).
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
141
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
142
|
+
expect(res.body).toEqual(
|
|
143
|
+
expect.objectContaining({
|
|
144
|
+
pollingIntervalMs: 86400000,
|
|
145
|
+
requiredVersion: 50,
|
|
146
|
+
status: "ok",
|
|
147
|
+
warningVersion: 100,
|
|
148
|
+
})
|
|
149
|
+
);
|
|
131
150
|
});
|
|
132
151
|
|
|
133
152
|
it("returns pollingIntervalMs from config pollingIntervalMinutes", async () => {
|
|
@@ -139,6 +158,7 @@ describe("VersionCheckPlugin", () => {
|
|
|
139
158
|
|
|
140
159
|
const res = await app.get("/version-check").query({platform: "web", version: 100});
|
|
141
160
|
expect(res.status).toBe(200);
|
|
161
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
142
162
|
expect(res.body.pollingIntervalMs).toBe(3600000);
|
|
143
163
|
});
|
|
144
164
|
|
|
@@ -150,6 +170,7 @@ describe("VersionCheckPlugin", () => {
|
|
|
150
170
|
|
|
151
171
|
const res = await app.get("/version-check").query({platform: "web", version: 100});
|
|
152
172
|
expect(res.status).toBe(200);
|
|
173
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
153
174
|
expect(res.body.pollingIntervalMs).toBe(86400000);
|
|
154
175
|
});
|
|
155
176
|
|
|
@@ -161,6 +182,7 @@ describe("VersionCheckPlugin", () => {
|
|
|
161
182
|
|
|
162
183
|
const res = await app.get("/version-check?version=50&platform=web");
|
|
163
184
|
expect(res.status).toBe(200);
|
|
185
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
164
186
|
expect(res.body.status).toBe("required");
|
|
165
187
|
});
|
|
166
188
|
|
|
@@ -172,6 +194,7 @@ describe("VersionCheckPlugin", () => {
|
|
|
172
194
|
|
|
173
195
|
const res = await app.get("/version-check").query({platform: "web", version: 50});
|
|
174
196
|
expect(res.status).toBe(200);
|
|
197
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
175
198
|
expect(res.body.status).toBe("warning");
|
|
176
199
|
expect(res.body.message).toBe(
|
|
177
200
|
"A new version is available. Please update for the best experience."
|
|
@@ -186,6 +209,7 @@ describe("VersionCheckPlugin", () => {
|
|
|
186
209
|
|
|
187
210
|
const res = await app.get("/version-check").query({platform: "web", version: 50});
|
|
188
211
|
expect(res.status).toBe(200);
|
|
212
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
189
213
|
expect(res.body.status).toBe("required");
|
|
190
214
|
expect(res.body.message).toBe(
|
|
191
215
|
"This version is no longer supported. Please update to continue."
|
|
@@ -200,6 +224,7 @@ describe("VersionCheckPlugin", () => {
|
|
|
200
224
|
|
|
201
225
|
const res = await app.get("/version-check").query({platform: "web", version: 100});
|
|
202
226
|
expect(res.status).toBe(200);
|
|
227
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
203
228
|
expect(res.body.status).toBe("warning");
|
|
204
229
|
});
|
|
205
230
|
|
|
@@ -210,12 +235,14 @@ describe("VersionCheckPlugin", () => {
|
|
|
210
235
|
});
|
|
211
236
|
|
|
212
237
|
const warningRes = await app.get("/version-check").query({platform: "web", version: 150});
|
|
238
|
+
expect(warningRes.body.requestId).toBe(warningRes.headers["x-request-id"]);
|
|
213
239
|
expect(warningRes.body.status).toBe("warning");
|
|
214
240
|
expect(warningRes.body.message).toBe(
|
|
215
241
|
"A new version is available. Please update for the best experience."
|
|
216
242
|
);
|
|
217
243
|
|
|
218
244
|
const requiredRes = await app.get("/version-check").query({platform: "web", version: 50});
|
|
245
|
+
expect(requiredRes.body.requestId).toBe(requiredRes.headers["x-request-id"]);
|
|
219
246
|
expect(requiredRes.body.status).toBe("required");
|
|
220
247
|
expect(requiredRes.body.message).toBe(
|
|
221
248
|
"This version is no longer supported. Please update to continue."
|
|
@@ -225,6 +252,7 @@ describe("VersionCheckPlugin", () => {
|
|
|
225
252
|
it("handles numeric version parameter", async () => {
|
|
226
253
|
const res = await app.get("/version-check?version=50&platform=web");
|
|
227
254
|
expect(res.status).toBe(200);
|
|
255
|
+
expect(res.body.requestId).toBe(res.headers["x-request-id"]);
|
|
228
256
|
});
|
|
229
257
|
});
|
|
230
258
|
|
|
@@ -3,8 +3,6 @@ import {beforeEach, describe, expect, it} from "bun:test";
|
|
|
3
3
|
import type express from "express";
|
|
4
4
|
import supertest from "supertest";
|
|
5
5
|
import {type ModelRouterOptions, modelRouter} from "./api";
|
|
6
|
-
import {addAuthRoutes, setupAuth} from "./auth";
|
|
7
|
-
import {setupServer} from "./expressServer";
|
|
8
6
|
import {Permissions} from "./permissions";
|
|
9
7
|
import {TerrenoApp} from "./terrenoApp";
|
|
10
8
|
import {FoodModel, setupDb, UserModel} from "./tests";
|
|
@@ -57,6 +55,8 @@ const primeActionOpenApiRoutes = async (
|
|
|
57
55
|
};
|
|
58
56
|
|
|
59
57
|
const assertActionOpenApiSpec = (spec: Record<string, unknown>): void => {
|
|
58
|
+
expect(spec.requestId).toBeUndefined();
|
|
59
|
+
|
|
60
60
|
const paths = spec.paths as Record<string, Record<string, unknown>>;
|
|
61
61
|
const collectionPath = paths["/food/summarize"];
|
|
62
62
|
const instancePath = paths["/food/{id}/ping"];
|
|
@@ -139,14 +139,18 @@ describe("action OpenAPI emission", () => {
|
|
|
139
139
|
|
|
140
140
|
const specRes = await server.get("/openapi.json").expect(200);
|
|
141
141
|
assertActionOpenApiSpec(specRes.body);
|
|
142
|
+
|
|
143
|
+
const pingRes = await server.get(`/food/${foodId}/ping`).expect(200);
|
|
144
|
+
expect(pingRes.body.data).toEqual({id: foodId});
|
|
145
|
+
expect(pingRes.body.requestId).toBe(pingRes.headers["x-request-id"]);
|
|
142
146
|
});
|
|
143
147
|
});
|
|
144
148
|
|
|
145
|
-
describe("
|
|
149
|
+
describe("configureApp", () => {
|
|
146
150
|
let app: express.Application;
|
|
147
151
|
|
|
148
152
|
beforeEach(() => {
|
|
149
|
-
const
|
|
153
|
+
const configureApp = (
|
|
150
154
|
router: express.Router,
|
|
151
155
|
routerOptions?: Partial<ModelRouterOptions<unknown>>
|
|
152
156
|
): void => {
|
|
@@ -156,16 +160,14 @@ describe("action OpenAPI emission", () => {
|
|
|
156
160
|
);
|
|
157
161
|
};
|
|
158
162
|
|
|
159
|
-
app =
|
|
160
|
-
|
|
163
|
+
app = new TerrenoApp({
|
|
164
|
+
configureApp,
|
|
161
165
|
skipListen: true,
|
|
162
166
|
userModel: UserModel as any,
|
|
163
|
-
});
|
|
164
|
-
setupAuth(app, UserModel as any);
|
|
165
|
-
addAuthRoutes(app, UserModel as any);
|
|
167
|
+
}).build();
|
|
166
168
|
});
|
|
167
169
|
|
|
168
|
-
it("emits the same action operations on first hit via
|
|
170
|
+
it("emits the same action operations on first hit via configureApp", async () => {
|
|
169
171
|
const server = supertest(app);
|
|
170
172
|
await primeActionOpenApiRoutes(server, foodId);
|
|
171
173
|
|
package/src/api.query.test.ts
CHANGED
|
@@ -95,7 +95,17 @@ describe("query and list methods", () => {
|
|
|
95
95
|
update: [Permissions.IsOwner],
|
|
96
96
|
},
|
|
97
97
|
populatePaths: [{path: "ownerId"}],
|
|
98
|
-
queryFields: [
|
|
98
|
+
queryFields: [
|
|
99
|
+
"hidden",
|
|
100
|
+
"name",
|
|
101
|
+
"calories",
|
|
102
|
+
"created",
|
|
103
|
+
"created_gte",
|
|
104
|
+
"created_lte",
|
|
105
|
+
"source.name",
|
|
106
|
+
"tags",
|
|
107
|
+
"eatenBy",
|
|
108
|
+
],
|
|
99
109
|
sort: {created: "descending"},
|
|
100
110
|
})
|
|
101
111
|
);
|
|
@@ -190,6 +200,19 @@ describe("query and list methods", () => {
|
|
|
190
200
|
expect(res.body.data[0].id).toBe((apple as any).id);
|
|
191
201
|
});
|
|
192
202
|
|
|
203
|
+
it("list applies created_gte and created_lte as a Date range", async () => {
|
|
204
|
+
const res = await agent
|
|
205
|
+
.get("/food")
|
|
206
|
+
.query({
|
|
207
|
+
created_gte: "2021-12-03T00:00:05.000Z",
|
|
208
|
+
created_lte: "2021-12-03T00:00:25.000Z",
|
|
209
|
+
limit: 10,
|
|
210
|
+
})
|
|
211
|
+
.expect(200);
|
|
212
|
+
const names = (res.body.data as {name: string}[]).map((d) => d.name).sort();
|
|
213
|
+
expect(names).toEqual(["Pizza", "Spinach"]);
|
|
214
|
+
});
|
|
215
|
+
|
|
193
216
|
it("list query params not in list", async () => {
|
|
194
217
|
const res = await agent.get(`/food?ownerId=${admin._id}`).expect(400);
|
|
195
218
|
expect(res.body.title).toBe("ownerId is not allowed as a query param.");
|