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

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


The following commit(s) were added to refs/heads/master by this push:
     new d9c6197  Remove path projections on web actions. (#4415)
d9c6197 is described below

commit d9c61978c53e77ffe08e51493802495e81a387de
Author: rodric rabbah <[email protected]>
AuthorDate: Thu Dec 12 11:45:32 2019 -0500

    Remove path projections on web actions. (#4415)
---
 .../openwhisk/core/controller/WebActions.scala     | 86 +++++++++++-----------
 .../core/cli/test/WskWebActionsTests.scala         | 21 +++---
 .../core/controller/test/WebActionsApiTests.scala  | 65 ++++------------
 3 files changed, 67 insertions(+), 105 deletions(-)

diff --git 
a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala
 
b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala
index a784f33..182881f 100644
--- 
a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala
+++ 
b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala
@@ -146,11 +146,11 @@ protected[core] object WhiskWebActionsApi extends 
Directives {
   private val mediaTranscoders = {
     // extensions are expected to contain only [a-z]
     Seq(
-      MediaExtension(".http", None, false, resultAsHttp _),
-      MediaExtension(".json", None, true, resultAsJson _),
-      MediaExtension(".html", Some(List("html")), true, resultAsHtml _),
-      MediaExtension(".svg", Some(List("svg")), true, resultAsSvg _),
-      MediaExtension(".text", Some(List("text")), true, resultAsText _))
+      MediaExtension(".http", resultAsHttp _),
+      MediaExtension(".json", resultAsJson _),
+      MediaExtension(".html", resultAsHtml _),
+      MediaExtension(".svg", resultAsSvg _),
+      MediaExtension(".text", resultAsText _))
   }
 
   private val defaultMediaTranscoder: MediaExtension = 
mediaTranscoders.find(_.extension == ".http").get
@@ -177,35 +177,52 @@ protected[core] object WhiskWebActionsApi extends 
Directives {
   /**
    * Supported extensions, their default projection and transcoder to complete 
a request.
    *
-   * @param extension         the supported media types for action response
-   * @param defaultProjection the default media extensions for action 
projection
-   * @param transcoder        the HTTP decoder and terminator for the extension
+   * @param extension  the supported media types for action response
+   * @param transcoder the HTTP decoder and terminator for the extension
    */
   protected case class MediaExtension(extension: String,
-                                      defaultProjection: Option[List[String]],
-                                      projectionAllowed: Boolean,
                                       transcoder: (JsValue, TransactionId, 
WebApiDirectives) => Route) {
     val extensionLength = extension.length
   }
 
-  private def resultAsHtml(result: JsValue, transid: TransactionId, rp: 
WebApiDirectives) = result match {
-    case JsString(html) => 
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, html))
-    case _              => terminate(BadRequest, 
Messages.invalidMedia(`text/html`))(transid, jsonPrettyPrinter)
+  private def resultAsHtml(result: JsValue, transid: TransactionId, rp: 
WebApiDirectives) = {
+    val htmlResult = result match {
+      case JsObject(fields) => 
fields.get("body").orElse(fields.get("html")).getOrElse(JsNull)
+      case _                => result
+    }
+
+    htmlResult match {
+      case JsString(html) => 
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, html))
+      case _              => terminate(BadRequest, 
Messages.invalidMedia(`text/html`))(transid, jsonPrettyPrinter)
+    }
   }
 
-  private def resultAsSvg(result: JsValue, transid: TransactionId, rp: 
WebApiDirectives) = result match {
-    case JsString(svg) => complete(HttpEntity(`image/svg+xml`, svg.getBytes))
-    case _             => terminate(BadRequest, 
Messages.invalidMedia(`image/svg+xml`))(transid, jsonPrettyPrinter)
+  private def resultAsSvg(result: JsValue, transid: TransactionId, rp: 
WebApiDirectives) = {
+    val svgResult = result match {
+      case JsObject(fields) => 
fields.get("body").orElse(fields.get("svg")).getOrElse(JsNull)
+      case _                => result
+    }
+
+    svgResult match {
+      case JsString(svg) => complete(HttpEntity(`image/svg+xml`, svg.getBytes))
+      case _             => terminate(BadRequest, 
Messages.invalidMedia(`image/svg+xml`))(transid, jsonPrettyPrinter)
+    }
   }
 
   private def resultAsText(result: JsValue, transid: TransactionId, rp: 
WebApiDirectives) = {
-    result match {
-      case r: JsObject  => complete(OK, r.prettyPrint)
-      case r: JsArray   => complete(OK, r.prettyPrint)
-      case JsString(s)  => complete(OK, s)
-      case JsBoolean(b) => complete(OK, b.toString)
-      case JsNumber(n)  => complete(OK, n.toString)
-      case JsNull       => complete(OK, JsNull.toString)
+    val txtResult = result match {
+      case JsObject(fields) => fields.get("body").orElse(fields.get("text"))
+      case _                => Some(result)
+    }
+
+    txtResult match {
+      case Some(r: JsObject)  => complete(OK, r.prettyPrint)
+      case Some(r: JsArray)   => complete(OK, r.prettyPrint)
+      case Some(JsString(s))  => complete(OK, s)
+      case Some(JsBoolean(b)) => complete(OK, b.toString)
+      case Some(JsNumber(n))  => complete(OK, n.toString)
+      case Some(JsNull)       => complete(OK, JsNull.toString)
+      case _                  => terminate(NotFound, 
Messages.propertyNotFound)(transid, jsonPrettyPrinter)
     }
   }
 
@@ -617,11 +634,10 @@ trait WhiskWebActionsApi
       }
     }
 
