vulcain-corejs
Version: 
Vulcain micro-service framework
271 lines (269 loc) • 10.8 kB
JavaScript
"use strict";
const expressAuthentication_1 = require("./servers/expressAuthentication");
const apiKeyService_1 = require("./auth/apiKeyService");
const preloader_1 = require("./preloader");
const hystrixSSEStream_1 = require("./commands/http/hystrixSSEStream");
const localAdapter_1 = require("./bus/localAdapter");
const Path = require("path");
const schema_1 = require("./schemas/schema");
const containers_1 = require("./di/containers");
const files_1 = require("./utils/files");
const expressAdapter_1 = require("./servers/expressAdapter");
require("reflect-metadata");
const annotations_1 = require("./di/annotations");
const conventions_1 = require("./utils/conventions");
const provider_1 = require("./providers/memory/provider");
const requestContext_1 = require("./servers/requestContext");
require("./defaults/serviceExplorer"); // Don't remove (auto register)
require("./defaults/dependencyExplorer"); // Don't remove (auto register)
require("./pipeline/scopeDescriptors"); // Don't remove (auto register)
const system_1 = require("./configurations/globals/system");
/**
 * Application base class
 *
 * @export
 * @abstract
 * @class Application
 */
class Application {
    /**
     * Create new application
     * @param path Files base path for components discovery
     * @param container Global component container
     * @param app  (optional)Server adapter
     */
    constructor(domainName, container) {
        domainName = domainName;
        if (!domainName) {
            throw new Error("Domain name is required.");
        }
        system_1.System.defaultDomainName = domainName;
        system_1.System.log.info(null, "Starting application");
        this._executablePath = Path.dirname(module.filename);
        this._basePath = this.findBasePath();
        // Ensure initializing this first
        const test = system_1.System.isDevelopment;
        this._container = container || new containers_1.Container();
        this._container.injectTransient(provider_1.MemoryProvider, annotations_1.DefaultServiceNames.Provider);
        this._container.injectInstance(this, annotations_1.DefaultServiceNames.Application);
        this._container.injectSingleton(expressAuthentication_1.Authentication, annotations_1.DefaultServiceNames.Authentication);
        this._domain = new schema_1.Domain(domainName, this._container);
        this._container.injectInstance(this.domain, annotations_1.DefaultServiceNames.Domain);
    }
    /**
     * Enable api key authentication
     *
     * @param {string} apiKeyServiceName Vulcain service name
     * @param {string} [version="1.0"] Service version
     *
     * @memberOf Application
     */
    enableApiKeyAuthentication(apiKeyServiceName, version = "1.0") {
        this.container.injectInstance(new apiKeyService_1.ApiKeyService(this.container, apiKeyServiceName, version), annotations_1.DefaultServiceNames.ApiKeyService);
    }
    /**
     * Set the user to use in local development
     *
     * @param {UserContext} user
     * @returns
     */
    setTestUser(user) {
        user = user || requestContext_1.RequestContext.TestUser;
        if (!user.id || !user.name || !user.scopes) {
            throw new Error("Invalid test user - Properties must be set.");
        }
        if (!system_1.System.isTestEnvironnment) {
            system_1.System.log.info(null, "Warning : TestUser ignored");
            return;
        }
        this._container.injectInstance(user, annotations_1.DefaultServiceNames.TestUser);
    }
    /**
     * Called when the server adapter is started
     *
     * @param {*} server
     */
    onServerStarted(server, adapter) { }
    /**
     * Current component container
     * @returns {Container}
     */
    get container() { return this._container; }
    /**
     * Get the current domain model
     * @returns {Domain}
     */
    get domain() {
        return this._domain;
    }
    findBasePath() {
        let parent = module.parent;
        while (parent.parent) {
            parent = parent.parent;
        }
        return Path.dirname(parent.filename);
    }
    startHystrixStream() {
        if (!this.enableHystrixStream) {
            return;
        }
        this.adapter.useMiddleware("get", conventions_1.Conventions.instance.defaultHystrixPath, (request, response) => {
            response.append('Content-Type', 'text/event-stream;charset=UTF-8');
            response.append('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate');
            response.append('Pragma', 'no-cache');
            system_1.System.log.info(null, "get hystrix.stream");
            let subscription = hystrixSSEStream_1.HystrixSSEStream.toObservable().subscribe(function onNext(sseData) {
                response.write('data: ' + sseData + '\n\n');
            }, function onError(error) {
                system_1.System.log.info(null, "hystrixstream: error");
            }, function onComplete() {
                system_1.System.log.info(null, "end hystrix.stream");
                return response.end();
            });
            request.on("close", () => {
                system_1.System.log.info(null, "close hystrix.stream");
                subscription.dispose();
            });
            return subscription;
        });
    }
    defineScopes(scopes) {
    }
    /**
     * Override this method to initialize default containers
     *
     * @protected
     * @param {IContainer} container
     */
    initializeDefaultServices(container) {
    }
    /**
     * Override this method to add your custom services
     *
     * @protected
     * @param {IContainer} container
     */
    initializeServices(container) {
    }
    /**
     * Called before the server adapter is started
     *
     * @protected
     * @param {AbstractAdapter} abstractAdapter
     */
    initializeServerAdapter(abstractAdapter) {
    }
    /**
     * Initialize and start application
     *
     * @param {number} port
     */
    start(port) {
        this.initializeDefaultServices(this.container);
        let local = new localAdapter_1.LocalAdapter();
        let eventBus = this.container.get(annotations_1.DefaultServiceNames.EventBusAdapter, true);
        if (!eventBus) {
            this.container.injectInstance(local, annotations_1.DefaultServiceNames.EventBusAdapter);
            eventBus = local;
        }
        let commandBus = this.container.get(annotations_1.DefaultServiceNames.ActionBusAdapter, true);
        if (!commandBus) {
            this.container.injectInstance(local, annotations_1.DefaultServiceNames.ActionBusAdapter);
            commandBus = local;
        }
        eventBus.startAsync().then(() => {
            commandBus.startAsync().then(() => {
                try {
                    this.registerModelsInternal();
                    this.registerServicesInternal();
                    this.registerHandlersInternal();
                    this.initializeServices(this.container);
                    preloader_1.Preloader.instance.runPreloads(this.container, this._domain);
                    let scopes = this.container.get(annotations_1.DefaultServiceNames.ScopesDescriptor);
                    this.defineScopes(scopes);
                    let descriptors = this.container.get(annotations_1.DefaultServiceNames.ServiceDescriptors);
                    descriptors.createHandlersTable();
                    this.adapter = this.container.get(annotations_1.DefaultServiceNames.ServerAdapter, true);
                    if (!this.adapter) {
                        this.adapter = new expressAdapter_1.ExpressAdapter(this.domain.name, this._container, this);
                        this.container.injectInstance(this.adapter, annotations_1.DefaultServiceNames.ServerAdapter);
                        this.initializeServerAdapter(this.adapter);
                        this.adapter.initialize();
                    }
                    this.startHystrixStream();
                    this.adapter.start(port);
                }
                catch (err) {
                    system_1.System.log.error(null, err);
                }
            });
        });
    }
    registerModelsInternal() {
        this.registerModels(Path.join(this._executablePath, "defaults/models"));
        let path = conventions_1.Conventions.instance.defaultModelsFolderPattern.replace("${base}", conventions_1.Conventions.instance.defaultApplicationFolder);
        this.registerModels(Path.join(this._basePath, path));
    }
    /**
     * Inject all components from a specific folder (relative to the current folder)
     *
     * @protected
     * @param {string} path Folder path
     * @returns The current container
     */
    injectFrom(path) {
        if (!Path.isAbsolute(path)) {
            path = Path.join(this._basePath, path);
        }
        this._container.injectFrom(path);
        return this._container;
    }
    /**
     * Discover models components
     * @param path Where to find models component relative to base path (default=/api/models)
     * @returns {Container}
     */
    registerModels(path) {
        if (!Path.isAbsolute(path)) {
            path = Path.join(this._basePath, path);
        }
        files_1.Files.traverse(path);
        return this._container;
    }
    registerHandlersInternal() {
        this.registerHandlers(Path.join(this._executablePath, "defaults/handlers"));
        let path = conventions_1.Conventions.instance.defaultHandlersFolderPattern.replace("${base}", conventions_1.Conventions.instance.defaultApplicationFolder);
        this.registerHandlers(Path.join(this._basePath, path));
    }
    /**
     * Discover models components
     * @param path Where to find models component relative to base path (default=/api/models)
     * @returns {Container}
     */
    registerHandlers(path) {
        if (!Path.isAbsolute(path)) {
            path = Path.join(this._basePath, path);
        }
        files_1.Files.traverse(path);
        return this._container;
    }
    registerServicesInternal() {
        this.registerServices(Path.join(this._executablePath, "defaults/services"));
        let path = conventions_1.Conventions.instance.defaultServicesFolderPattern.replace("${base}", conventions_1.Conventions.instance.defaultApplicationFolder);
        this.registerServices(Path.join(this._basePath, path));
    }
    /**
     * Discover service components
     * @param path Where to find services component relative to base path (default=/core/services)
     * @returns {Container}
     */
    registerServices(path) {
        if (!Path.isAbsolute(path)) {
            path = Path.join(this._basePath, path);
        }
        files_1.Files.traverse(path);
        return this._container;
    }
}
exports.Application = Application;
//# sourceMappingURL=application.js.map