Reference Source

js/topology/deploy/topology_deploy_item.js

// NPM IMPORTS
import assert from 'assert'

// COMMON IMPORTS
import T from '../../utils/types'

// COMMON IMPORTS
import Collection from '../../base/collection'
import DistributedInstance from '../../base/distributed_instance'


let context = 'common/topology/deploy/topology_deploy_item'



/**
 * @file TopologyDeployItem class.
 * 
 * @author Luc BORIES
 * 
 * @license Apache-2.0
 */
export default class TopologyDeployItem extends DistributedInstance
{
	/**
	 * Create a topology deploy item instance.
	 * @class TopologyDeployItem
	 * @extends DistributedInstance
	 * 
	 * A deployment item is a runtime dynamic object corresponding to a deployed topology definition item.
	 * Each deployment item has one corresponding definition item.
	 * One definition item could have many deployment items.
	 * 
	 * API:
	 * 		->load():nothing - load Topology item settings.
	 * 
	 * 		->get_owner():TopologyDeployItem - deployment container
	 * 		->get_topology_define_item():object - get topology definition item.
	 * 		
	 * 		->deploy():Promise(boolean) - .
	 * 		->undeploy():Promise(boolean) - .
	 * 
	 * 
	 * @param {string} arg_name - instance name.
	 * @param {TopologyDefineItem} arg_definition_item - topology definition item.
	 * @param {object} arg_deploy_settings - deployment settings map.
	 * @param {class} arg_topology_class - item TopologyDefineItem child class.
	 * @param {string} arg_log_context - trace context string.
	 * 
	 * @returns {nothing}
	 */
	constructor(arg_name, arg_definition_item, arg_deploy_settings, arg_topology_class, arg_log_context)
	{
		const log_context = arg_log_context ? arg_log_context : context
		assert( T.isObject(arg_deploy_settings), log_context + ':bad settings object')
		
		super('deployed_topology', arg_topology_class ? arg_topology_class : 'TopologyDeployItem', arg_name, arg_deploy_settings, log_context)
		
		this.is_topology_deploy_item = true

		// TOPOLOGY INFORMATIONS
		this.topology_definition_item = arg_definition_item
		this.topology_children = {}
		this.topology_owner = undefined

		this.enable_trace()
	}



	/**
	 * Get topology definition item.
	 * 
	 * return {TopologyDefineItem}
	 */
	get_topology_definition_item()
	{
		return this.topology_definition_item
	}



	/**
	 * Get topology owner.
	 * 
	 * returns {TopologyDeployItem}
	 */
	get_topology_owner()
	{
		return this.topology_owner
	}
	
	

	/**
	 * Load Topology item settings.
	 * 
	 * @returns {Promise} - Promise(boolean): true=success, false=failure
	 */
	load()
	{
		this.enter_group('load')
		
		super.load()
		
		const promises = []
		const children_names = Object.keys(this.topology_children)
		this.debug('load:children_names [' + children_names.toString() + ']')
		
		children_names.forEach(
			(child_name)=>{
				this.debug('loop on child [' + child_name + ']')

				const child_collection = this.topology_children[child_name]
				const child_promise = this.load_collection(child_collection.plural, child_collection, child_collection.item_class, child_collection.item_init)
				promises.push(child_promise)
			}
		)
		
		this.leave_group('load:async wait')
		return Promise.all(promises).then(
			(result)=>{
				this.leave_group('load:async is resolved with ' + (result ? 'success' : 'failure'))
				return result
			}
		)
	}
	


	/**
	 * Check functional format.
	 * 
	 * @returns {boolean}
	 */
	is_valid()
	{
		return true
	}



