nope/lib/module/GenericModule.ts
Martin Karkowski 596bdcd7c8 Adding loggers
2021-10-18 08:00:35 +02:00

281 lines
7.5 KiB
TypeScript

/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2020-10-12 18:53:00
* @modify date 2020-12-30 16:48:42
* @desc [description]
*/
import { deepClone } from "../helpers/objectMethods";
import { getNopeLogger } from "../logger/getLogger";
import { ICallOptions } from "../types/nope/nopeCommunication.interface";
import { INopeDispatcher } from "../types/nope/nopeDispatcher.interface";
import {
IFunctionOptions,
INopeModuleDescription,
IPropertyOptions
} from "../types/nope/nopeModule.interface";
import { INopeObservable } from "../types/nope/nopeObservable.interface";
import { INopePromise } from "../types/nope/nopePromise.interface";
import { NopeBaseModule } from "./BaseModule";
function _invertMode(options: IPropertyOptions<any>) {
const _ret = deepClone(options);
if (Array.isArray(_ret.mode)) {
if (_ret.mode.includes("subscribe")) {
_ret.mode = ["publish", "subscribe"];
} else {
_ret.mode = ["subscribe"];
}
} else if (_ret.mode === "subscribe") {
_ret.mode = ["publish", "subscribe"];
} else {
_ret.mode = ["subscribe"];
}
return _ret;
}
export class NopeGenericModule extends NopeBaseModule {
private _type = "";
/**
*
* Return the Class Identifier.
*/
public get type(): string {
return this._type;
}
/**
* Return the Class Identifier.
*/
public set type(value: string) {
this._type = value;
}
public dynamicInstanceMethods: {
[index: string]: <T>(...args) => INopePromise<T>;
} = {};
public dynamicInstanceProperties: {
[index: string]: INopeObservable<any>;
} = {};
public dynamicInstanceMethodsWithOptions: {
[index: string]: <T>(
options: Partial<ICallOptions>,
...args
) => INopePromise<T>;
} = {};
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"
): void {
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;
this._logger = getNopeLogger("generic-wrapper-" + this.identifier, "debug");
}
for (const name in description.functions) {
this._logger.debug("Create function interface for \"" + name + "\"");
const options = description.functions[name];
const func = (...args) => {
return _this._dispatcher.performCall(options.id, args, options);
};
const funcWithCustomOptions = (
_options: Partial<ICallOptions>,
...args
) => {
return _this._dispatcher.performCall(
options.id,
args,
Object.assign({}, options, _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 (this.dynamicInstanceMethodsWithOptions[name]) {
throw Error("Name alread used. Not able to use the method name twice");
}
(this.dynamicInstanceMethodsWithOptions as any)[
name
] = funcWithCustomOptions;
// 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) {
this._logger.debug("Create property interface for \"" + name + "\"");
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:
_invertMode(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._logger.debug("Register Property \"" + name + "\"", options);
this._registeredProperties.set(name, {
observable: this.dynamicInstanceProperties[name],
options
});
}
}
/**
* Creates an instance of NopeGenericModule.
* @param {INopeDispatcher} _dispatcher
* @param {() => INopeObservable<any>} _observableFactory
* @memberof NopeGenericModule
*/
constructor(
_dispatcher: INopeDispatcher,
protected _observableFactory: () => INopeObservable<any>
) {
super(_dispatcher);
}
public async listFunctions(): Promise<
{ func: (...args: any[]) => Promise<any>; options: IFunctionOptions }[]
> {
const _this = this;
return Object.getOwnPropertyNames(this.dynamicInstanceMethods).map(
(name) => {
return {
func: _this.dynamicInstanceMethods[name],
options: null
};
}
);
}
public async registerProperty<T, K = any, S = T, G = T>(
name: string,
observable: INopeObservable<T, S, G>,
options: IPropertyOptions<K>
): Promise<void> {
throw Error("Function Should not be called on remote!");
}
public async registerMethod(
name: string,
func: (...args: any[]) => Promise<any>,
options: IFunctionOptions
): Promise<void> {
throw Error("Function Should not be called on remote!");
}
public async unregisterFunction(name: string): Promise<void> {
throw Error("Function Should not be called on remote!");
}
public async unregisterProperty(name: string): Promise<void> {
throw Error("Function Should not be called on remote!");
}
public async init(): Promise<void> {
try {
await super.init();
} catch (e) {
throw Error("Call fromDescription before using");
}
}
public async dispose(): Promise<void> {
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();
}
}