-    completeRequest(queuedActivation, projectResultField(context, 
responseType), responseType)
+    completeRequest(queuedActivation, responseType)
   }
 
   private def completeRequest(queuedActivation: Future[Either[ActivationId, 
WhiskActivation]],
-                              projectResultField: => List[String],
                               responseType: MediaExtension)(implicit transid: 
TransactionId) = {
     onComplete(queuedActivation) {
       case Success(Right(activation)) =>
@@ -630,7 +646,7 @@ trait WhiskWebActionsApi
 
           if (activation.response.isSuccess || 
activation.response.isApplicationError) {
             val resultPath = if (activation.response.isSuccess) {
-              projectResultField
+              List.empty
             } else {
               // the activation produced an error response: therefore ignore
               // the requested projection and unwrap the error instead
@@ -735,27 +751,11 @@ trait WhiskWebActionsApi
   }
 
   /**
-   * Determines the result projection path, if any.
-   *
-   * @return optional list of projections
-   */
-  private def projectResultField(context: Context, responseType: 
MediaExtension): List[String] = {
-    val projection = if (responseType.projectionAllowed) {
-      Option(context.path)
-        .filter(_.nonEmpty)
-        .map(_.split("/").filter(_.nonEmpty).toList)
-        .orElse(responseType.defaultProjection)
-    } else responseType.defaultProjection
-
-    projection.getOrElse(List.empty)
-  }
-
-  /**
    * Check if "require-whisk-auth" authentication is needed, and if so, 
authenticate the request
    * NOTE: Only number or string JSON "require-whisk-auth" annotation values 
are supported
    *
    * @param annotations - web action annotations
-   * @param reqHeaders - web action invocation request headers
+   * @param reqHeaders  - web action invocation request headers
    * @return Option[Boolean]
    *         None if annotations does not include require-whisk-auth (i.e. 
auth test not needed)
    *         Some(true) if annotations includes require-whisk-auth and it's 
value matches the request header `X-Require-Whisk-Auth` value
diff --git 
a/tests/src/test/scala/org/apache/openwhisk/core/cli/test/WskWebActionsTests.scala
 
b/tests/src/test/scala/org/apache/openwhisk/core/cli/test/WskWebActionsTests.scala
index fb6783d..85008c3 100644
--- 
a/tests/src/test/scala/org/apache/openwhisk/core/cli/test/WskWebActionsTests.scala
+++ 
b/tests/src/test/scala/org/apache/openwhisk/core/cli/test/WskWebActionsTests.scala
@@ -17,7 +17,6 @@
 
 package org.apache.openwhisk.core.cli.test
 
-import java.nio.charset.StandardCharsets
 import java.util.Base64
 
 import scala.util.Failure
@@ -88,7 +87,7 @@ class WskWebActionsTests extends TestHelpers with 
WskTestHelpers with RestUtil w
     val name = "webaction"
     val file = Some(TestUtils.getTestActionFilename("echo.js"))
     val host = getServiceURL()
-    val requestPath = host + 
s"$testRoutePath/$namespace/default/$name.text/a?a="
+    val requestPath = host + 
s"$testRoutePath/$namespace/default/$name.json/a?a="
     val padAmount = MAX_URL_LENGTH - requestPath.length
 
     assetHelper.withCleaner(wsk.action, name) { (action, _) =>
@@ -111,7 +110,7 @@ class WskWebActionsTests extends TestHelpers with 
WskTestHelpers with RestUtil w
           withClue(s"response code: $responseCode, url length: ${url.length}, 
pad amount: ${pad.length}, url: $url") {
             responseCode shouldBe code
             if (code == 200) {
-              response.body.asString shouldBe pad
+              
response.body.asString.parseJson.asJsObject.fields("a").convertTo[String] 
shouldBe pad
             } else {
               response.body.asString should include("414 Request-URI Too 
Large") // from nginx
             }
@@ -127,7 +126,7 @@ class WskWebActionsTests extends TestHelpers with 
WskTestHelpers with RestUtil w
       val name = "webaction"
       val file = Some(TestUtils.getTestActionFilename("echo.js"))
       val host = getServiceURL()
-      val url = s"$host$testRoutePath/$namespace/default/$name.text/__ow_user"
+      val url = s"$host$testRoutePath/$namespace/default/$name.json"
 
       assetHelper.withCleaner(wsk.action, name) { (action, _) =>
         action.create(name, file, web = Some("true"), annotations = 
Map("require-whisk-auth" -> true.toJson))
@@ -145,7 +144,7 @@ class WskWebActionsTests extends TestHelpers with 
WskTestHelpers with RestUtil w
         .get(url)
 
       authorizedResponse.statusCode shouldBe 200
-      authorizedResponse.body.asString shouldBe namespace
+      
authorizedResponse.body.asString.parseJson.asJsObject.fields("__ow_user").convertTo[String]
 shouldBe namespace
   }
 
   it should "ensure that CORS header is preserved for custom options" in 
withAssetCleaner(wskprops) {
@@ -216,7 +215,7 @@ class WskWebActionsTests extends TestHelpers with 
WskTestHelpers with RestUtil w
       val file = Some(TestUtils.getTestActionFilename("echo.js"))
       val bodyContent = "This is the body"
       val host = getServiceURL()
-      val url = 
s"$host$testRoutePath/$namespace/default/webaction.text/__ow_body"
+      val url = s"$host$testRoutePath/$namespace/default/webaction.json"
 
       assetHelper.withCleaner(wsk.action, name) { (action, _) =>
         action.create(name, file, web = Some("true"))
@@ -224,11 +223,11 @@ class WskWebActionsTests extends TestHelpers with 
WskTestHelpers with RestUtil w
 
       val paramRes = RestAssured.given().contentType("text/html").param("key", 
"value").config(sslconfig).post(url)
       paramRes.statusCode shouldBe 200
-      new String(paramRes.body.asByteArray, StandardCharsets.UTF_8) shouldBe 
"key=value"
+      
paramRes.body.asString().parseJson.asJsObject.fields("__ow_body").convertTo[String]
 shouldBe "key=value"
 
       val bodyRes = 
RestAssured.given().contentType("text/html").body(bodyContent).config(sslconfig).post(url)
       bodyRes.statusCode shouldBe 200
-      new String(bodyRes.body.asByteArray, StandardCharsets.UTF_8) shouldBe 
bodyContent
+      
bodyRes.body.asString().parseJson.asJsObject.fields("__ow_body").convertTo[String]
 shouldBe bodyContent
   }
 
   it should "reject invocation of web action with invalid accept header" in 
withAssetCleaner(wskprops) {
@@ -368,7 +367,7 @@ class WskWebActionsTests extends TestHelpers with 
WskTestHelpers with RestUtil w
         action.create(actionName, file, web = Some(true.toString))(wp)
       }
 
-      val url = getServiceApiHost(vanitySubdomain, true) + 
s"/default/$actionName.text/a?a=A"
+      val url = getServiceApiHost(vanitySubdomain, true) + 
s"/default/$actionName.json/a?a=A"
       println(s"url: $url")
 
       // try the rest assured path first, failing that, try curl with explicit 
resolve
@@ -376,7 +375,7 @@ class WskWebActionsTests extends TestHelpers with 
WskTestHelpers with RestUtil w
         val response = RestAssured.given().config(sslconfig).get(url)
         val responseCode = response.statusCode
         responseCode shouldBe 200
-        response.body.asString shouldBe "A"
+        
response.body.asString.parseJson.asJsObject.fields("a").convertTo[String] 
shouldBe "A"
       } match {
         case Failure(t) =>
           println(s"RestAssured path failed, trying curl: $t")
@@ -390,7 +389,7 @@ class WskWebActionsTests extends TestHelpers with 
WskTestHelpers with RestUtil w
           val cmd = Seq("curl", "-k", url, "--resolve", s"$host:$ip")
           val (stdout, stderr, exitCode) = SimpleExec.syncRunCmd(cmd)
           withClue(s"\n$stderr\n") {
-            stdout shouldBe "A"
+            stdout.parseJson.asJsObject.fields("a").convertTo[String] shouldBe 
"A"
             exitCode shouldBe 0
           }
 
diff --git 
a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/WebActionsApiTests.scala
 
b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/WebActionsApiTests.scala
index fcbd460..c580de3 100644
--- 
a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/WebActionsApiTests.scala
+++ 
b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/WebActionsApiTests.scala
@@ -824,7 +824,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
       }
     }
 
-    it should s"project a field from the result object (auth? 
${creds.isDefined})" in {
+    it should s"not project a field from the result object (auth? 
${creds.isDefined})" in {
       implicit val tid = transid()
 
       Seq(s"$systemId/proxy/export_c.json/content").foreach { path =>
@@ -833,7 +833,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
 
           m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
-            val response = responseAs[JsObject]
+            val response = responseAs[JsObject].fields("content")
             response shouldBe metaPayload(
               m.method.name.toLowerCase,
               JsObject.empty,
@@ -843,38 +843,6 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
           }
         }
       }
-
-      Seq(
-        s"$systemId/proxy/export_c.text/content/z",
-        s"$systemId/proxy/export_c.text/content/z/",
-        s"$systemId/proxy/export_c.text/content/z//").foreach { path =>
-        allowedMethods.foreach { m =>
-          invocationsAllowed += 1
-
-          m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
-            status should be(OK)
-            val response = responseAs[String]
-            response shouldBe "Z"
-          }
-        }
-      }
-    }
-
-    it should s"reject when projecting a result which does not match expected 
type (auth? ${creds.isDefined})" in {
-      implicit val tid = transid()
-
-      // these project a result which does not match expected type
-      Seq(s"$systemId/proxy/export_c.json/a").foreach { path =>
-        allowedMethods.foreach { m =>
-          invocationsAllowed += 1
-          actionResult = Some(JsObject("a" -> JsString("b")))
-
-          m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
-            status should be(BadRequest)
-            confirmErrorWithTid(responseAs[JsObject], 
Some(Messages.invalidMedia(MediaTypes.`application/json`)))
-          }
-        }
-      }
     }
 
     it should s"reject when projecting a field from the result object that 
does not exist (auth? ${creds.isDefined})" in {
@@ -1038,10 +1006,10 @@ trait WebActionsApiBaseTests extends 
ControllerTestCommon with BeforeAndAfterEac
 
       Seq(JsObject("a" -> "A".toJson), JsArray("a".toJson), JsString("a"), 
JsTrue, JsNumber(1), JsNull)
         .foreach { jsval =>
-          val path = s"$systemId/proxy/export_c.text/res"
+          val path = s"$systemId/proxy/export_c.text"
           allowedMethods.foreach { m =>
             invocationsAllowed += 1
-            actionResult = Some(JsObject("res" -> jsval))
+            actionResult = Some(JsObject("body" -> jsval))
 
             m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
               responseAs[String] shouldBe {
@@ -1472,20 +1440,15 @@ trait WebActionsApiBaseTests extends 
ControllerTestCommon with BeforeAndAfterEac
     it should s"support formdata (auth? ${creds.isDefined})" in {
       implicit val tid = transid()
 
-      Seq(s"$systemId/proxy/export_c.text/content/field1", 
s"$systemId/proxy/export_c.text/content/field2").foreach {
-        path =>
-          val form = FormData(Map("field1" -> "value1", "field2" -> "value2"))
-          invocationsAllowed += 2
-
-          Post(s"$testRoutePath/$path", form.toEntity) ~> 
Route.seal(routes(creds)) ~> check {
-            status should be(OK)
-            responseAs[String] should (be("value1") or be("value2"))
-          }
+      Seq(s"$systemId/proxy/export_c.json").foreach { path =>
+        val form = FormData(Map("field1" -> "value1", "field2" -> "value2"))
+        invocationsAllowed += 1
 
-          Post(s"$testRoutePath/$path", form.toEntity) ~> 
Route.seal(routes(creds)) ~> check {
-            status should be(OK)
-            responseAs[String] should (be("value1") or be("value2"))
-          }
+        Post(s"$testRoutePath/$path", form.toEntity) ~> 
Route.seal(routes(creds)) ~> check {
+          status should be(OK)
+          responseAs[JsObject].fields("content").asJsObject.fields("field1") 
shouldBe JsString("value1")
+          responseAs[JsObject].fields("content").asJsObject.fields("field2") 
shouldBe JsString("value2")
+        }
       }
     }
 
@@ -1952,7 +1915,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
     it should s"support json (including +json subtypes) (auth? 
${creds.isDefined})" in {
       implicit val tid = transid()
 
-      val path = s"$systemId/proxy/export_c.text/content/field1"
+      val path = s"$systemId/proxy/export_c.json"
       val entity = JsObject("field1" -> "value1".toJson)
 
       Seq(
@@ -1961,7 +1924,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon 
with BeforeAndAfterEac
         invocationsAllowed += 1
         Post(s"$testRoutePath/$path", HttpEntity(ct, entity.compactPrint)) ~> 
Route.seal(routes(creds)) ~> check {
           status should be(OK)
-          responseAs[String] shouldBe "value1"
+          responseAs[JsObject].fields("content").asJsObject.fields("field1") 
shouldBe entity.fields("field1")
         }
       }
     }

Reply via email to