add annotations and test case for the new filter support (#98)

diff --git a/installCatalog.sh b/installCatalog.sh
index e895f36..eeaba47 100755
--- a/installCatalog.sh
+++ b/installCatalog.sh
@@ -58,7 +58,8 @@
     -t 90000 \
     -a feed true \
     -a description 'Database change feed' \
-    -a parameters '[ {"name":"dbname", "required":true} ]'
+    -a parameters '[ {"name":"dbname", "required":true}, {"name": "filter", "required":false, "type": "string", "description": "The name of your Cloudant database filter"}, {"name": "query_params", "required":false, "description": "JSON Object containing query parameters that are passed to the filter"} ]' \
+    -a sampleInput '{ "dbname": "mydb", "filter": "mailbox/by_status", "query_params": {"status": "new"} }'
 
 # Cloudant account actions
 
diff --git a/tests/dat/filterdesigndoc.txt b/tests/dat/filterdesigndoc.txt
new file mode 100644
index 0000000..5baaece
--- /dev/null
+++ b/tests/dat/filterdesigndoc.txt
@@ -0,0 +1,6 @@
+{
+  "_id": "_design/test_filter",
+    "filters": {
+      "fruit": "function (doc, req) { return doc.kind === 'fruit' || (req && req.query && req.query.type === doc.type); }"
+    }
+}
diff --git a/tests/src/test/scala/system/CloudantUtil.java b/tests/src/test/scala/system/CloudantUtil.java
index 23ef659..a7df6d7 100755
--- a/tests/src/test/scala/system/CloudantUtil.java
+++ b/tests/src/test/scala/system/CloudantUtil.java
@@ -50,6 +50,7 @@
     public static final File ATTACHMENT_FILE_PATH = getFileRelativeToCloudantHome("tests/dat/attach.txt");
     public static final File INDEX_DDOC_PATH = getFileRelativeToCloudantHome("tests/dat/indexdesigndoc.txt");
     public static final File VIEW_AND_SEARCH_DDOC_PATH = getFileRelativeToCloudantHome("tests/dat/searchdesigndoc.txt");
+    public static final File FILTER_DDOC_PATH = getFileRelativeToCloudantHome("tests/dat/filterdesigndoc.txt");
 
 
     private static Gson gson = new Gson();
diff --git a/tests/src/test/scala/system/packages/CloudantFeedTests.scala b/tests/src/test/scala/system/packages/CloudantFeedTests.scala
index e1c6543..efb5b19 100644
--- a/tests/src/test/scala/system/packages/CloudantFeedTests.scala
+++ b/tests/src/test/scala/system/packages/CloudantFeedTests.scala
@@ -21,7 +21,7 @@
 import org.scalatest.FlatSpec
 import org.scalatest.junit.JUnitRunner
 import spray.json.DefaultJsonProtocol.{IntJsonFormat, StringJsonFormat}
-import spray.json.pimpAny
+import spray.json.{JsObject, JsString, pimpAny}
 import system.CloudantUtil
 
 /**
@@ -233,7 +233,7 @@
     it should "should disable after reaching max triggers" in withAssetCleaner(wskprops) {
         (wp, assetHelper) =>
             implicit val wskprops = wp // shadow global props and make implicit
-        val triggerName = s"dummyCloudantTrigger-${System.currentTimeMillis}"
+            val triggerName = s"dummyCloudantTrigger-${System.currentTimeMillis}"
             val packageName = "dummyCloudantPackage"
             val feed = "changes"
 
@@ -285,4 +285,75 @@
             }
     }
 
+    it should """filter out triggers that do not meet the filter criteria""" in withAssetCleaner(wskprops) {
+        (wp, assetHelper) =>
+            implicit val wskprops = wp // shadow global props and make implicit
+            val triggerName = s"dummyCloudantTrigger-${System.currentTimeMillis}"
+            val packageName = "dummyCloudantPackage"
+            val feed = "changes"
+
+            try {
+                CloudantUtil.setUp(myCloudantCreds)
+
+                val packageGetResult = wsk.pkg.get("/whisk.system/cloudant")
+                println("Fetching cloudant package.")
+                packageGetResult.stdout should include("ok")
+
+                println("Creating cloudant package binding.")
+                assetHelper.withCleaner(wsk.pkg, packageName) {
+                    (pkg, name) => pkg.bind("/whisk.system/cloudant", name)
+                }
+
+                //Create filter design doc
+                val filterDesignDoc = CloudantUtil.createDesignFromFile(CloudantUtil.FILTER_DDOC_PATH).toString
+                val getResponse = CloudantUtil.createDocument(myCloudantCreds, filterDesignDoc)
+                getResponse.get("ok").getAsString shouldBe "true"
+
+                println("Creating cloudant trigger feed.")
+                val feedCreationResult = assetHelper.withCleaner(wsk.trigger, triggerName, confirmDelete = false) {
+                    (trigger, name) =>
+                        trigger.create(name, feed = Some(s"$packageName/$feed"), parameters = Map(
+                            "username" -> myCloudantCreds.user.toJson,
+                            "password" -> myCloudantCreds.password.toJson,
+                            "host" -> myCloudantCreds.host().toJson,
+                            "dbname" -> myCloudantCreds.dbname.toJson,
+                            "filter" -> "test_filter/fruit".toJson,
+                            "query_params" -> JsObject("type" -> JsString("tomato"))))
+                }
+                feedCreationResult.stdout should include("ok")
+
+                // Create test docs in cloudant and assert that document was inserted successfully
+                println("Creating a test doc-1 in the cloudant")
+                val response1 = CloudantUtil.createDocument(myCloudantCreds, "{\"kind\":\"fruit\", \"type\":\"apple\"}")
+                response1.get("ok").getAsString() should be("true")
+
+                println("Checking for activations")
+                val activations = wsk.activation.pollFor(N = 1, Some(triggerName)).length
+                println(s"Found activation size (should be exactly 1): $activations")
+                activations should be(1)
+
+                println("Creating a test doc-2 in the cloudant")
+                val response2 = CloudantUtil.createDocument(myCloudantCreds, "{\"kind\":\"dairy\",\"type\":\"butter\"}")
+                response2.get("ok").getAsString() should be("true")
+
+                println("checking for new activations (not expected since it should be filtered out)")
+                val noNewActivations = wsk.activation.pollFor(N = 2, Some(triggerName)).length
+                println(s"Found activation size (should still be 1): $noNewActivations")
+                noNewActivations should be(1)
+
+                println("Creating a test doc-3 in the cloudant")
+                val response3 = CloudantUtil.createDocument(myCloudantCreds, "{\"kind\":\"debatable\", \"type\":\"tomato\"}")
+                response3.get("ok").getAsString() should be("true")
+
+                println("Checking for new activations (should now have 2)")
+                val newActivations = wsk.activation.pollFor(N = 3, Some(triggerName)).length
+                println(s"Found activation size (should be 2): $newActivations")
+                newActivations should be(2)
+
+            }
+            finally {
+                CloudantUtil.unsetUp(myCloudantCreds)
+            }
+    }
+
 }