	/**
	 * Load a collection of topology items.
	 * 
	 * @param {string} arg_plural_name - plural name of the collection.
	 * @param {string} arg_single_name - single name of the collection item.
	 * @param {class} arg_topology_class - item TopologyDeployItem child class.
	 * @param {function} arg_init_cb - optional callback to complete item loading:(name,settings)=>Promise(boolean).
	 * 
	 * @returns {nothing}
	 */
	declare_collection(arg_plural_name, arg_single_name, arg_topology_class, arg_init_cb)
	{
		this.enter_group('declare_collection of ' + arg_plural_name)

		// CHECK ARGS
		assert( T.isString(arg_plural_name) && arg_plural_name.length > 0, context + ':declare_collection:bad plural name for plural=' + arg_plural_name + ',single=' + arg_single_name)
		assert( T.isString(arg_single_name) && arg_single_name.length > 0, context + ':declare_collection:bad single string for plural=' + arg_plural_name + ',single=' + arg_single_name)
		assert( T.isFunction(arg_topology_class), context + ':declare_collection:bad class object for plural=' + arg_plural_name + ',single=' + arg_single_name)
		
		// DECLARE COLLECTION
		this.topology_children[arg_plural_name] = {
			// TODO : create a VersionnedCollection class
			is_versionned_collection: true,
			plural:arg_plural_name,
			single:arg_single_name,
			item_class:arg_topology_class,
			item_init:arg_init_cb,

			latest:new Collection(),   // KEY is name@latest
			versions:new Collection(), // KEY is name@version
			
			has:(arg_name, arg_version)=>{
				if (arg_version == 'latest')
				{
					return this.topology_children[arg_plural_name].latest.has(arg_name + '@latest')
				}
				return this.topology_children[arg_plural_name].versions.has(arg_name + '@' + arg_version)
			},
			get:(arg_name, arg_version)=>{
				if (arg_version == 'latest')
				{
					return this.topology_children[arg_plural_name].latest.find_by_name(arg_name + '@latest')
				}
				return this.topology_children[arg_plural_name].versions.find_by_name(arg_name + '@' + arg_version)
			},
			add:(arg_item)=>{
				if ( T.isObject(arg_item) && arg_item.is_topology_deploy_item )
				{
					const name = arg_item.get_name()
					if ( this.topology_children[arg_plural_name].latest.has(name) )
					{
						const latest = this.topology_children[arg_plural_name].latest.get(name)
						if ( latest.get_topology_version() < arg_item.get_topology_version() )
						{
							this.topology_children[arg_plural_name].latest.remove(latest)
							this.topology_children[arg_plural_name].latest.add(arg_item)
						}
					} else {
						this.topology_children[arg_plural_name].latest.add(arg_item)
					}
					this.topology_children[arg_plural_name].versions.add(arg_item)
				}
			},
			remove:(arg_item)=>{
				if ( T.isObject(arg_item) && arg_item.is_topology_item )
				{
					const name = arg_item.get_name()
					if ( this.topology_children[arg_plural_name].latest.has(name) )
					{
						this.topology_children[arg_plural_name].latest.remove(arg_item)
					}
					if ( this.topology_children[arg_plural_name].versions.has(name) )
					{
						this.topology_children[arg_plural_name].versions.remove(arg_item)
					}
				}
			}
		}

		// DECLARE ACCESSORS
		this[arg_plural_name] = ()=>{
			return this.topology_children[arg_plural_name]
		}

		this[arg_single_name] = (arg_name, arg_version='latest')=>{
			if (arg_version == 'latest')
			{
				return this.topology_children[arg_plural_name].latest.find_by_name(arg_name + '@latest')
			}
			return this.topology_children[arg_plural_name].versions.find_by_name(arg_name + '@' + arg_version)
		}

		this.leave_group('declare_collection of ' + arg_plural_name)
	}



	/**
	 * Load a collection of topology items.
	 * 
	 * @param {string} arg_collection_name - name of the collection in the item settings.
	 * @param {object} arg_collection - child collection of items.
	 * @param {class} arg_topology_class - item TopologyDeployItem child class.
	 * @param {function} arg_init_cb - optional callback to complete item loading:(name,settings)=>Promise(boolean).
	 * 
	 * @returns {Promise} - Promise(boolean): true=success, false=failure.
	 */
	load_collection(arg_collection_name, arg_collection, arg_topology_class, arg_init_cb)
	{
		this.enter_group('load_collection of ' + arg_collection_name)
		
		const promises = []
		const collection_settings = this.get_setting(arg_collection_name, undefined)
		if (collection_settings)
		{
			const all_map = collection_settings.toMap()
			all_map.forEach(
				(settings, name) => {
					this.info('load_collection:Processing collection [' + arg_collection_name + '] item creation of [' + name + ']')
					// console.log(settings, 'settings')
					// console.log(name, 'name')
					if (! settings.has('tenant'))
					{
						settings.set('tenant', this.get_topology_tenant)
					}
					if (! settings.has('package'))
					{
						settings.set('package', this.get_topology_package)
					}
					
					const item = new arg_topology_class(name, settings)
					item.topology_owner = this

					arg_collection.add(item)

					let load_promise = item.load()

					if ( T.isFunction(arg_init_cb) )
					{
						load_promise = load_promise.then(
							(result)=>{
								if (result)
								{
									return arg_init_cb(name, settings)
								}
								return false
							}
						)
					}

					load_promise = load_promise.then(
						(result)=>{
							return result && item.is_valid()
						}
					)

					promises.push(load_promise)
				}
			)
		}
		
		this.leave_group('load_collection of ' + arg_collection_name + ':async wait')
		return Promise.all(promises).then(
			(result)=>{
				this.leave_group('load_collection of ' + arg_collection_name + ':async is resolved with ' + (result ? 'success' : 'failure'))
				return result
			}
		)
	}



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

