UNPKG

vulcain-corejs

Version:
306 lines (304 loc) 12.1 kB
"use strict"; require("reflect-metadata"); const preloader_1 = require("../preloader"); const scope_1 = require("./scope"); const resolvers_1 = require("./resolvers"); const resolvers_2 = require("../di/resolvers"); const annotations_1 = require("../di/annotations"); const rabbitAdapter_1 = require("../bus/rabbitAdapter"); const provider_1 = require("../providers/memory/provider"); const provider_2 = require("../providers/mongo/provider"); const providerFactory_1 = require("../providers/providerFactory"); const schema_1 = require("../schemas/schema"); const annotations_2 = require("./annotations"); const files_1 = require("../utils/files"); const requestContext_1 = require("./../servers/requestContext"); const serviceDescriptions_1 = require("./../pipeline/serviceDescriptions"); const vulcainLogger_1 = require("./../configurations/log/vulcainLogger"); const system_1 = require("./../configurations/globals/system"); const consoleMetrics_1 = require("../metrics/consoleMetrics"); const statsdMetrics_1 = require("../metrics/statsdMetrics"); const defaultAuthorizationPolicy_1 = require("../servers/policy/defaultAuthorizationPolicy"); const defaultTenantPolicy_1 = require("../servers/policy/defaultTenantPolicy"); const dynamicConfiguration_1 = require("../configurations/dynamicConfiguration"); /** * Component container for dependency injection * * @export * @class Container * @implements {IContainer} */ class Container { /** * Creates an instance of Container. * * @param {IContainer} [parent] * @param {RequestContext} [requestContext] * * @memberOf Container */ constructor(parent, requestContext) { this.parent = parent; this.resolvers = new Map(); if (parent && !requestContext) throw new Error("RequestContext must not be null."); this.scope = new scope_1.Scope(parent && parent.scope, requestContext); this.injectInstance(this, annotations_1.DefaultServiceNames.Container); this.setRequestContext(requestContext); if (!parent) { this.injectInstance(new vulcainLogger_1.VulcainLogger(), annotations_1.DefaultServiceNames.Logger); this.injectSingleton(serviceDescriptions_1.ServiceDescriptors, annotations_1.DefaultServiceNames.ServiceDescriptors); this.injectSingleton(providerFactory_1.ProviderFactory, annotations_1.DefaultServiceNames.ProviderFactory); this.injectSingleton(system_1.System.isDevelopment ? consoleMetrics_1.ConsoleMetrics : statsdMetrics_1.StatsdMetrics, annotations_1.DefaultServiceNames.Metrics); this.injectSingleton(defaultAuthorizationPolicy_1.DefaultAuthorizationPolicy, annotations_1.DefaultServiceNames.AuthorizationPolicy); this.injectSingleton(defaultTenantPolicy_1.DefaultTenantPolicy, annotations_1.DefaultServiceNames.TenantPolicy); } } /** * used by test * * @protected * @param {RequestContext} requestContext * * @memberOf Container */ setRequestContext(requestContext) { if (requestContext) { this.injectInstance(requestContext, annotations_1.DefaultServiceNames.RequestContext); this.scope.requestContext = requestContext; } } dispose() { this.scope.dispose(); this.resolvers.clear(); this.parent = null; } /** * Inject all components from files of the specified folder. * Files are loaded recursively * * @param {string} folder path relative to the current directory * @returns the current container */ injectFrom(path) { files_1.Files.traverse(path); return this; } /** * * * @param {string} address rabbitmq server address (only host name and optional port) * @param {any} [usage=BusUsage.all] */ useRabbitBusAdapter(address, usage = resolvers_2.BusUsage.all) { let uri = system_1.System.resolveAlias(address) || address || dynamicConfiguration_1.DynamicConfiguration.getPropertyValue("rabbit"); if (!uri) { system_1.System.log.info(null, "no value found for rabbit address. Ignore adapter"); return; } if (!uri.startsWith("amqp://")) { uri = "amqp://" + uri; } let bus = new rabbitAdapter_1.RabbitAdapter(uri); if (usage === resolvers_2.BusUsage.all || usage === resolvers_2.BusUsage.eventOnly) this.injectInstance(bus, annotations_1.DefaultServiceNames.EventBusAdapter); if (usage === resolvers_2.BusUsage.all || usage === resolvers_2.BusUsage.commandOnly) this.injectInstance(bus, annotations_1.DefaultServiceNames.ActionBusAdapter); } /** * Use mongo provider * * @param {string} mongo server address (only host name or list of host names) * @param {any} [mongoOptions] Mongodb options */ useMongoProvider(address, mongoOptions) { let uri = system_1.System.resolveAlias(address) || address || dynamicConfiguration_1.DynamicConfiguration.getPropertyValue("mongo"); if (!uri) { system_1.System.log.info(null, "no value found for mongo address. Ignore adapter"); return; } if (!uri.startsWith("mongodb://")) { uri = "mongodb://" + uri; } this.injectTransient(provider_2.MongoProvider, annotations_1.DefaultServiceNames.Provider, uri, mongoOptions); } /** * Use a memory provider by default * * @param {string} [folder] Data can be persisted on disk (on every change) */ useMemoryProvider(folder) { this.injectTransient(provider_1.MemoryProvider, annotations_1.DefaultServiceNames.Provider, folder); } /** * Register a instance of a component * * @param name * @param fn * @returns {Container} */ injectInstance(fn, name) { if (!name) throw new Error("Name is required."); this.resolvers.set(name, new resolvers_1.InstanceResolver(fn)); if (name !== "Container" && fn.name) system_1.System.log.info(null, "INFO: Register instance component " + name + " as " + fn.name); return this; } injectSingleton(fn, nameOrArray, ...args) { let name; if (Array.isArray(nameOrArray)) { args = nameOrArray; name = null; } else { name = nameOrArray; } let attr = Reflect.getOwnMetadata(Symbol.for("di:export"), fn); name = name || attr && attr.name || fn.name; if (!name) throw new Error("Can not find a name when injecting component. Use @Export."); this.resolvers.set(name, new resolvers_1.SingletonResolver(fn, Array.from(args))); if (fn.name) system_1.System.log.info(null, "INFO: Register instance component " + name + " as " + fn.name); return this; } injectTransient(fn, nameOrArray, ...args) { let name; if (Array.isArray(nameOrArray)) { args = nameOrArray; name = null; } else { name = nameOrArray; } let attr = Reflect.getOwnMetadata(Symbol.for("di:export"), fn); name = name || attr && attr.name || fn.name; if (!name) return; this.resolvers.set(name, new resolvers_1.Resolver(fn, annotations_2.LifeTime.Transient, Array.from(args))); if (fn.name) system_1.System.log.info(null, "INFO: Register instance component " + name + " as " + fn.name); return this; } injectScoped(fn, nameOrArray, ...args) { let name; if (Array.isArray(nameOrArray)) { args = nameOrArray; name = null; } else { name = nameOrArray; } let attr = Reflect.getOwnMetadata(Symbol.for("di:export"), fn); name = name || attr && attr.name || fn.name; if (!name) throw new Error("Cannot find a name when injecting component. Use @Export."); this.resolvers.set(name, new resolvers_1.ScopedResolver(fn, Array.from(args))); if (fn.name) system_1.System.log.info(null, "INFO: Register instance component " + name + " as " + fn.name); return this; } /** * Helper for injecting component * * @param {string} name Component name * @param {any} fn Component constructor * @param {LifeTime} lifeTime Component lifetime * * @memberOf Container */ inject(name, fn, lifeTime) { if (lifeTime) { switch (lifeTime) { case annotations_2.LifeTime.Singleton: this.injectSingleton(fn, name); break; case annotations_2.LifeTime.Scoped: this.injectScoped(fn, name); break; case annotations_2.LifeTime.Transient: this.injectTransient(fn, name); break; } } else this.injectTransient(fn, name); } /** * * Instanciate a component and resolve all of its dependencies * @param fn Component constructor * @param args List of optional arguments * @returns {null} */ resolve(fn, ...args) { if (typeof fn !== "function") throw new Error("fn must be a ctor"); let resolver = new resolvers_1.Resolver(fn, annotations_2.LifeTime.Transient, Array.from(args)); return this._resolve(this, resolver); } _resolve(parentContainer, resolver, name, optional) { let instance = resolver && resolver.resolve(this, name, parentContainer); if (!instance && !optional) throw new Error("Unable to resolve component " + name); return instance; } findResolver(name) { let self = this; while (self) { let resolver = self.resolvers.get(name); if (resolver) return { resolver, container: self }; self = self.parent; } return null; } /** * Get a component by name. * Throw an exception if the component doesn't exist * @template T * @param {string} component name * @param {boolean} [optional] if true no exception are raised if the component doesn't exist * @param {LifeTime} [assertLifeTime] If provide check if the registered component has the expected {LifeTime} * @returns A component */ get(name, optional, assertLifeTime) { let res = this.findResolver(name); let resolver = res && res.resolver; if (assertLifeTime && resolver) { if (!(assertLifeTime & resolver.lifeTime)) throw new Error("Component " + name + " must be declared with a life time = " + assertLifeTime); } let component = this._resolve(res && res.container, resolver, name, optional); return component; } } exports.Container = Container; /** * Default container for test * * @export * @class TestContainer * @extends {Container} */ class TestContainer extends Container { /** * Creates an instance of TestContainer. * * @param {string} domainName Domain name * @param {(container: IContainer) => void} [addDefaultServices] Additional default services to register */ constructor(domainName, addDefaultServices) { super(); this.domainName = domainName; this.setRequestContext(requestContext_1.RequestContext.createMock(this)); this.injectTransient(provider_1.MemoryProvider, annotations_1.DefaultServiceNames.Provider); let domain = new schema_1.Domain(domainName, this); this.injectInstance(domain, annotations_1.DefaultServiceNames.Domain); addDefaultServices && addDefaultServices(this); preloader_1.Preloader.instance.runPreloads(this, domain); } } exports.TestContainer = TestContainer; //# sourceMappingURL=containers.js.map