Integrating remoteObservable

This commit is contained in:
Martin Karkowski 2020-08-25 12:33:33 +02:00
parent 3430a33cc2
commit 5612ef0460
9 changed files with 185 additions and 38 deletions

View File

@ -4,14 +4,15 @@ const _registeredDispatcherParams_ = Symbol('_registeredDispatcherParams_');
// Interfaces for the Class // Interfaces for the Class
export interface IExportToDispatcherParameters { export interface IExportToDispatcherParameters {
uri?: string,
} }
export interface IExportMethodToDispatcherParameters { export interface IExportMethodToDispatcherParameters {
url?: string, uri?: string,
} }
export interface IExportPropertyToDispatcherParameters { export interface IExportPropertyToDispatcherParameters {
url?: string, uri?: string,
readonly?: boolean readonly?: boolean
} }
@ -59,7 +60,7 @@ export function exportsElementsToDispatcher(options: IExportToDispatcherParamete
* Decorator, used to export the Method. * Decorator, used to export the Method.
* @param options * @param options
*/ */
export function exportMethodToDispatcher(options: IExportMethodToDispatcherParameters = {}) { export function exportMethodToDispatcher(options: IExportMethodToDispatcherParameters) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
target[_registeredDispatcherMethods_] = target[_registeredDispatcherMethods_] || new Map<string, IExportMethodToDispatcherParameters>(); target[_registeredDispatcherMethods_] = target[_registeredDispatcherMethods_] || new Map<string, IExportMethodToDispatcherParameters>();
// Here we just add some information that class decorator will use // Here we just add some information that class decorator will use
@ -71,7 +72,7 @@ export function exportMethodToDispatcher(options: IExportMethodToDispatcherParam
* Decorator, will create a POST and GET api for the Parameter. * Decorator, will create a POST and GET api for the Parameter.
* @param options * @param options
*/ */
export function exportPropertyToDispatcher(options: IExportPropertyToDispatcherParameters = {}) { export function exportPropertyToDispatcher(options: IExportPropertyToDispatcherParameters) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
target[_registeredDispatcherParams_] = target[_registeredDispatcherParams_] || new Map<string, IExportPropertyToDispatcherParameters>(); target[_registeredDispatcherParams_] = target[_registeredDispatcherParams_] || new Map<string, IExportPropertyToDispatcherParameters>();
// Here we just add some information that class decorator will use // Here we just add some information that class decorator will use

View File

@ -81,7 +81,7 @@ export interface IAnalyzeResult {
} }
} }
export type PropertyInformation = ModifierInformation & TypeInformation & { name: string; declaration: PropertyDeclaration; }; export type PropertyInformation = DecoratorInformation & ModifierInformation & TypeInformation & { name: string; declaration: PropertyDeclaration; };
/** /**
* Helperfunction, used to extract a Mapping of imported Files. This allows the user to rename the interfaces * Helperfunction, used to extract a Mapping of imported Files. This allows the user to rename the interfaces

View File

@ -19,18 +19,47 @@ export async function generateClientTemplate(options: {
logger?: Logger logger?: Logger
}) { }) {
// Create the Output dir (if it doenst exists)
await createPath(options.outputDir);
if (options.logger) { if (options.logger) {
options.logger.info('Templates will be stored in ' + options.outputDir); options.logger.info('Templates will be stored in ' + options.outputDir);
} }
// Firstly copy the nopeDispatcher const filesToCopy: {
await copyFile( src: string[],
join(__dirname, '..', '..', '..', 'lib', 'dispatcher', 'nopeDispatcher.ts'), name: string,
join(options.outputDir, 'nopeDispatcher.ts') path: string[]
); }[] = [
{
name: 'nopeDispatcher.ts',
path: ['dispatcher'],
src: ['lib', 'dispatcher']
},
{
name: 'nopeObservable.ts',
path: ['observables'],
src: ['lib', 'observables']
},
{
name: 'nopeRemoteObservable.ts',
path: ['observables'],
src: ['lib', 'observables']
}
];
for (const file of filesToCopy) {
// Create the Output dir (if it doenst exists)
await createPath(join(options.outputDir, ...file.path));
// Copy the File.
await copyFile(
join(__dirname, '..', '..', '..', ...file.src, file.name),
join(options.outputDir, ...file.path, file.name)
);
if (options.logger) {
options.logger.info('Copied -> ' + file.name);
}
}
// Function to Determine new project files. // Function to Determine new project files.
const project = new Project({ const project = new Project({
@ -60,7 +89,27 @@ export async function generateClientTemplate(options: {
content: string, content: string,
}[] = result.map(item => { }[] = result.map(item => {
item.className = item.className + 'ClientInterface' // Extract the Class uri
const classUri = item.classDecorator.decoratorSettings.exportsElementsToDispatcher ? item.classDecorator.decoratorSettings.exportsElementsToDispatcher.uri || item.className : item.className;
item.className = item.className + 'ClientInterface';
item.methods = item.methods.map(m => {
const methodUri = m.decoratorSettings.exportMethodToDispatcher ? m.decoratorSettings.exportMethodToDispatcher.uri || m.name : m.name;
(m as any).uri = classUri + '_' + methodUri;
return m;
});
item.properties = item.properties.map(p => {
const methodUri = p.decoratorSettings.exportPropertyToDispatcher ? p.decoratorSettings.exportPropertyToDispatcher.uri || p.name : p.name;
(p as any).uri = classUri + '_' + methodUri;
return p;
});
return { return {
name: item.className + '.ts', name: item.className + '.ts',
@ -79,7 +128,7 @@ export async function generateClientTemplate(options: {
); );
if (options.logger) { if (options.logger) {
options.logger.info('Generated -> ' + fileName); options.logger.info('Generated -> ' + join('clients', fileName));
} }
// Function to Determine new project files. // Function to Determine new project files.

View File

@ -1,10 +1,13 @@
import { BehaviorSubject, CompletionObserver, ErrorObserver, NextObserver, Subscription } from 'rxjs'; import { BehaviorSubject, CompletionObserver, ErrorObserver, NextObserver, Observable, Subscription } from 'rxjs';
export interface nopeObserver { export interface nopeObserver {
active: boolean; active: boolean;
} }
export declare type NopePartialObserver<T> = NextObserver<T> & nopeObserver | ErrorObserver<T> & nopeObserver | CompletionObserver<T> & nopeObserver; export declare type NopePartialObserver<T> = NextObserver<T> & nopeObserver | ErrorObserver<T> & nopeObserver | CompletionObserver<T> & nopeObserver;
export type pipe<T, K> = (scope: { [index: string]: any }, observable: Observable<T>) => Observable<K>
/** /**
* RsJX based Observable. * RsJX based Observable.
@ -119,4 +122,28 @@ export class nopeObservable<T> extends BehaviorSubject<T> {
return super.subscribe(_observerOrNext, _error, _complete); return super.subscribe(_observerOrNext, _error, _complete);
} }
/**
* Function to enhance the Subscription
* @param next Method, that will be called on next.
* @param options
*/
public enhancedSubscribe<K>(next: (data: K) => void, options: {
scope?: { [index: string]: any },
pipe?: pipe<T, K>
} = {}) {
let observable: Observable<K> = this as any as Observable<K>;
if (options.pipe) {
observable = options.pipe(options.scope, this);
}
const subscription = observable.subscribe({
next
});
const unsubscribe = () => { subscription.unsubscribe(); }
return unsubscribe;
}
} }

