src.dualinventive.com/go/devsim/simulator/js/request.go

179 lines
3.8 KiB
Go

package js
import (
"errors"
"github.com/dop251/goja"
"src.dualinventive.com/go/dinet/rpc"
)
var errNotFound = errors.New("not found")
// requestHandler registered by javascript
type requestHandler struct {
goja.Callable
classMethod rpc.ClassMethod
uid uint16
}
type requestHandlers []requestHandler
func (s *Simulator) getRequestHandler(classMethod rpc.ClassMethod, uid uint16) (goja.Callable, error) {
// Search for
for _, h := range s.requestHandlers {
if h.classMethod == classMethod &&
h.uid == uid {
return h.Callable, nil
}
}
if uid == 0 {
return nil, errNotFound
}
// Try to get the catch-all request handler for the class:method instead of UID specific handler
for _, h := range s.requestHandlers {
if h.classMethod == classMethod {
return h.Callable, nil
}
}
return nil, errNotFound
}
func (s *Simulator) replyWithError(req *rpc.Msg, code rpc.ErrorCode) {
err := s.m.Send(req.CreateReply().SetError(code))
if err != nil {
s.m.Error("unable to send reply")
}
}
func (s *Simulator) msgToJSObject(msg *rpc.Msg) (*goja.Object, error) {
this := s.vm.NewObject()
err := this.Set("Msg", s.vm.ToValue(msg))
if err != nil {
return nil, err
}
var params map[string]interface{}
err = msg.Params.Unmarshal(&params)
if err != nil && err != rpc.ErrMsgNoData {
return nil, err
}
err = this.Set("Params", s.vm.ToValue(params))
if err != nil {
return nil, err
}
err = this.Set("Result", s.vm.NewObject())
if err != nil {
return nil, err
}
return this, nil
}
func getJSResult(this *goja.Object) interface{} {
switch result := this.Get("Result").Export().(type) {
case map[string]interface{}:
if len(result) == 0 {
break
}
return result
}
return nil
}
func jsToReplyMsg(replyMsg *rpc.Msg, this goja.Value) {
switch rep := this.Export().(type) {
case *rpc.Msg:
case map[string]interface{}:
if err, ok := rep["error"]; ok {
code, ok := err.(int64)
if ok {
replyMsg.Error = rpc.GetError(rpc.ErrorCode(code))
}
}
if result, ok := rep["result"]; ok {
replyMsg.Result = rpc.NewResult(result)
}
}
}
func (s *Simulator) requestHandleMsg(msg *rpc.Msg) {
var param rpc.ConfigParam
if msg.Params != nil {
err := msg.Params.Unmarshal(&param)
if err != nil && err != rpc.ErrMsgNoData {
s.m.Error("RPC request params invalid")
}
}
requestHandler, err := s.getRequestHandler(msg.ClassMethod, param.UID)
if err != nil {
s.replyWithError(msg, rpc.EIofailed)
return
}
fn := func() {
this, err := s.msgToJSObject(msg)
if err != nil {
s.replyWithError(msg, rpc.EIofailed)
return
}
rhv, err := requestHandler(this)
if err != nil {
s.replyWithError(msg, rpc.EIofailed)
return
}
replyMsg := msg.CreateReply()
result := getJSResult(this)
if result != nil {
replyMsg.Result = rpc.NewResult([]interface{}{result})
} else {
jsToReplyMsg(replyMsg, rhv)
}
err = s.m.Send(replyMsg)
if err != nil {
s.m.Error("unable to send reply")
}
}
s.dispatchC <- fn
}
// Register a request handler from javascript
func (s *Simulator) requestRegister(jsFunction, classMethod, uid goja.Value) {
hUID := uint16(uid.ToInteger())
hClassMethod := rpc.ClassMethod(classMethod.String())
hJsFunction, ok := goja.AssertFunction(jsFunction)
if !ok {
// TODO
return
}
rHandler := requestHandler{Callable: hJsFunction, uid: hUID, classMethod: hClassMethod}
s.requestHandlers = append(s.requestHandlers, rHandler)
}
// request registers a javascript DI-Net RPC message request handler
func (s *Simulator) request(call goja.FunctionCall) goja.Value {
switch len(call.Arguments) {
case 2:
// string("class:method"), function
s.requestRegister(call.Argument(1), call.Argument(0), goja.Undefined())
case 3:
// string("class:method"), integer(uid), function
s.requestRegister(call.Argument(2), call.Argument(0), call.Argument(1))
}
// TODO error
return nil
}