This is an automated email from the ASF dual-hosted git repository.
ningyougang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk-runtime-go.git
The following commit(s) were added to refs/heads/master by this push:
new 6696cee Support array result include sequence action (#170)
6696cee is described below
commit 6696cee26ee7107ff523d9748d6a81f3ca5e8d7f
Author: ningyougang <[email protected]>
AuthorDate: Fri Aug 5 09:05:39 2022 +0800
Support array result include sequence action (#170)
* Make /run to return array result
* Make go1.17 to return array result
* Make go1.18 to support array result
* Make common launcher.go to support array result
* Add test case
* Optimize the test result
* Add test case for support array as input param
---
common/gobuild.py.launcher.go | 33 +++++++++++----
golang1.17/lib/launcher.go | 33 +++++++++++----
golang1.18/lib/launcher.go | 33 +++++++++++----
openwhisk/runHandler.go | 8 +++-
.../ActionLoopGoContainerTests.scala | 49 +++++++++++++++++++++-
5 files changed, 132 insertions(+), 24 deletions(-)
diff --git a/common/gobuild.py.launcher.go b/common/gobuild.py.launcher.go
index 386c126..91f9b2e 100644
--- a/common/gobuild.py.launcher.go
+++ b/common/gobuild.py.launcher.go
@@ -25,6 +25,7 @@ import (
"io"
"log"
"os"
+ "reflect"
"strings"
)
@@ -42,10 +43,11 @@ func main() {
log.Printf("ACTION ENV: %v", os.Environ())
}
- // assign the main function
- type Action func(event map[string]interface{}) map[string]interface{}
- var action Action
- action = Main
+ resultKind := reflect.TypeOf(Main).Out(0).Kind()
+ if resultKind != reflect.Map && resultKind != reflect.Slice &&
resultKind != reflect.Array {
+ fmt.Println("Support map and slice and array only")
+ os.Exit(1)
+ }
// input
out := os.NewFile(3, "pipe")
@@ -89,12 +91,29 @@ func main() {
}
}
// get payload if not empty
- var payload map[string]interface{}
+ isJsonObjectParam := true
+ var payloadForJsonObject map[string]interface{}
+ var payloadForJsonArray []interface{}
if value, ok := input["value"].(map[string]interface{}); ok {
- payload = value
+ payloadForJsonObject = value
+ } else {
+ if value, ok := input["value"].([]interface{}); ok {
+ payloadForJsonArray = value
+ isJsonObjectParam = false
+ }
}
// process the request
- result := action(payload)
+ var result interface{}
+ funcMain := reflect.ValueOf(Main)
+ if isJsonObjectParam {
+ param :=
[]reflect.Value{reflect.ValueOf(payloadForJsonObject)}
+ reflectResult := funcMain.Call(param)
+ result = reflectResult[0].Interface()
+ } else {
+ param :=
[]reflect.Value{reflect.ValueOf(payloadForJsonArray)}
+ reflectResult := funcMain.Call(param)
+ result = reflectResult[0].Interface()
+ }
// encode the answer
output, err := json.Marshal(&result)
if err != nil {
diff --git a/golang1.17/lib/launcher.go b/golang1.17/lib/launcher.go
index 3f95c15..b432b88 100644
--- a/golang1.17/lib/launcher.go
+++ b/golang1.17/lib/launcher.go
@@ -25,6 +25,7 @@ import (
"io"
"log"
"os"
+ "reflect"
"strings"
)
@@ -50,10 +51,11 @@ func main() {
log.Printf("Environment: %v", os.Environ())
}
- // assign the main function
- type Action func(event map[string]interface{}) map[string]interface{}
- var action Action
- action = Main
+ resultKind := reflect.TypeOf(Main).Out(0).Kind()
+ if resultKind != reflect.Map && resultKind != reflect.Slice &&
resultKind != reflect.Array {
+ fmt.Println("Support map and slice and array only")
+ os.Exit(1)
+ }
// input
out := os.NewFile(3, "pipe")
@@ -100,12 +102,29 @@ func main() {
}
}
// get payload if not empty
- var payload map[string]interface{}
+ isJsonObjectParam := true
+ var payloadForJsonObject map[string]interface{}
+ var payloadForJsonArray []interface{}
if value, ok := input["value"].(map[string]interface{}); ok {
- payload = value
+ payloadForJsonObject = value
+ } else {
+ if value, ok := input["value"].([]interface{}); ok {
+ payloadForJsonArray = value
+ isJsonObjectParam = false
+ }
}
// process the request
- result := action(payload)
+ var result interface{}
+ funcMain := reflect.ValueOf(Main)
+ if isJsonObjectParam {
+ param :=
[]reflect.Value{reflect.ValueOf(payloadForJsonObject)}
+ reflectResult := funcMain.Call(param)
+ result = reflectResult[0].Interface()
+ } else {
+ param :=
[]reflect.Value{reflect.ValueOf(payloadForJsonArray)}
+ reflectResult := funcMain.Call(param)
+ result = reflectResult[0].Interface()
+ }
// encode the answer
output, err := json.Marshal(&result)
if err != nil {
diff --git a/golang1.18/lib/launcher.go b/golang1.18/lib/launcher.go
index 3f95c15..b432b88 100644
--- a/golang1.18/lib/launcher.go
+++ b/golang1.18/lib/launcher.go
@@ -25,6 +25,7 @@ import (
"io"
"log"
"os"
+ "reflect"
"strings"
)
@@ -50,10 +51,11 @@ func main() {
log.Printf("Environment: %v", os.Environ())
}
- // assign the main function
- type Action func(event map[string]interface{}) map[string]interface{}
- var action Action
- action = Main
+ resultKind := reflect.TypeOf(Main).Out(0).Kind()
+ if resultKind != reflect.Map && resultKind != reflect.Slice &&
resultKind != reflect.Array {
+ fmt.Println("Support map and slice and array only")
+ os.Exit(1)
+ }
// input
out := os.NewFile(3, "pipe")
@@ -100,12 +102,29 @@ func main() {
}
}
// get payload if not empty
- var payload map[string]interface{}
+ isJsonObjectParam := true
+ var payloadForJsonObject map[string]interface{}
+ var payloadForJsonArray []interface{}
if value, ok := input["value"].(map[string]interface{}); ok {
- payload = value
+ payloadForJsonObject = value
+ } else {
+ if value, ok := input["value"].([]interface{}); ok {
+ payloadForJsonArray = value
+ isJsonObjectParam = false
+ }
}
// process the request
- result := action(payload)
+ var result interface{}
+ funcMain := reflect.ValueOf(Main)
+ if isJsonObjectParam {
+ param :=
[]reflect.Value{reflect.ValueOf(payloadForJsonObject)}
+ reflectResult := funcMain.Call(param)
+ result = reflectResult[0].Interface()
+ } else {
+ param :=
[]reflect.Value{reflect.ValueOf(payloadForJsonArray)}
+ reflectResult := funcMain.Call(param)
+ result = reflectResult[0].Interface()
+ }
// encode the answer
output, err := json.Marshal(&result)
if err != nil {
diff --git a/openwhisk/runHandler.go b/openwhisk/runHandler.go
index ea8bc5d..348a751 100644
--- a/openwhisk/runHandler.go
+++ b/openwhisk/runHandler.go
@@ -82,10 +82,14 @@ func (ap *ActionProxy) runHandler(w http.ResponseWriter, r
*http.Request) {
// check if the answer is an object map
var objmap map[string]*json.RawMessage
+ var objarray []interface{}
err = json.Unmarshal(response, &objmap)
if err != nil {
- sendError(w, http.StatusBadGateway, "The action did not return
a dictionary or array.")
- return
+ err = json.Unmarshal(response, &objarray)
+ if err != nil {
+ sendError(w, http.StatusBadGateway, "The action did not
return a dictionary or array.")
+ return
+ }
}
w.Header().Set("Content-Type", "application/json")
diff --git
a/tests/src/test/scala/runtime/actionContainers/ActionLoopGoContainerTests.scala
b/tests/src/test/scala/runtime/actionContainers/ActionLoopGoContainerTests.scala
index dfb1591..6344d94 100644
---
a/tests/src/test/scala/runtime/actionContainers/ActionLoopGoContainerTests.scala
+++
b/tests/src/test/scala/runtime/actionContainers/ActionLoopGoContainerTests.scala
@@ -21,7 +21,7 @@ import actionContainers.{ActionContainer,
ActionProxyContainerTestUtils}
import actionContainers.ActionContainer.withContainer
import common.WskActorSystem
-import spray.json.{JsObject, JsString}
+import spray.json.{JsArray, JsObject, JsString}
abstract class ActionLoopGoContainerTests
extends ActionProxyContainerTestUtils
@@ -135,4 +135,51 @@ abstract class ActionLoopGoContainerTests
c.run(helloMsg()) should be(okMsg("hello-Hello", "Hello, Demo!"))
}
}
+
+ it should "support return array result" in {
+ val helloArrayGo = {
+ s"""
+ |package main
+ |
+ |func Main(obj map[string]interface{}) []interface{} {
+ | result := []interface{}{"a", "b"}
+ | return result
+ |}
+ |
+ """.stripMargin
+ }
+ val src = ExeBuilder.mkBase64SrcZip(
+ Seq(
+ Seq(s"main.go") -> helloArrayGo
+ ))
+ withActionLoopContainer { c =>
+ c.init(initPayload(src))._1 shouldBe (200)
+ val result = c.runForJsArray(JsObject())
+ result._1 shouldBe (200)
+ result._2 shouldBe Some(JsArray(JsString("a"), JsString("b")))
+ }
+ }
+
+ it should "support array as input param" in {
+ val helloArrayGo = {
+ s"""
+ |package main
+ |
+ |func Main(obj []interface{}) []interface{} {
+ | return obj
+ |}
+ |
+ """.stripMargin
+ }
+ val src = ExeBuilder.mkBase64SrcZip(
+ Seq(
+ Seq(s"main.go") -> helloArrayGo
+ ))
+ withActionLoopContainer { c =>
+ c.init(initPayload(src))._1 shouldBe (200)
+ val result = c.runForJsArray(runPayload(JsArray(JsString("a"),
JsString("b"))))
+ result._1 shouldBe (200)
+ result._2 shouldBe Some(JsArray(JsString("a"), JsString("b")))
+ }
+ }
}