Reference Source

js/services/service.js

// NPM IMPORTS
import assert from 'assert'

// COMMON IMPORTS
import T                    from '../utils/types'
import Instance             from '../base/instance'
import Collection           from '../base/collection'
import ServiceActivator     from './service_activator'
import ServiceProvider      from './service_provider'
import ServiceConsumerByUrl from './service_consumer_by_url'


let context = 'common/services'



// STATUS CONSTANTS
// unknow -> created -> enabled -> disabled -> enabled
const STATUS_UNKNOW = 'unknow'
const STATUS_ERROR = 'error'
const STATUS_CREATED = 'created'
const STATUS_ENABLED = 'enabled'
const STATUS_DISABLED = 'disabled'



/**
 * Service base class.
 * 
 * @author Luc BORIES
 * @license Apache-2.0
 * 
 * @example
* 	API:
* 		->is_unknow():boolean - STATUS_UNKNOW ?
* 		->is_error():boolean - STATUS_ERROR ?
* 		->is_created():boolean - STATUS_CREATED ?
* 		->is_enabled():boolean - STATUS_ENABLED ?
* 		->is_disabled():boolean - STATUS_DISABLED ?
* 
* 		->enable():boolean - Enable service.
* 		->disable():boolean - Disable service.
* 
* 		->activate(arg_application, arg_app_svc_cfg):nothing - Activate a service feature for an application.
* 		->activate_on_server(arg_application, arg_server, arg_app_svc_cfg):nothing - Activate a service feature for an application on a server.
* 
* 		->get_providers():Collection - Get service providers.
* 		->get_a_provider(arg_strategy):ServiceProvider|null - Get one service provider corresponding to the given strategy.
* 		->get_provider_by_app_server(arg_app_name, arg_server_name):ServiceProvider|null - Get one service provider corresponding to the given application and server.
* 		->create_provider(arg_name, arg_service):ServiceProvider - Create a new ServiceProvider instance for this service.
* 		->create_consumer():ServiceConsumer - Create a new ServiceConsumer instance.
* 		->provider(arg_name):ServiceProvider - Get a service provider by its name.
* 
* 		->get_topology_info(arg_deep=true, arg_visited={}):object - Get topology item informations.
* 		->get_assets_services_names(arg_region='any'):object - Get assets services names.
* 
 */
export default class Service extends Instance
{
	// STATIC CONST ACCESSORS
	static STATUS_UNKNOW()   { return STATUS_UNKNOW }
	static STATUS_ERROR()	 { return STATUS_ERROR }
	static STATUS_CREATED()  { return STATUS_CREATED }
	static STATUS_ENABLED()  { return STATUS_ENABLED }
	static STATUS_DISABLED() { return STATUS_DISABLED }
	
	
	
	/**
	 * Create a service instance.
	 * 
	 * @param {string} arg_name - plugin name
	 * @param {object} arg_settings - plugin settings map
	 * @param {string} arg_log_context - trace context string.
	 * 
	 * @returns {nothing}
	 */
	constructor(arg_svc_name, arg_settings, arg_log_context)
	{
		super('services', 'Service', arg_svc_name, arg_settings, arg_log_context ? arg_log_context : context)
		
		this.status = STATUS_UNKNOW
		
		/**
		 * Class type flag.
		 * @type {boolean}
		 */
		this.is_service = true
		
		/**
		 * Service status.
		 * @type {string}
		 */
		this.status = Service.STATUS_CREATED

		this._activator = new ServiceActivator(this)
		this._providers = new Collection()
	}
	
	
	// STATUS TEST
	is_unknow()   { return this.status === STATUS_UNKNOW }
	is_error()	  { return this.status === STATUS_ERROR }
	is_created()  { return this.status === STATUS_CREATED }
	is_enabled()  { return this.status === STATUS_ENABLED }
	is_disabled() { return this.status === STATUS_DISABLED }
	
	
	
	/**
	 * Enable service.
	 * 
	 * @returns {boolean}
	 */
	enable()
	{
		if (this.is_unknow() || this.is_error() || this.is_enabled())
		{
			return false
		}
		
		this.status = Service.STATUS_ENABLED
		return true
	}
	
	
	
