Browse Source

clean up RPC structure

master
peter 4 years ago
parent
commit
67278ff2fe
8 changed files with 93 additions and 75 deletions
  1. 0
    2
      Index.ts
  2. 4
    2
      src/Backend.ts
  3. 2
    2
      src/Frontend.ts
  4. 2
    2
      src/Interfaces.ts
  5. 0
    37
      src/Responses.ts
  6. 18
    10
      src/Types.ts
  7. 24
    16
      src/Utils.ts
  8. 43
    4
      test/TestBackend.ts

+ 0
- 2
Index.ts View File

3
 import * as Types from './src/Types'; 
3
 import * as Types from './src/Types'; 
4
 import * as Utils from './src/Utils'; 
4
 import * as Utils from './src/Utils'; 
5
 import * as Interfaces from './src/Interfaces';
5
 import * as Interfaces from './src/Interfaces';
6
-import * as Responses from './src/Responses';
7
 
6
 
8
 export {
7
 export {
9
     Back as Backend,
8
     Back as Backend,
11
     Types, 
10
     Types, 
12
     Utils, 
11
     Utils, 
13
     Interfaces,
12
     Interfaces,
14
-    Responses
15
 }
13
 }

+ 4
- 2
src/Backend.ts View File

7
 import * as U from './Utils'; 
7
 import * as U from './Utils'; 
8
 import * as I from './Interfaces';
8
 import * as I from './Interfaces';
9
 
9
 
