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
export interface IExportToDispatcherParameters {
uri?: string,
}
export interface IExportMethodToDispatcherParameters {
url?: string,
uri?: string,
}
export interface IExportPropertyToDispatcherParameters {
url?: string,
uri?: string,
readonly?: boolean
}
@ -59,7 +60,7 @@ export function exportsElementsToDispatcher(options: IExportToDispatcherParamete
* Decorator, used to export the Method.
* @param options
*/
export function exportMethodToDispatcher(options: IExportMethodToDispatcherParameters = {}) {
export function exportMethodToDispatcher(options: IExportMethodToDispatcherParameters) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
target[_registeredDispatcherMethods_] = target[_registeredDispatcherMethods_] || new Map<string, IExportMethodToDispatcherParameters>();
// 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.
* @param options
*/
export function exportPropertyToDispatcher(options: IExportPropertyToDispatcherParameters = {}) {
export function exportPropertyToDispatcher(options: IExportPropertyToDispatcherParameters) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
target[_registeredDispatcherParams_] = target[_registeredDispatcherParams_] || new Map<string, IExportPropertyToDispatcherParameters>();
// 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

View File

@ -19,18 +19,47 @@ export async function generateClientTemplate(options: {
logger?: Logger
}) {
// Create the Output dir (if it doenst exists)
await createPath(options.outputDir);
if (options.logger) {
options.logger.info('Templates will be stored in ' + options.outputDir);
}
// Firstly copy the nopeDispatcher
await copyFile(
join(__dirname, '..', '..', '..', 'lib', 'dispatcher', 'nopeDispatcher.ts'),
join(options.outputDir, 'nopeDispatcher.ts')
);
const filesToCopy: {
src: string[],
name: string,
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.
const project = new Project({
@ -60,7 +89,27 @@ export async function generateClientTemplate(options: {
content: string,
}[] = 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 {
name: item.className + '.ts',
@ -79,7 +128,7 @@ export async function generateClientTemplate(options: {
);
if (options.logger) {
options.logger.info('Generated -> ' + fileName);
options.logger.info('Generated -> ' + join('clients', fileName));
}
// 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 {
active: boolean;
}
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.
@ -119,4 +122,28 @@ export class nopeObservable<T> extends BehaviorSubject<T> {
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}}"
// 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}}
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;
@ -47,7 +57,7 @@ export class {{className}} {
{{{authorDescription}}}
public async {{name}}{{{head}}}{
// 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}}
}

View File

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

View File

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

View File

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