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

dubeejw 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 25c8e3c  Impose list limit on all entity types. (#3087)
25c8e3c is described below

commit 25c8e3c5c109f59093e3bcd8f102a295fea69869
Author: rodric rabbah <rod...@gmail.com>
AuthorDate: Wed Jan 10 23:51:41 2018 -0500

    Impose list limit on all entity types. (#3087)
    
    * Impose list limit on all entity types.
---
 .../src/main/scala/whisk/http/ErrorResponse.scala  |  6 +-
 .../main/scala/whisk/core/controller/Actions.scala | 21 +++----
 .../scala/whisk/core/controller/Activations.scala  | 36 ++++++------
 .../scala/whisk/core/controller/Packages.scala     | 50 +++++++++--------
 .../scala/whisk/core/controller/RestAPIs.scala     | 21 +++++++
 .../main/scala/whisk/core/controller/Rules.scala   | 37 ++++++-------
 .../scala/whisk/core/controller/Triggers.scala     | 64 ++++++++--------------
 .../scala/whisk/core/entitlement/Collection.scala  |  7 ++-
 .../test/scala/system/basic/WskBasicTests.scala    |  7 ---
 .../whisk/core/cli/test/WskBasicUsageTests.scala   |  2 +-
 .../core/controller/test/ActionsApiTests.scala     | 12 ++++
 .../core/controller/test/ActivationsApiTests.scala | 20 +++----
 .../core/controller/test/PackagesApiTests.scala    | 16 ++++--
 .../whisk/core/controller/test/RulesApiTests.scala | 17 ++++--
 .../core/controller/test/TriggersApiTests.scala    | 12 ++++
 15 files changed, 186 insertions(+), 142 deletions(-)

diff --git a/common/scala/src/main/scala/whisk/http/ErrorResponse.scala 
b/common/scala/src/main/scala/whisk/http/ErrorResponse.scala
index c6ee4ae..d26ebf3 100644
--- a/common/scala/src/main/scala/whisk/http/ErrorResponse.scala
+++ b/common/scala/src/main/scala/whisk/http/ErrorResponse.scala
@@ -132,7 +132,11 @@ object Messages {
   def entityTooBig(error: SizeError) = {
     s"${error.field} larger than allowed: ${error.is.toBytes} > 
${error.allowed.toBytes} bytes."
   }
-  def maxActivationLimitExceeded(value: Int, max: Int) = s"Activation limit of 
$value exceeds maximum limit of $max."
+
+  def maxListLimitExceeded(collection: String, value: Int, max: Int) = {
+    s"The value $value exceeds the allowed limit $max for $collection."
+  }
+  def listLimitIsNotAString = s"The API expects the 'limit' value to be an 
integer but the given value is not."
 
   def truncateLogs(limit: ByteSize) = {
     s"Logs were truncated because the total bytes size exceeds the limit of 
${limit.toBytes} bytes."
diff --git a/core/controller/src/main/scala/whisk/core/controller/Actions.scala 
b/core/controller/src/main/scala/whisk/core/controller/Actions.scala
index 97489bf..2dd0bbc 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Actions.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Actions.scala
@@ -21,9 +21,7 @@ import scala.concurrent.Future
 import scala.concurrent.duration._
 import scala.language.postfixOps
 import scala.util.{Failure, Success, Try}
-
 import org.apache.kafka.common.errors.RecordTooLargeException
-
 import akka.actor.ActorSystem
 import akka.http.scaladsl.model.HttpMethod
 import akka.http.scaladsl.model.StatusCodes._
@@ -32,12 +30,11 @@ import akka.http.scaladsl.server.RouteResult
 import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
 import 
akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonMarshaller
 import akka.http.scaladsl.unmarshalling._
-
 import spray.json._
 import spray.json.DefaultJsonProtocol._
-
 import whisk.common.TransactionId
 import whisk.core.WhiskConfig
+import whisk.core.controller.RestApiCommons.ListLimit
 import whisk.core.controller.actions.PostActionActivation
 import whisk.core.database.CacheChangeNotification
 import whisk.core.database.NoDocumentException
@@ -331,13 +328,14 @@ trait WhiskActionsApi extends WhiskCollectionAPI with 
PostActionActivation with
     // and would require finding all bindings in namespace and
     // joining the actions explicitly here.
     val docs = false
-    parameter('skip ? 0, 'limit ? collection.listLimit, 'count ? false) { 
(skip, limit, count) =>
-      listEntities {
-        WhiskAction.listCollectionInNamespace(entityStore, namespace, skip, 
limit, docs) map { list =>
-          val actions = list.fold((js) => js, (as) => 
as.map(WhiskAction.serdes.write(_)))
-          FilterEntityList.filter(actions, excludePrivate)
+    parameter('skip ? 0, 'limit.as[ListLimit] ? 
ListLimit(collection.defaultListLimit), 'count ? false) {
+      (skip, limit, count) =>
+        listEntities {
+          WhiskAction.listCollectionInNamespace(entityStore, namespace, skip, 
limit.n, docs) map { list =>
+            val actions = list.fold((js) => js, (as) => 
as.map(WhiskAction.serdes.write(_)))
+            FilterEntityList.filter(actions, excludePrivate)
+          }
         }
-      }
     }
   }
 
@@ -682,6 +680,9 @@ trait WhiskActionsApi extends WhiskCollectionAPI with 
PostActionActivation with
       }
     }
   }
+
+  /** Custom unmarshaller for query parameters "limit" for "list" operations. 
*/
+  private implicit val stringToListLimit: Unmarshaller[String, ListLimit] = 
RestApiCommons.stringToListLimit(collection)
 }
 
 private case class TooManyActionsInSequence() extends IllegalArgumentException
diff --git 
a/core/controller/src/main/scala/whisk/core/controller/Activations.scala 
b/core/controller/src/main/scala/whisk/core/controller/Activations.scala
index c51e00a..c136eb2 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Activations.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Activations.scala
@@ -19,30 +19,27 @@ package whisk.core.controller
 
 import java.time.Instant
 
+import scala.concurrent.Future
 import scala.language.postfixOps
-import scala.util.Failure
-import scala.util.Success
-import scala.util.Try
+import scala.util.{Failure, Success, Try}
 import 
akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonMarshaller
 import akka.http.scaladsl.model.StatusCodes.BadRequest
 import akka.http.scaladsl.server.Directives
 import akka.http.scaladsl.unmarshalling._
-import scala.concurrent.Future
 import spray.json._
 import spray.json.DefaultJsonProtocol.RootJsObjectFormat
-import spray.json.DeserializationException
 import whisk.common.TransactionId
 import whisk.core.containerpool.logging.LogStore
+import whisk.core.controller.RestApiCommons.ListLimit
 import whisk.core.database.StaleParameter
-import whisk.core.entitlement.{Collection, Privilege, Resource}
 import whisk.core.entitlement.Privilege.READ
+import whisk.core.entitlement.{Collection, Privilege, Resource}
 import whisk.core.entity._
 import whisk.core.entity.types.ActivationStore
 import whisk.http.ErrorResponse.terminate
 import whisk.http.Messages
 
 object WhiskActivationsApi {
-  protected[core] val maxActivationLimit = 200
 
   /** Custom unmarshaller for query parameters "name" into valid 
package/action name path. */
   private implicit val stringToRestrictedEntityPath: Unmarshaller[String, 
Option[EntityPath]] =
@@ -62,6 +59,11 @@ object WhiskActivationsApi {
         case Failure(t) => throw new 
IllegalArgumentException(Messages.badEpoch(value))
       }
     }
+
+  /** Custom unmarshaller for query parameters "limit" for "list" operations. 
*/
+  private implicit val stringToListLimit: Unmarshaller[String, ListLimit] =
+    RestApiCommons.stringToListLimit(Collection(Collection.ACTIVATIONS))
+
 }
 
 /** A trait implementing the activations API. */
