Reference Source

js/plugins/plugins_manager.js

// NPM IMPORTS
import assert from 'assert'

// COMMON IMPORTS
import T          from 'devapt-core-common/dist/js/utils/types'
import Errorable  from 'devapt-core-common/dist/js/base/errorable'
import Collection from 'devapt-core-common/dist/js/base/collection'

// SERVER IMPORTS
import runtime from '../base/runtime'


let context = 'server/plugins/plugins_manager'



/**
 * @file Plugins manager class.
 * @author Luc BORIES
 * @license Apache-2.0
 */
export default class PluginsManager extends Errorable
{
	/**
	 * Create a plugins manager instance.
	 * @extends Errorable
	 * 
	 * @param {RuntimeBase} arg_runtime - runtime.
	 * @param {string|undefined} arg_log_context - optional.
	 * @param {LoggerManager} arg_logger_manager - logger manager object (optional).
	 * @returns {nothing}
	 */
	constructor(arg_runtime, arg_log_context, arg_logger_manager)
	{
		super(arg_log_context ? arg_log_context : context, arg_logger_manager)
		
		this.is_plugins_manager = true
		
		this._runtime = arg_runtime
		this.registered_plugins = new Collection()
		this.enabled_plugins = new Collection()
	}


	/**
	 * Get class name.
	 * 
	 * @return {string}
	 */
	get_class()
	{
		return 'PluginsManager'
	}


	get_runtime()
	{
		return this._runtime
	}
	
	
    /**
     * Load a map of plugins.
     * @param {array} arg_plugins - plugins file name or class array.
     * @returns {nothing}
     */
	load(arg_plugins)
	{
		this.update_trace_enabled()
		
		// console.log(arg_plugins, 'arg_plugins')
		// const base_dir = get_base_dir()
        
		for(let plugin of arg_plugins)
		{
			// GIVEN PLUGIN IS A RELATIVE FILE PATH NAME
			if ( T.isString(plugin) )
			{
				const file_path_name = runtime.context.get_absolute_plugin_path(plugin)

				// DEBUG
				// console.info('loading plugin [' + plugin + '] at [' + (file_path_name == plugin ? 'same' : file_path_name) + ']')

				try
				{
					const required = require(file_path_name)
					const PluginClass = ('default' in required) ? required.default : required
					// console.log('loading rendering plugin class', PluginClass)
					
					plugin = new PluginClass(this._runtime, this)
				}
				catch(e)
				{
					console.error(context + '.load:' + plugin + 'failed', e)
				}
			}

			// GIVEN PLUGIN IS A PLUGIN CLASS INSTANCE
			if ( T.isObject(plugin) && this.plugin_is_valid(plugin) )
			{
				const plugin_name = plugin.get_name()
				if ( T.isString(plugin_name) )
				{
					this.register_plugin(plugin)
					continue
				}
			}

			// UNKNOW PLUGIN TYPE
			console.error(plugin, 'plugin')
			assert(false, context + ':bad plugin')
		}
	}
	
	
	/**
	 * Load plugin at first position
	 * @param {Plugin} arg_plugin - plugin instance
	 * @returns {nothing}
	 */
	load_at_first(arg_plugin)
	{
		assert( T.isObject(arg_plugin) && this.plugin_is_valid(arg_plugin), context + ':load_at_first:bad plugin object')
		this.register_plugin(arg_plugin, 0)
	}
	
	
	/**
	 * Test if plugin is valid.
	 * @abstract
	 * @method plugin_is_valid
	 * @param {Plugin} arg_plugin - plugin instance.
	 * @returns {boolean} - given plugin is valid for this manager.
	 */
	
	
	