	/**
	 * Disable service.
	 * 
	 * @returns {boolean}
	 */
	disable()
	{
		if (this.is_unknow() || this.is_error() || this.is_disabled())
		{
			return false
		}
		
		this.status = Service.STATUS_DISABLED
		return true
	}
	
	
	
	/**
	 * Activate a service feature for an application.
	 * 
	 * @param {Application} arg_application - Application instance.
	 * @param {object} arg_app_svc_cfg - service configuration on application.
	 * 
	 * @returns {nothing}
	 */
	activate(arg_application, arg_app_svc_cfg)
	{
		// console.log(arg_app_svc_cfg, context + ':arg_app_svc_cfg')
		assert( T.isObject(arg_application) && arg_application.is_topology_define_application , context + ':bad application object')
		assert( T.isObject(arg_app_svc_cfg) , context + ':bad app svc settings object')
		assert( T.isArray(arg_app_svc_cfg.servers), context + ':bad app svc servers array')
		// this.info('servers ' + arg_app_svc_cfg.servers.length)
		
		for(let i in arg_app_svc_cfg.servers)
		{
			const server_name = arg_app_svc_cfg.servers[i]
			assert(T.isString(server_name), context + ':bad server name string')
			this.info('activate on server:' + server_name)
			
			const server = this.get_runtime().node.get_servers().find_by_name(server_name)
			if ( T.isObject(server) )
			{
				// this.activate_on_server(arg_application, server, arg_app_svc_cfg)

				// TODO
				server.use_service_on_loading(arg_application, this, arg_app_svc_cfg)
			}
			else
			{
				this.info('server_name not found ' + server_name)
			}
		}
	}
	
	

	/**
	 * Activate a service feature for an application on a server.
	 * 
	 * @param {Application} arg_application - Application instance.
	 * @param {Server} arg_server - Server instance.
	 * 
	 * @returns {nothing}
	 */
	activate_on_server(arg_application, arg_server)
	{
		assert( T.isObject(arg_application) && arg_application.is_topology_define_application , context + ':bad application object')
		assert( T.isObject(arg_server) && arg_server.is_server , context + ':bad server object')
		
		this.info('activate_on_server [' + arg_server.get_name() + '] for application [' + arg_application.get_name() + ']')
		
		let provider = this.get_provider_by_app_server(arg_application.get_name(), arg_server.get_name())
		
		this._activator.activate(provider, arg_application, arg_server)
	}
	

	
	/**
	 * Get service providers.
	 * 
	 * @returns {Collection}
	 */
	get_providers()
	{
		return this._providers
	}
	

	
	/**
	 * Get one service provider corresponding to the given strategy.
	 * 
	 * @param {object} arg_strategy - search stratgey (TODO).
	 * 
	 * @returns {ServiceProvider|null} - found service provider or null
	 */
	get_a_provider(arg_strategy)
	{
		let provider = null
		
		if (! arg_strategy)
		{
			// USE THE FIRST ITEM OF THE LIST OR THE WEAKED LIST IF ENABLED
			provider = this._providers.get_first()
		}
		
		// TODO: define metrics on the provider and update the weak at each turn
		// TODO: define Strategy class with: bablance, round
		
		if (! provider)
		{
			// const key = 'app' + '-' + 'name'
			// provider = this.create_provider(this.get_name() + '_provider_for_' + key, this)
			// this._providers.add(provider)
		}
		
		return provider
	}
	
	
	
	/**
	 * Get one service provider corresponding to the given application and server.
	 * 
	 * @param {string} arg_app_name - application name.
	 * @param {string} arg_server_name - server name.
	 * 
	 * @returns {ServiceProvider|null} - found service provider or null
	 */
	get_provider_by_app_server(arg_app_name, arg_server_name)
	{
		const key = arg_app_name + '-' + arg_server_name
		let provider = this._providers.find_by_attr('application_server', key)
		assert(! provider, context + ':service provider already activated')
		
		if (! provider)
		{
			provider = this.create_provider(this.get_name() + '_provider_for_' + key, this)
			this._providers.add(provider)
		}
		
		return provider
	}
	
	
	
