vulcain-corejs
Version:
Vulcain micro-service framework
350 lines • 14.4 kB
JavaScript
"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