@terreno/api 0.10.0 → 0.11.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/bunfig.toml +5 -2
- package/dist/auth.test.js +257 -0
- package/dist/consentApp.test.js +245 -0
- package/dist/githubAuth.test.js +380 -0
- package/dist/logger.test.d.ts +1 -0
- package/dist/logger.test.js +143 -0
- package/dist/notifiers/googleChatNotifier.test.js +37 -0
- package/dist/openApi.js +2 -2
- package/dist/openApi.test.js +122 -0
- package/dist/openApiBuilder.d.ts +1 -0
- package/dist/openApiBuilder.js +13 -2
- package/dist/openApiBuilder.test.js +66 -0
- package/dist/openApiValidator.test.js +309 -0
- package/dist/plugins.d.ts +8 -8
- package/dist/plugins.js +38 -32
- package/dist/populate.test.js +99 -0
- package/dist/syncConsents.test.js +273 -0
- package/dist/tests.d.ts +3 -3
- package/dist/tests.js +78 -82
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +7 -7
- package/package.json +2 -1
- package/src/__snapshots__/openApi.test.ts.snap +48 -0
- package/src/auth.test.ts +147 -0
- package/src/consentApp.test.ts +162 -0
- package/src/githubAuth.test.ts +307 -1
- package/src/logger.test.ts +149 -0
- package/src/notifiers/googleChatNotifier.test.ts +24 -0
- package/src/openApi.test.ts +152 -0
- package/src/openApi.ts +6 -2
- package/src/openApiBuilder.test.ts +81 -0
- package/src/openApiBuilder.ts +17 -2
- package/src/openApiValidator.test.ts +410 -0
- package/src/plugins.ts +32 -23
- package/src/populate.test.ts +78 -2
- package/src/syncConsents.test.ts +145 -0
- package/src/tests.ts +8 -8
- package/src/utils.ts +4 -4
package/dist/openApi.test.js
CHANGED
|
@@ -71,6 +71,7 @@ var supertest_1 = __importDefault(require("supertest"));
|
|
|
71
71
|
var api_1 = require("./api");
|
|
72
72
|
var auth_1 = require("./auth");
|
|
73
73
|
var expressServer_1 = require("./expressServer");
|
|
74
|
+
var openApi_1 = require("./openApi");
|
|
74
75
|
var permissions_1 = require("./permissions");
|
|
75
76
|
var tests_1 = require("./tests");
|
|
76
77
|
function getMessageSummaryOpenApiMiddleware(options) {
|
|
@@ -450,3 +451,124 @@ function addRoutesPopulate(router, options) {
|
|
|
450
451
|
});
|
|
451
452
|
}); });
|
|
452
453
|
});
|
|
454
|
+
(0, bun_test_1.describe)("openApi middleware no-op paths", function () {
|
|
455
|
+
(0, bun_test_1.it)("getOpenApiMiddleware returns noop when openApi not configured", function () {
|
|
456
|
+
var mw = (0, openApi_1.getOpenApiMiddleware)(tests_1.FoodModel, {});
|
|
457
|
+
(0, bun_test_1.expect)(typeof mw).toBe("function");
|
|
458
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
459
|
+
});
|
|
460
|
+
(0, bun_test_1.it)("getOpenApiMiddleware returns noop when read permissions are empty", function () {
|
|
461
|
+
var mw = (0, openApi_1.getOpenApiMiddleware)(tests_1.FoodModel, {
|
|
462
|
+
openApi: { component: function () { }, path: function () { return (function () { }); } },
|
|
463
|
+
permissions: {
|
|
464
|
+
create: [],
|
|
465
|
+
delete: [],
|
|
466
|
+
list: [],
|
|
467
|
+
read: [],
|
|
468
|
+
update: [],
|
|
469
|
+
},
|
|
470
|
+
});
|
|
471
|
+
(0, bun_test_1.expect)(typeof mw).toBe("function");
|
|
472
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
473
|
+
});
|
|
474
|
+
(0, bun_test_1.it)("listOpenApiMiddleware returns noop when openApi not configured", function () {
|
|
475
|
+
var mw = (0, openApi_1.listOpenApiMiddleware)(tests_1.FoodModel, {});
|
|
476
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
477
|
+
});
|
|
478
|
+
(0, bun_test_1.it)("listOpenApiMiddleware returns noop when list permissions are empty", function () {
|
|
479
|
+
var mw = (0, openApi_1.listOpenApiMiddleware)(tests_1.FoodModel, {
|
|
480
|
+
openApi: { path: function () { return (function () { }); } },
|
|
481
|
+
permissions: {
|
|
482
|
+
create: [],
|
|
483
|
+
delete: [],
|
|
484
|
+
list: [],
|
|
485
|
+
read: [],
|
|
486
|
+
update: [],
|
|
487
|
+
},
|
|
488
|
+
});
|
|
489
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
490
|
+
});
|
|
491
|
+
(0, bun_test_1.it)("createOpenApiMiddleware returns noop when openApi not configured", function () {
|
|
492
|
+
var mw = (0, openApi_1.createOpenApiMiddleware)(tests_1.FoodModel, {});
|
|
493
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
494
|
+
});
|
|
495
|
+
(0, bun_test_1.it)("createOpenApiMiddleware returns noop when create permissions are empty", function () {
|
|
496
|
+
var mw = (0, openApi_1.createOpenApiMiddleware)(tests_1.FoodModel, {
|
|
497
|
+
openApi: { path: function () { return (function () { }); } },
|
|
498
|
+
permissions: {
|
|
499
|
+
create: [],
|
|
500
|
+
delete: [],
|
|
501
|
+
list: [],
|
|
502
|
+
read: [],
|
|
503
|
+
update: [],
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
507
|
+
});
|
|
508
|
+
(0, bun_test_1.it)("patchOpenApiMiddleware returns noop when openApi not configured", function () {
|
|
509
|
+
var mw = (0, openApi_1.patchOpenApiMiddleware)(tests_1.FoodModel, {});
|
|
510
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
511
|
+
});
|
|
512
|
+
(0, bun_test_1.it)("patchOpenApiMiddleware returns noop when update permissions are empty", function () {
|
|
513
|
+
var mw = (0, openApi_1.patchOpenApiMiddleware)(tests_1.FoodModel, {
|
|
514
|
+
openApi: { path: function () { return (function () { }); } },
|
|
515
|
+
permissions: {
|
|
516
|
+
create: [],
|
|
517
|
+
delete: [],
|
|
518
|
+
list: [],
|
|
519
|
+
read: [],
|
|
520
|
+
update: [],
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
524
|
+
});
|
|
525
|
+
(0, bun_test_1.it)("deleteOpenApiMiddleware returns noop when openApi not configured", function () {
|
|
526
|
+
var mw = (0, openApi_1.deleteOpenApiMiddleware)(tests_1.FoodModel, {});
|
|
527
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
528
|
+
});
|
|
529
|
+
(0, bun_test_1.it)("deleteOpenApiMiddleware returns noop when delete permissions are empty", function () {
|
|
530
|
+
var mw = (0, openApi_1.deleteOpenApiMiddleware)(tests_1.FoodModel, {
|
|
531
|
+
openApi: { path: function () { return (function () { }); } },
|
|
532
|
+
permissions: {
|
|
533
|
+
create: [],
|
|
534
|
+
delete: [],
|
|
535
|
+
list: [],
|
|
536
|
+
read: [],
|
|
537
|
+
update: [],
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
541
|
+
});
|
|
542
|
+
(0, bun_test_1.it)("readOpenApiMiddleware returns noop when openApi not configured", function () {
|
|
543
|
+
var mw = (0, openApi_1.readOpenApiMiddleware)({}, {}, [], []);
|
|
544
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
545
|
+
});
|
|
546
|
+
(0, bun_test_1.it)("readOpenApiMiddleware returns noop when read permissions are empty", function () {
|
|
547
|
+
var mw = (0, openApi_1.readOpenApiMiddleware)({
|
|
548
|
+
openApi: { path: function () { return (function () { }); } },
|
|
549
|
+
permissions: {
|
|
550
|
+
create: [],
|
|
551
|
+
delete: [],
|
|
552
|
+
list: [],
|
|
553
|
+
read: [],
|
|
554
|
+
update: [],
|
|
555
|
+
},
|
|
556
|
+
}, { id: { type: "string" } }, ["id"], []);
|
|
557
|
+
(0, bun_test_1.expect)(mw.length).toBe(3);
|
|
558
|
+
});
|
|
559
|
+
(0, bun_test_1.it)("readOpenApiMiddleware returns middleware when configured with permissions", function () {
|
|
560
|
+
// Use a simple path stub that returns a middleware function
|
|
561
|
+
var pathFn = function () { return (function (_req, _res, next) { return next(); }); };
|
|
562
|
+
var mw = (0, openApi_1.readOpenApiMiddleware)({
|
|
563
|
+
openApi: { path: pathFn },
|
|
564
|
+
permissions: {
|
|
565
|
+
create: [],
|
|
566
|
+
delete: [],
|
|
567
|
+
list: [],
|
|
568
|
+
read: [permissions_1.Permissions.IsAny],
|
|
569
|
+
update: [],
|
|
570
|
+
},
|
|
571
|
+
}, { name: { type: "string" } }, ["name"], [{ in: "query", name: "search", schema: { type: "string" } }]);
|
|
572
|
+
(0, bun_test_1.expect)(typeof mw).toBe("function");
|
|
573
|
+
});
|
|
574
|
+
});
|
package/dist/openApiBuilder.d.ts
CHANGED
|
@@ -210,6 +210,7 @@ export declare class OpenApiMiddlewareBuilder {
|
|
|
210
210
|
* @param options - Router options containing the OpenAPI path configuration
|
|
211
211
|
*/
|
|
212
212
|
constructor(options: Partial<ModelRouterOptions<unknown>>);
|
|
213
|
+
private describeRoute;
|
|
213
214
|
/**
|
|
214
215
|
* Sets the tags for the OpenAPI operation.
|
|
215
216
|
*
|
package/dist/openApiBuilder.js
CHANGED
|
@@ -108,6 +108,17 @@ var OpenApiMiddlewareBuilder = /** @class */ (function () {
|
|
|
108
108
|
};
|
|
109
109
|
this.validationConfig = {};
|
|
110
110
|
}
|
|
111
|
+
OpenApiMiddlewareBuilder.prototype.describeRoute = function () {
|
|
112
|
+
var _c;
|
|
113
|
+
var parts = [];
|
|
114
|
+
if (this.config.summary) {
|
|
115
|
+
parts.push("\"".concat(this.config.summary, "\""));
|
|
116
|
+
}
|
|
117
|
+
if ((_c = this.config.tags) === null || _c === void 0 ? void 0 : _c.length) {
|
|
118
|
+
parts.push("tags=[".concat(this.config.tags.join(", "), "]"));
|
|
119
|
+
}
|
|
120
|
+
return parts.length > 0 ? parts.join(" ") : "unnamed route";
|
|
121
|
+
};
|
|
111
122
|
/**
|
|
112
123
|
* Sets the tags for the OpenAPI operation.
|
|
113
124
|
*
|
|
@@ -423,7 +434,7 @@ var OpenApiMiddlewareBuilder = /** @class */ (function () {
|
|
|
423
434
|
openApiMiddleware = this.options.openApi.path((0, merge_1.default)(__assign(__assign({}, this.config), { responses: __assign(__assign({}, this.config.responses), openApi_1.defaultOpenApiErrorResponses) }), (_e = (_d = this.options.openApiOverwrite) === null || _d === void 0 ? void 0 : _d.get) !== null && _e !== void 0 ? _e : {}));
|
|
424
435
|
}
|
|
425
436
|
else {
|
|
426
|
-
logger_1.logger.debug("No options.openApi provided, skipping OpenApiMiddleware");
|
|
437
|
+
logger_1.logger.debug("No options.openApi provided in buildWithSchemas for ".concat(this.describeRoute(), ", skipping OpenApiMiddleware"));
|
|
427
438
|
}
|
|
428
439
|
var globalConfig = (0, openApiValidator_1.getOpenApiValidatorConfig)();
|
|
429
440
|
var validationEnabled = (_f = this.validationConfig.enabled) !== null && _f !== void 0 ? _f : ((0, openApiValidator_1.isOpenApiValidatorConfigured)() && ((_g = globalConfig.validateRequests) !== null && _g !== void 0 ? _g : false));
|
|
@@ -468,7 +479,7 @@ var OpenApiMiddlewareBuilder = /** @class */ (function () {
|
|
|
468
479
|
openApiMiddleware = this.options.openApi.path((0, merge_1.default)(__assign(__assign({}, this.config), { responses: __assign(__assign({}, this.config.responses), openApi_1.defaultOpenApiErrorResponses) }), (_e = (_d = this.options.openApiOverwrite) === null || _d === void 0 ? void 0 : _d.get) !== null && _e !== void 0 ? _e : {}));
|
|
469
480
|
}
|
|
470
481
|
else {
|
|
471
|
-
logger_1.logger.debug("No options.openApi provided, skipping OpenApiMiddleware");
|
|
482
|
+
logger_1.logger.debug("No options.openApi provided in build for ".concat(this.describeRoute(), ", skipping OpenApiMiddleware"));
|
|
472
483
|
}
|
|
473
484
|
// Check if validation should be enabled
|
|
474
485
|
var globalConfig = (0, openApiValidator_1.getOpenApiValidatorConfig)();
|
|
@@ -507,3 +507,69 @@ function addRoutesWithBuilder(router, options) {
|
|
|
507
507
|
(0, bun_test_1.expect)(builder).toBeInstanceOf(openApiBuilder_1.OpenApiMiddlewareBuilder);
|
|
508
508
|
});
|
|
509
509
|
});
|
|
510
|
+
(0, bun_test_1.describe)("OpenApiMiddlewareBuilder withValidation / buildWithSchemas", function () {
|
|
511
|
+
(0, bun_test_1.beforeEach)(function () {
|
|
512
|
+
var resetOpenApiValidatorConfig = require("./openApiValidator").resetOpenApiValidatorConfig;
|
|
513
|
+
resetOpenApiValidatorConfig();
|
|
514
|
+
});
|
|
515
|
+
(0, bun_test_1.it)("buildWithSchemas returns bodySchema and querySchema", function () {
|
|
516
|
+
var _a, _b;
|
|
517
|
+
var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
|
|
518
|
+
.withRequestBody({ name: { required: true, type: "string" } })
|
|
519
|
+
.withQueryParameter("page", { type: "number" })
|
|
520
|
+
.buildWithSchemas();
|
|
521
|
+
(0, bun_test_1.expect)(result.bodySchema).toBeDefined();
|
|
522
|
+
(0, bun_test_1.expect)((_a = result.bodySchema) === null || _a === void 0 ? void 0 : _a.name).toBeDefined();
|
|
523
|
+
(0, bun_test_1.expect)(result.querySchema).toBeDefined();
|
|
524
|
+
(0, bun_test_1.expect)((_b = result.querySchema) === null || _b === void 0 ? void 0 : _b.page).toBeDefined();
|
|
525
|
+
(0, bun_test_1.expect)(typeof result.middleware).toBe("function");
|
|
526
|
+
});
|
|
527
|
+
(0, bun_test_1.it)("buildWithSchemas returns undefined schemas when no body/query defined", function () {
|
|
528
|
+
var result = (0, openApiBuilder_1.createOpenApiBuilder)({}).buildWithSchemas();
|
|
529
|
+
(0, bun_test_1.expect)(result.bodySchema).toBeUndefined();
|
|
530
|
+
(0, bun_test_1.expect)(result.querySchema).toBeUndefined();
|
|
531
|
+
});
|
|
532
|
+
(0, bun_test_1.it)("withValidation with defaults enables body and query validation", function () {
|
|
533
|
+
var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
|
|
534
|
+
.withRequestBody({ name: { required: true, type: "string" } })
|
|
535
|
+
.withQueryParameter("page", { type: "number" })
|
|
536
|
+
.withValidation()
|
|
537
|
+
.buildWithSchemas();
|
|
538
|
+
(0, bun_test_1.expect)(result.validationEnabled).toBe(true);
|
|
539
|
+
});
|
|
540
|
+
(0, bun_test_1.it)("withValidation with enabled=false disables validation", function () {
|
|
541
|
+
var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
|
|
542
|
+
.withRequestBody({ name: { required: true, type: "string" } })
|
|
543
|
+
.withValidation({ enabled: false })
|
|
544
|
+
.buildWithSchemas();
|
|
545
|
+
(0, bun_test_1.expect)(result.validationEnabled).toBe(false);
|
|
546
|
+
});
|
|
547
|
+
(0, bun_test_1.it)("build() returns an array with validators when validation is enabled", function () {
|
|
548
|
+
var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
|
|
549
|
+
.withRequestBody({ name: { required: true, type: "string" } })
|
|
550
|
+
.withQueryParameter("page", { type: "number" })
|
|
551
|
+
.withValidation()
|
|
552
|
+
.build();
|
|
553
|
+
(0, bun_test_1.expect)(Array.isArray(result)).toBe(true);
|
|
554
|
+
(0, bun_test_1.expect)(result.length).toBeGreaterThan(1);
|
|
555
|
+
});
|
|
556
|
+
(0, bun_test_1.it)("build() returns a single middleware when validation is disabled", function () {
|
|
557
|
+
var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
|
|
558
|
+
.withRequestBody({ name: { required: true, type: "string" } })
|
|
559
|
+
.build();
|
|
560
|
+
(0, bun_test_1.expect)(typeof result).toBe("function");
|
|
561
|
+
});
|
|
562
|
+
(0, bun_test_1.it)("build() falls back to single middleware when there is nothing to validate", function () {
|
|
563
|
+
var result = (0, openApiBuilder_1.createOpenApiBuilder)({}).withValidation().build();
|
|
564
|
+
(0, bun_test_1.expect)(typeof result).toBe("function");
|
|
565
|
+
});
|
|
566
|
+
(0, bun_test_1.it)("withValidation options body=false and query=false is respected", function () {
|
|
567
|
+
var result = (0, openApiBuilder_1.createOpenApiBuilder)({})
|
|
568
|
+
.withRequestBody({ name: { required: true, type: "string" } })
|
|
569
|
+
.withQueryParameter("page", { type: "number" })
|
|
570
|
+
.withValidation({ body: false, query: false })
|
|
571
|
+
.build();
|
|
572
|
+
// No body/query validation = just the openApi middleware (single fn)
|
|
573
|
+
(0, bun_test_1.expect)(typeof result).toBe("function");
|
|
574
|
+
});
|
|
575
|
+
});
|
|
@@ -342,5 +342,314 @@ var setupFreshApp = function () { return __awaiter(void 0, void 0, void 0, funct
|
|
|
342
342
|
middleware(req, res, function () { });
|
|
343
343
|
}).toThrow();
|
|
344
344
|
});
|
|
345
|
+
(0, bun_test_1.it)("calls onError callback instead of throwing when provided at option level", function () {
|
|
346
|
+
(0, openApiValidator_1.configureOpenApiValidator)();
|
|
347
|
+
var capturedErrors = [];
|
|
348
|
+
var middleware = (0, openApiValidator_1.validateRequestBody)({ name: { required: true, type: "string" } }, {
|
|
349
|
+
onError: function (errors) {
|
|
350
|
+
capturedErrors = errors;
|
|
351
|
+
},
|
|
352
|
+
});
|
|
353
|
+
var nextCalled = false;
|
|
354
|
+
var req = { body: {}, method: "POST", path: "/test" };
|
|
355
|
+
var res = {};
|
|
356
|
+
middleware(req, res, function () {
|
|
357
|
+
nextCalled = true;
|
|
358
|
+
});
|
|
359
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
360
|
+
(0, bun_test_1.expect)(capturedErrors.length).toBeGreaterThan(0);
|
|
361
|
+
});
|
|
362
|
+
(0, bun_test_1.it)("calls global onValidationError when no per-route handler", function () {
|
|
363
|
+
var capturedErrors = [];
|
|
364
|
+
(0, openApiValidator_1.configureOpenApiValidator)({
|
|
365
|
+
onValidationError: function (errors) {
|
|
366
|
+
capturedErrors = errors;
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
var middleware = (0, openApiValidator_1.validateRequestBody)({ name: { required: true, type: "string" } });
|
|
370
|
+
var nextCalled = false;
|
|
371
|
+
var req = { body: {}, method: "POST", path: "/test" };
|
|
372
|
+
var res = {};
|
|
373
|
+
middleware(req, res, function () {
|
|
374
|
+
nextCalled = true;
|
|
375
|
+
});
|
|
376
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
377
|
+
(0, bun_test_1.expect)(capturedErrors.length).toBeGreaterThan(0);
|
|
378
|
+
});
|
|
379
|
+
(0, bun_test_1.it)("skips validation when enabled=false on options", function () {
|
|
380
|
+
(0, openApiValidator_1.configureOpenApiValidator)();
|
|
381
|
+
var middleware = (0, openApiValidator_1.validateRequestBody)({ name: { required: true, type: "string" } }, { enabled: false });
|
|
382
|
+
var nextCalled = false;
|
|
383
|
+
var req = { body: {}, method: "POST", path: "/test" };
|
|
384
|
+
var res = {};
|
|
385
|
+
middleware(req, res, function () {
|
|
386
|
+
nextCalled = true;
|
|
387
|
+
});
|
|
388
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
389
|
+
});
|
|
390
|
+
(0, bun_test_1.it)("respects validateRequests=false in global config", function () {
|
|
391
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ validateRequests: false });
|
|
392
|
+
var middleware = (0, openApiValidator_1.validateRequestBody)({ name: { required: true, type: "string" } });
|
|
393
|
+
var nextCalled = false;
|
|
394
|
+
var req = { body: {}, method: "POST", path: "/test" };
|
|
395
|
+
var res = {};
|
|
396
|
+
middleware(req, res, function () {
|
|
397
|
+
nextCalled = true;
|
|
398
|
+
});
|
|
399
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
(0, bun_test_1.describe)("validateQueryParams middleware", function () {
|
|
403
|
+
(0, bun_test_1.it)("is a no-op when not configured", function () {
|
|
404
|
+
(0, openApiValidator_1.resetOpenApiValidatorConfig)();
|
|
405
|
+
var middleware = (0, openApiValidator_1.validateQueryParams)({
|
|
406
|
+
page: { type: "number" },
|
|
407
|
+
});
|
|
408
|
+
var nextCalled = false;
|
|
409
|
+
var req = { method: "GET", path: "/test", query: {} };
|
|
410
|
+
var res = {};
|
|
411
|
+
middleware(req, res, function () {
|
|
412
|
+
nextCalled = true;
|
|
413
|
+
});
|
|
414
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
415
|
+
});
|
|
416
|
+
(0, bun_test_1.it)("coerces types when configured", function () {
|
|
417
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: true });
|
|
418
|
+
var middleware = (0, openApiValidator_1.validateQueryParams)({
|
|
419
|
+
page: { type: "number" },
|
|
420
|
+
});
|
|
421
|
+
var req = { method: "GET", path: "/test", query: { page: "3" } };
|
|
422
|
+
var res = {};
|
|
423
|
+
middleware(req, res, function () { });
|
|
424
|
+
(0, bun_test_1.expect)(req.query.page).toBe(3);
|
|
425
|
+
});
|
|
426
|
+
(0, bun_test_1.it)("throws validation error when query does not match", function () {
|
|
427
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: false });
|
|
428
|
+
var middleware = (0, openApiValidator_1.validateQueryParams)({
|
|
429
|
+
page: { required: true, type: "number" },
|
|
430
|
+
});
|
|
431
|
+
var req = { method: "GET", path: "/test", query: {} };
|
|
432
|
+
var res = {};
|
|
433
|
+
// Required top-level property missing triggers an error
|
|
434
|
+
// We need required: [] at schema level via required: true on property. Confirm via calling.
|
|
435
|
+
(0, bun_test_1.expect)(function () {
|
|
436
|
+
middleware(req, res, function () { });
|
|
437
|
+
}).toThrow();
|
|
438
|
+
});
|
|
439
|
+
(0, bun_test_1.it)("uses onError callback for query validation", function () {
|
|
440
|
+
var captured = [];
|
|
441
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: false });
|
|
442
|
+
var middleware = (0, openApiValidator_1.validateQueryParams)({ page: { required: true, type: "number" } }, {
|
|
443
|
+
onError: function (errors) {
|
|
444
|
+
captured = errors;
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
var nextCalled = false;
|
|
448
|
+
var req = { method: "GET", path: "/test", query: {} };
|
|
449
|
+
var res = {};
|
|
450
|
+
middleware(req, res, function () {
|
|
451
|
+
nextCalled = true;
|
|
452
|
+
});
|
|
453
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
454
|
+
(0, bun_test_1.expect)(captured.length).toBeGreaterThan(0);
|
|
455
|
+
});
|
|
456
|
+
(0, bun_test_1.it)("skips query validation when enabled=false", function () {
|
|
457
|
+
(0, openApiValidator_1.configureOpenApiValidator)();
|
|
458
|
+
var middleware = (0, openApiValidator_1.validateQueryParams)({ page: { required: true, type: "number" } }, { enabled: false });
|
|
459
|
+
var nextCalled = false;
|
|
460
|
+
var req = { method: "GET", path: "/test", query: {} };
|
|
461
|
+
var res = {};
|
|
462
|
+
middleware(req, res, function () {
|
|
463
|
+
nextCalled = true;
|
|
464
|
+
});
|
|
465
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
(0, bun_test_1.describe)("validateResponseData", function () {
|
|
469
|
+
(0, bun_test_1.it)("returns valid when validateResponses is disabled", function () {
|
|
470
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ validateResponses: false });
|
|
471
|
+
var result = (0, openApiValidator_1.validateResponseData)({ foo: "bar" }, { name: { type: "string" } });
|
|
472
|
+
(0, bun_test_1.expect)(result.valid).toBe(true);
|
|
473
|
+
});
|
|
474
|
+
(0, bun_test_1.it)("validates response shape when validateResponses is enabled", function () {
|
|
475
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ validateResponses: true });
|
|
476
|
+
var result = (0, openApiValidator_1.validateResponseData)({ name: "Apple" }, { name: { required: true, type: "string" } });
|
|
477
|
+
(0, bun_test_1.expect)(result.valid).toBe(true);
|
|
478
|
+
});
|
|
479
|
+
(0, bun_test_1.it)("returns errors for invalid response shape", function () {
|
|
480
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: false, validateResponses: true });
|
|
481
|
+
var result = (0, openApiValidator_1.validateResponseData)({ name: 42 }, { name: { required: true, type: "string" } });
|
|
482
|
+
(0, bun_test_1.expect)(result.valid).toBe(false);
|
|
483
|
+
(0, bun_test_1.expect)(result.errors).toBeDefined();
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
(0, bun_test_1.describe)("createValidator", function () {
|
|
487
|
+
(0, bun_test_1.it)("runs body then query validation and calls next once both pass", function () {
|
|
488
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: true });
|
|
489
|
+
var middleware = (0, openApiValidator_1.createValidator)({
|
|
490
|
+
body: { name: { required: true, type: "string" } },
|
|
491
|
+
query: { page: { type: "number" } },
|
|
492
|
+
});
|
|
493
|
+
var nextCalled = false;
|
|
494
|
+
var req = {
|
|
495
|
+
body: { name: "ok" },
|
|
496
|
+
method: "POST",
|
|
497
|
+
path: "/test",
|
|
498
|
+
query: { page: "2" },
|
|
499
|
+
};
|
|
500
|
+
var res = {};
|
|
501
|
+
middleware(req, res, function () {
|
|
502
|
+
nextCalled = true;
|
|
503
|
+
});
|
|
504
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
505
|
+
(0, bun_test_1.expect)(req.query.page).toBe(2);
|
|
506
|
+
});
|
|
507
|
+
(0, bun_test_1.it)("skips when neither body nor query schemas are provided", function () {
|
|
508
|
+
(0, openApiValidator_1.configureOpenApiValidator)();
|
|
509
|
+
var middleware = (0, openApiValidator_1.createValidator)({});
|
|
510
|
+
var nextCalled = false;
|
|
511
|
+
var req = { body: {}, method: "POST", path: "/test", query: {} };
|
|
512
|
+
var res = {};
|
|
513
|
+
middleware(req, res, function () {
|
|
514
|
+
nextCalled = true;
|
|
515
|
+
});
|
|
516
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
517
|
+
});
|
|
518
|
+
(0, bun_test_1.it)("runs only query validation when only query provided", function () {
|
|
519
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: true });
|
|
520
|
+
var middleware = (0, openApiValidator_1.createValidator)({
|
|
521
|
+
query: { page: { type: "number" } },
|
|
522
|
+
});
|
|
523
|
+
var nextCalled = false;
|
|
524
|
+
var req = { method: "GET", path: "/test", query: { page: "5" } };
|
|
525
|
+
var res = {};
|
|
526
|
+
middleware(req, res, function () {
|
|
527
|
+
nextCalled = true;
|
|
528
|
+
});
|
|
529
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
530
|
+
(0, bun_test_1.expect)(req.query.page).toBe(5);
|
|
531
|
+
});
|
|
532
|
+
(0, bun_test_1.it)("propagates body validation error via next", function () {
|
|
533
|
+
var capturedErrors = [];
|
|
534
|
+
(0, openApiValidator_1.configureOpenApiValidator)({
|
|
535
|
+
onValidationError: function (errors) {
|
|
536
|
+
capturedErrors = errors;
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
var middleware = (0, openApiValidator_1.createValidator)({
|
|
540
|
+
body: { name: { required: true, type: "string" } },
|
|
541
|
+
query: { page: { type: "number" } },
|
|
542
|
+
});
|
|
543
|
+
var req = { body: {}, method: "POST", path: "/test", query: {} };
|
|
544
|
+
var res = {};
|
|
545
|
+
var nextCalled = false;
|
|
546
|
+
middleware(req, res, function () {
|
|
547
|
+
nextCalled = true;
|
|
548
|
+
});
|
|
549
|
+
(0, bun_test_1.expect)(capturedErrors.length).toBeGreaterThan(0);
|
|
550
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
(0, bun_test_1.describe)("createModelValidators", function () {
|
|
554
|
+
(0, bun_test_1.beforeEach)(function () {
|
|
555
|
+
(0, openApiValidator_1.configureOpenApiValidator)();
|
|
556
|
+
});
|
|
557
|
+
(0, bun_test_1.it)("returns create and update middleware", function () {
|
|
558
|
+
var validators = (0, openApiValidator_1.createModelValidators)(tests_1.RequiredModel);
|
|
559
|
+
(0, bun_test_1.expect)(typeof validators.create).toBe("function");
|
|
560
|
+
(0, bun_test_1.expect)(typeof validators.update).toBe("function");
|
|
561
|
+
});
|
|
562
|
+
(0, bun_test_1.it)("create validator rejects body with wrong type", function () {
|
|
563
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: false });
|
|
564
|
+
var create = (0, openApiValidator_1.createModelValidators)(tests_1.RequiredModel).create;
|
|
565
|
+
var req = { body: { name: 123 }, method: "POST", path: "/required" };
|
|
566
|
+
var res = {};
|
|
567
|
+
(0, bun_test_1.expect)(function () {
|
|
568
|
+
create(req, res, function () { });
|
|
569
|
+
}).toThrow();
|
|
570
|
+
});
|
|
571
|
+
(0, bun_test_1.it)("update validator allows a partial body", function () {
|
|
572
|
+
var update = (0, openApiValidator_1.createModelValidators)(tests_1.RequiredModel).update;
|
|
573
|
+
var nextCalled = false;
|
|
574
|
+
var req = { body: { about: "info" }, method: "PATCH", path: "/required" };
|
|
575
|
+
var res = {};
|
|
576
|
+
update(req, res, function () {
|
|
577
|
+
nextCalled = true;
|
|
578
|
+
});
|
|
579
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
580
|
+
});
|
|
581
|
+
(0, bun_test_1.it)("supports onAdditionalPropertiesRemoved option", function () {
|
|
582
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: true });
|
|
583
|
+
var removedProps = [];
|
|
584
|
+
var create = (0, openApiValidator_1.createModelValidators)(tests_1.RequiredModel, {
|
|
585
|
+
onAdditionalPropertiesRemoved: function (props) {
|
|
586
|
+
removedProps.push.apply(removedProps, __spreadArray([], __read(props), false));
|
|
587
|
+
},
|
|
588
|
+
}).create;
|
|
589
|
+
// Extra property gets stripped and hook is called
|
|
590
|
+
var req = {
|
|
591
|
+
body: { extra: "strip me", name: "Apple" },
|
|
592
|
+
method: "POST",
|
|
593
|
+
path: "/required",
|
|
594
|
+
};
|
|
595
|
+
create(req, {}, function () { });
|
|
596
|
+
(0, bun_test_1.expect)(removedProps).toContain("extra");
|
|
597
|
+
});
|
|
598
|
+
(0, bun_test_1.it)("supports onError option", function () {
|
|
599
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ coerceTypes: false });
|
|
600
|
+
var errorHandled = [];
|
|
601
|
+
var create = (0, openApiValidator_1.createModelValidators)(tests_1.RequiredModel, {
|
|
602
|
+
onError: function (errors) {
|
|
603
|
+
errorHandled = errors;
|
|
604
|
+
},
|
|
605
|
+
}).create;
|
|
606
|
+
// Wrong type triggers error handler
|
|
607
|
+
var req = { body: { name: 42 }, method: "POST", path: "/required" };
|
|
608
|
+
create(req, {}, function () { });
|
|
609
|
+
(0, bun_test_1.expect)(errorHandled.length).toBeGreaterThan(0);
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
(0, bun_test_1.describe)("validateModelRequestBody", function () {
|
|
613
|
+
(0, bun_test_1.beforeEach)(function () {
|
|
614
|
+
(0, openApiValidator_1.configureOpenApiValidator)();
|
|
615
|
+
});
|
|
616
|
+
(0, bun_test_1.it)("creates a validator from a Mongoose model", function () {
|
|
617
|
+
var middleware = (0, openApiValidator_1.validateModelRequestBody)(tests_1.RequiredModel);
|
|
618
|
+
var req = { body: {}, method: "POST", path: "/required" };
|
|
619
|
+
var res = {};
|
|
620
|
+
(0, bun_test_1.expect)(function () {
|
|
621
|
+
middleware(req, res, function () { });
|
|
622
|
+
}).toThrow();
|
|
623
|
+
});
|
|
624
|
+
(0, bun_test_1.it)("respects excludeFields option", function () {
|
|
625
|
+
var middleware = (0, openApiValidator_1.validateModelRequestBody)(tests_1.RequiredModel, {
|
|
626
|
+
excludeFields: ["name"],
|
|
627
|
+
});
|
|
628
|
+
// Without 'name' required, empty body passes
|
|
629
|
+
var nextCalled = false;
|
|
630
|
+
var req = { body: {}, method: "POST", path: "/required" };
|
|
631
|
+
var res = {};
|
|
632
|
+
middleware(req, res, function () {
|
|
633
|
+
nextCalled = true;
|
|
634
|
+
});
|
|
635
|
+
(0, bun_test_1.expect)(nextCalled).toBe(true);
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
(0, bun_test_1.describe)("getSchemaFromModel", function () {
|
|
639
|
+
(0, bun_test_1.it)("returns properties for a Mongoose model", function () {
|
|
640
|
+
var schema = (0, openApiValidator_1.getSchemaFromModel)(tests_1.RequiredModel);
|
|
641
|
+
(0, bun_test_1.expect)(schema.name).toBeDefined();
|
|
642
|
+
(0, bun_test_1.expect)(schema.about).toBeDefined();
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
(0, bun_test_1.describe)("getOpenApiValidatorConfig", function () {
|
|
646
|
+
(0, bun_test_1.it)("returns a shallow copy of the config", function () {
|
|
647
|
+
(0, openApiValidator_1.configureOpenApiValidator)({ removeAdditional: false });
|
|
648
|
+
var config = (0, openApiValidator_1.getOpenApiValidatorConfig)();
|
|
649
|
+
(0, bun_test_1.expect)(config.removeAdditional).toBe(false);
|
|
650
|
+
// Mutating the returned copy should not affect internal state
|
|
651
|
+
config.removeAdditional = true;
|
|
652
|
+
(0, bun_test_1.expect)((0, openApiValidator_1.getOpenApiValidatorConfig)().removeAdditional).toBe(false);
|
|
653
|
+
});
|
|
345
654
|
});
|
|
346
655
|
});
|
package/dist/plugins.d.ts
CHANGED
|
@@ -4,13 +4,13 @@ export interface BaseUser {
|
|
|
4
4
|
admin: boolean;
|
|
5
5
|
email: string;
|
|
6
6
|
}
|
|
7
|
-
export declare
|
|
7
|
+
export declare const baseUserPlugin: (schema: Schema<any, any, any, any>) => void;
|
|
8
8
|
/** For models with the isDeletedPlugin, extend this interface to add the appropriate fields. */
|
|
9
9
|
export interface IsDeleted {
|
|
10
10
|
deleted: boolean;
|
|
11
11
|
}
|
|
12
|
-
export declare
|
|
13
|
-
export declare
|
|
12
|
+
export declare const isDeletedPlugin: (schema: Schema<any, any, any, any>, defaultValue?: boolean) => void;
|
|
13
|
+
export declare const isDisabledPlugin: (schema: Schema<any, any, any, any>, defaultValue?: boolean) => void;
|
|
14
14
|
export interface CreatedDeleted {
|
|
15
15
|
updated: {
|
|
16
16
|
type: Date;
|
|
@@ -21,8 +21,8 @@ export interface CreatedDeleted {
|
|
|
21
21
|
required: true;
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
-
export declare
|
|
25
|
-
export declare
|
|
24
|
+
export declare const createdUpdatedPlugin: (schema: Schema<any, any, any, any>) => void;
|
|
25
|
+
export declare const firebaseJWTPlugin: (schema: Schema) => void;
|
|
26
26
|
/**
|
|
27
27
|
* This adds a static method `Model.findOneOrNone` to the schema. This should replace `Model.findOne` in most instances.
|
|
28
28
|
* `Model.findOne` should only be used with a unique index, but that's not apparent from the docs. Otherwise you can wind
|
|
@@ -30,7 +30,7 @@ export declare function firebaseJWTPlugin(schema: Schema): void;
|
|
|
30
30
|
* document, or throws an exception if multiple are found.
|
|
31
31
|
* @param schema Mongoose Schema
|
|
32
32
|
*/
|
|
33
|
-
export declare
|
|
33
|
+
export declare const findOneOrNone: <T>(schema: Schema<T>) => void;
|
|
34
34
|
/**
|
|
35
35
|
* This adds a static method `Model.findExactlyOne` to the schema. This or findOneOrNone should replace `Model.findOne`
|
|
36
36
|
* in most instances.
|
|
@@ -39,14 +39,14 @@ export declare function findOneOrNone<T>(schema: Schema<T>): void;
|
|
|
39
39
|
* multiple or none are found.
|
|
40
40
|
* @param schema Mongoose Schema
|
|
41
41
|
*/
|
|
42
|
-
export declare
|
|
42
|
+
export declare const findExactlyOne: <T>(schema: Schema<T>) => void;
|
|
43
43
|
/**
|
|
44
44
|
* This adds a static method `Model.upsert` to the schema. This method will either update an existing document
|
|
45
45
|
* that matches the conditions or create a new document if none exists. It throws an error if multiple documents
|
|
46
46
|
* match the conditions to prevent ambiguous updates.
|
|
47
47
|
* @param schema Mongoose Schema
|
|
48
48
|
*/
|
|
49
|
-
export declare
|
|
49
|
+
export declare const upsertPlugin: <T>(schema: Schema<any, any, any, any>) => void;
|
|
50
50
|
/** For models with the upsertPlugin, extend this interface to add the upsert static method. */
|
|
51
51
|
export interface HasUpsert<T> {
|
|
52
52
|
upsert(conditions: Record<string, any>, update: Record<string, any>): Promise<T>;
|