@selfage/service_handler
Version:
Http-based service handlers on top of expressjs.
81 lines • 12.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceHandler = exports.HandlerRegister = void 0;
const express = require("express");
const promClient = require("prom-client");
const base_remote_call_handler_1 = require("./base_remote_call_handler");
const cors_allowed_preflight_handler_1 = require("./cors_allowed_preflight_handler");
class HandlerRegister {
constructor(app, baseHandler, serviceDescriptor) {
this.baseHandler = baseHandler;
this.serviceDescriptor = serviceDescriptor;
this.router = express.Router();
this.addedPaths = new Set();
app.use(this.serviceDescriptor.path, this.router);
}
add(remoteCallHandler) {
if (remoteCallHandler.descriptor.service !== this.serviceDescriptor) {
throw new Error(`The remote call handler ${remoteCallHandler.descriptor.name} is defined in service ${remoteCallHandler.descriptor.service.name} but being added to service ${this.serviceDescriptor.name}.`);
}
if (this.addedPaths.has(remoteCallHandler.descriptor.path)) {
throw new Error(`The remote call handler ${remoteCallHandler.descriptor.name} is defined with a path path ${remoteCallHandler.descriptor.path} that's already registered.`);
}
this.addedPaths.add(remoteCallHandler.descriptor.path);
this.router.post(remoteCallHandler.descriptor.path, (req, res) => this.baseHandler.handle(remoteCallHandler, req, res));
return this;
}
}
exports.HandlerRegister = HandlerRegister;
class ServiceHandler {
static create(server, allowOrigin = "*", app = express()) {
return new ServiceHandler(server, base_remote_call_handler_1.BaseRemoteCallHandler.create(allowOrigin), cors_allowed_preflight_handler_1.CorsAllowedPreflightHandler.create(allowOrigin), app);
}
constructor(server, baseHandler, corsAllowedPreflightHandler, app) {
this.server = server;
this.baseHandler = baseHandler;
this.corsAllowedPreflightHandler = corsAllowedPreflightHandler;
this.app = app;
this.server.on("request", this.app);
}
addCorsAllowedPreflightHandler() {
this.app.options("/*", (req, res) => this.corsAllowedPreflightHandler.handle(res));
return this;
}
addMetricsHandler() {
this.app.get("/metricsz", async (req, res) => {
res.setHeader("Content-Type", "text/plain");
res.end(await promClient.register.metrics());
});
return this;
}
addHealthCheckHandler() {
this.app.get("/healthz", (req, res) => {
res.end("OK");
});
return this;
}
addReadinessHandler() {
this.app.get("/readiness", (req, res) => {
res.end("OK");
});
return this;
}
addHandlerRegister(serviceDescriptor) {
return new HandlerRegister(this.app, this.baseHandler, serviceDescriptor);
}
async start(port) {
await new Promise((resolve) => {
this.server.listen(port, () => resolve());
});
console.log(`Server is listening on port ${port}.`);
return this;
}
async stop() {
await new Promise((resolve) => this.server.close(() => resolve()));
console.log(`Server has stopped.`);
// Wait for the server to close all connections.
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
exports.ServiceHandler = ServiceHandler;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZV9oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsic2VydmljZV9oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQUFvQztBQUdwQywwQ0FBMkM7QUFDM0MseUVBQW1FO0FBQ25FLHFGQUErRTtBQUkvRSxNQUFhLGVBQWU7SUFJMUIsWUFDRSxHQUF3QixFQUNoQixXQUFrQyxFQUNsQyxpQkFBb0M7UUFEcEMsZ0JBQVcsR0FBWCxXQUFXLENBQXVCO1FBQ2xDLHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBbUI7UUFOdEMsV0FBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUMxQixlQUFVLEdBQWdCLElBQUksR0FBRyxFQUFFLENBQUM7UUFPMUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNwRCxDQUFDO0lBRU0sR0FBRyxDQUFDLGlCQUE2QztRQUN0RCxJQUFJLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxPQUFPLEtBQUssSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7WUFDcEUsTUFBTSxJQUFJLEtBQUssQ0FDYiwyQkFBMkIsaUJBQWlCLENBQUMsVUFBVSxDQUFDLElBQUksMEJBQTBCLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsSUFBSSwrQkFBK0IsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksR0FBRyxDQUM3TCxDQUFDO1FBQ0osQ0FBQztRQUNELElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDM0QsTUFBTSxJQUFJLEtBQUssQ0FDYiwyQkFBMkIsaUJBQWlCLENBQUMsVUFBVSxDQUFDLElBQUksZ0NBQWdDLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxJQUFJLDZCQUE2QixDQUMz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QyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQzVDLENBQUMsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxDQUFDLEdBQUcsQ0FBQywrQkFBK0IsSUFBSSxHQUFHLENBQUMsQ0FBQztRQUNwRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFTSxLQUFLLENBQUMsSUFBSTtRQUNmLE1BQU0sSUFBSSxPQUFPLENBQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUN6RSxPQUFPLENBQUMsR0FBRyxDQUFDLHFCQUFxQixDQUFDLENBQUM7UUFDbkMsZ0RBQWdEO1FBQ2hELE1BQU0sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUM1RCxDQUFDO0NBQ0Y7QUF4RUQsd0NBd0VDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGV4cHJlc3MgPSByZXF1aXJlKFwiZXhwcmVzc1wiKTtcbmltcG9ydCBodHRwID0gcmVxdWlyZShcImh0dHBcIik7XG5pbXBvcnQgaHR0cHMgPSByZXF1aXJlKFwiaHR0cHNcIik7XG5pbXBvcnQgcHJvbUNsaWVudCA9IHJlcXVpcmUoXCJwcm9tLWNsaWVudFwiKTtcbmltcG9ydCB7IEJhc2VSZW1vdGVDYWxsSGFuZGxlciB9IGZyb20gXCIuL2Jhc2VfcmVtb3RlX2NhbGxfaGFuZGxlclwiO1xuaW1wb3J0IHsgQ29yc0FsbG93ZWRQcmVmbGlnaHRIYW5kbGVyIH0gZnJvbSBcIi4vY29yc19hbGxvd2VkX3ByZWZsaWdodF9oYW5kbGVyXCI7XG5pbXBvcnQgeyBTZXJ2aWNlRGVzY3JpcHRvciB9IGZyb20gXCJAc2VsZmFnZS9zZXJ2aWNlX2Rlc2NyaXB0b3JcIjtcbmltcG9ydCB7IFJlbW90ZUNhbGxIYW5kbGVySW50ZXJmYWNlIH0gZnJvbSBcIkBzZWxmYWdlL3NlcnZpY2VfZGVzY3JpcHRvci9yZW1vdGVfY2FsbF9oYW5kbGVyX2ludGVyZmFjZVwiO1xuXG5leHBvcnQgY2xhc3MgSGFuZGxlclJlZ2lzdGVyIHtcbiAgcHJpdmF0ZSByb3V0ZXIgPSBleHByZXNzLlJvdXRlcigpO1xuICBwcml2YXRlIGFkZGVkUGF0aHM6IFNldDxzdHJpbmc+ID0gbmV3IFNldCgpO1xuXG4gIHB1YmxpYyBjb25zdHJ1Y3RvcihcbiAgICBhcHA6IGV4cHJlc3MuQXBwbGljYXRpb24sXG4gICAgcHJpdmF0ZSBiYXNlSGFuZGxlcjogQmFzZVJlbW90ZUNhbGxIYW5kbGVyLFxuICAgIHByaXZhdGUgc2VydmljZURlc2NyaXB0b3I6IFNlcnZpY2VEZXNjcmlwdG9yLFxuICApIHtcbiAgICBhcHAudXNlKHRoaXMuc2VydmljZURlc2NyaXB0b3IucGF0aCwgdGhpcy5yb3V0ZXIpO1xuICB9XG5cbiAgcHVibGljIGFkZChyZW1vdGVDYWxsSGFuZGxlcjogUmVtb3RlQ2FsbEhhbmRsZXJJbnRlcmZhY2UpOiB0aGlzIHtcbiAgICBpZiAocmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5zZXJ2aWNlICE9PSB0aGlzLnNlcnZpY2VEZXNjcmlwdG9yKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBUaGUgcmVtb3RlIGNhbGwgaGFuZGxlciAke3JlbW90ZUNhbGxIYW5kbGVyLmRlc2NyaXB0b3IubmFtZX0gaXMgZGVmaW5lZCBpbiBzZXJ2aWNlICR7cmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5zZXJ2aWNlLm5hbWV9IGJ1dCBiZWluZyBhZGRlZCB0byBzZXJ2aWNlICR7dGhpcy5zZXJ2aWNlRGVzY3JpcHRvci5uYW1lfS5gLFxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuYWRkZWRQYXRocy5oYXMocmVtb3RlQ2FsbEhhbmRsZXIuZGVzY3JpcHRvci5wYXRoKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgVGhlIHJlbW90ZSBjYWxsIGhhbmRsZXIgJHtyZW1vdGVDYWxsSGFuZGxlci5kZXNjcmlwdG9yLm5hbWV9IGlzIGRlZmluZWQgd2l0aCBhIHBhdGggcGF0aCAke3JlbW90ZUNhbGxIYW5kbGVyLmRlc2NyaXB0b3IucGF0aH0gdGhhdCdzIGFscmVhZHkgcmVnaXN0ZXJlZC5gLFxuICAgICAgKTtcbiAgICB9XG4gICAgdGhpcy5hZGRlZFBhdGhzLmFkZChyZW1vdGVDYWxsSGFuZGxlci5kZXNjcmlwdG9yLnBhdGgpO1xuXG4gICAgdGhpcy5yb3V0ZXIucG9zdChyZW1vdGVDYWxsSGFuZGxlci5kZXNjcmlwdG9yLnBhdGgsIChyZXEsIHJlcykgPT5cbiAgICAgIHRoaXMuYmFzZUhhbmRsZXIuaGFuZGxlKHJlbW90ZUNhbGxIYW5kbGVyLCByZXEsIHJlcyksXG4gICAgKTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgU2VydmljZUhhbmRsZXIge1xuICBwdWJsaWMgc3RhdGljIGNyZWF0ZShcbiAgICBzZXJ2ZXI6IGh0dHAuU2VydmVyIHwgaHR0cHMuU2VydmVyLFxuICAgIGFsbG93T3JpZ2luOiBzdHJpbmcgPSBcIipcIixcbiAgICBhcHA6IGV4cHJlc3MuRXhwcmVzcyA9IGV4cHJlc3MoKSxcbiAgKTogU2VydmljZUhhbmRsZXIge1xuICAgIHJldHVybiBuZXcgU2VydmljZUhhbmRsZXIoXG4gICAgICBzZXJ2ZXIsXG4gICAgICBCYXNlUmVtb3RlQ2FsbEhhbmRsZXIuY3JlYXRlKGFsbG93T3JpZ2luKSxcbiAgICAgIENvcnNBbGxvd2VkUHJlZmxpZ2h0SGFuZGxlci5jcmVhdGUoYWxsb3dPcmlnaW4pLFxuICAgICAgYXBwLFxuICAgICk7XG4gIH1cblxuICBwdWJsaWMgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBzZXJ2ZXI6IGh0dHAuU2VydmVyIHwgaHR0cHMuU2VydmVyLFxuICAgIHByaXZhdGUgYmFzZUhhbmRsZXI6IEJhc2VSZW1vdGVDYWxsSGFuZGxlcixcbiAgICBwcml2YXRlIGNvcnNBbGxvd2VkUHJlZmxpZ2h0SGFuZGxlcjogQ29yc0FsbG93ZWRQcmVmbGlnaHRIYW5kbGVyLFxuICAgIHByaXZhdGUgYXBwOiBleHByZXNzLkV4cHJlc3MsXG4gICkge1xuICAgIHRoaXMuc2VydmVyLm9uKFwicmVxdWVzdFwiLCB0aGlzLmFwcCk7XG4gIH1cblxuICBwdWJsaWMgYWRkQ29yc0FsbG93ZWRQcmVmbGlnaHRIYW5kbGVyKCk6IHRoaXMge1xuICAgIHRoaXMuYXBwLm9wdGlvbnMoXCIvKlwiLCAocmVxLCByZXMpID0+XG4gICAgICB0aGlzLmNvcnNBbGxvd2VkUHJlZmxpZ2h0SGFuZGxlci5oYW5kbGUocmVzKSxcbiAgICApO1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgcHVibGljIGFkZE1ldHJpY3NIYW5kbGVyKCk6IHRoaXMge1xuICAgIHRoaXMuYXBwLmdldChcIi9tZXRyaWNzelwiLCBhc3luYyAocmVxLCByZXMpID0+IHtcbiAgICAgIHJlcy5zZXRIZWFkZXIoXCJDb250ZW50LVR5cGVcIiwgXCJ0ZXh0L3BsYWluXCIpO1xuICAgICAgcmVzLmVuZChhd2FpdCBwcm9tQ2xpZW50LnJlZ2lzdGVyLm1ldHJpY3MoKSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICBwdWJsaWMgYWRkSGVhbHRoQ2hlY2tIYW5kbGVyKCk6IHRoaXMge1xuICAgIHRoaXMuYXBwLmdldChcIi9oZWFsdGh6XCIsIChyZXEsIHJlcykgPT4ge1xuICAgICAgcmVzLmVuZChcIk9LXCIpO1xuICAgIH0pO1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgcHVibGljIGFkZFJlYWRpbmVzc0hhbmRsZXIoKTogdGhpcyB7XG4gICAgdGhpcy5hcHAuZ2V0KFwiL3JlYWRpbmVzc1wiLCAocmVxLCByZXMpID0+IHtcbiAgICAgIHJlcy5lbmQoXCJPS1wiKTtcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIHB1YmxpYyBhZGRIYW5kbGVyUmVnaXN0ZXIoXG4gICAgc2VydmljZURlc2NyaXB0b3I6IFNlcnZpY2VEZXNjcmlwdG9yLFxuICApOiBIYW5kbGVyUmVnaXN0ZXIge1xuICAgIHJldHVybiBuZXcgSGFuZGxlclJlZ2lzdGVyKHRoaXMuYXBwLCB0aGlzLmJhc2VIYW5kbGVyLCBzZXJ2aWNlRGVzY3JpcHRvcik7XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgc3RhcnQocG9ydDogbnVtYmVyKTogUHJvbWlzZTx0aGlzPiB7XG4gICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHtcbiAgICAgIHRoaXMuc2VydmVyLmxpc3Rlbihwb3J0LCAoKSA9PiByZXNvbHZlKCkpO1xuICAgIH0pO1xuICAgIGNvbnNvbGUubG9nKGBTZXJ2ZXIgaXMgbGlzdGVuaW5nIG9uIHBvcnQgJHtwb3J0fS5gKTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBzdG9wKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiB0aGlzLnNlcnZlci5jbG9zZSgoKSA9PiByZXNvbHZlKCkpKTtcbiAgICBjb25zb2xlLmxvZyhgU2VydmVyIGhhcyBzdG9wcGVkLmApO1xuICAgIC8vIFdhaXQgZm9yIHRoZSBzZXJ2ZXIgdG8gY2xvc2UgYWxsIGNvbm5lY3Rpb25zLlxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDEwMDApKTtcbiAgfVxufVxuIl19