@@ -139,20 +141,19 @@ trait WhiskActivationsApi extends Directives with 
AuthenticatedRouteProvider wit
   private def list(namespace: EntityPath)(implicit transid: TransactionId) = {
     import WhiskActivationsApi.stringToRestrictedEntityPath
     import WhiskActivationsApi.stringToInstantDeserializer
+    import WhiskActivationsApi.stringToListLimit
 
     parameter(
       'skip ? 0,
-      'limit ? collection.listLimit,
+      'limit.as[ListLimit] ? ListLimit(collection.defaultListLimit),
       'count ? false,
       'docs ? false,
       'name.as[Option[EntityPath]] ?,
       'since.as[Instant] ?,
       'upto.as[Instant] ?) { (skip, limit, count, docs, name, since, upto) =>
       val invalidDocs = count && docs
-      val cappedLimit = if (limit == 0) WhiskActivationsApi.maxActivationLimit 
else limit
-
       // regardless of limit, cap at maxActivationLimit (200) records, client 
must paginate
-      if (!invalidDocs && cappedLimit <= 
WhiskActivationsApi.maxActivationLimit) {
+      if (!invalidDocs) {
         val activations = name.flatten match {
           case Some(action) =>
             WhiskActivation.listActivationsMatchingName(
@@ -160,7 +161,7 @@ trait WhiskActivationsApi extends Directives with 
AuthenticatedRouteProvider wit
               namespace,
               action,
               skip,
-              cappedLimit,
+              limit.n,
               docs,
               since,
               upto,
@@ -170,20 +171,15 @@ trait WhiskActivationsApi extends Directives with 
AuthenticatedRouteProvider wit
               activationStore,
               namespace,
               skip,
-              cappedLimit,
+              limit.n,
               docs,
               since,
               upto,
               StaleParameter.UpdateAfter)
         }
-
-        listEntities {
-          activations map (_.fold((js) => js, (wa) => 
wa.map(_.toExtendedJson)))
-        }
-      } else if (invalidDocs) {
-        terminate(BadRequest, Messages.docsNotAllowedWithCount)
+        listEntities(activations map (_.fold((js) => js, (wa) => 
wa.map(_.toExtendedJson))))
       } else {
-        terminate(BadRequest, Messages.maxActivationLimitExceeded(limit, 
WhiskActivationsApi.maxActivationLimit))
+        terminate(BadRequest, Messages.docsNotAllowedWithCount)
       }
     }
   }
diff --git 
a/core/controller/src/main/scala/whisk/core/controller/Packages.scala 
b/core/controller/src/main/scala/whisk/core/controller/Packages.scala
index 74b2d93..a6388b1 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Packages.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Packages.scala
@@ -18,20 +18,18 @@
 package whisk.core.controller
 
 import scala.concurrent.Future
-import scala.util.Failure
-import scala.util.Success
+import scala.util.{Failure, Success}
 
-import akka.http.scaladsl.model.StatusCodes._
 import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
-import akka.http.scaladsl.server.RequestContext
-import akka.http.scaladsl.server.RouteResult
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.server.{RequestContext, RouteResult}
+import akka.http.scaladsl.unmarshalling.Unmarshaller
 
 import spray.json._
 
 import whisk.common.TransactionId
-import whisk.core.database.DocumentTypeMismatchException
-import whisk.core.database.CacheChangeNotification
-import whisk.core.database.NoDocumentException
+import whisk.core.controller.RestApiCommons.ListLimit
+import whisk.core.database.{CacheChangeNotification, 
DocumentTypeMismatchException, NoDocumentException}
 import whisk.core.entitlement._
 import whisk.core.entity._
 import whisk.core.entity.types.EntityStore
@@ -182,23 +180,24 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with 
ReferencedEntities {
     // Actions API for more.
     val docs = false
 
-    parameter('skip ? 0, 'limit ? collection.listLimit, 'count ? false) { 
(skip, limit, count) =>
-      listEntities {
-        WhiskPackage.listCollectionInNamespace(entityStore, namespace, skip, 
limit, docs) map { list =>
-          // any subject is entitled to list packages in any namespace
-          // however, they shall only observe public packages if the packages
-          // are not in one of the namespaces the subject is entitled to
-          val packages = list.fold((js) => js, (ps) => 
ps.map(WhiskPackage.serdes.write(_)))
-
-          FilterEntityList.filter(packages, excludePrivate, additionalFilter = 
{
-            // additionally exclude bindings
-            _.fields.get(WhiskPackage.bindingFieldName) match {
-              case Some(JsBoolean(isbinding)) => !isbinding
-              case _                          => false // exclude anything 
that does not conform
-            }
-          })
+    parameter('skip ? 0, 'limit.as[ListLimit] ? 
ListLimit(collection.defaultListLimit), 'count ? false) {
+      (skip, limit, count) =>
+        listEntities {
+          WhiskPackage.listCollectionInNamespace(entityStore, namespace, skip, 
limit.n, docs) map { list =>
+            // any subject is entitled to list packages in any namespace
+            // however, they shall only observe public packages if the packages
+            // are not in one of the namespaces the subject is entitled to
+            val packages = list.fold((js) => js, (ps) => 
ps.map(WhiskPackage.serdes.write(_)))
+
+            FilterEntityList.filter(packages, excludePrivate, additionalFilter 
= {
+              // additionally exclude bindings
+              _.fields.get(WhiskPackage.bindingFieldName) match {
+                case Some(JsBoolean(isbinding)) => !isbinding
+                case _                          => false // exclude anything 
that does not conform
+              }
+            })
+          }
         }
-      }
     }
   }
 
@@ -338,4 +337,7 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with 
ReferencedEntities {
       }
     }
   }
+
+  /** Custom unmarshaller for query parameters "limit" for "list" operations. 
*/
+  private implicit val stringToListLimit: Unmarshaller[String, ListLimit] = 
RestApiCommons.stringToListLimit(collection)
 }
