From e2c2fbbb33c0559d3b52645e24ddf99f99d07628 Mon Sep 17 00:00:00 2001
From: Jono Wenger <jono@elixxir.io>
Date: Tue, 16 Aug 2022 16:39:41 -0700
Subject: [PATCH] Implement single.go

---
 main.go             |   4 ++
 wasm/e2e.go         |   2 +-
 wasm/e2e_test.go    |   1 -
 wasm/single.go      | 128 ++++++++++++++++++++++++++++++++++++++++++++
 wasm/single_test.go |  39 ++++++++++++++
 5 files changed, 172 insertions(+), 2 deletions(-)
 create mode 100644 wasm/single.go
 create mode 100644 wasm/single_test.go

diff --git a/main.go b/main.go
index 23fa7127..d53e5c47 100644
--- a/main.go
+++ b/main.go
@@ -104,6 +104,10 @@ func main() {
 	js.Global().Set("AsyncRequestRestLike",
 		js.FuncOf(wasm.AsyncRequestRestLike))
 
+	// bindings/single.go
+	js.Global().Set("TransmitSingleUse", js.FuncOf(wasm.TransmitSingleUse))
+	js.Global().Set("Listen", js.FuncOf(wasm.Listen))
+
 	<-make(chan bool)
 	os.Exit(0)
 }
