/** * @author Martin Karkowski * @email m.karkowski@zema.de * @create date 2020-10-12 18:53:00 * @modify date 2020-10-13 13:10:50 * @desc [description] */ import { inject, injectable } from "inversify"; import { DISPATCHER_INSTANCE, OBSERVABLE_FACTORY } from "../symbols/identifiers"; import { IFunctionOptions, IPropertyOptions } from "../types/descriptor.interface"; import { INopeDispatcher } from "../types/nopeDispatcher.interface"; import { INopeModuleDescription } from "../types/nopeModule.interface"; import { INopeObservable } from "../types/nopeObservable.interface"; import { NopeBaseModule } from "./BaseModule"; export interface ICallableObject { [index: string]: (...args) => Promise } @injectable() export class NopeGenericModule extends NopeBaseModule { dynamicInstanceMethods: ICallableObject = {}; dynamicInstanceProperties: {[index: string]: INopeObservable} = {}; protected _description: INopeModuleDescription; /** * Function, used to add the Attributes based on the Description. * * @param {INopeModuleDescription} description * @param {('overwrite' | 'add')} [mode='overwrite'] * @memberof NopeGenericModule */ public fromDescription(description: INopeModuleDescription, mode:'overwrite' | 'add' = 'overwrite'){ const _this = this; if (mode === 'overwrite'){ this.dispose(); this.author = description.author; this.description = description.description; this.type = description.type; this.identifier = description.identifier; } if (this.author == null){ this.author = description.author; } if (this.description == null){ this.description = description.description; } if (this.version == null){ this.version = description.version; } if (this.identifier == null){ this.identifier = description.identifier; } for (const name in description.functions){ const options = description.functions[name]; const func = async (...args) => { return await _this._dispatcher.performCall(options.id, args, options); }; if (this.dynamicInstanceMethods[name]){ throw Error('Name alread used. Not able to use the method name twice') } (this.dynamicInstanceMethods as any)[name] = func; // If the Function isnt dynamic, register it on the Object itself. if (!options.isDynamic) { if (this[name]){ throw Error('Name alread used. Not able to use the method name twice') } this[name] = func; } this._registeredFunctions.set(name, { func, options }); } for (const name in description.properties){ const options = description.properties[name]; // Add only elements, that are subscribed. // Properties, which are only publishing // should throw an error, if data is published // in a remote. This is done to maintain // consistency. // let mode = prop.mode; if (this.dynamicInstanceProperties[name]){ throw Error('Name alread used. Not able to use the method name twice') } // Make shure it isnt published. options.preventSendingToRegistery = true; // Register the Observable: this.dynamicInstanceProperties[name] = this._dispatcher.registerObservable( // Assign a new Observable. this._observableFactory(), // Use the provided Properties: options ); if (!options.isDynamic) { if (this[name]){ throw Error('Name alread used. Not able to use the method name twice') } // Use the Same Element. this[name] = this.dynamicInstanceProperties[name]; } this._registeredProperties.set(name, { observable: this.dynamicInstanceProperties[name], options }); } } constructor( @inject(DISPATCHER_INSTANCE) _dispatcher: INopeDispatcher, @inject(OBSERVABLE_FACTORY) protected _observableFactory: () => INopeObservable ) { super(_dispatcher); } public async listFunctions(): Promise<{ func: (...args: any[]) => Promise; options: IFunctionOptions; }[]> { const _this =this; return Object.getOwnPropertyNames(this.dynamicInstanceMethods).map(name => { return { func: _this.dynamicInstanceMethods[name], options: null } }) } public async registerProperty(name: string, observable: INopeObservable, options: IPropertyOptions): Promise { throw Error('Function Should not be called on remote!'); } public async registerFunction(name: string, func: (...args: any[]) => Promise, options: IFunctionOptions): Promise { throw Error('Function Should not be called on remote!'); } public async unregisterFunction(name: string): Promise { throw Error('Function Should not be called on remote!'); } public async unregisterProperty(name: string): Promise { throw Error('Function Should not be called on remote!'); } public async init(): Promise { try { await super.init(); } catch (e) { throw Error('Call fromDescription before using'); } } public async dispose(){ for (const name in this.dynamicInstanceProperties){ this.dynamicInstanceProperties[name].dispose(); // Remove Reference delete this[name]; } this._registeredFunctions.clear(); this.dynamicInstanceProperties = {}; for (const name in this.dynamicInstanceMethods) { delete this[name]; delete this.dynamicInstanceMethods[name]; } this._registeredProperties.clear(); } }