dubee closed pull request #3919: Allow raw JSON string as response for 
webaction without base64 encoding.
URL: https://github.com/apache/incubator-openwhisk/pull/3919
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

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 e195561b04..6015525511 100644
--- a/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
@@ -324,12 +324,10 @@ protected[core] object WhiskWebActionsApi extends 
Directives {
     findContentTypeInHeader(headers, transid, `text/html`).flatMap { mediaType 
=>
       val ct = ContentType(mediaType, () => HttpCharsets.`UTF-8`)
       ct match {
-        // base64 encoded json will appear as non-binary but it is excluded 
here for legacy reasons
-        case nonbinary: ContentType.NonBinary if !isJsonFamily(mediaType) => 
Success(HttpEntity(nonbinary, str))
+        case nonbinary: ContentType.NonBinary => Success(HttpEntity(nonbinary, 
str))
 
         // because of the default charset provided to the content type 
constructor
-        // the remaining content types to match against are binary at this 
point, or
-        // the legacy base64 encoded json
+        // the remaining content types to match against are binary at this 
point
         case _ /* ContentType.Binary */ => 
Try(Base64.getDecoder().decode(str)).map(HttpEntity(ct, _))
       }
     } match {
@@ -592,7 +590,7 @@ trait WhiskWebActionsApi extends Directives with 
ValidateRequestSize with PostAc
           }
 
         case HttpEntity.Strict(contentType, data) =>
-          // application/json is not a binary type in Akka, but is binary in 
Spray
+          // for legacy, we are encoding application/json still
           if (contentType.mediaType.binary || contentType.mediaType == 
`application/json`) {
             Try(JsString(Base64.getEncoder.encodeToString(data.toArray))) 
match {
               case Success(bytes) => process(Some(bytes), isRawHttpAction)
diff --git a/core/routemgmt/common/apigw-utils.js 
b/core/routemgmt/common/apigw-utils.js
index 84d4104c15..a4eebbc4be 100644
--- a/core/routemgmt/common/apigw-utils.js
+++ b/core/routemgmt/common/apigw-utils.js
@@ -529,8 +529,6 @@ function addEndpointToSwaggerApi(swaggerApi, endpoint, 
responsetype) {
   responsetype = responsetype || 'json';
   console.log('addEndpointToSwaggerApi: operationid = '+operationId);
   try {
-    var auth_base64 = 
Buffer.from(endpoint.action.authkey,'ascii').toString('base64');
-
     // If the relative path already exists, append to it; otherwise create it
     if (!swaggerApi.paths[endpoint.gatewayPath]) {
       swaggerApi.paths[endpoint.gatewayPath] = {};
@@ -890,7 +888,7 @@ function replaceNamespaceInUrl(url, namespace) {
  *     {
  *        statusCode: 502,    <- signifies an application error
  *        headers: {'Content-Type': 'application/json'},
- *        body: Base64 encoded JSON error string
+ *        body: JSON object or JSON string
  *     }
  * Otherwise, the action was invoked as a regular OpenWhisk action
  * (i.e. https://OW-HOST/api/v1/namesapces/NS/actions/ACTION) and the
@@ -909,18 +907,16 @@ function makeErrorResponseObject(err, isWebAction) {
     return err;
   }
 
-  var bodystr;
+  var bodystr = err;
   if (typeof err === 'string') {
-    bodystr = JSON.stringify({
+    bodystr = {
       "error": JSON.parse(makeJsonString(err)),  // Make sure err is plain old 
string to avoid duplicate JSON escaping
-    });
-  } else {
-    bodystr = JSON.stringify(err);
+    };
   }
   return {
     statusCode: 502,
     headers: { 'Content-Type': 'application/json' },
-    body: new Buffer(bodystr).toString('base64'),
+    body: bodystr
   };
 }
 
@@ -935,7 +931,7 @@ function makeErrorResponseObject(err, isWebAction) {
  *     {
  *        statusCode: 200,    <- signifies a successful action
  *        headers: {'Content-Type': 'application/json'},
- *        body: Base64 encoded JSON error string
+ *        body: JSON object or JSON string
  *     }
  * Otherwise, the action was invoked as a regular OpenWhisk action
  * (i.e. https://OW-HOST/api/v1/namesapces/NS/actions/ACTION) and the
@@ -950,20 +946,18 @@ function makeErrorResponseObject(err, isWebAction) {
 function makeResponseObject(resp, isWebAction) {
   console.log('makeResponseObject: isWebAction: '+isWebAction);
   if (!isWebAction) {
-    console.log('makeErrorResponseObject: not called as a web action');
+    console.log('makeResponseObject: not called as a web action');
     return resp;
   }
 
-  var bodystr;
+  var bodystr = resp;
   if (typeof resp === 'string') {
-    bodystr = makeJsonString(resp);
-  } else {
-    bodystr = JSON.stringify(resp);
+    bodystr = JSON.parse(makeJsonString(resp));
   }
   retobj = {
     statusCode: 200,
     headers: { 'Content-Type': 'application/json' },
-    body: new Buffer(bodystr).toString('base64')
+    body: bodystr
   };
   return retobj;
 }
diff --git a/tests/dat/actions/echo-web-http.js 
b/tests/dat/actions/echo-web-http.js
index 8933dbea01..b69fb9a6c4 100644
--- a/tests/dat/actions/echo-web-http.js
+++ b/tests/dat/actions/echo-web-http.js
@@ -2,11 +2,9 @@
 // license agreements; and to You under the Apache License, Version 2.0.
 
 function main(params) {
-    var bodyobj = params || {};
-    bodystr = JSON.stringify(bodyobj);
     return {
         statusCode: 200,
         headers: { 'Content-Type': 'application/json' },
-        body: new Buffer(bodystr).toString('base64')
+        body: params
     };
 }
diff --git a/tests/dat/actions/base64Web.js 
b/tests/dat/actions/jsonStringWebAction.js
similarity index 78%
rename from tests/dat/actions/base64Web.js
rename to tests/dat/actions/jsonStringWebAction.js
index 4f945cbb34..bd1c4bef23 100644
--- a/tests/dat/actions/base64Web.js
+++ b/tests/dat/actions/jsonStringWebAction.js
@@ -7,6 +7,6 @@ function main() {
             "content-type": "application/json"
         },
         statusCode: 200,
-        body: new Buffer(JSON.stringify({'status': 
'success'})).toString('base64')
+        body: '{"status": "success"}'
     }
 }
diff --git a/tests/src/test/scala/whisk/core/cli/test/ApiGwRestBasicTests.scala 
b/tests/src/test/scala/whisk/core/cli/test/ApiGwRestBasicTests.scala
index 97b903a4ca..bee7a02dd2 100644
--- a/tests/src/test/scala/whisk/core/cli/test/ApiGwRestBasicTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/ApiGwRestBasicTests.scala
@@ -97,7 +97,7 @@ abstract class ApiGwRestBasicTests extends BaseApiGwTests {
   }
 
   def verifyApiGet(rr: RunResult): Unit = {
-    rr.stdout should include regex 
(s""""operationId":\\s+"getPathWithSub_pathsInIt"""")
+    rr.stdout should include regex 
(s""""operationId":\\s*"getPathWithSub_pathsInIt"""")
   }
 
   def verifyApiFullList(rr: RunResult,
diff --git a/tests/src/test/scala/whisk/core/cli/test/ApiGwRestTests.scala 
b/tests/src/test/scala/whisk/core/cli/test/ApiGwRestTests.scala
index 7a2447cf13..2c41afa5fc 100644
--- a/tests/src/test/scala/whisk/core/cli/test/ApiGwRestTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/ApiGwRestTests.scala
@@ -142,7 +142,7 @@ class ApiGwRestTests extends ApiGwRestBasicTests with 
RestUtil with WskActorSyst
   }
 
   override def verifyApiGet(rr: RunResult): Unit = {
-    rr.stdout should include(s""""operationId":"getPathWithSub_pathsInIt"""")
+    rr.stdout should include regex 
(s""""operationId":\\s*"getPathWithSub_pathsInIt"""")
   }
 
   override def verifyApiFullList(rr: RunResult,
@@ -202,7 +202,7 @@ class ApiGwRestTests extends ApiGwRestBasicTests with 
RestUtil with WskActorSyst
     val openwhisk = RestResult.getFieldJsObject(urlop, "x-openwhisk")
     val actionN = RestResult.getField(openwhisk, "action")
     actionN shouldBe actionName
-    rr.stdout should include regex 
(s""""target-url":".*${actionName}.${responseType}"""")
+    rr.stdout should include regex 
(s""""target-url":\\s*".*${actionName}.${responseType}"""")
   }
 
   override def verifyInvalidSwagger(rr: RunResult): Unit = {
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 62bef1968c..61f374bd54 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
@@ -265,9 +265,9 @@ class WskWebActionsTests extends TestHelpers with 
WskTestHelpers with RestUtil w
     new Header("Set-Cookie", "c=d"))
   }
 
-  it should "handle http web action with base64 encoded response" in 
withAssetCleaner(wskprops) { (wp, assetHelper) =>
-    val name = "base64Web"
-    val file = Some(TestUtils.getTestActionFilename("base64Web.js"))
+  it should "handle http web action returning JSON as string" in 
withAssetCleaner(wskprops) { (wp, assetHelper) =>
+    val name = "jsonStringWebAction"
+    val file = Some(TestUtils.getTestActionFilename("jsonStringWebAction.js"))
     val host = getServiceURL
     val url = host + s"$testRoutePath/$namespace/default/$name.http"
 
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 b500fce265..98df205766 100644
--- a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
@@ -168,6 +168,12 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
     allowedMethodsWithEntity ++ Seq(Head)
   }
 
+  val pngSample = 
"iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAA/klEQVQYGWNgAAEHBxaG//+ZQMyyn581Pfas+cRQnf1LfF"
 +
+    
"Ljf+62smUgcUbt0FA2Zh7drf/ffMy9vLn3RurrW9e5hCU11i2azfD4zu1/DHz8TAy/foUxsXBrFzHzC7r8+M9S1vn1qxQT07dDjL"
 +
+    
"9fdemrqKxlYGT6z8AIMo6hgeUfA0PUvy9fGFh5GWK3z7vNxSWt++jX99+8SoyiGQwsW38w8PJEM7x5v5SJ8f+/xv8MDAzffv9hev"
 +
+    
"fkWjiXBGMpMx+j2awovjcMjFztDO8+7GF49LkbZDCDeXLTWnZO7qDfn1/+5jbw/8pjYWS4wZLztXnuEuYTk2M+MzIw/AcA36Vewa"
 +
+    "D6fzsAAAAASUVORK5CYII="
+
   // there is only one package that is predefined 'proxy'
   val packages = Seq(
     WhiskPackage(
@@ -840,7 +846,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
         }
     }
 
-    it should s"handle http web action with base64 encoded JSON response 
(auth? ${creds.isDefined})" in {
+    it should s"handle http web action with JSON object as string response 
(auth? ${creds.isDefined})" in {
       implicit val tid = transid()
 
       Seq(s"$systemId/proxy/export_c.http").foreach { path =>
@@ -851,12 +857,11 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
               JsObject(
                 "headers" -> JsObject("content-type" -> 
"application/json".toJson),
                 webApiDirectives.statusCode -> statusCode.intValue.toJson,
-                "body" -> Base64.getEncoder.encodeToString {
-                  JsObject("field" -> "value".toJson).compactPrint.getBytes
-                }.toJson))
+                "body" -> JsObject("field" -> 
"value".toJson).compactPrint.toJson))
 
             m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
               status should be(statusCode)
+              mediaType shouldBe MediaTypes.`application/json`
               responseAs[JsObject] shouldBe JsObject("field" -> "value".toJson)
             }
           }
@@ -874,9 +879,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
           actionResult = Some(
             JsObject(
               "headers" -> JsObject("content-type" -> 
"application/json".toJson),
-              "body" -> Base64.getEncoder.encodeToString {
-                JsObject("field" -> "value".toJson).compactPrint.getBytes
-              }.toJson))
+              "body" -> JsObject("field" -> "value".toJson)))
 
           m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
@@ -887,9 +890,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
         // omit status code and headers
         allowedMethods.foreach { m =>
           invocationsAllowed += 1
-          actionResult = Some(JsObject("body" -> 
Base64.getEncoder.encodeToString {
-            JsObject("field" -> "value".toJson).compactPrint.getBytes
-          }.toJson))
+          actionResult = Some(JsObject("body" -> JsObject("field" -> 
"value".toJson).compactPrint.toJson))
 
           m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
@@ -904,9 +905,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
           actionResult = Some(
             JsObject(
               webApiDirectives.statusCode -> Created.intValue.toJson,
-              "body" -> Base64.getEncoder.encodeToString {
-                JsObject("field" -> "value".toJson).compactPrint.getBytes
-              }.toJson))
+              "body" -> JsObject("field" -> 
"value".toJson).compactPrint.toJson))
 
           m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
             status should be(Created)
@@ -985,53 +984,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
       }
     }
 
-    it should s"handle http web action with base64 encoded known '+json' 
response (auth? ${creds.isDefined})" in {
-      implicit val tid = transid()
-
-      Seq(s"$systemId/proxy/export_c.http").foreach { path =>
-        allowedMethods.foreach { m =>
-          invocationsAllowed += 1
-          actionResult = Some(
-            JsObject(
-              "headers" -> JsObject("content-type" -> 
"application/json-patch+json".toJson),
-              webApiDirectives.statusCode -> OK.intValue.toJson,
-              "body" -> Base64.getEncoder.encodeToString {
-                JsObject("field" -> "value".toJson).compactPrint.getBytes
-              }.toJson))
-
-          m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
-            status should be(OK)
-            mediaType.value shouldBe "application/json-patch+json"
-            responseAs[String].parseJson shouldBe JsObject("field" -> 
"value".toJson)
-          }
-        }
-      }
-    }
-
-    it should s"handle http web action with base64 encoded unknown '+json' 
response (auth? ${creds.isDefined})" in {
-      implicit val tid = transid()
-
-      Seq(s"$systemId/proxy/export_c.http").foreach { path =>
-        allowedMethods.foreach { m =>
-          invocationsAllowed += 1
-          actionResult = Some(
-            JsObject(
-              "headers" -> JsObject("content-type" -> 
"application/hal+json".toJson),
-              webApiDirectives.statusCode -> OK.intValue.toJson,
-              "body" -> Base64.getEncoder.encodeToString {
-                JsObject("field" -> "value".toJson).compactPrint.getBytes
-              }.toJson))
-
-          m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
-            status should be(OK)
-            mediaType.value shouldBe "application/hal+json"
-            responseAs[String].parseJson shouldBe JsObject("field" -> 
"value".toJson)
-          }
-        }
-      }
-    }
-
-    it should s"handle http web action without base64 encoded JSON response 
(auth? ${creds.isDefined})" in {
+    it should s"handle http web action with JSON object response (auth? 
${creds.isDefined})" in {
       implicit val tid = transid()
 
       Seq(
@@ -1063,7 +1016,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
       }
     }
 