	/**
	 * Create a new ServiceProvider instance for this service.
	 * 
	 * @param {string} arg_name - instance name.
	 * @param {Service} arg_service - service instance.
	 * 
	 * @returns {ServiceProvider} - created service provider.
	 */
	create_provider(arg_name, arg_service)
	{
		const provider_class = this.get_setting('provider_class', ServiceProvider)
		return new provider_class(arg_name, arg_service)
	}
	

	
	/**
	 * Create a new ServiceConsumer instance.
	 * 
	 * @returns {ServiceConsumer} - created service consumer.
	 */
	create_consumer()
	{
		const consumer_name  = this.get_name() + '_consumer_' + this.get_id()
		const consumer_class = this.get_setting('consumer_class', ServiceConsumerByUrl)
		return new consumer_class(consumer_name, this)
	}

	
	
	/**
	 * Get a service provider by its name.
	 * 
	 * @param {string} arg_name - service provider name.
	 * 
	 * @returns {ServiceProvider}
	 */
	provider(arg_name)
	{
		return this._providers.item(arg_name)
	}



	/**
	 * Get topology item informations.
	 * 
	 * @param {boolean} arg_deep - get deep sub items information on true (default:false).
	 * @param {object} arg_visited - visited items plain object map.
	 * 
	 * @returns {object} - topology informations (plain object).
	 */
	get_topology_info(arg_deep=true, arg_visited={})
	{
		const info = {
			name:this.get_name(),
			uid_desc:'N/A',
			uid:'N/A',

			tenant:'N/A',
			package:'N/A',
			version:'N/A',
			
			type:'service',
			security:'N/A',
			
			children:['N/A']
		}

		if ( arg_visited && (this.topology_uid in arg_visited) )
		{
			return Object.assign(info, { note:'already dumped' } )
		}

		arg_visited[this.topology_uid] = info

		return info
	}



	/**
	 * Get assets services names.
	 * 
	 * @param {string} arg_region - region name.
	 * 
	 * @returns {object} - assets { style:'', script:'', image:'', html:'' }
	 */
	get_assets_services_names(arg_region='any')
	{
		assert( T.isString(arg_region), context + ':get_assets_services_names:bad region string')

		// GET ASSETS CONFIG
		const assets = this.topology_deploy_assets
		const assets_region = arg_region == 'any' ? 'all' : arg_region
		const assets_for_region = T.isObject(assets) && T.isObject(assets[assets_region]) ? assets[assets_region] : undefined
		
		const assets_style  = T.isObject(assets_for_region) && T.isArray(assets_for_region.style)  ? assets_for_region.style  : []
		const assets_script = T.isObject(assets_for_region) && T.isArray(assets_for_region.script) ? assets_for_region.script : []
		const assets_image  = T.isObject(assets_for_region) && T.isArray(assets_for_region.image)  ? assets_for_region.image  : []
		const assets_html   = T.isObject(assets_for_region) && T.isArray(assets_for_region.html)   ? assets_for_region.html   : []

		const assets_style_selected  = assets_style.length  > 0 ? assets_style[0]  : undefined
		const assets_script_selected = assets_script.length > 0 ? assets_script[0] : undefined
		const assets_image_selected  = assets_image.length  > 0 ? assets_image[0]  : undefined
		const assets_html_selected   = assets_html.length   > 0 ? assets_html[0]   : undefined

		return {
			style:assets_style_selected,
			script:assets_script_selected,
			image:assets_image_selected,
			html:assets_html_selected
		}
	}
}


/*
Loading:
	create rt = new Runtime()
	rt.load()
		load_config
			fill config.*
		load_runtime
			create instances and fill runtime.*
			1 create servers
			2 create services
			3 create applications

EXAMPLES
	'rest_api_models_query':['*'],
	'rest_api_models_modifier':['*'],
	'rest_api_resources_query':['*'],
	'rest_api_resources_modifier':['*'],
	'html_assets':false,
	'html_app':false
*/