diff --git a/wasm/e2e.go b/wasm/e2e.go
index 46196fdf..de160033 100644
--- a/wasm/e2e.go
+++ b/wasm/e2e.go
@@ -83,7 +83,7 @@ func (e *E2e) GetID(js.Value, []js.Value) interface{} {
 //  - args[0] - ID of Cmix object in tracker (int).
 //  - args[1] - Javascript object that has functions that implement the
 //    [bindings.AuthCallbacks] interface.
-//  - args[2] - JSON of the [xxdk.ReceptionIdentity] object (Uint8Array).
+//  - args[2] - JSON of the [xxdk.ReceptionIdentity] (Uint8Array).
 //  - args[3] - JSON of [xxdk.E2EParams] (Uint8Array).
 //
 // Returns:
diff --git a/wasm/e2e_test.go b/wasm/e2e_test.go
index ef6323ac..6d652c95 100644
--- a/wasm/e2e_test.go
+++ b/wasm/e2e_test.go
@@ -6,7 +6,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 //go:build js && wasm
-// +build js,wasm
 
 package wasm
 
diff --git a/wasm/single.go b/wasm/single.go
new file mode 100644
index 00000000..7b664ca4
--- /dev/null
+++ b/wasm/single.go
@@ -0,0 +1,128 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+//go:build js && wasm
+
+package wasm
+
+import (
+	"gitlab.com/elixxir/client/bindings"
+	"syscall/js"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Public Wrapper Methods                                                     //
+////////////////////////////////////////////////////////////////////////////////
+
+// TransmitSingleUse transmits payload to recipient via single-use.
+//
+// Parameters:
+//  - args[0] - ID of E2e object in tracker (int).
+//  - args[1] - JSON of recipient [contact.Contact] (Uint8Array).
+//  - args[2] - tag that identifies the single-use message (string).
+//  - args[3] - message contents (Uint8Array).
+//  - args[4] - JSON of [single.RequestParams] (Uint8Array).
+//  - args[5] - the callback that will be called when a response is received. It
+//    is a Javascript object that has functions that implement the
+//    [bindings.SingleUseResponse] interface.
+//
+// Returns:
+//  - JSON [bindings.SingleUseSendReport], which can be passed into
+//    Cmix.WaitForRoundResult to see if the send succeeded (Uint8Array).
+//  - Throws a TypeError if transmission fails.
+func TransmitSingleUse(_ js.Value, args []js.Value) interface{} {
+	e2eID := args[0].Int()
+	recipient := CopyBytesToGo(args[1])
+	tag := args[2].String()
+	payload := CopyBytesToGo(args[3])
+	paramsJSON := CopyBytesToGo(args[4])
+	responseCB := &singleUseResponse{args[5].Get("Callback").Invoke}
+
+	report, err := bindings.TransmitSingleUse(
+		e2eID, recipient, tag, payload, paramsJSON, responseCB)
+	if err != nil {
+		Throw(TypeError, err.Error())
+		return nil
+	}
+
+	return CopyBytesToJS(report)
+}
+
+// Listen starts a single-use listener on a given tag using the passed in E2e
+// object and SingleUseCallback func.
+//
+// Parameters:
+//  - args[0] - ID of E2e object in tracker (int).
+//  - args[1] - tag that identifies the single-use message (string).
+//  - args[2] - the callback that will be called when a response is received. It
+//    is a Javascript object that has functions that implement the
+//    [bindings.SingleUseCallback] interface.
+//
+// Returns:
+//  - Javascript representation of the Stopper object, an interface containing a
+//    function used to stop the listener.
+//  - Throws a TypeError if listening fails.
+func Listen(_ js.Value, args []js.Value) interface{} {
+	cb := &singleUseCallback{args[2].Get("Callback").Invoke}
+	api, err := bindings.Listen(args[0].Int(), args[1].String(), cb)
+	if err != nil {
+		Throw(TypeError, err.Error())
+		return nil
+	}
+
+	return newStopperJS(api)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Function Types                                                             //
+////////////////////////////////////////////////////////////////////////////////
+
+// Stopper wraps the [bindings.Stopper] interface so its methods can be wrapped
+// to be Javascript compatible.
+type Stopper struct {
+	api bindings.Stopper
+}
+
+// newStopperJS creates a new Javascript compatible object
+// (map[string]interface{}) that matches the Stopper structure.
+func newStopperJS(api bindings.Stopper) map[string]interface{} {
+	s := Stopper{api}
+	stopperMap := map[string]interface{}{
+		"Stop": js.FuncOf(s.Stop),
+	}
+
+	return stopperMap
+}
+
+func (s *Stopper) Stop(js.Value, []js.Value) interface{} {
+	s.api.Stop()
+	return nil
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Callback Wrappers                                                          //
+////////////////////////////////////////////////////////////////////////////////
+
+// singleUseCallback wraps Javascript callbacks to adhere to the
+// [bindings.SingleUseCallback] interface.
+type singleUseCallback struct {
+	callback func(args ...interface{}) js.Value
+}
+
+func (suc *singleUseCallback) Callback(callbackReport []byte, err error) {
+	suc.callback(CopyBytesToJS(callbackReport), err.Error())
+}
+
+// singleUseResponse wraps Javascript callbacks to adhere to the
+// [bindings.SingleUseResponse] interface.
+type singleUseResponse struct {
+	callback func(args ...interface{}) js.Value
+}
+
+func (sur *singleUseResponse) Callback(responseReport []byte, err error) {
+	sur.callback(CopyBytesToJS(responseReport), err.Error())
+}
diff --git a/wasm/single_test.go b/wasm/single_test.go
new file mode 100644
index 00000000..3bdd9933
--- /dev/null
+++ b/wasm/single_test.go
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+//go:build js && wasm
+
+package wasm
+
+import (
+	"reflect"
+	"testing"
+)
+
+// Tests that the map representing Stopper returned by newStopperJS contains all
+// of the methods on Stopper.
+func Test_newStopperJS(t *testing.T) {
+	stopperType := reflect.TypeOf(&Stopper{})
+
+	s := newStopperJS(&stopper{})
+	if len(s) != stopperType.NumMethod() {
+		t.Errorf("Stopper JS object does not have all methods."+
+			"\nexpected: %d\nreceived: %d", stopperType.NumMethod(), len(s))
+	}
+
+	for i := 0; i < stopperType.NumMethod(); i++ {
+		method := stopperType.Method(i)
+
+		if _, exists := s[method.Name]; !exists {
+			t.Errorf("Method %s does not exist.", method.Name)
+		}
+	}
+}
+
+type stopper struct{}
+
+func (s *stopper) Stop() {}
-- 
GitLab