-    it should s"handle http web action without base64 encoded known '+json' 
response (auth? ${creds.isDefined})" in {
+    it should s"handle http web action for known '+json' response (auth? 
${creds.isDefined})" in {
       implicit val tid = transid()
 
       Seq(
@@ -1094,7 +1047,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
       }
     }
 
-    it should s"handle http web action without base64 encoded unknown '+json' 
response (auth? ${creds.isDefined})" in {
+    it should s"handle http web action for unknown '+json' response (auth? 
${creds.isDefined})" in {
       implicit val tid = transid()
 
       Seq(
@@ -1140,12 +1093,7 @@ trait WebActionsApiBaseTests extends 
ControllerTestCommon with BeforeAndAfterEac
 
     it should s"handle http web action with base64 encoded binary response 
(auth? ${creds.isDefined})" in {
       implicit val tid = transid()
-      val png = 
"iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAA/klEQVQYGWNgAAEHBxaG//+ZQMyyn581Pfas+cRQnf1LfF"
 +
-        
"Ljf+62smUgcUbt0FA2Zh7drf/ffMy9vLn3RurrW9e5hCU11i2azfD4zu1/DHz8TAy/foUxsXBrFzHzC7r8+M9S1vn1qxQT07dDjL"
 +
-        
"9fdemrqKxlYGT6z8AIMo6hgeUfA0PUvy9fGFh5GWK3z7vNxSWt++jX99+8SoyiGQwsW38w8PJEM7x5v5SJ8f+/xv8MDAzffv9hev"
 +
-        
"fkWjiXBGMpMx+j2awovjcMjFztDO8+7GF49LkbZDCDeXLTWnZO7qDfn1/+5jbw/8pjYWS4wZLztXnuEuYTk2M+MzIw/AcA36Vewa"
 +
-        "D6fzsAAAAASUVORK5CYII="
-      val expectedEntity = HttpEntity(ContentType(MediaTypes.`image/png`), 
Base64.getDecoder().decode(png))
+      val expectedEntity = HttpEntity(ContentType(MediaTypes.`image/png`), 
Base64.getDecoder().decode(pngSample))
 
       Seq(s"$systemId/proxy/export_c.http").foreach { path =>
         allowedMethods.foreach { m =>
@@ -1153,7 +1101,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
           actionResult = Some(
             JsObject(
               "headers" -> JsObject(`Content-Type`.lowercaseName -> 
MediaTypes.`image/png`.toString.toJson),
-              "body" -> png.toJson))
+              "body" -> pngSample.toJson))
 
           m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
@@ -1180,7 +1128,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
       }
     }
 