diff --git 
a/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala 
b/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala
index 8015f60..ffcf01b 100644
--- a/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala
@@ -19,6 +19,10 @@ package whisk.core.controller
 
 import scala.concurrent.ExecutionContext
 
+import scala.util.Failure
+import scala.util.Success
+import scala.util.Try
+
 import akka.actor.ActorSystem
 import akka.http.scaladsl.model.StatusCodes._
 import akka.http.scaladsl.model.Uri
@@ -43,6 +47,7 @@ import whisk.core.entity.ActivationId.ActivationIdGenerator
 import whisk.core.entity.WhiskAuthStore
 import whisk.core.entity.types._
 import whisk.core.loadBalancer.LoadBalancerService
+import whisk.http.Messages
 
 /**
  * Abstract class which provides basic Directives which are used to construct 
route structures
@@ -115,6 +120,22 @@ protected[controller] object RestApiCommons {
     }
   }
 
+  /** Custom unmarshaller for query parameters "limit" for "list" operations. 
*/
+  case class ListLimit(n: Int)
+
+  def stringToListLimit(collection: Collection): Unmarshaller[String, 
ListLimit] = {
+    Unmarshaller.strict[String, ListLimit] { value =>
+      Try { value.toInt } match {
+        case Success(n) if (n == 0)                                  => 
ListLimit(Collection.MAX_LIST_LIMIT)
+        case Success(n) if (n > 0 && n <= Collection.MAX_LIST_LIMIT) => 
ListLimit(n)
+        case Success(n) =>
+          throw new IllegalArgumentException(
+            Messages.maxListLimitExceeded(collection.path, n, 
Collection.MAX_LIST_LIMIT))
+        case Failure(t) => throw new 
IllegalArgumentException(Messages.listLimitIsNotAString)
+      }
+    }
+  }
+
   /** Pretty print JSON response. */
   implicit val jsonPrettyResponsePrinter = PrettyPrinter
 
