Source: core.js

'use strict';
const Promisie = require('promisie');
const fs = Promisie.promisifyAll(require('fs-extra'));
const path = require('path');
const DBAdapterInterface = require('periodicjs.core.data');
const ResponderAdapterInterface = require('periodicjs.core.responder');
const ProtocolAdapterInterface = require('periodicjs.core.protocols');
const Utilities = require('periodicjs.core.utilities');
const express = require('express');
const UTILITY = require(path.join(__dirname, '../utility/index'));

var periodic_resources;
var logger;
var application_settings;
var initialization_options;
var appenvironment;
var applocals;
var CoreUtilities;
var theme;
var _logError;
var _logWarning;

/**
 * Initializes a utility HTML responder adapter that is used to render templates if protocol responder is not an HTML adapter and set it to the "_utility_responder" property. This property is not writable, configurable or enumerable
 */
const _INITIALIZE_UTILITY_RESPONDER = function () {
	Object.defineProperty(this, '_utility_responder', {
		writable: false,
		value: ResponderAdapterInterface.create({ adapter: 'html', themename: theme }),
		configurable: false,
		enumerable: false
	});
};

/**
 * Initializes aliased utility methods and set the on "this"
 */
const _INITIALIZE_ALIASED_UTILITIES = function () {
	let aliased = Object.assign({}, UTILITY.aliased);
	for (let key in aliased) {
		this[key] = aliased[key].call(this);
	}
	for (let key in UTILITY.middleware) {
		this[key] = UTILITY.middleware[key];
	}
};

/**
 * Called a initialization of a "new" CoreController. Implements the "meta" property which grants additional access to child adapter metods through a Proxy. Initialized adapters according to configurations and default adapters
 * @param  {Object} options Configuration options for adapters
 * @param {Boolean} options.skip_responder If true responder adapters will not be initialized
 * @param {Boolean} options.skip_db If true db adapters will not be initialized
 * @param {Booelean} options.skip_protocol If true protocol adapter will not be initialized
 * @return {Object}         "this"
 */
const _STARTUP = function (options) {
	if (!options.meta) {
		Object.defineProperty(this, 'meta', {
			writable: false,
			value: new Proxy({}, UTILITY.traps(this)),
			configurable: false,
			enumerable: false
		});
	}
	if (!options.skip_responder) this.initialize_responder(options);
	if (!options.skip_db) this.initialize_db(options);
	if (!options.skip_protocol) this.initialize_protocol(options);
	_INITIALIZE_UTILITY_RESPONDER.call(this);
	_INITIALIZE_ALIASED_UTILITIES.call(this);
	return this;
};

/**
 * CoreController - a class that handles implementing API strategies and providing CRUD operation convenience methods and middleware
 * @type {Core}
 */
const CORE = class Core {
	/**
	 * Contructs a new Core instance
	 * @param  {Object} resources Periodicjs shared resources
	 * @param {Object} resources.logger A logger module that should be used in logging errors, info and warns
	 * @param {Object} resources.settings Contains various application settings including theme and environment specific configurations
	 * @param {Object} resources.app Should contain a locals property that has local data to be shared with constructed object
	 * @param  {Object} options   Configurable options for adapters and overrides
	 * @return {Object}           "this"
	 */
	constructor (resources, options = {}) {
		periodic_resources = resources;
		logger = resources.logger;
		application_settings = resources.settings;
		theme = resources.settings.theme;
		appenvironment = application_settings.application.environment;
		if (periodic_resources.app && periodic_resources.app.locals) applocals = periodic_resources.app.locals;
		CoreUtilities = new Utilities(resources);
		this.responder = null;
		this.protocol = null;
		this.db = null;
		this.theme = theme;
		this.appenvironment = appenvironment;
		return _STARTUP.call(this, options, CoreUtilities);
	}
	/**
	 * Initializes a responder and assigns it to the .responder property on "this". The active responder will also be used as the responder for any protocol adapters initialized after this is called
	 * @param  {Object} options Configurable options for responder (see periodicjs.core.responder for more details)
	 * @param {Object} options.responder_configuration Configuration for response adapter
	 * @return {Object}         "this"
	 */
	initialize_responder (options = {}) {
		this.responder = (options.responder_configuration) ? ResponderAdapterInterface.create(options.responder_configuration) : ResponderAdapterInterface.create({ adapter: 'json' });
		return this;
	}
	/**
	 * Initializes a protcol adapter and assigns it to the .protocol property on "this". The active protocol adapter is used for sending responses to the client and for setting up an API strategy
	 * @param  {Object} options   Configurable options for protocol adapter (see periodicjs.core.protocols for more details)
	 * @param {Object} options.protocol_configuration Configuration for protocol adapter
	 * @param  {Object} [utilities=CoreUtilities] A set of utility methods that should be accessible by protocol adapter
	 * @return {Object}           "this"
	 */
	initialize_protocol (options = {}, utilities = CoreUtilities) {
		let protocolOptions = Object.assign({
			responder: this.responder,
			db: this.db,
			settings: Object.assign({}, application_settings),
			logger,
			resources: periodic_resources,
			api: 'rest',
			adapter: 'http',
			express,
			utilities
		}, options.protocol_configuration || {});
		this.protocol = ProtocolAdapterInterface.create(protocolOptions);
		return this;
	}
	/**
	 * Initializes database adapters for each given model in configuration and assigns them to the this.db object indexed by model name and assigns a default mongo adapter to the .default property on this.db
	 * @param  {Object} options Configurable options for db adapters (see periodicjs.core.data for more details)
	 * @param {Object} options.db_configuration Configuration for db adapters
	 * @return {Object}         "this"
	 */
	initialize_db (options = {}) {
		this.db = (this.db && typeof this.db === 'object') ? this.db : {};
		if (options.db_configuration) {
			if (Array.isArray(options.db_configuration)) {
				options.db_configuration.forEach(configuration => {
					this.db[configuration.model_name] = DBAdapterInterface.create(configuration);
				});
			}
			else this.db[options.db_configuration.model_name] = DBAdapterInterface.create(options.db_configuration);
		}
		if (!this.db.default) this.db.default = DBAdapterInterface.create({ adapter: 'mongo' });
		return this;
	}
};

module.exports = CORE;