-    it should s"reject http web action with mismatch between header and 
response (auth? ${creds.isDefined})" in {
+    it should s"allow web action with incorrect application/json header and 
text response (auth? ${creds.isDefined})" in {
       implicit val tid = transid()
 
       Seq(s"$systemId/proxy/export_c.http").foreach { path =>
@@ -1193,8 +1141,10 @@ trait WebActionsApiBaseTests extends 
ControllerTestCommon with BeforeAndAfterEac
               "body" -> "hello world".toJson))
 
           m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
-            status should be(BadRequest)
-            confirmErrorWithTid(responseAs[JsObject], 
Some(Messages.httpContentTypeError))
+            status should be(OK)
+            mediaType shouldBe MediaTypes.`application/json`
+            headers shouldBe empty
+            responseAs[String] shouldBe "hello world"
           }
         }
       }
@@ -1417,16 +1367,6 @@ trait WebActionsApiBaseTests extends 
ControllerTestCommon with BeforeAndAfterEac
       val str = "1,2,3"
       invocationsAllowed = 3
 
-      /*
-       * Now supporting all content types with inlined "body".
-       *
-             Post(s"$testRoutePath/$systemId/proxy/export_c.json?a=b&c=d", 
"1,2,3") ~> Route.seal(routes(creds)) ~> check {
-                 status should be(BadRequest)
-                 confirmErrorWithTid(responseAs[JsObject], 
Some(Messages.contentTypeNotSupported))
-             }
-       *
-       */
-
       Post(s"$testRoutePath/$systemId/proxy/export_c.json", 
HttpEntity(ContentTypes.`text/html(UTF-8)`, str)) ~> Route
         .seal(routes(creds)) ~> check {
         status should be(OK)
@@ -1697,7 +1637,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
       }
     }
 