	/**
	 * Get registered plugins list.
	 * @returns {array} - plugins list 
	 */
	get_plugins()
	{
		return this.registered_plugins.get_all()
	}
	
	
	/**
	 * Get registered plugins list with a filtered type.
	 * @param {string|array} arg_type_or_types - type name or types names array
	 * @returns {array} - plugins list 
	 */
	get_typed_plugins(arg_type_or_types)
	{
		return this.registered_plugins.get_all(arg_type_or_types)
	}
	
	
	/**
	 * Register a plugin to be used later, do not active it now.
	 * @param {object} arg_plugin - plugin instance.
	 * @param {integer} arg_position - index in array (0:first or undefined)
	 * @returns {object} - a promise object of a boolean result (success:true, failure:false)
	 */
	register_plugin(arg_plugin, arg_position)
	{
		assert( T.isObject(arg_plugin) && arg_plugin.is_plugin, context + ':bad plugin object')

		const plugin_name = arg_plugin.get_name()
		if (this.registered_plugins.find_by_name(plugin_name) )
		{
			// this.error_already_registered(plugin_name)
			// return Promise.resolve(false)
			return Promise.resolve(true)
		}
		
		if (arg_position === 0)
		{
			this.registered_plugins.add_first(arg_plugin)
		}
		else
		{
			this.registered_plugins.add(arg_plugin)
		}
		// arg_plugin.manager = this

		return Promise.resolve(true)
	}
	
	
	/**
	 * Unregister a registered plugin and disble it before if needed.
	 * @param {object} arg_plugin - plugin instance.
	 * @returns {object} - a promise object of a boolean result (success:true, failure:false)
	 */
	unregister_plugin(arg_plugin)
	{
		assert( T.isObject(arg_plugin) && arg_plugin.is_plugin, context + ':bad plugin object')
		
		const plugin_name = arg_plugin.get_name()
		
		// PLUGIN IS REGISTERED ?
		if ( ! this.registered_plugins.has(arg_plugin) )
		{
			this.error_not_registered(plugin_name)
			return Promise.resolve(false)
		}
		
		let disable_promise = Promise.resolve(true)
		
		// PLUGIN IS ENABLED ?
		if (this.enabled_plugins.has(arg_plugin) )
		{
			this.enabled_plugins.remove(arg_plugin)
			disable_promise = arg_plugin.disable()
		}
		
		// UNREGISTER
		this.registered_plugins.remove(arg_plugin)
		arg_plugin.manager = null
		delete arg_plugin.manager
		
		return disable_promise
	}
	
	
	/**
	 * Get a registered plugin by its name and its enabled flag.
	 * @param {string} arg_name - registered plugin name
	 * @param {boolean} arg_enabled - plugin is enabled ?
	 * @returns {Plugin}
	 */
	plugin(arg_name, arg_enabled)
	{
		if (arg_enabled)
		{
			return this.enabled_plugins.item(arg_name)
		}
		return this.registered_plugins.item(arg_name)
	}
	
	
	/**
	 * Get a registered plugin by its name.
	 * @param {string} arg_name - registered plugin name
	 * @returns {Plugin}
	 */
	registered_plugin(arg_name)
	{
		return this.registered_plugins.item(arg_name)
	}
	
	
	/**
	 * Get a enabled plugin by its name.
	 * @param {string} arg_name - enabled plugin name
	 * @returns {Plugin}
	 */
	enabled_plugin(arg_name)
	{
		return this.enabled_plugins.item(arg_name)
	}
	
	
	/**
	 * Error wrapper - on registering an already registered plugin
	 * @param {string} arg_plugin_name - plugin name
	 * @returns {nothing}
	 */
	error_already_registered(arg_plugin_name)
	{
		this.error('plugin with name [' + arg_plugin_name + '] is already registered')
	}
	
	
	/**
	 * Error wrapper - a plugin is not registered
	 * @param {string} arg_plugin_name - plugin name
	 * @returns {nothing}
	 */
	error_not_registered(arg_plugin_name)
	{
		this.error('plugin with name [' + arg_plugin_name + '] is not registered')
	}
}