Add Property Projections

- Allows projections for actions, activations, packages, rules, and triggers
diff --git a/tests/src/common/Wsk.scala b/tests/src/common/Wsk.scala
index 1bc5938..371bd9d 100644
--- a/tests/src/common/Wsk.scala
+++ b/tests/src/common/Wsk.scala
@@ -145,11 +145,14 @@
     def get(
         name: String,
         expectedExitCode: Int = SUCCESS_EXIT,
-        summary: Boolean = false)(
+        summary: Boolean = false,
+        fieldFilter: Option[String] = None)(
             implicit wp: WskProps): RunResult = {
         val params = Seq(noun, "get", "--auth", wp.authKey) ++
             Seq(fqn(name)) ++
-            { if (summary) Seq("--summary") else Seq() }
+            { if (summary) Seq("--summary") else Seq() } ++
+            { fieldFilter map { f => Seq(f) } getOrElse Seq() }
+
         cli(wp.overrides ++ params, expectedExitCode)
     }
 }
@@ -543,9 +546,11 @@
      */
     def get(
         activationId: String,
-        expectedExitCode: Int = SUCCESS_EXIT)(
+        expectedExitCode: Int = SUCCESS_EXIT,
+        fieldFilter: Option[String] = None)(
             implicit wp: WskProps): RunResult = {
-        cli(wp.overrides ++ Seq(noun, "get", "--auth", wp.authKey, activationId), expectedExitCode)
+        val params = { fieldFilter map { f => Seq(f) } getOrElse Seq() }
+        cli(wp.overrides ++ Seq(noun, "get", "--auth", wp.authKey, activationId) ++ params, expectedExitCode)
     }
 
     /**
diff --git a/tests/src/system/basic/WskBasicTests.scala b/tests/src/system/basic/WskBasicTests.scala
index bda657a..ded6890 100644
--- a/tests/src/system/basic/WskBasicTests.scala
+++ b/tests/src/system/basic/WskBasicTests.scala
@@ -28,6 +28,7 @@
 import common.TestUtils.SUCCESS_EXIT
 import common.TestUtils.UNAUTHORIZED
 import common.TestUtils.FORBIDDEN
+import common.TestUtils.ERROR_EXIT
 import common.Wsk
 import common.WskProps
 import common.WskTestHelpers
@@ -162,6 +163,30 @@
             res.stdout should include(s"ok: created package $name")
     }
 
+    it should "create a package, and get its individual fields" in withAssetCleaner(wskprops) {
+        val name = "packageFields"
+        val paramInput = Map("payload" -> "test".toJson)
+        val successMsg = s"ok: got package $name, displaying field"
+
+        (wp, assetHelper) =>
+            assetHelper.withCleaner(wsk.pkg, name) {
+                (action, _) => action.create(name, parameters = paramInput)
+            }
+
+            val expectedParam = JsObject(
+                "payload" -> JsString("test")
+            )
+
+            val ns_regex_list = wsk.namespace.list().stdout.trim.replace('\n', '|')
+
+            wsk.pkg.get(name, fieldFilter = Some("namespace")).stdout should include regex (s"""(?i)$successMsg namespace\n$ns_regex_list""")
+            wsk.pkg.get(name, fieldFilter = Some("name")).stdout should include (s"""$successMsg name\n"$name"""")
+            wsk.pkg.get(name, fieldFilter = Some("version")).stdout should include (s"""$successMsg version\n"0.0.1"""")
+            wsk.pkg.get(name, fieldFilter = Some("publish")).stdout should include (s"""$successMsg publish\nfalse""")
+            wsk.pkg.get(name, fieldFilter = Some("binding")).stdout should include regex (s"""\\{\\}""")
+            wsk.pkg.get(name, fieldFilter = Some("invalid"), expectedExitCode = ERROR_EXIT).stderr should include ("error: Invalid field filter 'invalid'.")
+    }
+
     behavior of "Wsk Action CLI"
 
     it should "create the same action twice with different cases" in withAssetCleaner(wskprops) {
@@ -269,6 +294,33 @@
             }
     }
 
+    it should "create an action, and get its individual fields" in withAssetCleaner(wskprops) {
+        val name = "actionFields"
+        val paramInput = Map("payload" -> "test".toJson)
+        val successMsg = s"ok: got action $name, displaying field"
+
+        (wp, assetHelper) =>
+            assetHelper.withCleaner(wsk.action, name) {
+                (action, _) => action.create(name, defaultAction, parameters = paramInput)
+            }
+
+            val expectedParam = JsObject(
+                "payload" -> JsString("test")
+            )
+
+            val ns_regex_list = wsk.namespace.list().stdout.trim.replace('\n', '|')
+
+            wsk.action.get(name, fieldFilter = Some("name")).stdout should include (s"""$successMsg name\n"$name"""")
+            wsk.action.get(name, fieldFilter = Some("version")).stdout should include (s"""$successMsg version\n"0.0.1"""")
+            wsk.action.get(name, fieldFilter = Some("publish")).stdout should include (s"""$successMsg publish\nfalse""")
+            wsk.action.get(name, fieldFilter = Some("exec")).stdout should include regex (s"""$successMsg exec\n\\{\\s+"kind":\\s+"nodejs:6",\\s+"code":\\s+"\\/\\*\\*\\\\n \\* Hello, world.\\\\n \\*\\/\\\\nfunction main\\(params\\) \\{\\\\n    console.log\\('hello', params.payload\\+'!'\\);\\\\n\\}\\\\n"\n\\}""")
+            wsk.action.get(name, fieldFilter = Some("parameters")).stdout should include regex (s"""$successMsg parameters\n\\[\\s+\\{\\s+"key":\\s+"payload",\\s+"value":\\s+"test"\\s+\\}\\s+\\]""")
+            wsk.action.get(name, fieldFilter = Some("annotations")).stdout should include regex (s"""$successMsg annotations\n\\[\\]""")
+            wsk.action.get(name, fieldFilter = Some("limits")).stdout should include regex (s"""$successMsg limits\n\\{\\s+"timeout":\\s+60000,\\s+"memory":\\s+256,\\s+"logs":\\s+10\\s+\\}""")
+            wsk.action.get(name, fieldFilter = Some("namespace")).stdout should include regex (s"""(?i)$successMsg namespace\n$ns_regex_list""")
+            wsk.action.get(name, fieldFilter = Some("invalid"), expectedExitCode = ERROR_EXIT).stderr should include ("error: Invalid field filter 'invalid'.")
+    }
+
     /**
      * Tests creating an action from a malformed js file. This should fail in
      * some way - preferably when trying to create the action. If not, then
@@ -459,6 +511,32 @@
             }
     }
 
+    it should "create a trigger, and get its individual fields" in withAssetCleaner(wskprops) {
+        val name = "triggerFields"
+        val paramInput = Map("payload" -> "test".toJson)
+        val successMsg = s"ok: got trigger $name, displaying field"
+
+        (wp, assetHelper) =>
+            assetHelper.withCleaner(wsk.trigger, name) {
+                (action, _) => action.create(name, parameters = paramInput)
+            }
+
+            val expectedParam = JsObject(
+                "payload" -> JsString("test")
+            )
+
+            val ns_regex_list = wsk.namespace.list().stdout.trim.replace('\n', '|')
+
+            wsk.trigger.get(name, fieldFilter = Some("namespace")).stdout should include regex (s"""(?i)$successMsg namespace\n$ns_regex_list""")
+            wsk.trigger.get(name, fieldFilter = Some("name")).stdout should include (s"""$successMsg name\n"$name"""")
+            wsk.trigger.get(name, fieldFilter = Some("version")).stdout should include (s"""$successMsg version\n"0.0.1"""")
+            wsk.trigger.get(name, fieldFilter = Some("publish")).stdout should include (s"""$successMsg publish\nfalse""")
+            wsk.trigger.get(name, fieldFilter = Some("annotations")).stdout should include (s"""$successMsg annotations\n[]""")
+            wsk.trigger.get(name, fieldFilter = Some("parameters")).stdout should include regex (s"""$successMsg parameters\n\\[\\s+\\{\\s+"key":\\s+"payload",\\s+"value":\\s+"test"\\s+\\}\\s+\\]""")
+            wsk.trigger.get(name, fieldFilter = Some("limits")).stdout should include  (s"""$successMsg limits\n{}""")
+            wsk.trigger.get(name, fieldFilter = Some("invalid"), expectedExitCode = ERROR_EXIT).stderr should include ("error: Invalid field filter 'invalid'.")
+    }
+
     behavior of "Wsk Rule CLI"
 
     it should "create rule, get rule, update rule and list rule" in withAssetCleaner(wskprops) {
@@ -536,6 +614,34 @@
             stdout should include regex (s"(?i)rule /${ns_regex_list}/${ruleName}\\s*\\(status: active\\)")
     }
 
+    it should "create a rule, and get its individual fields" in withAssetCleaner(wskprops) {
+        val ruleName = "ruleFields"
+        val triggerName = "ruleTriggerFields"
+        val actionName = "ruleActionFields";val paramInput = Map("payload" -> "test".toJson)
+        val successMsg = s"ok: got rule $ruleName, displaying field"
+
+        (wp, assetHelper) =>
+
+            assetHelper.withCleaner(wsk.trigger, triggerName) {
+                (trigger, name) => trigger.create(name)
+            }
+            assetHelper.withCleaner(wsk.action, actionName) {
+                (action, name) => action.create(name, defaultAction)
+            }
+            assetHelper.withCleaner(wsk.rule, ruleName) {
+                (rule, name) => rule.create(name, trigger = triggerName, action = actionName)
+            }
+
+            val ns_regex_list = wsk.namespace.list().stdout.trim.replace('\n', '|')
+
+            wsk.rule.get(ruleName, fieldFilter = Some("namespace")).stdout should include regex (s"""(?i)$successMsg namespace\n$ns_regex_list""")
+            wsk.rule.get(ruleName, fieldFilter = Some("name")).stdout should include (s"""$successMsg name\n"$ruleName"""")
+            wsk.rule.get(ruleName, fieldFilter = Some("version")).stdout should include (s"""$successMsg version\n"0.0.1"""")
+            wsk.rule.get(ruleName, fieldFilter = Some("status")).stdout should include (s"""$successMsg status\n"active"""")
+            wsk.rule.get(ruleName, fieldFilter = Some("trigger")).stdout should include regex (s"""$successMsg trigger\n"$triggerName"""")
+            wsk.rule.get(ruleName, fieldFilter = Some("action")).stdout should include regex (s"""$successMsg action\n"$actionName"""")
+    }
+
     behavior of "Wsk Namespace CLI"
 
     it should "list namespaces" in {
@@ -556,4 +662,34 @@
 
         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(wskprops) {
+        (wp, assetHelper) =>
+            val name = "activationFields"
+
+            assetHelper.withCleaner(wsk.trigger, name) {
+                (trigger, _) =>
+                    trigger.create(name)
+            }
+
+            val ns_regex_list = wsk.namespace.list().stdout.trim.replace('\n', '|')
+
+            val run = wsk.trigger.fire(name)
+            withActivation(wsk.activation, run) {
+                activation =>
+                    val successMsg = s"ok: got activation ${activation.activationId}, displaying field"
+                    wsk.activation.get(activation.activationId, fieldFilter = Some("namespace")).stdout should include regex (s"""(?i)$successMsg namespace\n$ns_regex_list""")
+                    wsk.activation.get(activation.activationId, fieldFilter = Some("name")).stdout should include (s"""$successMsg name\n"$name"""")
+                    wsk.activation.get(activation.activationId, fieldFilter = Some("version")).stdout should include (s"""$successMsg version\n"0.0.1"""")
+                    wsk.activation.get(activation.activationId, fieldFilter = Some("publish")).stdout should include (s"""$successMsg publish\nfalse""")
+                    wsk.activation.get(activation.activationId, fieldFilter = Some("subject")).stdout should include regex (s"""(?i)$successMsg subject\n$ns_regex_list""")
+                    wsk.activation.get(activation.activationId, fieldFilter = Some("activationid")).stdout should include (s"""$successMsg activationid\n"${activation.activationId}""")
+                    wsk.activation.get(activation.activationId, fieldFilter = Some("start")).stdout should include regex (s"""$successMsg start\n\\d""")
+                    wsk.activation.get(activation.activationId, fieldFilter = Some("end")).stdout should include regex (s"""$successMsg end\n\\d""")
+                    wsk.activation.get(activation.activationId, fieldFilter = Some("duration")).stdout should include regex (s"""$successMsg duration\n\\d""")
+                    wsk.activation.get(activation.activationId, fieldFilter = Some("annotations")).stdout should include (s"""$successMsg annotations\n[]""")
+            }
+    }
 }
diff --git a/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala b/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala
index f149163..77ac738 100644
--- a/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala
+++ b/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala
@@ -902,14 +902,14 @@
             (Seq("action", "delete"), s"${tooFewArgsMsg} ${actionNameReqMsg}"),
             (Seq("action", "delete", "actionName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("action", "get"), s"${tooFewArgsMsg} ${actionNameReqMsg}"),
-            (Seq("action", "get", "actionName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+            (Seq("action", "get", "actionName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("action", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
             (Seq("action", "invoke"), s"${tooFewArgsMsg} ${actionNameReqMsg}"),
             (Seq("action", "invoke", "actionName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("activation", "list", "namespace", invalidArg),
                 s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
             (Seq("activation", "get"), s"${tooFewArgsMsg} ${activationIdReq}"),
-            (Seq("activation", "get", "activationID", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+            (Seq("activation", "get", "activationID", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("activation", "logs"), s"${tooFewArgsMsg} ${activationIdReq}"),
             (Seq("activation", "logs", "activationID", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("activation", "result"), s"${tooFewArgsMsg} ${activationIdReq}"),
@@ -924,7 +924,7 @@
             (Seq("package", "update"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
             (Seq("package", "update", "packageName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("package", "get"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
-            (Seq("package", "get", "packageName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+            (Seq("package", "get", "packageName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("package", "bind"), s"${tooFewArgsMsg} ${packageNameBindingReqMsg}"),
             (Seq("package", "bind", "packageName"), s"${tooFewArgsMsg} ${packageNameBindingReqMsg}"),
             (Seq("package", "bind", "packageName", "bindingName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
@@ -951,7 +951,7 @@
             (Seq("rule", "update", "ruleName", "triggerName", "actionName", invalidArg),
                 s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("rule", "get"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
-            (Seq("rule", "get", "ruleName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+            (Seq("rule", "get", "ruleName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("rule", "delete"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
             (Seq("rule", "delete", "ruleName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("rule", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
@@ -963,7 +963,7 @@
             (Seq("trigger", "update"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
             (Seq("trigger", "update", "triggerName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("trigger", "get"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
-            (Seq("trigger", "get", "triggerName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+            (Seq("trigger", "get", "triggerName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("trigger", "delete"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
             (Seq("trigger", "delete", "triggerName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
             (Seq("trigger", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"))