View File

@ -0,0 +1,70 @@
import { nopeDispatcher } from "../dispatcher/nopeDispatcher";
import { pipe } from "./nopeObservable";
export class nopeRemoteObservable<T> {
protected _currentLocalValue: T;
protected _pathes: {
get: string,
set: string,
subscribe: string,
}
/**
* Getter for the Current Value.
*/
public get currentLocalValue(): T {
return this._currentLocalValue;
}
/**
* Setter for the Current value.
*/
public set currentLocalValue(value: T) {
this.setRemoteValue(value).catch(e => {
// Catch the Error an rethrow it.
throw e;
});
}
/**
* Creates a Remote Dispatcher.
* @param _dispatcher
* @param options
*/
constructor(
protected _dispatcher: nopeDispatcher,
public options: {
path: string,
}
) {
}
public get currentRemoteValue() {
return this.getRemoteValue()
}
public async getRemoteValue() {
return await this._dispatcher.performCall<T>(this._pathes.get, [])
}
public async setRemoteValue(value: T) {
// Update the Local Value as Well
this._currentLocalValue = value;
// Perform an Update.
return await this._dispatcher.performCall<T>(this._pathes.set, [value]);
}
/**
* Function to Subscribe a NopeObservable on a Remote.
* @param next the callback, which will be called on new Data.
* @param options
*/
public async subscribe<K>(next: (data: K) => void, options: {
scope?: { [index: string]: any },
pipe?: pipe<T, K>
} = {}) {
return await this._dispatcher.performCall<() => void>(this._pathes.subscribe, [next, options]);
}
}

View File