-    it should s"invoke web action ensuring JSON value body arguments are not 
Base64 encoded (auth? ${creds.isDefined})" in {
+    it should s"invoke web action ensuring JSON value body arguments are 
received as is (auth? ${creds.isDefined})" in {
       implicit val tid = transid()
 
       Seq("this is a string".toJson, JsArray(1.toJson, "str str".toJson, 
false.toJson), true.toJson, 99.toJson)
@@ -1721,6 +1661,26 @@ trait WebActionsApiBaseTests extends 
ControllerTestCommon with BeforeAndAfterEac
         }
     }
 
+    it should s"invoke web action ensuring binary body is base64 encoded 
(auth? ${creds.isDefined})" in {
+      implicit val tid = transid()
+      val entity = HttpEntity(ContentType(MediaTypes.`image/png`), 
Base64.getDecoder().decode(pngSample))
+
+      invocationsAllowed += 1
+      Post(s"$testRoutePath/$systemId/proxy/export_c.json", entity) ~> 
Route.seal(routes(creds)) ~> check {
+        status should be(OK)
+        val response = responseAs[JsObject]
+        response shouldBe JsObject(
+          "pkg" -> s"$systemId/proxy".toJson,
+          "action" -> "export_c".toJson,
+          "content" -> metaPayload(
+            Post.method.name.toLowerCase,
+            Map(webApiDirectives.body -> pngSample.toJson).toJson.asJsObject,
+            creds,
+            pkgName = "proxy",
+            headers = List(RawHeader(`Content-Type`.lowercaseName, 
MediaTypes.`image/png`.toString))))
+      }
+    }
+
     it should s"allowed string based status code (auth? ${creds.isDefined})" 
in {
       implicit val tid = transid()
       invocationsAllowed += 2


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to