UNPKG

inversify-express-utils

Version:

Some utilities for the development of express applications with Inversify

258 lines 11.6 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable @typescript-eslint/no-unused-vars */ const globals_1 = require("@jest/globals"); const inversify_1 = require("inversify"); const supertest_1 = __importDefault(require("supertest")); const index_1 = require("../index"); const utils_1 = require("../utils"); (0, globals_1.describe)('BaseMiddleware', () => { (0, globals_1.beforeEach)(() => { (0, utils_1.cleanUpMetadata)(); }); (0, globals_1.it)('Should be able to inject BaseMiddleware implementations', async () => { // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { LoggerMiddleware: Symbol.for('LoggerMiddleware'), SomeDependency: Symbol.for('SomeDependency'), }; let principalInstanceCount = 0; class PrincipalClass { details; constructor(details) { this.details = details; } async isAuthenticated() { return Promise.resolve(true); } async isResourceOwner(resourceId) { return Promise.resolve(resourceId === 1111); } async isInRole(role) { return Promise.resolve(role === 'admin'); } } let CustomAuthProvider = class CustomAuthProvider { async getUser(_req, _res, _next) { principalInstanceCount += 1; const principal = new PrincipalClass({ email: 'test@test.com', }); return principal; } }; CustomAuthProvider = __decorate([ (0, inversify_1.injectable)() ], CustomAuthProvider); const logEntries = []; let LoggerMiddleware = class LoggerMiddleware extends index_1.BaseMiddleware { _someDependency; handler(req, _res, next) { const { email } = this.httpContext.user.details; logEntries.push(`${email} => ${req.url} ${this._someDependency.name}`); next(); } }; __decorate([ (0, inversify_1.inject)(TYPES.SomeDependency), __metadata("design:type", Object) ], LoggerMiddleware.prototype, "_someDependency", void 0); LoggerMiddleware = __decorate([ (0, inversify_1.injectable)() ], LoggerMiddleware); let TestController = class TestController extends index_1.BaseHttpController { async getTest() { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (this.httpContext.user !== null) { const { email } = this.httpContext.user .details; const isAuthenticated = await this.httpContext.user.isAuthenticated(); logEntries.push(`${email} => isAuthenticated() => ${String(isAuthenticated)}`); return email; } return null; } }; __decorate([ (0, index_1.httpGet)('/', TYPES.LoggerMiddleware), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], TestController.prototype, "getTest", null); TestController = __decorate([ (0, index_1.controller)('/', (_req, _res, next) => { logEntries.push('Hello from controller middleware!'); next(); }) ], TestController); const container = new inversify_1.Container(); container .bind(TYPES.SomeDependency) .toConstantValue({ name: 'SomeDependency!' }); container .bind(TYPES.LoggerMiddleware) .to(LoggerMiddleware); const server = new index_1.InversifyExpressServer(container, null, null, null, CustomAuthProvider); await (0, supertest_1.default)(server.build()).get('/').expect(200, 'test@test.com'); (0, globals_1.expect)(principalInstanceCount).toBe(1); (0, globals_1.expect)(logEntries.length).toBe(3); (0, globals_1.expect)(logEntries[0]).toBe('Hello from controller middleware!'); (0, globals_1.expect)(logEntries[1]).toBe('test@test.com => / SomeDependency!'); (0, globals_1.expect)(logEntries[2]).toBe('test@test.com => isAuthenticated() => true'); }); (0, globals_1.it)('Should allow the middleware to inject services in a HTTP request scope', async () => { const TRACE_HEADER = 'X-Trace-Id'; // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Service: Symbol.for('Service'), TraceIdValue: Symbol.for('TraceIdValue'), TracingMiddleware: Symbol.for('TracingMiddleware'), }; let TracingMiddleware = class TracingMiddleware extends index_1.BaseMiddleware { handler(req, res, next) { setTimeout(() => { this.bind(TYPES.TraceIdValue).toConstantValue(req.header(TRACE_HEADER)); next(); }, someTimeBetween(0, 500)); } }; TracingMiddleware = __decorate([ (0, inversify_1.injectable)() ], TracingMiddleware); let Service = class Service { traceID; constructor(traceID) { this.traceID = traceID; } async doSomethingThatRequiresTraceId() { return new Promise((resolve) => { setTimeout(() => { resolve(this.traceID); }, someTimeBetween(0, 500)); }); } }; Service = __decorate([ (0, inversify_1.injectable)(), __param(0, (0, inversify_1.inject)(TYPES.TraceIdValue)), __metadata("design:paramtypes", [String]) ], Service); let TracingTestController = class TracingTestController extends index_1.BaseHttpController { service; constructor(service) { super(); this.service = service; } async getTest() { return this.service.doSomethingThatRequiresTraceId(); } }; __decorate([ (0, index_1.httpGet)('/', TYPES.TracingMiddleware), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], TracingTestController.prototype, "getTest", null); TracingTestController = __decorate([ (0, index_1.controller)('/'), __param(0, (0, inversify_1.inject)(TYPES.Service)), __metadata("design:paramtypes", [Service]) ], TracingTestController); const container = new inversify_1.Container(); container .bind(TYPES.TracingMiddleware) .to(TracingMiddleware); container.bind(TYPES.Service).to(Service); container.bind(TYPES.TraceIdValue).toConstantValue('undefined'); const api = new index_1.InversifyExpressServer(container).build(); const expectedRequests = 100; await run(expectedRequests, async (executionId) => { await (0, supertest_1.default)(api) .get('/') .set(TRACE_HEADER, `trace-id-${executionId.toString()}`) .expect(200, `trace-id-${executionId.toString()}`); }); }); (0, globals_1.it)('Should not allow services injected into a HTTP request scope to be accessible outside the request scope', async () => { // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Transaction: Symbol.for('Transaction'), TransactionMiddleware: Symbol.for('TransactionMiddleware'), }; class TransactionMiddleware extends index_1.BaseMiddleware { count = 0; handler(req, res, next) { this.bind(TYPES.Transaction).toConstantValue(`I am transaction #${(this.count += 1).toString()}\n`); next(); } } let TransactionTestController = class TransactionTestController extends index_1.BaseHttpController { transaction; constructor(transaction) { super(); this.transaction = transaction; } getTest1() { return this.transaction; } getTest2() { return 'No middleware!'; } }; __decorate([ (0, index_1.httpGet)('/1', TYPES.TransactionMiddleware), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], TransactionTestController.prototype, "getTest1", null); __decorate([ (0, index_1.httpGet)('/2' /*<= No middleware!*/), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], TransactionTestController.prototype, "getTest2", null); TransactionTestController = __decorate([ (0, index_1.controller)('/'), __param(0, (0, inversify_1.inject)(TYPES.Transaction)), __param(0, (0, inversify_1.optional)()), __metadata("design:paramtypes", [String]) ], TransactionTestController); const container = new inversify_1.Container(); container .bind(TYPES.TransactionMiddleware) .to(TransactionMiddleware) .inSingletonScope(); const app = new index_1.InversifyExpressServer(container).build(); await (0, supertest_1.default)(app).get('/1').expect(200, 'I am transaction #1\n'); await (0, supertest_1.default)(app).get('/1').expect(200, 'I am transaction #2\n'); await (0, supertest_1.default)(app).get('/2').expect(200, 'No middleware!'); }); }); async function run(parallelRuns, test) { const tasks = new Array(parallelRuns); for (let i = 0; i < parallelRuns; ++i) { tasks[i] = test(i); } await Promise.all(tasks); } function someTimeBetween(minimum, maximum) { const min = Math.ceil(minimum); const max = Math.floor(maximum); return Math.floor(Math.random() * (max - min + 1)) + min; } //# sourceMappingURL=base_middleware.test.js.map