diff --git a/core/controller/src/main/scala/whisk/core/controller/Rules.scala 
b/core/controller/src/main/scala/whisk/core/controller/Rules.scala
index 0964f38..9f4be64 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Rules.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Rules.scala
@@ -17,28 +17,23 @@
 
 package whisk.core.controller
 
-import scala.concurrent.Future
-import scala.util.Failure
-import scala.util.Success
-
 import akka.actor.ActorSystem
-import akka.http.scaladsl.model.StatusCodes._
 import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.model.StatusCodes._
 import akka.http.scaladsl.server.StandardRoute
-
+import akka.http.scaladsl.unmarshalling.Unmarshaller
 import spray.json.DeserializationException
-
 import whisk.common.TransactionId
-import whisk.core.database.DocumentConflictException
-import whisk.core.database.CacheChangeNotification
-import whisk.core.database.NoDocumentException
+import whisk.core.controller.RestApiCommons.ListLimit
+import whisk.core.database.{CacheChangeNotification, 
DocumentConflictException, NoDocumentException}
+import whisk.core.entitlement.{Collection, Privilege, ReferencedEntities}
 import whisk.core.entity._
 import whisk.core.entity.types.EntityStore
 import whisk.http.ErrorResponse.terminate
 import whisk.http.Messages._
