"use strict";
import {
dirname,
resolve
} from "path";
import callsite from "callsite";
import includeAll from "include-all";
import Registry from "./registry";
const DEFAULT_CONNECTION_SETTINGS = {
dialect: "sqlite",
database: "test",
username: "test",
password: "test"
};
const DEFAULT_LISTEN_PORT = 3000;
/**
* Base application
*
* @class Application
* @constructor
*
* @param {Object} options Application config options
* <a href="https://parch-js.github.io">
* See configuration
* </a>
*/
class Application {
/* eslint-disable complexity */
constructor(options) {
const projectDirectory = this._getProjectDirectory();
const registry = this.registry = new Registry();
this.DEFAULT_CONTROLLER_LOOKUP_PATH = resolve(
projectDirectory,
"controllers"
);
this.DEFAULT_INITIALIZERS_LOOKUP_PATH = resolve(
projectDirectory,
"initializers"
);
this.DEFAULT_MODEL_LOOKUP_PATH = resolve(projectDirectory, "models");
this.DEFAULT_SERIALIZER_LOOKUP_PATH = resolve(
projectDirectory,
"serializers"
);
options = this._configure(options);
registry.register("config:main", options);
this._initialize("logger");
this._initialize("server");
this._initialize("loaders");
this._initialize("model-manager");
this._initialize("models");
this._initialize("middleware");
this._initialize("router");
this._initialize("store");
this._initialize("application");
}
/**
* Restify application instance
*
* @property app
* @type {Object} Restify service
*/
get app() {
return this.registry.lookup("service:server");
}
/**
* Starts the server
* @method listen
* @param {Number} port
* @returns {Promise<void>}
*/
listen(port) {
return new Promise(done => {
this.app.listen(port, () => done());
});
}
/**
* The consuming project's directory
*
* @property projectDirectory
* @type {String}
*/
get projectDirectory() {
return this._getProjectDirectory();
}
/**
* Runs all consumer initializers
*
* @method runProjectInitializers
* @returns {Promise<void>}
*/
runProjectInitializers() {
const config = this.registry.lookup("config:main");
const initializersPath = config.initializers.dir;
let initializerModules;
/* eslint-disable no-empty */
try {
initializerModules = includeAll({
dirname: initializersPath,
filter: /(.+).js$/
});
} catch (err) {}
/* eslint-enable no-empty */
if (initializerModules) {
const initializers = Object.keys(initializerModules);
return Promise.all(initializers.map(name =>
initializerModules[name].initialize(this, this.registry)
));
} else {
return Promise.resolve();
}
}
/**
* starts listening on the defined port
*
* @method start
* @param {Number} port the port to listen on. Default: 3000
* @return {Promise<undefined, Error>}
*/
start(port = DEFAULT_LISTEN_PORT) {
return this.runProjectInitializers().then(() => this.listen(port));
}
/**
* Normalizes constructor options
*
* @method _configure
* @private
* @param {Object} config user passed config options
* @returns {Object} final config option
*/
_configure(config) {
config.controllers = config.controllers || {};
config.controllers.dir = config.controllers.dir || this.DEFAULT_CONTROLLER_LOOKUP_PATH;
config.database = config.database || {};
config.database.connection = config.database.connection || DEFAULT_CONNECTION_SETTINGS;
config.database.models = config.database.models || {};
config.database.models.dir = config.database.models.dir || this.DEFAULT_MODEL_LOOKUP_PATH;
config.initializers = config.initializers || {};
config.initializers.dir = config.initializers.dir || this.DEFAULT_INITIALIZERS_LOOKUP_PATH;
config.logging = config.logging || {};
config.serializers = config.serializers || {};
config.serializers.dir = config.serializers.dir || this.DEFAULT_SERIALIZER_LOOKUP_PATH;
config.server = config.server || {};
config.server.middlewares = config.server.middlewares || [];
return config;
}
/**
* Returns the project directory (cwd) from which Application is being instantiated
*
* @method _getProjectDirectory
* @private
* @returns {String} directory
*/
_getProjectDirectory() {
const caller = callsite()[2].getFileName();
const projectDirectory = dirname(caller);
return projectDirectory;
}
/**
* Run an initializer by name
*
* @method _initialize
* @private
* @param {String} name
*/
_initialize(name) {
const initializers = includeAll({
dirname: __dirname
}).initializers;
const logger = this.logger;
// TODO: throw an error if the initializer is missing
const [initializer] = Object.keys(initializers).filter(
init => initializers[init].name === name
);
if (logger) {
logger.info(`Running initializer '${name}'`);
}
return initializers[initializer].initialize(this, this.registry);
}
}
export default Application;