Skip to content

Commit 6696cee

Browse files
authored
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
1 parent b6577be commit 6696cee

5 files changed

Lines changed: 132 additions & 24 deletions

File tree

common/gobuild.py.launcher.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"io"
2626
"log"
2727
"os"
28+
"reflect"
2829
"strings"
2930
)
3031

@@ -42,10 +43,11 @@ func main() {
4243
log.Printf("ACTION ENV: %v", os.Environ())
4344
}
4445

45-
// assign the main function
46-
type Action func(event map[string]interface{}) map[string]interface{}
47-
var action Action
48-
action = Main
46+
resultKind := reflect.TypeOf(Main).Out(0).Kind()
47+
if resultKind != reflect.Map && resultKind != reflect.Slice && resultKind != reflect.Array {
48+
fmt.Println("Support map and slice and array only")
49+
os.Exit(1)
50+
}
4951

5052
// input
5153
out := os.NewFile(3, "pipe")
@@ -89,12 +91,29 @@ func main() {
8991
}
9092
}
9193
// get payload if not empty
92-
var payload map[string]interface{}
94+
isJsonObjectParam := true
95+
var payloadForJsonObject map[string]interface{}
96+
var payloadForJsonArray []interface{}
9397
if value, ok := input["value"].(map[string]interface{}); ok {
94-
payload = value
98+
payloadForJsonObject = value
99+
} else {
100+
if value, ok := input["value"].([]interface{}); ok {
101+
payloadForJsonArray = value
102+
isJsonObjectParam = false
103+
}
95104
}
96105
// process the request
97-
result := action(payload)
106+
var result interface{}
107+
funcMain := reflect.ValueOf(Main)
108+
if isJsonObjectParam {
109+
param := []reflect.Value{reflect.ValueOf(payloadForJsonObject)}
110+
reflectResult := funcMain.Call(param)
111+
result = reflectResult[0].Interface()
112+
} else {
113+
param := []reflect.Value{reflect.ValueOf(payloadForJsonArray)}
114+
reflectResult := funcMain.Call(param)
115+
result = reflectResult[0].Interface()
116+
}
98117
// encode the answer
99118
output, err := json.Marshal(&result)
100119
if err != nil {

golang1.17/lib/launcher.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"io"
2626
"log"
2727
"os"
28+
"reflect"
2829
"strings"
2930
)
3031

@@ -50,10 +51,11 @@ func main() {
5051
log.Printf("Environment: %v", os.Environ())
5152
}
5253

53-
// assign the main function
54-
type Action func(event map[string]interface{}) map[string]interface{}
55-
var action Action
56-
action = Main
54+
resultKind := reflect.TypeOf(Main).Out(0).Kind()
55+
if resultKind != reflect.Map && resultKind != reflect.Slice && resultKind != reflect.Array {
56+
fmt.Println("Support map and slice and array only")
57+
os.Exit(1)
58+
}
5759

5860
// input
5961
out := os.NewFile(3, "pipe")
@@ -100,12 +102,29 @@ func main() {
100102
}
101103
}
102104
// get payload if not empty
103-
var payload map[string]interface{}
105+
isJsonObjectParam := true
106+
var payloadForJsonObject map[string]interface{}
107+
var payloadForJsonArray []interface{}
104108
if value, ok := input["value"].(map[string]interface{}); ok {
105-
payload = value
109+
payloadForJsonObject = value
110+
} else {
111+
if value, ok := input["value"].([]interface{}); ok {
112+
payloadForJsonArray = value
113+
isJsonObjectParam = false
114+
}
106115
}
107116
// process the request
108-
result := action(payload)
117+
var result interface{}
118+
funcMain := reflect.ValueOf(Main)
119+
if isJsonObjectParam {
120+
param := []reflect.Value{reflect.ValueOf(payloadForJsonObject)}
121+
reflectResult := funcMain.Call(param)
122+
result = reflectResult[0].Interface()
123+
} else {
124+
param := []reflect.Value{reflect.ValueOf(payloadForJsonArray)}
125+
reflectResult := funcMain.Call(param)
126+
result = reflectResult[0].Interface()
127+
}
109128
// encode the answer
110129
output, err := json.Marshal(&result)
111130
if err != nil {

golang1.18/lib/launcher.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"io"
2626
"log"
2727
"os"
28+
"reflect"
2829
"strings"
2930
)
3031

@@ -50,10 +51,11 @@ func main() {
5051
log.Printf("Environment: %v", os.Environ())
5152
}
5253

