This is an automated email from the ASF dual-hosted git repository.

markusthoemmes pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git


The following commit(s) were added to refs/heads/master by this push:
     new c59ef2a  Support multiple response header values in raw web actions 
(#2577)
c59ef2a is described below

commit c59ef2a5621e1d2362801ce91b9eff1c3cf885e1
Author: Ben Browning <ben...@gmail.com>
AuthorDate: Sat Aug 5 16:14:52 2017 -0400

    Support multiple response header values in raw web actions (#2577)
    
    This change allows multiple response header values to be set in raw
    web actions by using a JSON array as the header value. For example:
    
    ```
    function main() {
      return {
        headers: {
          "Set-Cookie": ["a=b", "c=d"]
        },
        code: 200
      }
    }
    ```
---
 .../scala/whisk/core/controller/WebActions.scala   | 15 +++++++++-----
 docs/webactions.md                                 | 18 ++++++++++++++++-
 tests/dat/actions/multipleHeaders.js               |  8 ++++++++
 .../whisk/core/cli/test/WskWebActionsTests.scala   | 23 ++++++++++++++++++++++
 .../core/controller/test/WebActionsApiTests.scala  | 20 +++++++++++++++++++
 5 files changed, 78 insertions(+), 6 deletions(-)

diff --git 
a/core/controller/src/main/scala/whisk/core/controller/WebActions.scala 
b/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
index 9ac63ff..138bea9 100644
--- a/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
@@ -215,11 +215,8 @@ protected[core] object WhiskWebActionsApi extends 
Directives {
         Try {
             val JsObject(fields) = result
             val headers = fields.get("headers").map {
-                case JsObject(hs) => hs.map {
-                    case (k, JsString(v))  => RawHeader(k, v)
-                    case (k, JsBoolean(v)) => RawHeader(k, v.toString)
-                    case (k, JsNumber(v))  => RawHeader(k, v.toString)
-                    case _                 => throw new Throwable("Invalid 
header")
+                case JsObject(hs) => hs.flatMap {
+                    case (k, v) => headersFromJson(k, v)
                 }.toList
 
                 case _ => throw new Throwable("Invalid header")
@@ -251,6 +248,14 @@ protected[core] object WhiskWebActionsApi extends 
Directives {
         }
     }
 
+    private def headersFromJson(k: String, v: JsValue) : Seq[RawHeader] = v 
match {
+        case JsString(v)  => Seq(RawHeader(k, v))
+        case JsBoolean(v) => Seq(RawHeader(k, v.toString))
+        case JsNumber(v)  => Seq(RawHeader(k, v.toString))
+        case JsArray(v)   => v.flatMap(inner => headersFromJson(k, inner))
+        case _            => throw new Throwable("Invalid header")
+    }
+
     private def interpretHttpResponse(code: StatusCode, headers: 
List[RawHeader], str: String, transid: TransactionId): RequestContext => Unit = 
{
         val parsedHeader: Try[MediaType] = headers.find(_.lowercaseName == 
`Content-Type`.lowercaseName) match {
             case Some(header) =>
diff --git a/docs/webactions.md b/docs/webactions.md
index 28b5449..c597a98 100644
--- a/docs/webactions.md
+++ b/docs/webactions.md
@@ -70,6 +70,22 @@ function main() {
 }
 ```
 
+Or sets multiple cookies:
+```javascript
+function main() {
+  return { 
+    headers: { 
+      'Set-Cookie': [
+        'UserID=Jane; Max-Age=3600; Version=',
+        'SessionID=asdfgh123456; Path = /'
+      ],
+      'Content-Type': 'text/html'
+    }, 
+    statusCode: 200,
+    body: '<html><body><h3>hello</h3></body></html>' }
+}
+```
+
 Or returns an `image/png`:
 ```javascript
 function main() {
@@ -97,7 +113,7 @@ It is important to be aware of the [response size 
limit](reference.md) for actio
 
 An OpenWhisk action that is not a web action requires both authentication and 
must respond with a JSON object. In contrast, web actions may be invoked 
without authentication, and may be used to implement HTTP handlers that respond 
with _headers_, _statusCode_, and _body_ content of different types. The web 
action must still return a JSON object, but the OpenWhisk system (namely the 
`controller`) will treat a web action differently if its result includes one or 
more of the following as to [...]
 
-1. `headers`: a JSON object where the keys are header-names and the values are 
string values for those headers (default is no headers).
+1. `headers`: a JSON object where the keys are header-names and the values are 
string, number, or boolean values for those headers (default is no headers). To 
send multiple values for a single header, the header's value should be a JSON 
array of values.
 2. `statusCode`: a valid HTTP status code (default is 200 OK).
 3. `body`: a string which is either plain text or a base64 encoded string for 
binary data (default is empty response).
 
diff --git a/tests/dat/actions/multipleHeaders.js 
b/tests/dat/actions/multipleHeaders.js
new file mode 100644
index 0000000..06712c1
--- /dev/null
+++ b/tests/dat/actions/multipleHeaders.js
@@ -0,0 +1,8 @@
+function main() {
+    return {
+        headers: {
+            "Set-Cookie": ["a=b", "c=d"]
+        },
+        code: 200
+    }
+}
diff --git a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala 
b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
index 52f8d8b..517371c 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
@@ -27,6 +27,7 @@ import org.scalatest.BeforeAndAfterAll
 import org.scalatest.junit.JUnitRunner
 
 import com.jayway.restassured.RestAssured
+import com.jayway.restassured.response.Header
 
 import common.TestHelpers
 import common.TestUtils
@@ -296,4 +297,26 @@ trait WskWebActionsTests
             response.statusCode shouldBe 406
             response.body.asString should include("Resource representation is 
only available with these Content-Types:\\ntext/html")
     }
+
+    it should "support multiple response header values" in 
withAssetCleaner(wskprops) {
+        (wp, assetHelper) =>
+            val name = "webaction"
+            val file = 
Some(TestUtils.getTestActionFilename("multipleHeaders.js"))
+            val host = getServiceURL()
+            val url = host + 
s"$testRoutePath/$namespace/default/webaction.http"
+
+            assetHelper.withCleaner(wsk.action, name) {
+                (action, _) =>
+                    action.create(name, file, web = Some("true"), annotations 
= Map("web-custom-options" -> true.toJson))
+            }
+
+            val response = RestAssured.given().config(sslconfig).options(url)
+
+            response.statusCode shouldBe 200
+            val cookieHeaders = response.headers.getList("Set-Cookie")
+            cookieHeaders should contain allOf (
+                new Header("Set-Cookie", "a=b"),
+                new Header("Set-Cookie", "c=d")
+            )
+    }
 }
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
index d7c0511..3a7722a 100644
--- a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
@@ -1127,6 +1127,26 @@ trait WebActionsApiTests extends ControllerTestCommon 
with BeforeAndAfterEach wi
                 }
         }
 
+        it should s"support multiple values for headers (auth? 
${creds.isDefined})" in {
+            implicit val tid = transid()
+
+            Seq(s"$systemId/proxy/export_c.http").
+                foreach { path =>
+                    invocationsAllowed += 1
+                    actionResult = Some(
+                        JsObject(
+                            "headers" -> JsObject(
+                                "Set-Cookie" -> JsArray(JsString("a=b"), 
JsString("c=d; Path = /")))))
+
+                    Options(s"$testRoutePath/$path") ~> 
sealRoute(routes(creds)) ~> check {
+                        headers should contain allOf (
+                            HttpHeaders.RawHeader("Set-Cookie", "a=b"),
+                            HttpHeaders.RawHeader("Set-Cookie", "c=d; Path = 
/")
+                        )
+                    }
+                }
+        }
+
         it should s"invoke action with options verb without custom options 
(auth? ${creds.isDefined})" in {
             implicit val tid = transid()
             customOptions = false

-- 
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <commits@openwhisk.apache.org>'].

Reply via email to