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")))
+    }
+  }
 }

Reply via email to