nope/lib/module/BaseModule.ts

339 lines
11 KiB
TypeScript
Raw Normal View History

2020-10-12 16:13:10 +00:00
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-10-12 17:54:08
2020-11-24 14:14:56 +00:00
* @modify date 2020-11-23 08:11:41
2020-10-12 16:13:10 +00:00
* @desc [description]
*/
2020-11-24 14:14:56 +00:00
import { inject, injectable } from "inversify";
import { getMethodPath, getPropertyPath, isMethodPathCorrect, isPropertyPathCorrect } from "../helpers/dispatcherPathes";
import { deepClone } from "../helpers/objectMethods";
import { DISPATCHER_INSTANCE } from "../symbols/identifiers";
2020-11-06 08:10:30 +00:00
import { INopeDispatcher } from "../types/nope/nopeDispatcher.interface";
import { IAuthor, IFunctionOptions, INopeModule, INopeModuleDescription, IPropertyOptions, IVersion } from "../types/nope/nopeModule.interface";
import { INopeObservable } from "../types/nope/nopeObservable.interface";
2020-10-12 16:13:10 +00:00
/**
* Base Implementation of a Module.
2020-11-06 08:10:30 +00:00
*
* The Module is used to share information and data. Although it implements the
* the Basic behavior to fullfill a given traget.
2020-10-12 16:13:10 +00:00
*
* @export
* @class BaseModule
2020-10-13 13:18:25 +00:00
* @implements {INopeModule}
2020-10-12 16:13:10 +00:00
*/
2020-10-13 13:18:25 +00:00
@injectable()
export class NopeBaseModule implements INopeModule {
2020-10-12 16:13:10 +00:00
/**
* Return the Class Identifier.
2020-10-12 16:13:10 +00:00
*/
2020-10-13 13:18:25 +00:00
public get type(): string {
return Object.getPrototypeOf(this).constructor.name;
}
2020-10-12 16:13:10 +00:00
/**
* A Description of the Module. This is used to Describe roughly
* what the module is capable of
* doing.
*
* @type {string}
* @memberof BaseModule
*/
public description: string;
/**
* A Description of the Author. Use to Mail etc.
*
* @type {IAuthor}
* @memberof BaseModule
*/
public author: IAuthor;
/**
* Description of the provided Version of the Module.
*
* @type {IVersion}
* @memberof BaseModule
*/
public version: IVersion;
protected _registeredFunctions: Map<string, {
func: (...args: any[]) => Promise<any>,
options: IFunctionOptions
}>
protected _registeredProperties: Map<string, {
observable: INopeObservable<any>
options: IPropertyOptions
}>
2020-10-13 13:18:25 +00:00
/**
* Public getter for the functions
*
* @readonly
* @memberof BaseModule
*/
public get functions() {
const ret: { [index: string]: IFunctionOptions; } = {};
for (const [name, funcs] of this._registeredFunctions.entries()){
ret[name] = funcs.options;
}
return ret;
}
/**
* Public get to receive a Description of the Properties
*
* @readonly
* @memberof BaseModule
*/
public get properties() {
const ret: { [index: string]: IPropertyOptions<any>; } = {};
for (const [name, funcs] of this._registeredProperties.entries()){
ret[name] = funcs.options;
}
return ret;
}
/**
* The Identifier of the Module.
*
* @type {string}
* @memberof BaseModule
*/
public identifier: string;
2020-11-05 17:02:29 +00:00
2020-11-24 14:14:56 +00:00
public _markedElements: Array<{accessor: string, options:IPropertyOptions | IFunctionOptions, type: "method" | "prop"}>;
2020-10-12 16:13:10 +00:00
/**
* Creates an instance of BaseModule.
* @memberof BaseModule
*/
2020-10-13 13:18:25 +00:00
constructor(@inject(DISPATCHER_INSTANCE) protected _dispatcher: INopeDispatcher) {
2020-10-12 16:13:10 +00:00
this.description = null;
this.author = null;
this.version = null;
2020-10-13 13:18:25 +00:00
this.identifier = null;
this._registeredFunctions = new Map();
this._registeredProperties = new Map();
2020-10-12 16:13:10 +00:00
}
2020-11-06 08:10:30 +00:00
/**
* Helper Function to register an Observable (a Property.)
*
* @template T Type of the Property
* @template K Additional Pipe Options
* @template S Setter Type of the Property
* @template G Getter Type of the Property
* @param {string} name Name, which should be used to register the element. The Name will ALLWAYS (automatically) be assembled using the modules identifier an then the name.
* @param {INopeObservable<T, S, G>} observable The Observable representing the Property
* @param {IPropertyOptions<K>} options The Options used to define the registration.
* @return {*} {Promise<void>}
* @memberof NopeBaseModule
*/
2020-10-12 16:13:10 +00:00
public async registerProperty<T, K = any, S = T, G = T>(name: string, observable: INopeObservable<T, S, G>, options: IPropertyOptions<K>): Promise<void> {
// Unregister the Function
await this.unregisterProperty(name);
// Adapt the Topics
2020-11-24 14:14:56 +00:00
if (typeof options.topic === "string" && !isPropertyPathCorrect(this.identifier, options.topic as string)){
options.topic = getPropertyPath(this.identifier, options.topic);
2020-11-24 14:14:56 +00:00
} else if (typeof options.topic === "object"){
2020-11-09 06:42:24 +00:00
if (options.topic.subscribe && !isPropertyPathCorrect(this.identifier, options.topic.subscribe)){
options.topic.subscribe = getPropertyPath(this.identifier, options.topic.subscribe);
2020-10-12 16:13:10 +00:00
}
2020-11-09 06:42:24 +00:00
if (options.topic.publish && !isPropertyPathCorrect(this.identifier, options.topic.publish)){
options.topic.publish = getPropertyPath(this.identifier, options.topic.publish);
2020-10-12 16:13:10 +00:00
}
}
const _observable = await this._dispatcher.registerObservable(observable, options);
// Register the new Property.
this._registeredProperties.set(name, {
observable: _observable,
options
});
}
2020-11-06 08:10:30 +00:00
/**
* Function used to register a Method. This Method will be available in the shared network.
*
* @param {string} name Name of the Method, which is used during registration at the dispatcher
* @param {(...args: any[]) => Promise<any>} func The function itself. It must be async.
* @param {IFunctionOptions} options The Options, used for registering.
* @return {*} {Promise<void>}
* @memberof NopeBaseModule
*/
2020-11-05 17:02:29 +00:00
public async registerMethod(name: string, func: (...args: any[]) => Promise<any>, options: IFunctionOptions): Promise<void> {
2020-10-12 16:13:10 +00:00
// Unregister the Function
await this.unregisterFunction(name);
// Adapt the Method ID
2020-11-09 06:42:24 +00:00
if (options.id){
if (!isMethodPathCorrect(this.identifier, options.id)){
options.id = getMethodPath(this.identifier,options.id);
}
} else {
options.id = getMethodPath(this.identifier,name);
}
2020-10-12 16:13:10 +00:00
const _func = await this._dispatcher.registerFunction(func, options);
// Register the new Function.
this._registeredFunctions.set(name, {
func: _func,
options
});
}
2020-11-06 08:10:30 +00:00
/**
* Unregister a Function
*
* @param {string} name Name of the function used during registering.
* @return {*} {Promise<void>}
* @memberof NopeBaseModule
*/
2020-10-12 16:13:10 +00:00
public async unregisterFunction(name: string): Promise<void> {
// Test if the Method is already registerd,
// If so => unregister it first.
if (this._registeredFunctions.has(name)) {
2020-10-13 13:18:25 +00:00
this._dispatcher.unregisterFunction(this._registeredFunctions.get(name).func, {
2020-10-12 16:13:10 +00:00
preventSendingToRegistery: true
});
}
}
2020-11-06 08:10:30 +00:00
/**
* Helper Function to unregister an Observable (a Property.)
*
* @param {string} name Name of the Property, that has been used to register.
* @return {*} {Promise<void>}
* @memberof NopeBaseModule
*/
public async unregisterProperty(name: string): Promise<void> {
2020-10-12 16:13:10 +00:00
// Test if the Property is already registerd,
// If so => unregister it first.
if (this._registeredProperties.has(name)) {
2020-10-13 13:18:25 +00:00
this._dispatcher.unregisterObservable(this._registeredProperties.get(name).observable, {
2020-10-12 16:13:10 +00:00
preventSendingToRegistery: true
});
}
}
2020-11-06 08:10:30 +00:00
/**
* Function to return all available Methods.
*
* @return {*} {Promise<{ func: (...args: any[]) => Promise<any>; options: IFunctionOptions; }[]>}
* @memberof NopeBaseModule
*/
2020-10-13 13:18:25 +00:00
public async listFunctions(): Promise<{ func: (...args: any[]) => Promise<any>; options: IFunctionOptions; }[]> {
return Array.from(this._registeredFunctions.values());
2020-10-12 16:13:10 +00:00
}
2020-11-06 08:10:30 +00:00
/**
* Function used to list all available Properties.
*
* @return {*} {Promise<Array<{ observable: INopeObservable<any>, options: IPropertyOptions }>>}
* @memberof NopeBaseModule
*/
2020-10-13 13:18:25 +00:00
public async listProperties(): Promise<Array<{ observable: INopeObservable<any>, options: IPropertyOptions }>> {
return Array.from(this._registeredProperties.values());
2020-10-12 16:13:10 +00:00
}
2020-11-06 08:10:30 +00:00
/**
* An init Function. Used to initialize the Element.
*
* @return {*} {Promise<void>}
* @memberof NopeBaseModule
*/
public async init(...args): Promise<void> {
2020-10-12 16:13:10 +00:00
// In this base Implementation, check if every requried property is set
// correctly. If not => raise an error.
2020-10-13 13:18:25 +00:00
if (this.type === null){
2020-11-24 14:14:56 +00:00
throw Error("Please Provide a Name for the Module before initializing");
2020-10-12 16:13:10 +00:00
}
if (this.description === null){
2020-11-24 14:14:56 +00:00
throw Error("Please Provide a Description for the Module before initializing");
2020-10-12 16:13:10 +00:00
}
if (this.author === null){
2020-11-24 14:14:56 +00:00
throw Error("Please Provide an Author for the Module before initializing");
2020-10-12 16:13:10 +00:00
}
if (this.version === null){
2020-11-24 14:14:56 +00:00
throw Error("Please Provide a Version for the Module before initializing");
2020-10-12 16:13:10 +00:00
}
2020-10-13 13:18:25 +00:00
if (this.identifier === null){
2020-11-24 14:14:56 +00:00
throw Error("Please Provide an Identifier for the Module before initializing");
2020-10-13 13:18:25 +00:00
}
2020-11-05 17:02:29 +00:00
if (this._markedElements){
const _this = this;
2020-11-15 19:11:25 +00:00
for (const entry of deepClone(this._markedElements)){
2020-11-05 17:02:29 +00:00
switch(entry.type){
2020-11-24 14:14:56 +00:00
case "method":
2020-11-05 17:02:29 +00:00
await this.registerMethod(entry.accessor, (...args) => {
2020-11-24 14:14:56 +00:00
return _this[entry.accessor](...args);
}, entry.options as IFunctionOptions);
2020-11-05 17:02:29 +00:00
break;
2020-11-24 14:14:56 +00:00
case "prop":
2020-11-05 17:02:29 +00:00
await this.registerProperty(entry.accessor, _this[entry.accessor], entry.options as IPropertyOptions);
break;
}
}
}
2020-10-12 16:13:10 +00:00
}
2020-11-06 08:10:30 +00:00
/**
* Function, which is used to unregister the element.
*
* @memberof NopeBaseModule
*/
2020-10-12 16:13:10 +00:00
public async dispose(){
// Unregister all Methods and Functions
for (const name of this._registeredFunctions.keys()){
await this.unregisterFunction(name);
}
// Remove all known Functions
this._registeredFunctions.clear();
// Unregister all Properties.
for (const name of this._registeredProperties.keys()){
await this.unregisterProperty(name);
}
// Remove all known Properties.
this._registeredProperties.clear();
}
2020-10-13 13:18:25 +00:00
2020-11-06 08:10:30 +00:00
/**
* Helper function to extract an description of the Module.
*
* @return {INopeModuleDescription} a parsed description
* @memberof NopeBaseModule
*/
2020-10-13 13:18:25 +00:00
public toDescription(): INopeModuleDescription {
const ret: INopeModuleDescription = {
author: this.author,
description: this.description,
functions: this.functions,
identifier: this.identifier,
properties: this.properties,
type: this.type,
version: this.version
2020-11-24 14:14:56 +00:00
};
2020-10-12 16:13:10 +00:00
2020-10-13 13:18:25 +00:00
return ret;
}
2020-10-12 16:13:10 +00:00
}