10
-export class RPCServer{
10
+export class RPCServer<
11
+    SubResType = {}
12
+>{
11
     private ws = http.createServer()
13
     private ws = http.createServer()
12
     private io = bsock.createServer()
14
     private io = bsock.createServer()
13
     private visibility:T.Visibility
15
     private visibility:T.Visibility
18
 
20
 
19
     constructor(
21
     constructor(
20
         private port:number,
22
         private port:number,
21
-        private exporters: I.Exporter[] = [],
23
+        private exporters: I.Exporter<SubResType>[] = [],
22
         conf: T.SocketConf =  {}
24
         conf: T.SocketConf =  {}
23
     ){
25
     ){
24
         
26
         

+ 2
- 2
src/Frontend.ts View File

81
         const argParams = fnArgs.map(stripAfterEquals).join(",")
81
         const argParams = fnArgs.map(stripAfterEquals).join(",")
82
         return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => {
82
         return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => {
83
                             const r = await this.socket.call("`+fnName+`", `+argParams+`)
83
                             const r = await this.socket.call("`+fnName+`", `+argParams+`)
84
-                            if(r.uid != null){
85
-                                this.socket.hook(r.uid, callback)
84
+                            if(r.result === 'Success'){
85
+                                this.socket.hook(r.uuid, callback)
86
                             }
86
                             }
87
                             return r
87
                             return r
88
                         } )()` )
88
                         } )()` )

+ 2
- 2
src/Interfaces.ts View File

1
 import * as T from "./Types";
1
 import * as T from "./Types";
2
 import * as I from "./Interfaces"
2
 import * as I from "./Interfaces"
3
 
3
 
4
-export interface Exporter{
4
+export interface Exporter<T = {}>{
5
     name: T.Name
5
     name: T.Name
6
-    exportRPCs() : T.RPC[]
6
+    exportRPCs() : T.RPC<T>[]
7
 }
7
 }
8
 
8
 
9
 export interface Socket {
9
 export interface Socket {

+ 0
- 37
src/Responses.ts View File

1
-
2
-export type Outcome = "Success" | "Error"
3
-
4
-export class Response{
5
-    constructor(
6
-        public message?:string
7
-    ){}
8
-}
9
-
10
-export class SuccessResponse extends Response{
11
-    result:Outcome = "Success"
12
-
13
-    constructor(
14
-        message?:string
15
-    ){
16
-        super(message)
17
-    }
18
-}
19
-
20
-export class ErrorResponse extends Response{
21
-    result:Outcome = "Error"
22
-
23
-    constructor(
24
-        message: string = "Unknown error"
25
-    ){
26
-        super(message)
27
-    } 
28
-}
29
-
30
-export class SubscriptionResponse extends SuccessResponse{
31
-    constructor(
32
-        public uid: string,
33
-        message?:string
34
-    ){
35
-        super(message)
36
-    }
37
-}

+ 18
- 10
src/Types.ts View File

1
-import * as R from "./Responses";
2
 import * as I from "./Interfaces";
1
 import * as I from "./Interfaces";
3
 
2
 
4
 export type Visibility = "127.0.0.1" | "0.0.0.0"
3
 export type Visibility = "127.0.0.1" | "0.0.0.0"
16
     visibility?: Visibility
15
     visibility?: Visibility
17
 }
16
 }
18
 
17
 
18
+export type ResponseType = "Subscribe" | "Success" | "Error"
19
+export type Outcome = "Success" | "Error"
20
+
21
+
22
+export type Respose<T> = T & { result: Outcome }
23
+export type SuccessResponse<T = {}> = Respose<T> & { result: "Success" } 
24
+export type ErrorResponse<T = {}> = Respose<T> & { result: "Error" }
25
+export type SubscriptionResponse<T = {}> = Respose<T> & { result: "Success"; uuid: string }
26
+
19
 export type RPCType = 'Hook' | 'Unhook' | 'Call'
27
 export type RPCType = 'Hook' | 'Unhook' | 'Call'
20
 
28
 
21
-export type HookRPC = {
29
+export type HookRPC<T = {}> = {
22
     name: Name
30
     name: Name
23
-    hook: HookFunction
31
+    hook: HookFunction<T>
24
     onCallback?: CallbackFunction,
32
     onCallback?: CallbackFunction,
25
-    onClose?: HookCloseFunction
33
+    onClose?: HookCloseFunction<T>
26
 }
34
 }
27
 
35
 
28
 export type CallRPC = {
36
 export type CallRPC = {
30
     call: AsyncFunction
38
     call: AsyncFunction
31
 } | Function
39
 } | Function
32
 
40
 
33
-export type RPC = CallRPC | HookRPC
41
+export type RPC<T = {}> = CallRPC | HookRPC<T>
34
 
42
 
35
 export type BaseInfo = {
43
 export type BaseInfo = {
36
     name: Name,
44
     name: Name,
38
     argNames: Name[],
46
     argNames: Name[],
39
 }
47
 }
40
 
48
 
41
-export type HookInfo = BaseInfo & { 
49
+export type HookInfo<T = {}> = BaseInfo & { 
42
     type: 'Hook', 
50
     type: 'Hook', 
43
-    generator: (socket) => HookFunction
51
+    generator: (socket) => HookFunction<T>
44
 }
52
 }
45
 
53
 
46
 export type CallInfo = BaseInfo & {
54
 export type CallInfo = BaseInfo & {
52
 export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } 
60
 export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } 
53
 
61
 
54
 export type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => I.Socket
62
 export type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => I.Socket
55
-export type HookCloseFunction = (res:R.SubscriptionResponse, rpc:HookRPC) => any
56
-export type HookFunction = (...args:any[]) => Promise<R.SubscriptionResponse | R.ErrorResponse>
63
+export type HookCloseFunction<T = {}> = (res:SubscriptionResponse<T>, rpc:HookRPC<T>) => any
64
+export type HookFunction<T = {}> = (...args:[any, ...any[]]) => Promise<SubscriptionResponse<T> | ErrorResponse>
57
 export type AsyncFunction = (...args:any[]) => Promise<any>
65
 export type AsyncFunction = (...args:any[]) => Promise<any>
58
-export type CallbackFunction = (arg: any) => void
66
+export type CallbackFunction = (...args:any[]) => void

+ 24
- 16
src/Utils.ts View File

1
-import * as uuid from "uuid/v4"
1
+import * as uuidv4 from "uuid/v4"
2
 
2
 
3
 import * as T from "./Types";
3
 import * as T from "./Types";
4
 import * as I from "./Interfaces";
4
 import * as I from "./Interfaces";
5
+import { SubscriptionResponse } from "./Types";
5
 
6
 
6
-export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => {
7
+export const rpcToRpcinfo = <SubResT = {}>(rpc : T.RPC<SubResT>, owner: T.Owner):T.RpcInfo => {
7
     switch (typeof rpc){
8
     switch (typeof rpc){
8
         case  "object":
9
         case  "object":
9
             if(rpc['call']){
10
             if(rpc['call']){
15
                     call: rpc['call'],
16
                     call: rpc['call'],
16
                 }
17
                 }
17
             }else{
18
             }else{
18
-                const generator = hookGenerator(<T.HookRPC>rpc)
19
+                const generator = hookGenerator(<T.HookRPC<any>>rpc)
19
                 return {
20
                 return {
20
                     owner: owner,
21
                     owner: owner,
21
                     argNames: extractArgs(generator(undefined)),
22
                     argNames: extractArgs(generator(undefined)),
26
             }
27
             }
27
         case "function":
28
         case "function":
28
             if(!rpc.name) throw new Error(`
29
             if(!rpc.name) throw new Error(`
29
-            RPC did not provide a name. 
30
-            \nUse funtion name(..){ .. } syntax instead.
31
-            \n
32
-            \n<------------OFFENDING RPC:
33
-            \n`+rpc.toString()+`
34
-            \n>------------OFFENDING RPC`)
30
+RPC did not provide a name. 
31
+\nUse funtion name(..){ .. } syntax instead.
32
+\n
33
+\n<------------OFFENDING RPC:
34
+\n`+rpc.toString()+`
35
+\n>------------OFFENDING RPC`)
35
             return {
36
             return {
36
-                type: "Call",
37
                 owner : owner,
37
                 owner : owner,
38
                 argNames: extractArgs(rpc),
38
                 argNames: extractArgs(rpc),
39
+                type: "Call",
40
+                name: rpc.name,
39
                 call: async(...args) => rpc.apply({}, args),
41
                 call: async(...args) => rpc.apply({}, args),
40
-                name: rpc.name
41
             }
42
             }
42
     }
43
     }
43
     throw new Error("Bad socketIORPC type "+ typeof rpc)
44
     throw new Error("Bad socketIORPC type "+ typeof rpc)
44
 }
45
 }
45
 
46
 
46
-export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = true):T.ExtendedRpcInfo[]{
47
+export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.Exporter<SubResT>, makeUnique = true):T.ExtendedRpcInfo[]{
47
     const owner = exporter.name
48
     const owner = exporter.name
48
     const RPCs = [...exporter.exportRPCs()]
49
     const RPCs = [...exporter.exportRPCs()]
49
-    const suffix = makeUnique?"-"+uuid().substr(0,4):""
50
+    const suffix = makeUnique?"-"+uuidv4().substr(0,4):""
50
     return RPCs.map(rpc => rpcToRpcinfo(rpc, owner))
51
     return RPCs.map(rpc => rpcToRpcinfo(rpc, owner))
51
     .map(info => {
52
     .map(info => {
52
         const ret:any = info
53
         const ret:any = info
65
     })
66
     })
66
 }
67
 }
67
 
68
 
68
-const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { 
69
+const hookGenerator = (rpc:T.HookRPC<any>): T.HookInfo['generator'] => { 
69
     const argsArr = extractArgs(rpc.hook)
70
     const argsArr = extractArgs(rpc.hook)
70
     argsArr.pop()
71
     argsArr.pop()
71
     const args = argsArr.join(',')
72
     const args = argsArr.join(',')
73
     return eval(`(socket) => async (`+args+`) => { 
74
     return eval(`(socket) => async (`+args+`) => { 
74
         const res = await rpc.hook(`+args+(args.length!==0?',':'')+` (...cbargs) => {
75
         const res = await rpc.hook(`+args+(args.length!==0?',':'')+` (...cbargs) => {
75
             if(rpc.onCallback) rpc.onCallback.apply({}, cbargs)
76
             if(rpc.onCallback) rpc.onCallback.apply({}, cbargs)
76
-            socket.call.apply(socket, [res.uid, ...cbargs])
77
+            socket.call.apply(socket, [res.uuid, ...cbargs])
77
         })
78
         })
78
-        if(res.result == 'Success'){
79
+        if(res.result === 'Success'){
79
             if(rpc.onClose){
80
             if(rpc.onClose){
80
                 socket.on('close', async () => {
81
                 socket.on('close', async () => {
81
                     rpc.onClose(res, rpc)
82
                     rpc.onClose(res, rpc)
89
 const extractArgs = (f:Function):T.Arg[] => {
90
 const extractArgs = (f:Function):T.Arg[] => {
90
     let fn
91
     let fn
91
     return (fn = String(f)).substr(0, fn.indexOf(")")).substr(fn.indexOf("(")+1).split(",")
92
     return (fn = String(f)).substr(0, fn.indexOf(")")).substr(fn.indexOf("(")+1).split(",")
93
+}
94
+
95
+export function makeSubResponse(uuid?:string):SubscriptionResponse{
96
+    return {
97
+        result: "Success",
98
+        uuid: uuid?uuid:uuidv4(),
99
+    }
92
 }
100
 }

+ 43
- 4
test/TestBackend.ts View File

1
 import { RPCServer } from '../src/Backend'
1
 import { RPCServer } from '../src/Backend'
2
-import { SubscriptionResponse, ErrorResponse, SuccessResponse } from '../src/Responses'
2
+import { SubscriptionResponse, ErrorResponse, SuccessResponse } from '../src/Types'
3
 import { HookRPC } from '../src/Types'
3
 import { HookRPC } from '../src/Types'
4
+import * as uuidv4 from "uuid/v4"
5
+import { makeSubResponse } from '../src/Utils'
4
 
6
 
5
 let subcallback
7
 let subcallback
6
 
8
 
14
             name: 'simpleSubscribe',
16
             name: 'simpleSubscribe',
15
             hook: async(callback) => {
17
             hook: async(callback) => {
16
                 subcallback =  callback
18
                 subcallback =  callback
17
-                return new SubscriptionResponse(""+Math.random())
19
+                return makeSubResponse()
18
             }
20
             }
19
         },{
21
         },{
20
             name: 'subscribe',
22
             name: 'subscribe',
21
             hook: async (callback):Promise<any> => {
23
             hook: async (callback):Promise<any> => {
22
                 subcallback = callback
24
                 subcallback = callback
23
-                return new SubscriptionResponse(""+Math.random())
25
+                return makeSubResponse()
24
             },
26
             },
25
             onClose: (res:SubscriptionResponse, rpc:HookRPC) => { 
27
             onClose: (res:SubscriptionResponse, rpc:HookRPC) => { 
26
                 console.log("Specific close handler for", rpc.name, res)
28
                 console.log("Specific close handler for", rpc.name, res)
27
                 subcallback = null 
29
                 subcallback = null 
28
             },
30
             },
29
-            onCallback: (...args) => { console.log.apply(console, args) }
31
+            onCallback: (...args:any) => { console.log.apply(console, args) }
32
+        },
33
+        function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)},
34
+        function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)},
35
+    ]
36
+}])
37
+
38
+new RPCServer<{ topic: string}>(20001, [{
39
+    name: "HelloWorldRPCGroup",
40
+    exportRPCs: () => [
41
+        {
42
+            name: 'echo',
43
+            call: async (s:string) => s,
44
+        },{
45
+            name: 'simpleSubscribe',
46
+            hook: async(callback) => {
47
+                subcallback =  callback
48
+                return {
49
+                    result: "Success",
50
+                    uuid: uuidv4(),
51
+                    topic: ""
52
+                }
53
+            }
54
+        },{
55
+            name: 'subscribe',
56
+            hook: async (callback) => {
57
+                subcallback = callback
58
+                return {
59
+                    result: "Success",
60
+                    uuid: uuidv4(),
61
+                    topic: ""
62
+                }
63
+            },
64
+            onClose: (res, rpc) => { 
65
+                console.log("Specific close handler for", rpc.name, res)
66
+                subcallback = null 
67
+            },
68
+            onCallback: (...args:any) => { console.log.apply(console, args) }
30
         },
69
         },
31
         function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)},
70
         function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)},
32
         function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)},
71
         function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)},

Loading…
Cancel
Save