			tenant:this.topology_tenant,
			package:this.topology_package,
			version:this.topology_version,
			
			type:this.topology_type,
			security:this.topology_security,
			
			children:this.get_children_names()
		}

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

		arg_visited[this.topology_uid] = info

		if (arg_deep)
		{
			const children_names = info.children
			info.children = []
			children_names.forEach(
				(child_name) => {
					let child = this.topology_children[child_name]

					// CHILD IS NOT FOUND
					if (! child)
					{
						console.error(context + ':get_topology_info:child not found [' + child_name + '] for item [' + info.name + ']')
						return
					}

					// CHILD IS A TOPOLOGY DEFINE ITEM
					if ( child && T.isFunction(child.get_topology_info) )
					{
						info.children.push( child.get_topology_info(arg_deep, arg_visited) )
						return
					}

					// CHILD IS A VERSIONNED COLLECTION
					if ( T.isObject(child) && child.is_versionned_collection)
					{
						const all_versioned_items = child.collection.get_all()
						all_versioned_items.forEach(
							(item)=>{
								info.children.push( item.get_topology_info() )
							}
						)
					}
				}
			)
		}

		return info
	}



	/**
	 * Dump topology item informations.
	 * 
	 * @param {object} arg_info - topology info object.
	 * 
	 * @returns {nothing}
	 */
	dump_topology_info(arg_info, arg_tabs=' ', arg_eol='\n', arg_write = console.info)
	{
		arg_write(arg_tabs + '-------------------------------------' + arg_eol)
		arg_write(arg_tabs + 'name:'		+ arg_info.name + arg_eol)
		arg_write(arg_tabs + 'uid_desc:'	+ arg_info.uid_desc + arg_eol)
		arg_write(arg_tabs + 'uid:'			+ arg_info.uid + arg_eol)
		arg_write(arg_tabs + 'tenant:'		+ arg_info.tenant + arg_eol)
		arg_write(arg_tabs + 'package:'		+ arg_info.package + arg_eol)
		arg_write(arg_tabs + 'version:'		+ arg_info.version + arg_eol)
		arg_write(arg_tabs + 'type:'		+ arg_info.type + arg_eol)
		arg_write(arg_tabs + 'security:'	+ arg_info.security + arg_eol)
		
		arg_write(arg_tabs + 'children: [' + arg_eol)
		arg_info.children.forEach(
			(child_info)=>{
				this.dump_topology_info(child_info, arg_tabs + ' ', arg_eol, arg_write)
			}
		)
		arg_write(arg_tabs + ']' + arg_eol)
	}



	/**
	 * Get children names
	 */
	get_children_names()
	{
		return Object.keys(this.topology_children)
	}



	/**
	 * Get resource topology type.
	 */
	get_topology_type()
	{
		return this.topology_type
	}



	/**
	 * Get resource topology tenant.
	 */
	get_topology_tenant()
	{
		return this.topology_tenant
	}



	/**
	 * Get resource topology package.
	 */
	get_topology_package()
	{
		return this.topology_package
	}



	/**
	 * Get resource topology version.
	 */
	get_topology_version()
	{
		return this.topology_version
	}



	/**
	 * Get resource topology uid description.
	 */
	get_topology_uid_desc()
	{
		return this.topology_uid_desc
	}



	/**
	 * Get resource topology uid.
	 */
	get_topology_uid()
	{
		return this.topology_uid
	}



	/**
	 * Get resource topology security.
	 */
	get_topology_security()
	{
		return this.topology_security
	}



	/**
	 * Get topology children items.
	 * 
	 * @returns {object} - map of children items.
	 */
	get_children()
	{
		return this.topology_children
	}
}