diff --git a/Index.ts b/Index.ts index 5db6b50..18f91f1 100644 --- a/Index.ts +++ b/Index.ts @@ -3,7 +3,6 @@ import * as Front from './src/Frontend'; import * as Types from './src/Types'; import * as Utils from './src/Utils'; import * as Interfaces from './src/Interfaces'; -import * as Responses from './src/Responses'; export { Back as Backend, @@ -11,5 +10,4 @@ export { Types, Utils, Interfaces, - Responses } \ No newline at end of file diff --git a/src/Backend.ts b/src/Backend.ts index 55a6954..a78d38d 100644 --- a/src/Backend.ts +++ b/src/Backend.ts @@ -7,7 +7,9 @@ import * as T from './Types'; import * as U from './Utils'; import * as I from './Interfaces'; -export class RPCServer{ +export class RPCServer< + SubResType = {} +>{ private ws = http.createServer() private io = bsock.createServer() private visibility:T.Visibility @@ -18,7 +20,7 @@ export class RPCServer{ constructor( private port:number, - private exporters: I.Exporter[] = [], + private exporters: I.Exporter[] = [], conf: T.SocketConf = {} ){ diff --git a/src/Frontend.ts b/src/Frontend.ts index 6437777..d1dd1b9 100644 --- a/src/Frontend.ts +++ b/src/Frontend.ts @@ -81,8 +81,8 @@ export class RPCSocket implements I.Socket{ const argParams = fnArgs.map(stripAfterEquals).join(",") return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => { const r = await this.socket.call("`+fnName+`", `+argParams+`) - if(r.uid != null){ - this.socket.hook(r.uid, callback) + if(r.result === 'Success'){ + this.socket.hook(r.uuid, callback) } return r } )()` ) diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 65a7910..5d02c0b 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -1,9 +1,9 @@ import * as T from "./Types"; import * as I from "./Interfaces" -export interface Exporter{ +export interface Exporter{ name: T.Name - exportRPCs() : T.RPC[] + exportRPCs() : T.RPC[] } export interface Socket { diff --git a/src/Responses.ts b/src/Responses.ts deleted file mode 100644 index 2b44c45..0000000 --- a/src/Responses.ts +++ /dev/null @@ -1,37 +0,0 @@ - -export type Outcome = "Success" | "Error" - -export class Response{ - constructor( - public message?:string - ){} -} - -export class SuccessResponse extends Response{ - result:Outcome = "Success" - - constructor( - message?:string - ){ - super(message) - } -} - -export class ErrorResponse extends Response{ - result:Outcome = "Error" - - constructor( - message: string = "Unknown error" - ){ - super(message) - } -} - -export class SubscriptionResponse extends SuccessResponse{ - constructor( - public uid: string, - message?:string - ){ - super(message) - } -} \ No newline at end of file diff --git a/src/Types.ts b/src/Types.ts index fe551fd..f7010e9 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -1,4 +1,3 @@ -import * as R from "./Responses"; import * as I from "./Interfaces"; export type Visibility = "127.0.0.1" | "0.0.0.0" @@ -16,13 +15,22 @@ export type SocketConf = { visibility?: Visibility } +export type ResponseType = "Subscribe" | "Success" | "Error" +export type Outcome = "Success" | "Error" + + +export type Respose = T & { result: Outcome } +export type SuccessResponse = Respose & { result: "Success" } +export type ErrorResponse = Respose & { result: "Error" } +export type SubscriptionResponse = Respose & { result: "Success"; uuid: string } + export type RPCType = 'Hook' | 'Unhook' | 'Call' -export type HookRPC = { +export type HookRPC = { name: Name - hook: HookFunction + hook: HookFunction onCallback?: CallbackFunction, - onClose?: HookCloseFunction + onClose?: HookCloseFunction } export type CallRPC = { @@ -30,7 +38,7 @@ export type CallRPC = { call: AsyncFunction } | Function -export type RPC = CallRPC | HookRPC +export type RPC = CallRPC | HookRPC export type BaseInfo = { name: Name, @@ -38,9 +46,9 @@ export type BaseInfo = { argNames: Name[], } -export type HookInfo = BaseInfo & { +export type HookInfo = BaseInfo & { type: 'Hook', - generator: (socket) => HookFunction + generator: (socket) => HookFunction } export type CallInfo = BaseInfo & { @@ -52,7 +60,7 @@ export type RpcInfo = HookInfo | CallInfo export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } export type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => I.Socket -export type HookCloseFunction = (res:R.SubscriptionResponse, rpc:HookRPC) => any -export type HookFunction = (...args:any[]) => Promise +export type HookCloseFunction = (res:SubscriptionResponse, rpc:HookRPC) => any +export type HookFunction = (...args:[any, ...any[]]) => Promise | ErrorResponse> export type AsyncFunction = (...args:any[]) => Promise -export type CallbackFunction = (arg: any) => void \ No newline at end of file +export type CallbackFunction = (...args:any[]) => void \ No newline at end of file diff --git a/src/Utils.ts b/src/Utils.ts index 643b349..32b9e50 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1,9 +1,10 @@ -import * as uuid from "uuid/v4" +import * as uuidv4 from "uuid/v4" import * as T from "./Types"; import * as I from "./Interfaces"; +import { SubscriptionResponse } from "./Types"; -export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => { +export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => { switch (typeof rpc){ case "object": if(rpc['call']){ @@ -15,7 +16,7 @@ export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => { call: rpc['call'], } }else{ - const generator = hookGenerator(rpc) + const generator = hookGenerator(>rpc) return { owner: owner, argNames: extractArgs(generator(undefined)), @@ -26,27 +27,27 @@ export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => { } case "function": if(!rpc.name) throw new Error(` - RPC did not provide a name. - \nUse funtion name(..){ .. } syntax instead. - \n - \n<------------OFFENDING RPC: - \n`+rpc.toString()+` - \n>------------OFFENDING RPC`) +RPC did not provide a name. +\nUse funtion name(..){ .. } syntax instead. +\n +\n<------------OFFENDING RPC: +\n`+rpc.toString()+` +\n>------------OFFENDING RPC`) return { - type: "Call", owner : owner, argNames: extractArgs(rpc), + type: "Call", + name: rpc.name, call: async(...args) => rpc.apply({}, args), - name: rpc.name } } throw new Error("Bad socketIORPC type "+ typeof rpc) } -export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = true):T.ExtendedRpcInfo[]{ +export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = true):T.ExtendedRpcInfo[]{ const owner = exporter.name const RPCs = [...exporter.exportRPCs()] - const suffix = makeUnique?"-"+uuid().substr(0,4):"" + const suffix = makeUnique?"-"+uuidv4().substr(0,4):"" return RPCs.map(rpc => rpcToRpcinfo(rpc, owner)) .map(info => { const ret:any = info @@ -65,7 +66,7 @@ export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = tr }) } -const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { +const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { const argsArr = extractArgs(rpc.hook) argsArr.pop() const args = argsArr.join(',') @@ -73,9 +74,9 @@ const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { return eval(`(socket) => async (`+args+`) => { const res = await rpc.hook(`+args+(args.length!==0?',':'')+` (...cbargs) => { if(rpc.onCallback) rpc.onCallback.apply({}, cbargs) - socket.call.apply(socket, [res.uid, ...cbargs]) + socket.call.apply(socket, [res.uuid, ...cbargs]) }) - if(res.result == 'Success'){ + if(res.result === 'Success'){ if(rpc.onClose){ socket.on('close', async () => { rpc.onClose(res, rpc) @@ -89,4 +90,11 @@ const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { const extractArgs = (f:Function):T.Arg[] => { let fn return (fn = String(f)).substr(0, fn.indexOf(")")).substr(fn.indexOf("(")+1).split(",") +} + +export function makeSubResponse(uuid?:string):SubscriptionResponse{ + return { + result: "Success", + uuid: uuid?uuid:uuidv4(), + } } \ No newline at end of file diff --git a/test/TestBackend.ts b/test/TestBackend.ts index 994282d..6975b25 100644 --- a/test/TestBackend.ts +++ b/test/TestBackend.ts @@ -1,6 +1,8 @@ import { RPCServer } from '../src/Backend' -import { SubscriptionResponse, ErrorResponse, SuccessResponse } from '../src/Responses' +import { SubscriptionResponse, ErrorResponse, SuccessResponse } from '../src/Types' import { HookRPC } from '../src/Types' +import * as uuidv4 from "uuid/v4" +import { makeSubResponse } from '../src/Utils' let subcallback @@ -14,19 +16,56 @@ new RPCServer(20000, [{ name: 'simpleSubscribe', hook: async(callback) => { subcallback = callback - return new SubscriptionResponse(""+Math.random()) + return makeSubResponse() } },{ name: 'subscribe', hook: async (callback):Promise => { subcallback = callback - return new SubscriptionResponse(""+Math.random()) + return makeSubResponse() }, onClose: (res:SubscriptionResponse, rpc:HookRPC) => { console.log("Specific close handler for", rpc.name, res) subcallback = null }, - onCallback: (...args) => { console.log.apply(console, args) } + onCallback: (...args:any) => { console.log.apply(console, args) } + }, + function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)}, + function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)}, + ] +}]) + +new RPCServer<{ topic: string}>(20001, [{ + name: "HelloWorldRPCGroup", + exportRPCs: () => [ + { + name: 'echo', + call: async (s:string) => s, + },{ + name: 'simpleSubscribe', + hook: async(callback) => { + subcallback = callback + return { + result: "Success", + uuid: uuidv4(), + topic: "" + } + } + },{ + name: 'subscribe', + hook: async (callback) => { + subcallback = callback + return { + result: "Success", + uuid: uuidv4(), + topic: "" + } + }, + onClose: (res, rpc) => { + console.log("Specific close handler for", rpc.name, res) + subcallback = null + }, + onCallback: (...args:any) => { console.log.apply(console, args) } }, function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)}, function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)},