Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
Start PoC work on WASM bridge for bus
Browse files Browse the repository at this point in the history
Signed-off-by: Josh Kim <[email protected]>
  • Loading branch information
jooskim committed Dec 8, 2021
1 parent 6d9ecb6 commit 0e5ed52
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 0 deletions.
155 changes: 155 additions & 0 deletions wasm/bus_bridge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//go:build js && wasm

// Copyright 2021 VMware, Inc.
// SPDX-License-Identifier: BSD-2-Clause

package wasm

import (
"syscall/js"

"github.com/google/uuid"
"github.com/vmware/transport-go/bus"
"github.com/vmware/transport-go/model"
)

type TransportWasmBridge struct {
busRef bus.EventBus
}

func NewTransportWasmBridge(busRef bus.EventBus) *TransportWasmBridge {
return &TransportWasmBridge{
busRef: busRef,
}
}

func (t *TransportWasmBridge) sendRequestMessageWrapper() js.Func {
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
var destId *uuid.UUID
channel := args[0].String()
payload := args[1].JSValue()
if len(args) > 2 {
dId, err := uuid.Parse(args[2].String())
if err != nil {
errCtor := js.Global().Get("Error")
js.Global().Get("console").Call("error", errCtor.New(err.Error()))
return nil
}
destId = &dId
}
t.busRef.SendRequestMessage(channel, payload, destId)
return nil
})
}

func (t *TransportWasmBridge) sendResponseMessageWrapper() js.Func {
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
var destId *uuid.UUID
channel := args[0].String()
payload := args[1].JSValue()
if len(args) > 2 {
dId, err := uuid.Parse(args[2].String())
if err != nil {
errCtor := js.Global().Get("Error")
js.Global().Get("console").Call("error", errCtor.New(err.Error()))
return nil
}
destId = &dId
}
t.busRef.SendResponseMessage(channel, payload, destId)
return nil
})
}

func (t *TransportWasmBridge) listenStreamWrapper() js.Func {
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
handler, err := t.busRef.ListenStream(args[0].String())
if err != nil {
errCtor := js.Global().Get("Error")
return errCtor.New(err.Error())
}

return map[string]interface{}{
"handle": getResponseHandlerJsCallbackFunc(handler),
"close": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
handler.Close()
return nil
}),
}
})
}

func (t *TransportWasmBridge) listenStreamWithIdWrapper() js.Func {
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if len(args) <= 1 {
errCtor := js.Global().Get("Error")
js.Global().Get("console").Call("error", errCtor.New("cannot listen to channel. missing destination ID"))
return nil
}

destId, err := uuid.Parse(args[1].String())
if err != nil {
errCtor := js.Global().Get("Error")
return errCtor.New(err.Error())
}

handler, err := t.busRef.ListenStreamForDestination(args[0].String(), &destId)
if err != nil {
errCtor := js.Global().Get("Error")
return errCtor.New(err.Error())
}

return map[string]interface{}{
"handle": getResponseHandlerJsCallbackFunc(handler),
"close": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
handler.Close()
return nil
}),
}
})
}

func (t *TransportWasmBridge) browserBusWrapper() js.Func {
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
b := js.Global().Get("AppEventBus")
if b.Type() == js.TypeUndefined || b.Type() == js.TypeNull {
errCtor := js.Global().Get("Error")
return errCtor.New("bus not initialized")
}
return b
})
}

func (t *TransportWasmBridge) SetUpTransportWasmAPI() {
js.Global().Set("goBusInterface", map[string]interface{}{
"sendRequestMessage": t.sendRequestMessageWrapper(),
"sendRequestMessageWithId": t.sendRequestMessageWrapper(),
"sendResponseMessage": t.sendResponseMessageWrapper(),
"sendResponseMessageWithId": t.sendResponseMessageWrapper(),
"listenStream": t.listenStreamWrapper(),
"listenStreamWithId": t.listenStreamWithIdWrapper(),
"getBrowserBus": t.browserBusWrapper(),
})

js.Global().Get("console").Call("log", "Transport WASM bridge set up. APIs are exposed at window.goBusInterface")
}

func getResponseHandlerJsCallbackFunc(handler bus.MessageHandler) js.Func {
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
handler.Handle(func(m *model.Message) {
jsCallback := args[0]
if jsCallback.Type() != js.TypeFunction {
errCtor := js.Global().Get("Error")
js.Global().Get("console").Call("error", errCtor.New("invalid argument. not a function."))
handler.Close()
return
}
jsCallback.Invoke(m.Payload)
}, func(e error) {
errCtor := js.Global().Get("Error")
js.Global().Get("console").Call("error", errCtor.New(e.Error()))
handler.Close()
})
return nil
})
}
3 changes: 3 additions & 0 deletions wasm/example/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package example

//go:generate bash -c "GOARCH=wasm; GOOS=js; go build -o ${OUT_DIR}/main.wasm main.go"
42 changes: 42 additions & 0 deletions wasm/example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//go:build js && wasm

// Copyright 2021 VMware, Inc.
// SPDX-License-Identifier: BSD-2-Clause

package main

import (
"fmt"
"time"

"github.com/vmware/transport-go/bus"
"github.com/vmware/transport-go/wasm"
)

func init() {
b := bus.GetBus()
cm := b.GetChannelManager()
cm.CreateChannel("goBusChan")
cm.CreateChannel("broadcast")

ticker := time.NewTicker(1 * time.Second)
go func() {
for {
select {
case <-ticker.C:
b.SendResponseMessage("goBusChan", "sending", b.GetId())
b.SendResponseMessage("broadcast", "hello world", nil)
}
}
}()
}

func main() {
twa := wasm.NewTransportWasmBridge(bus.GetBus())
twa.SetUpTransportWasmAPI()

fmt.Println("go bus id: ", bus.GetBus().GetId())

loop := make(chan struct{})
<-loop
}

0 comments on commit 0e5ed52

Please sign in to comment.