2020-10-13 13:18:25 +00:00
|
|
|
/**
|
|
|
|
* @author Martin Karkowski
|
|
|
|
* @email m.karkowski@zema.de
|
|
|
|
* @create date 2020-10-12 18:53:00
|
2020-12-30 18:54:15 +00:00
|
|
|
* @modify date 2020-12-30 16:48:42
|
2020-10-13 13:18:25 +00:00
|
|
|
* @desc [description]
|
|
|
|
*/
|
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
import { deepClone } from "../helpers/objectMethods";
|
2020-11-09 08:46:06 +00:00
|
|
|
import { ICallOptions } from "../types/nope/nopeCommunication.interface";
|
2020-11-06 08:10:30 +00:00
|
|
|
import { INopeDispatcher } from "../types/nope/nopeDispatcher.interface";
|
2020-12-04 18:10:33 +00:00
|
|
|
import {
|
|
|
|
IFunctionOptions,
|
|
|
|
INopeModuleDescription,
|
|
|
|
IPropertyOptions
|
|
|
|
} from "../types/nope/nopeModule.interface";
|
2020-11-06 08:10:30 +00:00
|
|
|
import { INopeObservable } from "../types/nope/nopeObservable.interface";
|
|
|
|
import { INopePromise } from "../types/nope/nopePromise.interface";
|
2020-10-13 13:18:25 +00:00
|
|
|
import { NopeBaseModule } from "./BaseModule";
|
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
function _invertMode(options: IPropertyOptions<any>) {
|
|
|
|
const _ret = deepClone(options);
|
2020-12-30 18:54:15 +00:00
|
|
|
|
|
|
|
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"];
|
|
|
|
}
|
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
return _ret;
|
|
|
|
}
|
2020-10-13 13:18:25 +00:00
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
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;
|
2020-10-13 13:18:25 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
if (this.author == null) {
|
|
|
|
this.author = description.author;
|
2020-10-13 13:18:25 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
if (this.description == null) {
|
|
|
|
this.description = description.description;
|
2020-10-13 13:18:25 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
if (this.version == null) {
|
|
|
|
this.version = description.version;
|
2020-10-13 13:18:25 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
if (this.identifier == null) {
|
|
|
|
this.identifier = description.identifier;
|
2020-10-13 13:18:25 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
for (const name in description.functions) {
|
|
|
|
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;
|
|
|
|
}
|
2020-10-13 13:18:25 +00:00
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
this._registeredFunctions.set(name, {
|
|
|
|
func,
|
|
|
|
options
|
|
|
|
});
|
2020-10-13 13:18:25 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
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:
|
|
|
|
_invertMode(options)
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!options.isDynamic) {
|
|
|
|
if (this[name]) {
|
|
|
|
throw Error(
|
|
|
|
"Name alread used. Not able to use the method name twice"
|
|
|
|
);
|
2020-10-13 13:18:25 +00:00
|
|
|
}
|
2020-12-04 18:10:33 +00:00
|
|
|
// Use the Same Element.
|
|
|
|
this[name] = this.dynamicInstanceProperties[name];
|
|
|
|
}
|
|
|
|
|
|
|
|
this._registeredProperties.set(name, {
|
|
|
|
observable: this.dynamicInstanceProperties[name],
|
|
|
|
options
|
|
|
|
});
|
2020-10-13 13:18:25 +00:00
|
|
|
}
|
2020-12-04 18:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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");
|
|
|
|
}
|
|
|
|
}
|
2020-10-13 13:18:25 +00:00
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
public async dispose(): Promise<void> {
|
|
|
|
for (const name in this.dynamicInstanceProperties) {
|
|
|
|
this.dynamicInstanceProperties[name].dispose();
|
|
|
|
// Remove Reference
|
|
|
|
delete this[name];
|
|
|
|
}
|
2020-10-13 13:18:25 +00:00
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
this._registeredFunctions.clear();
|
2020-10-13 13:18:25 +00:00
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
this.dynamicInstanceProperties = {};
|
2020-10-13 13:18:25 +00:00
|
|
|
|
2020-12-04 18:10:33 +00:00
|
|
|
for (const name in this.dynamicInstanceMethods) {
|
|
|
|
delete this[name];
|
|
|
|
delete this.dynamicInstanceMethods[name];
|
2020-10-13 13:18:25 +00:00
|
|
|
}
|
2020-12-04 18:10:33 +00:00
|
|
|
|
|
|
|
this._registeredProperties.clear();
|
|
|
|
}
|
|
|
|
}
|