UNPKG

vulcain-corejs

Version:
350 lines 14.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); require("reflect-metadata"); const scope_1 = require("./scope"); const resolvers_1 = require("./resolvers"); const annotations_1 = require("./annotations"); const files_1 = require("../utils/files"); const vulcainLogger_1 = require("./../log/vulcainLogger"); const system_1 = require("./../globals/system"); const dynamicConfiguration_1 = require("../configurations/dynamicConfiguration"); const serviceResolver_1 = require("../di/serviceResolver"); const authorizationPolicy_1 = require("../security/authorizationPolicy"); const tokenService_1 = require("../security/services/tokenService"); const defaultTenantPolicy_1 = require("../pipeline/policies/defaultTenantPolicy"); const providerFactory_1 = require("../providers/memory/providerFactory"); const provider_1 = require("../providers/mongo/provider"); const metrics_1 = require("../instrumentations/metrics"); const serviceDescriptions_1 = require("../pipeline/handlers/descriptions/serviceDescriptions"); const stubManager_1 = require("../stubs/stubManager"); const index_1 = require("../instrumentations/trackers/index"); const scopeDescriptors_1 = require("../defaults/scopeDescriptors"); const swaggerServiceDescriptions_1 = require("../defaults/swagger/swaggerServiceDescriptions"); const rabbitAdapter_1 = require("../bus/rabbitAdapter"); /** * Component container for dependency injection * * @export * @class Container * @implements {IContainer} */ class Container { /** * Creates an instance of Container. * * @param {IContainer} [parent] * @param {RequestContext} [context] * * @memberOf Container */ constructor(parent, context) { this.parent = parent; this.resolvers = new Map(); this.disposed = false; this.customEndpoints = []; if (parent && !context) throw new Error("RequestContext must not be null."); this.scope = new scope_1.Scope(parent && parent.scope, context); this.injectInstance(this, annotations_1.DefaultServiceNames.Container); this.setRequestContext(context); if (!parent) { this.injectInstance(new vulcainLogger_1.VulcainLogger(), annotations_1.DefaultServiceNames.Logger); this.injectSingleton(authorizationPolicy_1.DefaultAuthorizationPolicy, annotations_1.DefaultServiceNames.AuthorizationPolicy); this.injectSingleton(serviceResolver_1.ServiceResolver, annotations_1.DefaultServiceNames.ServiceResolver); //this.injectScoped(SwaggerServiceDescriptor, DefaultServiceNames.SwaggerServiceDescriptor); this.injectSingleton(serviceDescriptions_1.ServiceDescriptors, annotations_1.DefaultServiceNames.ServiceDescriptors); this.injectSingleton(providerFactory_1.MemoryProviderFactory, annotations_1.DefaultServiceNames.ProviderFactory); this.injectSingleton(defaultTenantPolicy_1.DefaultTenantPolicy, annotations_1.DefaultServiceNames.TenantPolicy); this.injectSingleton(stubManager_1.StubManager, annotations_1.DefaultServiceNames.StubManager); this.injectSingleton(tokenService_1.TokenService, annotations_1.DefaultServiceNames.AuthenticationStrategy); this.injectInstance(metrics_1.MetricsFactory.create(this), annotations_1.DefaultServiceNames.Metrics); this.injectInstance(index_1.TrackerFactory.create(this), annotations_1.DefaultServiceNames.RequestTracker); this.injectSingleton(scopeDescriptors_1.ScopesDescriptor, annotations_1.DefaultServiceNames.ScopesDescriptor); this.injectScoped(swaggerServiceDescriptions_1.SwaggerServiceDescriptor, annotations_1.DefaultServiceNames.SwaggerServiceDescriptor); // Try to initialize Rabbit provider if there is a 'mongo' environment variable let rabbitAddress = dynamicConfiguration_1.DynamicConfiguration.getPropertyValue("rabbit"); if (rabbitAddress) this.useRabbitBusAdapter(rabbitAddress); // Try to initialize Mongo provider if there is a 'mongo' environment variable let mongoAddress = dynamicConfiguration_1.DynamicConfiguration.getPropertyValue("mongo"); if (mongoAddress) this.useMongoProvider(mongoAddress); } } /** * used by test * * @protected * @param {RequestContext} context * * @memberOf Container */ setRequestContext(context) { if (context) { this.scope.context = context; } } dispose() { this.scope.dispose(); this.resolvers.clear(); this.parent = null; this.disposed = true; } /** * 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_1.BusUsage.all) { let uri = system_1.Service.resolveAlias(address) || dynamicConfiguration_1.DynamicConfiguration.getPropertyValue("rabbit") || address; if (!uri) { system_1.Service.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_1.BusUsage.all || usage === resolvers_1.BusUsage.eventOnly) this.injectInstance(bus, annotations_1.DefaultServiceNames.EventBusAdapter); if (usage === resolvers_1.BusUsage.all || usage === resolvers_1.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.Service.resolveAlias(address) || dynamicConfiguration_1.DynamicConfiguration.getPropertyValue("mongo") || address; if (!uri) { system_1.Service.log.info(null, () => "no value found for mongo address. Ignore adapter"); return; } if (!uri.startsWith("mongodb://")) { uri = "mongodb://" + uri; } this.injectSingleton(provider_1.MongoProviderFactory, annotations_1.DefaultServiceNames.ProviderFactory, uri, mongoOptions); } /** * Use a memory provider by default * * @param {string} [folder] Data can be persisted on disk (on every change) */ useMemoryProvider(folder) { this.injectSingleton(providerFactory_1.MemoryProviderFactory, annotations_1.DefaultServiceNames.ProviderFactory, folder); } // Insert always in first position // so 'get' take the last inserted addResolver(name, resolver) { let list = this.resolvers.get(name); if (!list) { list = [resolver]; } else { list.unshift(resolver); } this.resolvers.set(name, list); } /** * Register a instance of a component * * @param name * @param fn * @returns {Container} */ injectInstance(fn, name) { if (!fn) return; if (!name) throw new Error("Name is required."); this.addResolver(name, new resolvers_1.InstanceResolver(fn)); if (name !== "Container" && fn.name) system_1.Service.log.verbose(null, () => "INFO: Register instance component " + name + " as " + fn.name); return this; } injectSingleton(fn, nameOrArray, ...args) { if (!fn) return; 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.addResolver(name, new resolvers_1.SingletonResolver(fn, Array.from(args))); if (fn.name) system_1.Service.log.verbose(null, () => "INFO: Register instance component " + name + " as " + fn.name); return this; } injectTransient(fn, nameOrArray, ...args) { if (!fn) return; 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.addResolver(name, new resolvers_1.Resolver(fn, annotations_1.LifeTime.Transient, Array.from(args))); if (fn.name) system_1.Service.log.verbose(null, () => "INFO: Register instance component " + name + " as " + fn.name); return this; } injectScoped(fn, nameOrArray, ...args) { if (!fn) return; 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.addResolver(name, new resolvers_1.ScopedResolver(fn, Array.from(args))); if (fn.name) system_1.Service.log.verbose(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_1.LifeTime.Singleton: this.injectSingleton(fn, name); break; case annotations_1.LifeTime.Scoped: this.injectScoped(fn, name); break; case annotations_1.LifeTime.Transient: this.injectTransient(fn, name); break; } } else this.injectTransient(fn, name); } /** * * Instantiate a component and resolve all 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_1.LifeTime.Transient, Array.from(args)); return this._resolve(this, resolver); } _resolve(parentContainer, resolver, name, optional) { if (this.disposed) { throw new Error("Can not resolved component from a disposed container."); } let instance = resolver && resolver.resolve(this, name, parentContainer); if (!instance && !optional) throw new Error("Unable to resolve component " + name); return instance; } findResolvers(name) { let self = this; let list = []; while (self) { let resolvers = self.resolvers.get(name); if (resolvers) { list = list.concat(resolvers.map(resolver => { return { resolver, container: self }; })); } self = self.parent; } return list; } /** * 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 resolvers = this.findResolvers(name); let item = resolvers.length > 0 && resolvers[0]; if (assertLifeTime && item) { if (!(assertLifeTime & item.resolver.lifeTime)) throw new Error("Component " + name + " must be declared with a life time = " + assertLifeTime); } let component = this._resolve(item && item.container, item.resolver, name, optional); return component; } getList(name) { let resolvers = this.findResolvers(name); let list = []; for (let item of resolvers) { let component = this._resolve(item && item.container, item.resolver, name, true); if (component) { list.push(component); } } return list; } registerHTTPEndpoint(verb, path, handler) { this.customEndpoints.push({ kind: "HTTP", verb, path, handler }); } registerSSEEndpoint(path, handler, verb = "GET") { this.customEndpoints.push({ kind: "SSE", verb, path, handler }); } getCustomEndpoints() { return this.customEndpoints; } } exports.Container = Container; //# sourceMappingURL=containers.js.map