UNPKG

container.ts

Version:
265 lines 11 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var awilix_1 = require("awilix"); var error_1 = require("../lib/error"); var Environment_1 = require("./Environment"); var Log_1 = require("./Log"); var RxJS_1 = require("./RxJS"); /** Container error class. */ var ContainerError = /** @class */ (function (_super) { __extends(ContainerError, _super); function ContainerError(name, cause) { return _super.call(this, { name: name }, cause) || this; } return ContainerError; }(error_1.ErrorChain)); exports.ContainerError = ContainerError; /** Log message class for stream of module logs. */ var ContainerLogMessage = /** @class */ (function () { function ContainerLogMessage(level, message, metadata, args) { this.level = level; this.message = message; this.metadata = metadata; this.args = args; } return ContainerLogMessage; }()); exports.ContainerLogMessage = ContainerLogMessage; /** Metric message class for stream of module metrics. */ var ContainerMetricMessage = /** @class */ (function () { function ContainerMetricMessage(type, name, value, tags) { this.type = type; this.name = name; this.value = value; this.tags = tags; } return ContainerMetricMessage; }()); exports.ContainerMetricMessage = ContainerMetricMessage; /** Wrapper around awilix library. */ var Container = /** @class */ (function () { /** Creates a new container in proxy resolution mode. */ function Container( /** Container name, used to namespace modules. */ name, /** Container environment. */ environment, /** Optional command line arguments. */ argv) { if (environment === void 0) { environment = new Environment_1.Environment(); } if (argv === void 0) { argv = { _: [], $0: "" }; } this.name = name; this.environment = environment; this.argv = argv; /** Observable module state. */ this.modules$ = new RxJS_1.BehaviorSubject({}); /** Container module logs. */ this.logs$ = new RxJS_1.Subject(); /** Container module metrics. */ this.metrics$ = new RxJS_1.Subject(); this.environment = environment; this.container = awilix_1.createContainer({ resolutionMode: awilix_1.ResolutionMode.PROXY }); this.registerValue(Container.REFERENCE, this); } Object.defineProperty(Container.prototype, "moduleNames", { /** Array of registered module names. */ get: function () { return Object.keys(this.modules$.value); }, enumerable: true, configurable: true }); Object.defineProperty(Container.prototype, "modules", { /** Array of registered modules. */ get: function () { var _this = this; return this.moduleNames.map(function (n) { return _this.container.resolve(n); }); }, enumerable: true, configurable: true }); /** Create scope from container. */ Container.prototype.createScope = function () { return this.container.createScope(); }; /** * Register a named module in container. * Throws an error if module of name is already registered. */ Container.prototype.registerModule = function (instance) { if (this.moduleRegistered(instance.moduleName)) { throw new ContainerError(Container.ERROR.MODULE_REGISTERED); } // TODO(MEDIUM): Create separate scope for modules. var factoryFunction = this.moduleFactory.bind(this, instance); this.container.register((_a = {}, _a[instance.moduleName] = awilix_1.asFunction(factoryFunction).singleton(), _a)); this.moduleState(instance.moduleName, false); return this; var _a; }; /** Register named modules in container. */ Container.prototype.registerModules = function (modules) { var _this = this; modules.map(function (mod) { return _this.registerModule(mod); }); return this; }; /** Register a value of type in container. */ Container.prototype.registerValue = function (name, value) { this.container.register((_a = {}, _a[name] = awilix_1.asValue(value), _a)); return this; var _a; }; /** Resolve value or module of type from container by name. */ Container.prototype.resolve = function (name) { return this.container.resolve(name); }; /** Send log message of level for module. */ Container.prototype.sendLog = function (level, message, metadata, args) { this.logs$.next(new ContainerLogMessage(level, message, metadata, args)); }; /** Send metric message of type for module. */ Container.prototype.sendMetric = function (type, name, value, tags) { this.metrics$.next(new ContainerMetricMessage(type, name, value, tags)); }; /** Observable stream of module logs filtered by level. */ Container.prototype.filterLogs = function (level) { return this.logs$.filter(function (m) { return m.level <= level; }); }; /** Observable stream of module metrics filtered by type. */ Container.prototype.filterMetrics = function (type) { return this.metrics$.filter(function (m) { return m.type === type; }); }; /** Signal modules to enter operational state. */ Container.prototype.up = function (timeout) { var _this = this; var observables$ = this.modules .map(function (mod) { return _this.waitUp.apply(_this, _this.moduleDependencies(mod)).switchMap(function () { var up$ = mod.moduleUp(); if (up$ == null) { // Module up returned void, set state now. return _this.moduleState(mod.moduleName, true); } // Observable returned, update state on next. return up$ .switchMap(function () { return _this.moduleState(mod.moduleName, true); }); }); }); return this.containerState(observables$, true, timeout); }; /** Signal modules to leave operational state. */ Container.prototype.down = function (timeout) { var _this = this; var observables$ = this.modules .map(function (mod) { return _this.waitDown.apply(_this, _this.moduleDependants(mod)).switchMap(function () { var down$ = mod.moduleDown(); if (down$ == null) { // Module down returned void, set state now. return _this.moduleState(mod.moduleName, false); } // Observable returned, update state on next. return down$ .switchMap(function () { return _this.moduleState(mod.moduleName, false); }); }); }); return this.containerState(observables$, false, timeout); }; /** Wait for modules to enter operational state before calling next. */ Container.prototype.waitUp = function () { var modules = []; for (var _i = 0; _i < arguments.length; _i++) { modules[_i] = arguments[_i]; } return this.modules$ .filter(function (states) { return modules.reduce(function (previous, current) { return previous && states[current]; }, true); }) .map(function () { return undefined; }) .take(1); }; /** Wait for modules to leave operational state before calling next. */ Container.prototype.waitDown = function () { var modules = []; for (var _i = 0; _i < arguments.length; _i++) { modules[_i] = arguments[_i]; } return this.modules$ .filter(function (states) { return !modules.reduce(function (previous, current) { return previous || states[current]; }, false); }) .map(function () { return undefined; }) .take(1); }; Container.prototype.moduleFactory = function (instance, opts) { return new instance({ moduleName: instance.moduleName, opts: opts }); }; Container.prototype.moduleDependencies = function (mod) { var dependencies = mod.moduleDependencies(); return Object.keys(dependencies).map(function (k) { return dependencies[k].moduleName; }); }; Container.prototype.moduleDependants = function (mod) { var dependants = []; this.modules.map(function (m) { var dependencies = m.moduleDependencies(); var dependant = Object.keys(dependencies).reduce(function (previous, key) { return previous || (dependencies[key].moduleName === mod.moduleName); }, false); if (dependant) { dependants.push(m.moduleName); } }); return dependants; }; Container.prototype.moduleRegistered = function (name) { return (this.modules$.value[name] != null); }; Container.prototype.moduleState = function (name, state) { this.modules$.value[name] = state; this.modules$.next(this.modules$.value); return RxJS_1.Observable.of(undefined); }; Container.prototype.containerState = function (observables$, state, timeout) { var _this = this; if (timeout === void 0) { timeout = 10000; } return RxJS_1.Observable.forkJoin.apply(RxJS_1.Observable, observables$).timeout(timeout) .catch(function (error) { var name = state ? Container.ERROR.UP : Container.ERROR.DOWN; return RxJS_1.Observable.throw(new ContainerError(name, error)); }) .map(function () { var message = state ? Container.LOG.UP : Container.LOG.DOWN; _this.sendLog(Log_1.ELogLevel.Informational, message, { name: _this.name }, []); }); }; /** Container reference name used internally by modules. */ Container.REFERENCE = "_container"; /** Error names. */ Container.ERROR = { UP: "ContainerUpError", DOWN: "ContainerDownError", MODULE_REGISTERED: "ContainerModuleRegisteredError", }; /** Log names. */ Container.LOG = { UP: "ContainerUp", DOWN: "ContainerDown", }; return Container; }()); exports.Container = Container; //# sourceMappingURL=Container.js.map