53-
// assign the main function
54-
type Action func(event map[string]interface{}) map[string]interface{}
55-
var action Action
56-
action = Main
54+
resultKind := reflect.TypeOf(Main).Out(0).Kind()
55+
if resultKind != reflect.Map && resultKind != reflect.Slice && resultKind != reflect.Array {
56+
fmt.Println("Support map and slice and array only")
57+
os.Exit(1)
58+
}
5759

5860
// input
5961
out := os.NewFile(3, "pipe")
@@ -100,12 +102,29 @@ func main() {
100102
}
101103
}
102104
// get payload if not empty
103-
var payload map[string]interface{}
105+
isJsonObjectParam := true
106+
var payloadForJsonObject map[string]interface{}
107+
var payloadForJsonArray []interface{}
104108
if value, ok := input["value"].(map[string]interface{}); ok {
105-
payload = value
109+
payloadForJsonObject = value
110+
} else {
111+
if value, ok := input["value"].([]interface{}); ok {
112+
payloadForJsonArray = value
113+
isJsonObjectParam = false
114+
}
106115
}
107116
// process the request
108-
result := action(payload)
117+
var result interface{}
118+
funcMain := reflect.ValueOf(Main)
119+
if isJsonObjectParam {
120+
param := []reflect.Value{reflect.ValueOf(payloadForJsonObject)}
121+
reflectResult := funcMain.Call(param)
122+
result = reflectResult[0].Interface()
123+
} else {
124+
param := []reflect.Value{reflect.ValueOf(payloadForJsonArray)}
125+
reflectResult := funcMain.Call(param)
126+
result = reflectResult[0].Interface()
127+
}
109128
// encode the answer
110129
output, err := json.Marshal(&result)
111130
if err != nil {

openwhisk/runHandler.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,14 @@ func (ap *ActionProxy) runHandler(w http.ResponseWriter, r *http.Request) {
8282

8383
// check if the answer is an object map
8484
var objmap map[string]*json.RawMessage
85+
var objarray []interface{}
8586
err = json.Unmarshal(response, &objmap)
8687
if err != nil {
87-
sendError(w, http.StatusBadGateway, "The action did not return a dictionary or array.")
88-
return
88+
err = json.Unmarshal(response, &objarray)
89+
if err != nil {
90+
sendError(w, http.StatusBadGateway, "The action did not return a dictionary or array.")
91+
return
92+
}
8993
}
9094

9195
w.Header().Set("Content-Type", "application/json")

tests/src/test/scala/runtime/actionContainers/ActionLoopGoContainerTests.scala

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import actionContainers.{ActionContainer, ActionProxyContainerTestUtils}
2121
import actionContainers.ActionContainer.withContainer
2222
import common.WskActorSystem
2323

24-
import spray.json.{JsObject, JsString}
24+
import spray.json.{JsArray, JsObject, JsString}
2525

2626
abstract class ActionLoopGoContainerTests
2727
extends ActionProxyContainerTestUtils
@@ -135,4 +135,51 @@ abstract class ActionLoopGoContainerTests
135135
c.run(helloMsg()) should be(okMsg("hello-Hello", "Hello, Demo!"))
136136
}
137137
}
138+
139+
it should "support return array result" in {
140+
val helloArrayGo = {
141+
s"""
142+
|package main
143+
|
144+
|func Main(obj map[string]interface{}) []interface{} {
145+
| result := []interface{}{"a", "b"}
146+
| return result
147+
|}
148+
|
149+
""".stripMargin
150+
}
151+
val src = ExeBuilder.mkBase64SrcZip(
152+
Seq(
153+
Seq(s"main.go") -> helloArrayGo
154+
))
155+
withActionLoopContainer { c =>
156+
c.init(initPayload(src))._1 shouldBe (200)
157+
val result = c.runForJsArray(JsObject())
158+
result._1 shouldBe (200)
159+
result._2 shouldBe Some(JsArray(JsString("a"), JsString("b")))
160+
}
161+
}
162+
163+
it should "support array as input param" in {
164+
val helloArrayGo = {
165+
s"""
166+
|package main
167+
|
168+
|func Main(obj []interface{}) []interface{} {
169+
| return obj
170+
|}
171+
|
172+
""".stripMargin
173+
}
174+
val src = ExeBuilder.mkBase64SrcZip(
175+
Seq(
176+
Seq(s"main.go") -> helloArrayGo
177+
))
178+
withActionLoopContainer { c =>
179+
c.init(initPayload(src))._1 shouldBe (200)
180+
val result = c.runForJsArray(runPayload(JsArray(JsString("a"), JsString("b"))))
181+
result._1 shouldBe (200)
182+
result._2 shouldBe Some(JsArray(JsString("a"), JsString("b")))
183+
}
184+
}
138185
}

0 commit comments

Comments
 (0)