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,7 +3,6 @@ import * as Front from './src/Frontend';
3 3
 import * as Types from './src/Types'; 
4 4
 import * as Utils from './src/Utils'; 
5 5
 import * as Interfaces from './src/Interfaces';
6
-import * as Responses from './src/Responses';
7 6
 
8 7
 export {
9 8
     Back as Backend,
@@ -11,5 +10,4 @@ export {
11 10
     Types, 
12 11
     Utils, 
13 12
     Interfaces,
14
-    Responses
15 13
 }

+ 4
- 2
src/Backend.ts View File

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

+ 2
- 2
src/Frontend.ts View File

@@ -81,8 +81,8 @@ export class RPCSocket implements I.Socket{
81 81
         const argParams = fnArgs.map(stripAfterEquals).join(",")
82 82
         return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => {
83 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 87
                             return r
88 88
                         } )()` )

+ 2
- 2
src/Interfaces.ts View File

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

+ 0
- 37
src/Responses.ts View File

@@ -1,37 +0,0 @@
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,4 +1,3 @@
1
-import * as R from "./Responses";
2 1
 import * as I from "./Interfaces";
3 2
 
4 3
 export type Visibility = "127.0.0.1" | "0.0.0.0"
@@ -16,13 +15,22 @@ export type SocketConf = {
16 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 27
 export type RPCType = 'Hook' | 'Unhook' | 'Call'
20 28
 
21
-export type HookRPC = {
29
+export type HookRPC<T = {}> = {
22 30
     name: Name
23
-    hook: HookFunction
31
+    hook: HookFunction<T>
24 32
     onCallback?: CallbackFunction,
25
-    onClose?: HookCloseFunction
33
+    onClose?: HookCloseFunction<T>
26 34
 }
27 35
 
28 36
 export type CallRPC = {
@@ -30,7 +38,7 @@ export type CallRPC = {
30 38
     call: AsyncFunction
31 39
 } | Function
32 40
 
33
-export type RPC = CallRPC | HookRPC
41
+export type RPC<T = {}> = CallRPC | HookRPC<T>
34 42
 
35 43
 export type BaseInfo = {
36 44
     name: Name,
@@ -38,9 +46,9 @@ export type BaseInfo = {
38 46
     argNames: Name[],
39 47
 }
40 48
 
41
-export type HookInfo = BaseInfo & { 
49
+export type HookInfo<T = {}> = BaseInfo & { 
42 50
     type: 'Hook', 
43
-    generator: (socket) => HookFunction
51
+    generator: (socket) => HookFunction<T>
44 52
 }
45 53
 
46 54
 export type CallInfo = BaseInfo & {
@@ -52,7 +60,7 @@ export type RpcInfo = HookInfo |  CallInfo
52 60
 export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } 
53 61
 
54 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 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,9 +1,10 @@
1
-import * as uuid from "uuid/v4"
1
+import * as uuidv4 from "uuid/v4"
2 2
 
3 3
 import * as T from "./Types";
4 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 8
     switch (typeof rpc){
8 9
         case  "object":
9 10
             if(rpc['call']){
@@ -15,7 +16,7 @@ export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => {
15 16
                     call: rpc['call'],
16 17
                 }
17 18
             }else{
18
-                const generator = hookGenerator(<T.HookRPC>rpc)
19
+                const generator = hookGenerator(<T.HookRPC<any>>rpc)
19 20
                 return {
20 21
                     owner: owner,
21 22
                     argNames: extractArgs(generator(undefined)),
@@ -26,27 +27,27 @@ export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => {
26 27
             }
27 28
         case "function":
28 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 36
             return {
36
-                type: "Call",
37 37
                 owner : owner,
38 38
                 argNames: extractArgs(rpc),
39
+                type: "Call",
40
+                name: rpc.name,
39 41
                 call: async(...args) => rpc.apply({}, args),
40
-                name: rpc.name
41 42
             }
42 43
     }
43 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 48
     const owner = exporter.name
48 49
     const RPCs = [...exporter.exportRPCs()]
49
-    const suffix = makeUnique?"-"+uuid().substr(0,4):""
50
+    const suffix = makeUnique?"-"+uuidv4().substr(0,4):""
50 51
     return RPCs.map(rpc => rpcToRpcinfo(rpc, owner))
51 52
     .map(info => {
52 53
         const ret:any = info
@@ -65,7 +66,7 @@ export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = tr
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 70
     const argsArr = extractArgs(rpc.hook)
70 71
     argsArr.pop()
71 72
     const args = argsArr.join(',')
@@ -73,9 +74,9 @@ const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => {
73 74
     return eval(`(socket) => async (`+args+`) => { 
74 75
         const res = await rpc.hook(`+args+(args.length!==0?',':'')+` (...cbargs) => {
75 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 80
             if(rpc.onClose){
80 81
                 socket.on('close', async () => {
81 82
                     rpc.onClose(res, rpc)
@@ -89,4 +90,11 @@ const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => {
89 90
 const extractArgs = (f:Function):T.Arg[] => {
90 91
     let fn
91 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,6 +1,8 @@
1 1
 import { RPCServer } from '../src/Backend'
2
-import { SubscriptionResponse, ErrorResponse, SuccessResponse } from '../src/Responses'
2
+import { SubscriptionResponse, ErrorResponse, SuccessResponse } from '../src/Types'
3 3
 import { HookRPC } from '../src/Types'
4
+import * as uuidv4 from "uuid/v4"
5
+import { makeSubResponse } from '../src/Utils'
4 6
 
5 7
 let subcallback
6 8
 
@@ -14,19 +16,56 @@ new RPCServer(20000, [{
14 16
             name: 'simpleSubscribe',
15 17
             hook: async(callback) => {
16 18
                 subcallback =  callback
17
-                return new SubscriptionResponse(""+Math.random())
19
+                return makeSubResponse()
18 20
             }
19 21
         },{
20 22
             name: 'subscribe',
21 23
             hook: async (callback):Promise<any> => {
22 24
                 subcallback = callback
23
-                return new SubscriptionResponse(""+Math.random())
25
+                return makeSubResponse()
24 26
             },
25 27
             onClose: (res:SubscriptionResponse, rpc:HookRPC) => { 
26 28
                 console.log("Specific close handler for", rpc.name, res)
27 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 70
         function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)},
32 71
         function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)},

Loading…
Cancel
Save