-import whisk.core.entitlement.Collection
-import whisk.core.entitlement.Privilege
-import whisk.core.entitlement.ReferencedEntities
+
+import scala.concurrent.Future
+import scala.util.{Failure, Success}
 
 /** A trait implementing the rules API */
 trait WhiskRulesApi extends WhiskCollectionAPI with ReferencedEntities {
@@ -245,13 +240,14 @@ trait WhiskRulesApi extends WhiskCollectionAPI with 
ReferencedEntities {
     // offer an option to fetch entities with full docs yet; see comment in
     // Actions API for more.
     val docs = false
-    parameter('skip ? 0, 'limit ? collection.listLimit, 'count ? false) { 
(skip, limit, count) =>
-      listEntities {
-        WhiskRule.listCollectionInNamespace(entityStore, namespace, skip, 
limit, docs) map { list =>
-          val rules = list.fold((js) => js, (rls) => 
rls.map(WhiskRule.serdes.write(_)))
-          FilterEntityList.filter(rules, excludePrivate)
+    parameter('skip ? 0, 'limit.as[ListLimit] ? 
ListLimit(collection.defaultListLimit), 'count ? false) {
+      (skip, limit, count) =>
+        listEntities {
+          WhiskRule.listCollectionInNamespace(entityStore, namespace, skip, 
limit.n, docs) map { list =>
+            val rules = list.fold((js) => js, (rls) => 
rls.map(WhiskRule.serdes.write(_)))
+            FilterEntityList.filter(rules, excludePrivate)
+          }
         }
-      }
     }
   }
 
@@ -410,6 +406,9 @@ trait WhiskRulesApi extends WhiskCollectionAPI with 
ReferencedEntities {
     implicit val statusSerdes = Status.serdesRestricted
     entity(as[Status])
   }
+
+  /** Custom unmarshaller for query parameters "limit" for "list" operations. 
*/
+  private implicit val stringToListLimit: Unmarshaller[String, ListLimit] = 
RestApiCommons.stringToListLimit(collection)
 }
 
 private case class IgnoredRuleActivation(noop: Boolean) extends Throwable
diff --git 
a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala 
b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
index f0fbf52..7cc292d 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
@@ -17,48 +17,28 @@
 
 package whisk.core.controller
 
-import java.time.Clock
-import java.time.Instant
-
-import scala.concurrent.Future
+import java.time.{Clock, Instant}
 
 import akka.actor.ActorSystem
-import akka.stream.ActorMaterializer
-import akka.http.scaladsl.model.headers.BasicHttpCredentials
-import akka.http.scaladsl.model.HttpRequest
-import akka.http.scaladsl.model.StatusCodes._
-import akka.http.scaladsl.model.Uri
-import akka.http.scaladsl.model.Uri.Path
-import akka.http.scaladsl.server.RouteResult
-import akka.http.scaladsl.model.HttpMethods.POST
-import akka.http.scaladsl.model.headers.Authorization
-import akka.http.scaladsl.model.HttpMethods._
-import akka.http.scaladsl.model.MediaTypes
-import akka.http.scaladsl.model.HttpEntity
-import akka.http.scaladsl.server.RequestContext
 import akka.http.scaladsl.Http
 import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
-import akka.http.scaladsl.unmarshalling.Unmarshal
-
+import akka.http.scaladsl.model.HttpMethods.POST
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.model.{HttpEntity, HttpRequest, MediaTypes, Uri}
+import akka.http.scaladsl.model.Uri.Path
+import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
+import akka.http.scaladsl.server.{RequestContext, RouteResult}
+import akka.http.scaladsl.unmarshalling.{Unmarshal, Unmarshaller}
+import akka.stream.ActorMaterializer
 import spray.json._
-import spray.json.DefaultJsonProtocol.RootJsObjectFormat
-
 import whisk.common.TransactionId
+import whisk.core.controller.RestApiCommons.ListLimit
 import whisk.core.database.CacheChangeNotification
 import whisk.core.entitlement.Collection
-import whisk.core.entity.ActivationResponse
-import whisk.core.entity.EntityPath
-import whisk.core.entity.Parameters
-import whisk.core.entity.SemVer
-import whisk.core.entity.Status
-import whisk.core.entity.TriggerLimits
-import whisk.core.entity.WhiskActivation
-import whisk.core.entity.WhiskTrigger
-import whisk.core.entity.WhiskTriggerPut
-import whisk.core.entity.types.ActivationStore
-import whisk.core.entity.types.EntityStore
-import whisk.core.entity.Identity
-import whisk.core.entity.FullyQualifiedEntityName
+import whisk.core.entity._
+import whisk.core.entity.types.{ActivationStore, EntityStore}
+
+import scala.concurrent.Future
 
 /** A trait implementing the triggers API. */
 trait WhiskTriggersApi extends WhiskCollectionAPI {
@@ -261,13 +241,14 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
     // offer an option to fetch entities with full docs yet; see comment in
     // Actions API for more.
     val docs = false
-    parameter('skip ? 0, 'limit ? collection.listLimit, 'count ? false) { 
(skip, limit, count) =>
-      listEntities {
-        WhiskTrigger.listCollectionInNamespace(entityStore, namespace, skip, 
limit, docs) map { list =>
-          val triggers = list.fold((js) => js, (ts) => 
ts.map(WhiskTrigger.serdes.write(_)))
-          FilterEntityList.filter(triggers, excludePrivate)
+    parameter('skip ? 0, 'limit.as[ListLimit] ? 
ListLimit(collection.defaultListLimit), 'count ? false) {
+      (skip, limit, count) =>
+        listEntities {
+          WhiskTrigger.listCollectionInNamespace(entityStore, namespace, skip, 
limit.n, docs) map { list =>
+            val triggers = list.fold((js) => js, (ts) => 
ts.map(WhiskTrigger.serdes.write(_)))
+            FilterEntityList.filter(triggers, excludePrivate)
+          }
         }
-      }
     }
   }
 
@@ -343,4 +324,7 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
   private def completeAsTriggerResponse(trigger: WhiskTrigger): RequestContext 
=> Future[RouteResult] = {
     complete(OK, trigger.withoutRules)
   }
+
+  /** Custom unmarshaller for query parameters "limit" for "list" operations. 
*/
+  private implicit val stringToListLimit: Unmarshaller[String, ListLimit] = 
RestApiCommons.stringToListLimit(collection)
 }
diff --git 
a/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala 
b/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala
index 3cd43f0..6bf6403 100644
--- a/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala
+++ b/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala
@@ -47,7 +47,8 @@ import whisk.core.entity.types.EntityStore
  * @param activate the privilege for an activate (may be ACTIVATE or REJECT 
for example)
  * @param listLimit the default limit on number of entities returned from a 
collection on a list operation
  */
-protected[core] case class Collection protected (val path: String, val 
listLimit: Int = 30) {
+protected[core] case class Collection protected (val path: String,
+                                                 val defaultListLimit: Int = 
Collection.DEFAULT_LIST_LIMIT) {
   override def toString = path
 
   /** Determines the right to request for the resources and context. */
@@ -109,6 +110,10 @@ protected[core] object Collection {
 
   protected[core] def requiredProperties = WhiskEntityStore.requiredProperties
 
+  /** Number of records allowed per query. */
+  protected[core] val DEFAULT_LIST_LIMIT = 30
+  protected[core] val MAX_LIST_LIMIT = 200
+
   protected[core] val ACTIONS = WhiskAction.collectionName
   protected[core] val TRIGGERS = WhiskTrigger.collectionName
   protected[core] val RULES = WhiskRule.collectionName
diff --git a/tests/src/test/scala/system/basic/WskBasicTests.scala 
b/tests/src/test/scala/system/basic/WskBasicTests.scala
index d5faee9..e51f5f3 100644
--- a/tests/src/test/scala/system/basic/WskBasicTests.scala
+++ b/tests/src/test/scala/system/basic/WskBasicTests.scala
@@ -781,13 +781,6 @@ class WskBasicTests extends TestHelpers with 
WskTestHelpers {
     wsk.namespace.get(expectedExitCode = SUCCESS_EXIT)(WskProps()).stdout 
should include("default")
   }
 
-  it should "not list entities with an invalid namespace" in {
-    val namespace = "fakeNamespace"
-    val stderr = wsk.namespace.get(Some(s"/${namespace}"), expectedExitCode = 
FORBIDDEN).stderr
-
-    stderr should include(s"Unable to obtain the list of entities for 
namespace '${namespace}'")
-  }
-
   behavior of "Wsk Activation CLI"
 
   it should "create a trigger, and fire a trigger to get its individual fields 
from an activation" in withAssetCleaner(
diff --git a/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala 
b/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
index 265b22f..eae45c0 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
@@ -1630,7 +1630,7 @@ class WskBasicUsageTests extends TestHelpers with 
WskTestHelpers {
       (Seq("activation", "result", "activationID", invalidArg), 
s"${tooManyArgsMsg}${invalidArg}."),
       (Seq("activation", "poll", "activationID", invalidArg), 
s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
       (Seq("namespace", "list", invalidArg), s"${tooManyArgsMsg}${invalidArg}. 
${noArgsReqMsg}"),
-      (Seq("namespace", "get", "namespace", invalidArg), 
s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
+      (Seq("namespace", "get", invalidArg), s"${tooManyArgsMsg}${invalidArg}. 
${noArgsReqMsg}"),
       (Seq("package", "create"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
       (Seq("package", "create", "packageName", invalidArg), 
s"${tooManyArgsMsg}${invalidArg}."),
       (Seq("package", "create", "packageName", "--shared", invalidArg), 
invalidShared),
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
index ac5558b..6c2dcd1 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
@@ -36,6 +36,7 @@ import spray.json.DefaultJsonProtocol._
 import whisk.core.controller.WhiskActionsApi
 import whisk.core.entity._
 import whisk.core.entity.size._
+import whisk.core.entitlement.Collection
 import whisk.http.ErrorResponse
 import whisk.http.Messages
 
@@ -90,6 +91,17 @@ class ActionsApiTests extends ControllerTestCommon with 
WhiskActionsApi {
     }
   }
 
+  it should "reject list when limit is greater than maximum allowed value" in {
+    implicit val tid = transid()
+    val exceededMaxLimit = Collection.MAX_LIST_LIMIT + 1
+    val response = Get(s"$collectionPath?limit=$exceededMaxLimit") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.maxListLimitExceeded(Collection.ACTIONS, exceededMaxLimit, 
Collection.MAX_LIST_LIMIT)
+      }
+    }
+  }
+
   // ?docs disabled
   ignore should "list action by default namespace with full docs" in {
     implicit val tid = transid()
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
index edda225..d37dae7 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
@@ -17,24 +17,22 @@
 
 package whisk.core.controller.test
 
-import java.time.Clock
-import java.time.Instant
-
-import org.junit.runner.RunWith
-import org.scalatest.junit.JUnitRunner
+import java.time.{Clock, Instant}
 
 import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
 import akka.http.scaladsl.model.StatusCodes._
 import akka.http.scaladsl.server.Route
 import akka.stream.ActorMaterializer
-import spray.json._
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
 import spray.json.DefaultJsonProtocol._
+import spray.json._
 import whisk.core.controller.WhiskActivationsApi
 import whisk.core.database.ArtifactStoreProvider
+import whisk.core.entitlement.Collection
 import whisk.core.entity._
 import whisk.core.entity.size._
-import whisk.http.ErrorResponse
-import whisk.http.Messages
+import whisk.http.{ErrorResponse, Messages}
 import whisk.spi.SpiLoader
 
 /**
@@ -381,11 +379,11 @@ class ActivationsApiTests extends ControllerTestCommon 
with WhiskActivationsApi
 
   it should "reject activation list when limit is greater than maximum allowed 
value" in {
     implicit val tid = transid()
-    val exceededMaxLimit = WhiskActivationsApi.maxActivationLimit + 1
+    val exceededMaxLimit = Collection.MAX_LIST_LIMIT + 1
     val response = Get(s"$collectionPath?limit=$exceededMaxLimit") ~> 
Route.seal(routes(creds)) ~> check {
       status should be(BadRequest)
-      responseAs[ErrorResponse].error shouldBe {
-        Messages.maxActivationLimitExceeded(exceededMaxLimit, 
WhiskActivationsApi.maxActivationLimit)
+      responseAs[String] should include {
+        Messages.maxListLimitExceeded(Collection.ACTIVATIONS, 
exceededMaxLimit, Collection.MAX_LIST_LIMIT)
       }
     }
   }
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
index 1e4c389..0fd2825 100644
--- a/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
@@ -18,19 +18,16 @@
 package whisk.core.controller.test
 
 import scala.language.postfixOps
-
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
-
 import akka.http.scaladsl.model.StatusCodes._
 import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
 import akka.http.scaladsl.server.Route
-
 import spray.json.DefaultJsonProtocol._
 import spray.json._
-
 import whisk.core.entity._
 import whisk.core.controller.WhiskPackagesApi
+import whisk.core.entitlement.Collection
 import whisk.http.ErrorResponse
 import whisk.http.Messages
 
@@ -136,6 +133,17 @@ class PackagesApiTests extends ControllerTestCommon with 
WhiskPackagesApi {
     }
   }
 
+  it should "reject list when limit is greater than maximum allowed value" in {
+    implicit val tid = transid()
+    val exceededMaxLimit = Collection.MAX_LIST_LIMIT + 1
+    val response = Get(s"$collectionPath?limit=$exceededMaxLimit") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.maxListLimitExceeded(Collection.PACKAGES, exceededMaxLimit, 
Collection.MAX_LIST_LIMIT)
+      }
+    }
+  }
+
   ignore should "list all public packages excluding bindings" in {
     implicit val tid = transid()
     // create packages and package bindings, set some public and confirm API 
lists only public packages
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala
index a4c19b8..60804b7 100644
--- a/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala
@@ -18,21 +18,19 @@
 package whisk.core.controller.test
 
 import scala.language.postfixOps
-
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
-
 import akka.http.scaladsl.model.StatusCodes._
 import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
 import akka.http.scaladsl.server.Route
-
 import spray.json.DefaultJsonProtocol._
 import spray.json._
-
 import whisk.core.controller.WhiskRulesApi
+import whisk.core.entitlement.Collection
 import whisk.core.entity._
 import whisk.core.entity.test.OldWhiskTrigger
 import whisk.http.ErrorResponse
+
 import scala.language.postfixOps
 import whisk.core.entity.test.OldWhiskRule
 import whisk.http.Messages
@@ -99,6 +97,17 @@ class RulesApiTests extends ControllerTestCommon with 
WhiskRulesApi {
     }
   }
 
+  it should "reject list when limit is greater than maximum allowed value" in {
+    implicit val tid = transid()
+    val exceededMaxLimit = Collection.MAX_LIST_LIMIT + 1
+    val response = Get(s"$collectionPath?limit=$exceededMaxLimit") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.maxListLimitExceeded(Collection.RULES, exceededMaxLimit, 
Collection.MAX_LIST_LIMIT)
+      }
+    }
+  }
+
   //?docs disabled
   ignore should "list rules by default namespace with full docs" in {
     implicit val tid = transid()
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
index 382d08d..513c05f 100644
--- a/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
@@ -34,6 +34,7 @@ import spray.json._
 import spray.json.DefaultJsonProtocol._
 
 import whisk.core.controller.WhiskTriggersApi
+import whisk.core.entitlement.Collection
 import whisk.core.entity._
 import whisk.core.entity.WhiskRule
 import whisk.core.entity.size._
@@ -99,6 +100,17 @@ class TriggersApiTests extends ControllerTestCommon with 
WhiskTriggersApi {
     }
   }
 
+  it should "reject list when limit is greater than maximum allowed value" in {
+    implicit val tid = transid()
+    val exceededMaxLimit = Collection.MAX_LIST_LIMIT + 1
+    val response = Get(s"$collectionPath?limit=$exceededMaxLimit") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.maxListLimitExceeded(Collection.TRIGGERS, exceededMaxLimit, 
Collection.MAX_LIST_LIMIT)
+      }
+    }
+  }
+
   // ?docs disabled
   ignore should "list triggers by default namespace with full docs" in {
     implicit val tid = transid()

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

Reply via email to