Introduce experimental immutable parameters.
- Uses final param annotation to exclude overriden params for meta actions.
- Disallow package bindings.
- Update annotations for meta action.
- Add helpers to filter immutable parameters from action.
diff --git a/tests/src/whisk/core/controller/test/MetaApiTests.scala b/tests/src/whisk/core/controller/test/MetaApiTests.scala
index 96f9e2c..f2d6b8e 100644
--- a/tests/src/whisk/core/controller/test/MetaApiTests.scala
+++ b/tests/src/whisk/core/controller/test/MetaApiTests.scala
@@ -23,6 +23,7 @@
import scala.concurrent.Future
import org.junit.runner.RunWith
+import org.scalatest.BeforeAndAfterEach
import org.scalatest.junit.JUnitRunner
import akka.event.Logging.InfoLevel
@@ -34,9 +35,8 @@
import spray.json.DefaultJsonProtocol._
import whisk.common.TransactionId
import whisk.core.controller.WhiskMetaApi
-import whisk.core.entity._
import whisk.core.database.NoDocumentException
-import org.scalatest.BeforeAndAfterEach
+import whisk.core.entity._
import whisk.http.ErrorResponse
import whisk.http.Messages
@@ -101,11 +101,15 @@
EntityName("publicmeta"),
publish = true,
annotations = Parameters("meta", JsBoolean(true)) ++
- Parameters("get", JsString("getApi"))))
+ Parameters("get", JsString("getApi"))),
+ WhiskPackage(
+ EntityPath(systemId),
+ EntityName("bindingmeta"),
+ Some(Binding(EntityName(systemId), EntityName("heavymeta"))),
+ annotations = Parameters("meta", JsBoolean(true))))
override protected[controller] def invokeAction(user: Identity, action: WhiskAction, payload: Option[JsObject], blocking: Boolean, waitOverride: Boolean = false)(
implicit transid: TransactionId): Future[(ActivationId, Option[WhiskActivation])] = {
-
if (failActivation == 0) {
// construct a result stub that includes:
// 1. the package name for the action (to confirm that this resolved to systemId)
@@ -127,9 +131,9 @@
// check that action parameters were merged with package
// all actions have default parameters (see actionLookup stub)
- pkgLookup(resolvePackageName(action.namespace.last)) map { pkg =>
+ pkgLookup(resolvePackageName(action.namespace.last)) foreach { pkg =>
action.parameters shouldBe (pkg.parameters ++ defaultActionParameters)
- action.parameters("z") shouldBe defaultActionParameters("z")
+ action.parameters.get("z") shouldBe defaultActionParameters.get("z")
}
Future.successful(activation.activationId, Some(activation))
@@ -151,13 +155,16 @@
}
}
- val defaultActionParameters = Parameters("y", JsString("Y")) ++ Parameters("z", JsString("Z"))
+ val defaultActionParameters = {
+ Parameters("y", JsString("Y")) ++ Parameters("z", JsString("Z"))
+ }
override protected def actionLookup(pkgName: FullyQualifiedEntityName, actionName: EntityName)(
implicit transid: TransactionId) = {
if (!failActionLookup) {
Future.successful {
- WhiskAction(pkgName.fullPath, actionName, Exec.js("??"), defaultActionParameters)
+ val annotations = Parameters(WhiskAction.finalParamsAnnotationName, JsBoolean(true))
+ WhiskAction(pkgName.fullPath, actionName, Exec.js("??"), defaultActionParameters, annotations = annotations)
}
} else {
Future.failed(NoDocumentException("doesn't exist"))
@@ -193,7 +200,7 @@
implicit val tid = transid()
val methods = Seq((Put, MethodNotAllowed))
- methods.map {
+ methods.foreach {
case (m, code) =>
m("/experimental/partialmeta") ~> sealRoute(routes(creds)) ~> check {
status should be(code)
@@ -205,15 +212,20 @@
implicit val tid = transid()
val methods = Seq(Get, Post, Delete)
- methods.map { m =>
+ methods.foreach { m =>
m("/experimental") ~> sealRoute(routes(creds)) ~> check {
status shouldBe NotFound
}
}
- val paths = Seq("/experimental/doesntexist", "/experimental/notmeta", "/experimental/badmeta")
- paths.map { p =>
- methods.map { m =>
+ val paths = Seq(
+ "/experimental/doesntexist",
+ "/experimental/notmeta",
+ "/experimental/badmeta",
+ "/experimental/bindingmeta")
+
+ paths.foreach { p =>
+ methods.foreach { m =>
m(p) ~> sealRoute(routes(creds)) ~> check {
withClue(p) {
status shouldBe MethodNotAllowed
@@ -232,7 +244,7 @@
implicit val tid = transid()
val methods = Seq((Get, "getApi"), (Post, "createRoute"), (Delete, "deleteApi"))
- methods.map {
+ methods.foreach {
case (m, name) =>
m("/experimental/heavymeta?a=b&c=d&namespace=xyz") ~> sealRoute(routes(creds)) ~> check {
status should be(OK)
@@ -249,7 +261,7 @@
implicit val tid = transid()
val methods = Seq((Get, OK), (Post, MethodNotAllowed), (Delete, MethodNotAllowed))
- methods.map {
+ methods.foreach {
case (m, code) =>
m("/experimental/partialmeta?a=b&c=d") ~> sealRoute(routes(creds)) ~> check {
status should be(code)
@@ -268,7 +280,7 @@
implicit val tid = transid()
val paths = Seq("", "/", "/foo", "/foo/bar", "/foo/bar/", "/foo%20bar")
- paths.map { p =>
+ paths.foreach { p =>
withClue(s"failed on path: '$p'") {
Get(s"/experimental/partialmeta$p?a=b&c=d") ~> sealRoute(routes(creds)) ~> check {
status should be(OK)
@@ -334,8 +346,8 @@
val methods = Seq(Get, Post, Delete)
- methods.map { m =>
- reservedProperties.map { p =>
+ methods.foreach { m =>
+ reservedProperties.foreach { p =>
m(s"/experimental/packagemeta/?$p=YYY") ~> sealRoute(routes(creds)) ~> check {
status should be(BadRequest)
responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
@@ -363,6 +375,32 @@
}
}
+ it should "invoke action and ignore invoke parameters that are immutable" in {
+ implicit val tid = transid()
+ val contentX = JsObject("x" -> "overriden".toJson)
+ val contentZ = JsObject("z" -> "overriden".toJson)
+
+ Get(s"/experimental/packagemeta?x=overriden") ~> sealRoute(routes(creds)) ~> check {
+ status should be(BadRequest)
+ responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
+ }
+
+ Get(s"/experimental/packagemeta?y=overriden") ~> sealRoute(routes(creds)) ~> check {
+ status should be(BadRequest)
+ responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
+ }
+
+ Get(s"/experimental/packagemeta", contentX) ~> sealRoute(routes(creds)) ~> check {
+ status should be(BadRequest)
+ responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
+ }
+
+ Get(s"/experimental/packagemeta?y=overriden", contentZ) ~> sealRoute(routes(creds)) ~> check {
+ status should be(BadRequest)
+ responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
+ }
+ }
+
it should "rejection invoke action when receiving entity that is not a JsObject" in {
implicit val tid = transid()
diff --git a/tests/src/whisk/core/entity/test/SchemaTests.scala b/tests/src/whisk/core/entity/test/SchemaTests.scala
index 4b7418f..0db33c6 100644
--- a/tests/src/whisk/core/entity/test/SchemaTests.scala
+++ b/tests/src/whisk/core/entity/test/SchemaTests.scala
@@ -402,6 +402,11 @@
assert(params(3).toString == json(3).compactPrint) // drops unknown prop "foo"
}
+ it should "filter immutable parameters" in {
+ val params = Parameters("k", "v") ++ Parameters("ns", null: String) ++ Parameters("njs", JsNull)
+ params.immutableParameters shouldBe Set("k")
+ }
+
it should "reject malformed JSON" in {
val params = Seq[JsValue](
null,