inversify-express-utils
Version:
Some utilities for the development of express applications with Inversify
258 lines • 11.6 kB
JavaScript
;
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