@ -1,7 +1,8 @@
// Automatic Genearted File for Backendclass: "{{className}}" // Automatic Genearted File for Backendclass: "{{className}}"
// To update run `npm run build:backend` // To update run `npm run build:backend`
import { nopeDispatcher } from "../nopeDispatcher" import { nopeDispatcher } from "../dispatcher/nopeDispatcher"
import { nopeRemoteObservable } from "../observables/nopeRemoteObservable"
{{!-- {{!--
/** /**
@ -28,8 +29,17 @@ export interface IAnalyzeResult {
{{/if}} {{/if}}
export class {{className}} { export class {{className}} {
constructor(protected _dispatcher: nopeDispatcher){
{{#each properties}}
public {{name}}: nopeRemoteObservable<{{{simplifiedSubType}}}>
{{/each}}
constructor(protected _dispatcher: nopeDispatcher){
{{#each properties}}
this.{{name}} = new nopeRemoteObservable(_dispatcher,{
path: '{{uri}}'
})
{{/each}}
} }
{{!-- {{!--
declaration: MethodDeclaration; declaration: MethodDeclaration;
@ -47,7 +57,7 @@ export class {{className}} {
{{{authorDescription}}} {{{authorDescription}}}
public async {{name}}{{{head}}}{ public async {{name}}{{{head}}}{
// Perform the Method // Perform the Method
return await this._dispatcher.performCall<{{{returnType.simplifiedSubType}}}>('{{../classDecorator.decoratorSettings.exportsElementsToDispatcher.uri}}/{{decoratorSettings.exportMethodToDispatcher.url}}', [{{#each params}}{{name}}{{#unless @last}}, {{/unless}}{{/each}}]) return await this._dispatcher.performCall<{{{returnType.simplifiedSubType}}}>('{{uri}}', [{{#each params}}{{name}}{{#unless @last}}, {{/unless}}{{/each}}])
} }
{{/each}} {{/each}}
} }

View File

@ -9,17 +9,13 @@ export interface IF00 {
hello: 'world' hello: 'world'
} }
@exportsElementsToOpenAPI({ @exportsElementsToOpenAPI({})
uri: 'test' @exportsElementsToDispatcher({})
})
@exportsElementsToDispatcher({
uri: 'test'
})
export class CLWithInterface { export class CLWithInterface {
@exportPropertyToDispatcher @exportPropertyToDispatcher({})
exportedAttributeSimple = new nopeObservable<boolean>(false); exportedAttributeSimple = new nopeObservable<boolean>(false);
@exportPropertyToDispatcher @exportPropertyToDispatcher({})
exportedAttributeComplex = new nopeObservable<{ exportedAttributeComplex = new nopeObservable<{
element01: IF00, element01: IF00,
num: number num: number
@ -31,9 +27,7 @@ export class CLWithInterface {
}); });
@exportMethodToOpenAPI({}) @exportMethodToOpenAPI({})
@exportMethodToDispatcher({ @exportMethodToDispatcher({})
url: 'exportedFunction'
})
async exportedFunctionShouldBeHosted(/* COMMENT */ a: IF00, b?: IF01) { async exportedFunctionShouldBeHosted(/* COMMENT */ a: IF00, b?: IF01) {
return a + b return a + b
} }
@ -45,9 +39,7 @@ export class CLWithInterface {
* @param b Parameter b * @param b Parameter b
*/ */
@exportMethodToOpenAPI({}) @exportMethodToOpenAPI({})
@exportMethodToDispatcher({ @exportMethodToDispatcher({})
url: 'exportedFunction'
})
async exportedFunctionShouldNotBeHosted(/* COMMENT */ a: number, b: IF00, operator: (a: number, b: IF01) => Promise<{ async exportedFunctionShouldNotBeHosted(/* COMMENT */ a: number, b: IF00, operator: (a: number, b: IF01) => Promise<{
test: IF02, test: IF02,
x: string x: string
@ -64,9 +56,7 @@ export class CLWithInterface {
* @param a Parameter a * @param a Parameter a
* @param b Parameter b * @param b Parameter b
*/ */
@exportMethodToDispatcher({ @exportMethodToDispatcher({})
url: 'exportedStringFunction'
})
async exportedStringFunction(a: string, b: string) { async exportedStringFunction(a: string, b: string) {
// Comment etc. // Comment etc.
return ""; return "";

View File

@ -17,7 +17,8 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve",
"downlevelIteration": true "downlevelIteration": true,
"experimentalDecorators": true
}, },
"include": [ "include": [
"next-env.d.ts", "next-env.d.ts",

View File

@ -19,8 +19,7 @@
"removeComments": true, "removeComments": true,
"rootDir": "./", "rootDir": "./",
"stripInternal": true, "stripInternal": true,
"downlevelIteration": true, "downlevelIteration": true
"noEmit": true
}, },
"include": [ "include": [
"next-env.d.ts", "next-env.d.ts",