You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ra...@apache.org on 2017/08/11 01:47:10 UTC

[incubator-openwhisk] branch master updated: Allow CLI to sort entities by name (#2326)

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

rabbah 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 fe42018  Allow CLI to sort entities by name (#2326)
fe42018 is described below

commit fe420184641134c411b4629e5edfee62140338ef
Author: Brandon Lee Underwood <Br...@ibm.com>
AuthorDate: Thu Aug 10 21:47:07 2017 -0400

    Allow CLI to sort entities by name (#2326)
    
    - Created interfaces `Printables` and `Sortables`
    - Made Actions, Triggers, Packages, Rules, APIs into Printables and Sortables
    - Made Activations into Printables and Sortables, Sort currently undefined
    - Made alphabetic sorting default, sort by last update time with --time flag
    - Changed sorting default back to last update time, --sort flag for alphabetical sorting
    - Updated flag name to "--name-sort"/"-n"
    - Updated Docs
    - Fixed rule status printing for `wsk list` and `wsk namespace get`
---
 docs/actions.md                                    |  28 +++-
 docs/apigateway.md                                 |  30 ++--
 tests/src/test/scala/common/Wsk.scala              |  25 +++-
 .../scala/whisk/core/cli/test/ApiGwTests.scala     |  39 ++++-
 .../whisk/core/cli/test/WskBasicUsageTests.scala   | 135 +++++++++++++++++
 tools/cli/go-whisk-cli/commands/action.go          |   4 +-
 tools/cli/go-whisk-cli/commands/activation.go      |   2 +-
 tools/cli/go-whisk-cli/commands/api.go             | 120 +++++++++------
 tools/cli/go-whisk-cli/commands/flags.go           |   1 +
 tools/cli/go-whisk-cli/commands/namespace.go       |  25 +++-
 tools/cli/go-whisk-cli/commands/package.go         |   4 +-
 tools/cli/go-whisk-cli/commands/rule.go            |   5 +-
 tools/cli/go-whisk-cli/commands/trigger.go         |   4 +-
 tools/cli/go-whisk-cli/commands/util.go            | 162 ++++++++++++---------
 tools/cli/go-whisk-cli/commands/wsk.go             |   2 +
 .../go-whisk-cli/wski18n/resources/en_US.all.json  |  19 ++-
 tools/cli/go-whisk/whisk/action.go                 |  45 ++++++
 tools/cli/go-whisk/whisk/activation.go             |  20 +++
 tools/cli/go-whisk/whisk/api.go                    |  89 ++++++++++-
 tools/cli/go-whisk/whisk/namespace.go              |  29 ++++
 tools/cli/go-whisk/whisk/package.go                |  39 +++++
 tools/cli/go-whisk/whisk/rule.go                   |  32 +++-
 tools/cli/go-whisk/whisk/trigger.go                |  34 ++++-
 tools/cli/go-whisk/whisk/util.go                   |  15 ++
 24 files changed, 750 insertions(+), 158 deletions(-)

diff --git a/docs/actions.md b/docs/actions.md
index 4073154..a1d161e 100644
--- a/docs/actions.md
+++ b/docs/actions.md
@@ -1028,13 +1028,37 @@ This command starts a polling loop that continuously checks for logs from activa
 
 ## Listing actions
 
-You can list all the actions that you have created using:
+You can list all the actions that you have created using `wsk action list`:
 
 ```
 wsk action list
+actions
+/guest/packageB/A                  private nodejs:6
+/guest/C                           private nodejs:6
+/guest/A                           private nodejs:6
+/guest/packageA/B                  private nodejs:6
+/guest/packageA/A                  private nodejs:6
+/guest/B                           private nodejs:6
 ```
 
-As you write more actions, this list gets longer and it can be helpful to group related actions into [packages](./packages.md). To filter your list of actions to just the those within a specific pacakge, you can use:
+Here, we see actions listed in order from most to least recently updated. For easier browsing, you can use the flag `--name-sort` or `-n` to sort the list alphabetically:
+
+```
+wsk action list --name-sort
+actions
+/guest/A                           private nodejs:6
+/guest/B                           private nodejs:6
+/guest/C                           private nodejs:6
+/guest/packageA/A                  private nodejs:6
+/guest/packageA/B                  private nodejs:6
+/guest/packageB/A                  private nodejs:6
+```
+
+Notice that the list is now sorted alphabetically by namespace, then package name, and finally action name, with the default package (no specified package) listed at the top.
+
+**Note**: The printed list is sorted alphabetically after it is received from the server. Other list flags such as `--limit` and `--skip` will be applied to the block of actions before they are received for sorting. To list actions in order by creation time, use the flag `--time`.
+
+As you write more actions, this list gets longer and it can be helpful to group related actions into [packages](./packages.md). To filter your list of actions to just those within a specific package, you can use:
 
 ```
 wsk action list [PACKAGE NAME]
diff --git a/docs/apigateway.md b/docs/apigateway.md
index 50dcd61..609656d 100644
--- a/docs/apigateway.md
+++ b/docs/apigateway.md
@@ -22,18 +22,18 @@ Follow the instructions in [Configure CLI](./README.md#setting-up-the-openwhisk-
       return {payload: `Hello world ${name}`};
   }
   ```
-  
+
 2. Create a web action from the following JavaScript function. For this example, the action is called 'hello'. Make sure to add the flag `--web true`
-  
+
   ```
   wsk action create hello hello.js --web true
   ```
   ```
   ok: created action hello
   ```
-  
+
 3. Create an API with base path `/hello`, path `/world` and method `get` with response type `json`
-  
+
   ```
   wsk api create /hello /world get hello --response-type json
   ```
@@ -42,9 +42,9 @@ Follow the instructions in [Configure CLI](./README.md#setting-up-the-openwhisk-
   https://${APIHOST}:9001/api/21ef035/hello/world
   ```
   A new URL is generated exposing the `hello` action via a __GET__ HTTP method.
-  
+
 4. Let's give it a try by sending a HTTP request to the URL.
-  
+
   ```
   $ curl https://${APIHOST}:9001/api/21ef035/hello/world?name=OpenWhisk
   ```
@@ -54,12 +54,12 @@ Follow the instructions in [Configure CLI](./README.md#setting-up-the-openwhisk-
   }
   ```
   The web action `hello` was invoked, returning back a JSON object including the parameter `name` sent via query parameter. You can pass parameters to the action via simple query parameters, or via the request body. Web actions allow you to invoke an action in a public way without the OpenWhisk authorization API key.
-  
+
 ### Full control over the HTTP response
-  
-  The `--response-type` flag controls the target URL of the web action to be proxied by the API Gateway. Using `--response-type json` as above returns the full result of the action in JSON format and automatically sets the Content-Type header to `application/json` which enables you to easily get started. 
-  
-  Once you get started you want to have full control over the HTTP response properties like `statusCode`, `headers` and return different content types in the `body`. You can do this by using `--response-type http`, this will configure the target URL of the web action with the `http` extension.
+
+  The `--response-type` flag controls the target URL of the web action to be proxied by the API Gateway. Using `--response-type json` as above returns the full result of the action in JSON format and automatically sets the Content-Type header to `application/json` which enables you to easily get started.
+
+  Once you get started, you will want to have full control over the HTTP response properties like `statusCode`, `headers` and return different content types in the `body`. You can do this by using `--response-type http`, this will configure the target URL of the web action with the `http` extension.
 
   You can choose to change the code of the action to comply with the return of web actions with `http` extension or include the action in a sequence passing its result to a new action that transforms the result to be properly formatted for an HTTP response. You can read more about response types and web actions extensions in the [Web Actions](webactions.md) documentation.
 
@@ -67,14 +67,14 @@ Follow the instructions in [Configure CLI](./README.md#setting-up-the-openwhisk-
   ```javascript
   function main({name:name='Serverless API'}) {
       return {
-        body: new Buffer(JSON.stringify({payload:`Hello world ${name}`})).toString('base64'), 
-        statusCode:200, 
+        body: new Buffer(JSON.stringify({payload:`Hello world ${name}`})).toString('base64'),
+        statusCode: 200,
         headers:{ 'Content-Type': 'application/json'}
       };
   }
   ```
   Notice that the body needs to be return encoded in `base64` and not a string.
-  
+
   Update the action with the modified result
   ```
   wsk action update hello hello.js --web true
@@ -170,7 +170,7 @@ curl -X GET https://${APIHOST}:9001/api/21ef035/club/books
 ```
 
 ### Exporting the configuration
-Let's export API named `Book Club` into a file that we can use as a base to to re-create the APIs using a file as input. 
+Let's export API named `Book Club` into a file that we can use as a base to to re-create the APIs using a file as input.
 ```
 wsk api get "Book Club" > club-swagger.json
 ```
diff --git a/tests/src/test/scala/common/Wsk.scala b/tests/src/test/scala/common/Wsk.scala
index 79a1054..8b1ad6c 100644
--- a/tests/src/test/scala/common/Wsk.scala
+++ b/tests/src/test/scala/common/Wsk.scala
@@ -144,10 +144,12 @@ trait ListOrGetFromCollection extends FullyQualifiedNames {
     def list(
         namespace: Option[String] = None,
         limit: Option[Int] = None,
+        nameSort: Option[Boolean] = None,
         expectedExitCode: Int = SUCCESS_EXIT)(
             implicit wp: WskProps): RunResult = {
         val params = Seq(noun, "list", resolve(namespace), "--auth", wp.authKey) ++
-            { limit map { l => Seq("--limit", l.toString) } getOrElse Seq() }
+            { limit map { l => Seq("--limit", l.toString) } getOrElse Seq() } ++
+            { nameSort map { n => Seq("--name-sort") } getOrElse Seq() }
         cli(wp.overrides ++ params, expectedExitCode)
     }
 
@@ -690,9 +692,12 @@ class WskNamespace()
      * @param expectedExitCode (optional) the expected exit code for the command
      * if the code is anything but DONTCARE_EXIT, assert the code is as expected
      */
-    def list(expectedExitCode: Int = SUCCESS_EXIT)(
+    def list(
+        expectedExitCode: Int = SUCCESS_EXIT,
+        nameSort: Option[Boolean] = None)(
         implicit wp: WskProps): RunResult = {
-        val params = Seq(noun, "list", "--auth", wp.authKey)
+        val params = Seq(noun, "list", "--auth", wp.authKey) ++
+            { nameSort map { n => Seq("--name-sort") } getOrElse Seq() }
         cli(wp.overrides ++ params, expectedExitCode)
     }
 
@@ -718,9 +723,11 @@ class WskNamespace()
      */
     def get(
         namespace: Option[String] = None,
-        expectedExitCode: Int)(
+        expectedExitCode: Int,
+        nameSort: Option[Boolean] = None)(
             implicit wp: WskProps): RunResult = {
-        cli(wp.overrides ++ Seq(noun, "get", resolve(namespace), "--auth", wp.authKey), expectedExitCode)
+            val params = { nameSort map { n => Seq("--name-sort") } getOrElse Seq() }
+        cli(wp.overrides ++ Seq(noun, "get", resolve(namespace), "--auth", wp.authKey) ++ params, expectedExitCode)
     }
 }
 
@@ -818,6 +825,7 @@ class WskApiExperimental extends RunWskCmd {
         limit: Option[Int] = None,
         since: Option[Instant] = None,
         full: Option[Boolean] = None,
+        nameSort: Option[Boolean] = None,
         expectedExitCode: Int = SUCCESS_EXIT)(
             implicit wp: WskProps): RunResult = {
         val params = Seq(noun, "list", "--auth", wp.authKey) ++
@@ -826,7 +834,8 @@ class WskApiExperimental extends RunWskCmd {
             { operation map { o => Seq(o) } getOrElse Seq() } ++
             { limit map { l => Seq("--limit", l.toString) } getOrElse Seq() } ++
             { since map { i => Seq("--since", i.toEpochMilli.toString) } getOrElse Seq() } ++
-            { full map { r => Seq("--full") } getOrElse Seq() }
+            { full map { r => Seq("--full") } getOrElse Seq() } ++
+            { nameSort map { n => Seq("--name-sort") } getOrElse Seq() }
         cli(wp.overrides ++ params, expectedExitCode, showCmd = true)
     }
 
@@ -912,6 +921,7 @@ class WskApi()
         limit: Option[Int] = None,
         since: Option[Instant] = None,
         full: Option[Boolean] = None,
+        nameSort: Option[Boolean] = None,
         expectedExitCode: Int = SUCCESS_EXIT,
         cliCfgFile: Option[String] = None)(
             implicit wp: WskProps): RunResult = {
@@ -921,7 +931,8 @@ class WskApi()
             { operation map { o => Seq(o) } getOrElse Seq() } ++
             { limit map { l => Seq("--limit", l.toString) } getOrElse Seq() } ++
             { since map { i => Seq("--since", i.toEpochMilli.toString) } getOrElse Seq() } ++
-            { full map { r => Seq("--full") } getOrElse Seq() }
+            { full map { r => Seq("--full") } getOrElse Seq() } ++
+            { nameSort map { n => Seq("--name-sort") } getOrElse Seq() }
         cli(wp.overrides ++ params, expectedExitCode, showCmd = true, env = Map("WSK_CONFIG_FILE" -> cliCfgFile.getOrElse("")))
     }
 
diff --git a/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala b/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
index e7abf10..d88cb98 100644
--- a/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
@@ -131,11 +131,12 @@ class ApiGwTests
         limit: Option[Int] = None,
         since: Option[Instant] = None,
         full: Option[Boolean] = None,
+        nameSort: Option[Boolean] = None,
         expectedExitCode: Int = SUCCESS_EXIT,
         cliCfgFile: Option[String] = Some(cliWskPropsFile.getCanonicalPath())): RunResult = {
 
         checkThrottle()
-        wsk.api.list(basepathOrApiName, relpath, operation, limit, since, full, expectedExitCode, cliCfgFile)
+        wsk.api.list(basepathOrApiName, relpath, operation, limit, since, full, nameSort, expectedExitCode, cliCfgFile)
     }
 
     def apiGet(
@@ -906,4 +907,40 @@ class ApiGwTests
             var deleteresult = apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
         }
     }
+
+    it should "list api alphabetically by Base/Rel/Verb" in {
+        val baseName = "/BaseTestPathApiList"
+        val actionName = "actionName"
+        val file = TestUtils.getTestActionFilename(s"echo-web-http.js")
+        try {
+            // Create Action for apis
+            var action = wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+            println("action creation: " + action.stdout)
+            // Create apis
+            for (i <- 1 to 3) {
+                val base = s"$baseName$i"
+                var api = apiCreate(
+                    basepath = Some(base),
+                    relpath = Some("/relPath"),
+                    operation = Some("GET"),
+                    action = Some(actionName))
+                println("api creation: " + api.stdout)
+            }
+            val original = apiList(nameSort = Some(true)).stdout
+            val originalFull = apiList(full = Some(true), nameSort = Some(true)).stdout
+            val scalaSorted = List(s"${baseName}1" + "/", s"${baseName}2" + "/", s"${baseName}3" + "/")
+            val regex = s"${baseName}[1-3]/".r
+            val list  = (regex.findAllMatchIn(original)).toList
+            val listFull = (regex.findAllMatchIn(originalFull)).toList
+
+            scalaSorted.toString shouldEqual list.toString
+            scalaSorted.toString shouldEqual listFull.toString
+        } finally {
+            // Clean up Apis
+            for (i <- 1 to 3) {
+                val deleteApis = apiDelete(basepathOrApiName = s"${baseName}$i", expectedExitCode = DONTCARE_EXIT)
+            }
+            val deleteAction = wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+        }
+    }
 }
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 c451bbd..3f3c730 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
@@ -1233,6 +1233,141 @@ class WskBasicUsageTests
             }, 5, Some(1 second))
     }
 
+    it should "return a list of alphabetized actions" in withAssetCleaner(wskprops) {
+        (wp, assetHelper) =>
+            // Declare 4 actions, create them out of alphabetical order
+            val actionName = "actionAlphaTest"
+            for (i <- 1 to 3) {
+                val name = s"$actionName$i"
+                assetHelper.withCleaner(wsk.action, name) {
+                    (action, name) =>
+                        action.create(name, defaultAction)
+                }
+            }
+            retry({
+                val original = wsk.action.list(nameSort = Some(true)).stdout
+                // Create list with action names in correct order
+                val scalaSorted = List(s"${actionName}1", s"${actionName}2", s"${actionName}3")
+                // Filter out everything not previously created
+                val regex = s"${actionName}[1-3]".r
+                // Retrieve action names into list as found in original
+                val list  = (regex.findAllMatchIn(original)).toList
+                scalaSorted.toString shouldEqual list.toString
+            }, 5, Some(1 second))
+    }
+
+    it should "return an alphabetized list with default package actions on top" in withAssetCleaner(wskprops) {
+        (wp, assetHelper) =>
+            // Declare 4 actions, create them out of alphabetical order
+            val actionName = "actionPackageAlphaTest"
+            val packageName = "packageAlphaTest"
+            assetHelper.withCleaner(wsk.action, actionName) {
+                (action, actionName) =>
+                    action.create(actionName, defaultAction)
+            }
+            assetHelper.withCleaner(wsk.pkg, packageName) {
+                (pkg, packageName) =>
+                    pkg.create(packageName)
+            }
+            for (i <- 1 to 3) {
+                val name = s"${packageName}/${actionName}$i"
+                assetHelper.withCleaner(wsk.action, name) {
+                    (action, name) =>
+                        action.create(name, defaultAction)
+                }
+            }
+            retry({
+                val original = wsk.action.list(nameSort = Some(true)).stdout
+                // Create list with action names in correct order
+                val scalaSorted = List(s"$actionName", s"${packageName}/${actionName}1", s"${packageName}/${actionName}2", s"${packageName}/${actionName}3")
+                // Filter out everything not previously created
+                val regexNoPackage = s"$actionName".r
+                val regexWithPackage = s"${packageName}/${actionName}[1-3]".r
+                // Retrieve action names into list as found in original
+                val list = regexNoPackage.findFirstIn(original).get :: (regexWithPackage.findAllMatchIn(original)).toList
+                scalaSorted.toString shouldEqual list.toString
+            }, 5, Some(1 second))
+    }
+
+    it should "return a list of alphabetized packages" in withAssetCleaner(wskprops) {
+        (wp, assetHelper) =>
+            // Declare 3 packages, create them out of alphabetical order
+            val packageName = "pkgAlphaTest"
+            for (i <- 1 to 3) {
+                val name = s"$packageName$i"
+                assetHelper.withCleaner(wsk.pkg, name) {
+                    (pkg, name) =>
+                        pkg.create(name)
+                }
+            }
+            retry({
+                val original = wsk.pkg.list(nameSort = Some(true)).stdout
+                // Create list with package names in correct order
+                val scalaSorted = List(s"${packageName}1", s"${packageName}2", s"${packageName}3")
+                // Filter out everything not previously created
+                val regex = s"${packageName}[1-3]".r
+                // Retrieve package names into list as found in original
+                val list  = (regex.findAllMatchIn(original)).toList
+                scalaSorted.toString shouldEqual list.toString
+            }, 5, Some(1 second))
+    }
+
+    it should "return a list of alphabetized triggers" in withAssetCleaner(wskprops) {
+        (wp, assetHelper) =>
+            // Declare 4 triggers, create them out of alphabetical order
+            val triggerName = "triggerAlphaTest"
+            for (i <- 1 to 3) {
+                val name = s"$triggerName$i"
+                assetHelper.withCleaner(wsk.trigger, name) {
+                    (trigger, name) =>
+                        trigger.create(name)
+                }
+            }
+            retry({
+                val original = wsk.trigger.list(nameSort = Some(true)).stdout
+                // Create list with trigger names in correct order
+                val scalaSorted = List(s"${triggerName}1", s"${triggerName}2", s"${triggerName}3")
+                // Filter out everything not previously created
+                val regex = s"${triggerName}[1-3]".r
+                // Retrieve trigger names into list as found in original
+                val list  = (regex.findAllMatchIn(original)).toList
+                scalaSorted.toString shouldEqual list.toString
+            }, 5, Some(1 second))
+    }
+
+    it should "return a list of alphabetized rules" in withAssetCleaner(wskprops) {
+        (wp, assetHelper) =>
+            // Declare a trigger and an action for the purposes of creating rules
+            val triggerName = "listRulesTrigger"
+            val actionName = "listRulesAction"
+
+            assetHelper.withCleaner(wsk.trigger, triggerName) {
+                (trigger, name) => trigger.create(name)
+            }
+            assetHelper.withCleaner(wsk.action, actionName) {
+                (action, name) => action.create(name, defaultAction)
+            }
+            // Declare 3 rules, create them out of alphabetical order
+            val ruleName = "ruleAlphaTest"
+            for (i <- 1 to 3) {
+                val name = s"$ruleName$i"
+                assetHelper.withCleaner(wsk.rule, name) {
+                    (rule, name) =>
+                        rule.create(name, trigger = triggerName, action = actionName)
+                }
+            }
+            retry({
+                val original = wsk.rule.list(nameSort = Some(true)).stdout
+                // Create list with rule names in correct order
+                val scalaSorted = List(s"${ruleName}1", s"${ruleName}2", s"${ruleName}3")
+                // Filter out everything not previously created
+                val regex = s"${ruleName}[1-3]".r
+                // Retrieve rule names into list as found in original
+                val list  = (regex.findAllMatchIn(original)).toList
+                scalaSorted.toString shouldEqual list.toString
+            })
+    }
+
     behavior of "Wsk params and annotations"
 
     it should "reject commands that are executed with invalid JSON for annotations and parameters" in {
diff --git a/tools/cli/go-whisk-cli/commands/action.go b/tools/cli/go-whisk-cli/commands/action.go
index 3008a57..bfa6294 100644
--- a/tools/cli/go-whisk-cli/commands/action.go
+++ b/tools/cli/go-whisk-cli/commands/action.go
@@ -320,7 +320,8 @@ var actionListCmd = &cobra.Command{
             return actionListError(qualifiedName.GetEntityName(), options, err)
         }
 
-        printList(actions)
+        sortByName := flags.common.nameSort
+        printList(actions, sortByName)
 
         return nil
     },
@@ -941,6 +942,7 @@ func init() {
 
     actionListCmd.Flags().IntVarP(&flags.common.skip, "skip", "s", 0, wski18n.T("exclude the first `SKIP` number of actions from the result"))
     actionListCmd.Flags().IntVarP(&flags.common.limit, "limit", "l", 30, wski18n.T("only return `LIMIT` number of actions from the collection"))
+    actionListCmd.Flags().BoolVarP(&flags.common.nameSort, "name-sort", "n", false, wski18n.T("sorts a list alphabetically by entity name; only applicable within the limit/skip returned entity block"))
 
     actionCmd.AddCommand(
         actionCreateCmd,
diff --git a/tools/cli/go-whisk-cli/commands/activation.go b/tools/cli/go-whisk-cli/commands/activation.go
index 4ae62fe..59e84fe 100644
--- a/tools/cli/go-whisk-cli/commands/activation.go
+++ b/tools/cli/go-whisk-cli/commands/activation.go
@@ -93,7 +93,7 @@ var activationListCmd = &cobra.Command{
         if options.Docs == true {
             printFullActivationList(activations)
         } else {
-            printList(activations)
+            printList(activations, false)   // Default sorting for Activations are by creation time, hence sortByName is always false
         }
 
         return nil
diff --git a/tools/cli/go-whisk-cli/commands/api.go b/tools/cli/go-whisk-cli/commands/api.go
index b4c4306..bd60492 100644
--- a/tools/cli/go-whisk-cli/commands/api.go
+++ b/tools/cli/go-whisk-cli/commands/api.go
@@ -93,7 +93,6 @@ func isValidRelpath(relpath string) (error, bool) {
     return nil, true
 }
 
-
 /*
  * Pull the managedUrl (external API URL) from the API configuration
  */
@@ -445,6 +444,8 @@ var apiListCmd = &cobra.Command{
         var retApiArray *whisk.RetApiArray
         var apiPath string
         var apiVerb string
+        var orderFilteredList []whisk.ApiFilteredList
+        var orderFilteredRow []whisk.ApiFilteredRow
 
         if whiskErr := checkArgs(args, 0, 3, "Api list",
             wski18n.T("Optional parameters are: API base path (or API name), API relative path and operation.")); whiskErr != nil {
@@ -518,7 +519,8 @@ var apiListCmd = &cobra.Command{
             // Cast to a common type to allow for code to print out apilist response or apiget response
             retApiArray = (*whisk.RetApiArray)(retApi)
         }
-
+        //Checks for any order flags being passed
+        sortByName := flags.common.nameSort
         // Display the APIs - applying any specified filtering
         if (flags.common.full) {
             fmt.Fprintf(color.Output,
@@ -526,10 +528,10 @@ var apiListCmd = &cobra.Command{
                     map[string]interface{}{
                         "ok": color.GreenString("ok:"),
                     }))
-
-            for i:=0; i<len(retApiArray.Apis); i++ {
-                printFilteredListApi(retApiArray.Apis[i].ApiValue, apiPath, apiVerb)
+            for i := 0; i < len(retApiArray.Apis); i++ {
+                orderFilteredList = append(orderFilteredList, genFilteredList(retApiArray.Apis[i].ApiValue, apiPath, apiVerb)...)
             }
+            printList(orderFilteredList, sortByName)  // Sends an array of structs that contains specifed variables that are not truncated
         } else {
             if (len(retApiArray.Apis) > 0) {
                 // Dynamically create the output format string based on the maximum size of the
@@ -542,17 +544,17 @@ var apiListCmd = &cobra.Command{
                         map[string]interface{}{
                             "ok": color.GreenString("ok:"),
                         }))
-                fmt.Printf(fmtString, "Action", "Verb", "API Name", "URL")
-                for i:=0; i<len(retApiArray.Apis); i++ {
-                    printFilteredListRow(retApiArray.Apis[i].ApiValue, apiPath, apiVerb, maxActionNameSize, maxApiNameSize)
+                for i := 0; i < len(retApiArray.Apis); i++ {
+                    orderFilteredRow = append(orderFilteredRow, genFilteredRow(retApiArray.Apis[i].ApiValue, apiPath, apiVerb, maxActionNameSize, maxApiNameSize)...)
                 }
+                printList(orderFilteredRow, sortByName)  // Sends an array of structs that contains specifed variables that are truncated
             } else {
                 fmt.Fprintf(color.Output,
                     wski18n.T("{{.ok}} APIs\n",
                         map[string]interface{}{
                             "ok": color.GreenString("ok:"),
                         }))
-                fmt.Printf(fmtString, "Action", "Verb", "API Name", "URL")
+                printList(orderFilteredRow, sortByName)  // Sends empty orderFilteredRow so that defaultHeader can be printed
             }
         }
 
@@ -560,24 +562,25 @@ var apiListCmd = &cobra.Command{
     },
 }
 
-/*
- * Takes an API object (containing one more more single basepath/relpath/operation triplets)
- * and some filtering configuration.  For each API endpoint matching the filtering criteria, display
- * each endpoint's configuration - one line per configuration property (action name, verb, api name, api gw url)
- */
-func printFilteredListApi(resultApi *whisk.RetApi, apiPath string, apiVerb string) {
+// genFilteredList(resultApi, api) generates an array of
+//      ApiFilteredLists for the purpose of ordering and printing in a list form.
+//      NOTE: genFilteredRow() generates entries with one line per configuration
+//         property (action name, verb, api name, api gw url)
+func genFilteredList(resultApi *whisk.RetApi, apiPath string, apiVerb string) []whisk.ApiFilteredList{
+    var orderInfo whisk.ApiFilteredList
+    var orderInfoArr []whisk.ApiFilteredList
     baseUrl := strings.TrimSuffix(resultApi.BaseUrl, "/")
     apiName := resultApi.Swagger.Info.Title
     basePath := resultApi.Swagger.BasePath
     if (resultApi.Swagger != nil && resultApi.Swagger.Paths != nil) {
         for path, _ := range resultApi.Swagger.Paths {
-            whisk.Debug(whisk.DbgInfo, "printFilteredListApi: comparing api relpath: %s\n", path)
+            whisk.Debug(whisk.DbgInfo, "genFilteredList: comparing api relpath: %s\n", path)
             if ( len(apiPath) == 0 || path == apiPath) {
-                whisk.Debug(whisk.DbgInfo, "printFilteredListApi: relpath matches\n")
+                whisk.Debug(whisk.DbgInfo, "genFilteredList: relpath matches\n")
                 for op, opv  := range resultApi.Swagger.Paths[path] {
-                    whisk.Debug(whisk.DbgInfo, "printFilteredListApi: comparing operation: '%s'\n", op)
+                    whisk.Debug(whisk.DbgInfo, "genFilteredList: comparing operation: '%s'\n", op)
                     if ( len(apiVerb) == 0 || strings.ToLower(op) == strings.ToLower(apiVerb)) {
-                        whisk.Debug(whisk.DbgInfo, "printFilteredListApi: operation matches: %#v\n", opv)
+                        whisk.Debug(whisk.DbgInfo, "genFilteredList: operation matches: %#v\n", opv)
                         var actionName string
                         if (opv.XOpenWhisk == nil) {
                             actionName = ""
@@ -586,38 +589,36 @@ func printFilteredListApi(resultApi *whisk.RetApi, apiPath string, apiVerb strin
                         } else {
                             actionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.ActionName
                         }
-                        fmt.Printf("%s: %s\n", wski18n.T("Action"), actionName)
-                        fmt.Printf("  %s: %s\n", wski18n.T("API Name"), apiName)
-                        fmt.Printf("  %s: %s\n", wski18n.T("Base path"), basePath)
-                        fmt.Printf("  %s: %s\n", wski18n.T("Path"), path)
-                        fmt.Printf("  %s: %s\n", wski18n.T("Verb"), op)
-                        fmt.Printf("  %s: %s\n", wski18n.T("URL"), baseUrl+path)
+                        orderInfo = AssignListInfo(actionName, op, apiName, basePath, path, baseUrl+path)
+                        whisk.Debug(whisk.DbgInfo, "Appening to orderInfoArr: %s %s\n", orderInfo.RelPath)
+                        orderInfoArr = append(orderInfoArr, orderInfo)
                     }
                 }
             }
         }
     }
+    return orderInfoArr
 }
 
-/*
- * Takes an API object (containing one more more single basepath/relpath/operation triplets)
- * and some filtering configuration.  For each API matching the filtering criteria, display the API
- * on a single line (action name, verb, api name, api gw url).
- *
- * NOTE: Large action name and api name value will be truncated by their associated max size parameters.
- */
-func printFilteredListRow(resultApi *whisk.RetApi, apiPath string, apiVerb string, maxActionNameSize int, maxApiNameSize int) {
+// genFilteredRow(resultApi, api, maxApiNameSize, maxApiNameSize) generates an array of
+//      ApiFilteredRows for the purpose of ordering and printing in a list form by parsing and
+//      initializing vaules for each individual ApiFilteredRow struct.
+//      NOTE: Large action and api name values will be truncated by their associated max size parameters.
+func genFilteredRow(resultApi *whisk.RetApi, apiPath string, apiVerb string, maxActionNameSize int, maxApiNameSize int) []whisk.ApiFilteredRow {
+    var orderInfo whisk.ApiFilteredRow
+    var orderInfoArr []whisk.ApiFilteredRow
     baseUrl := strings.TrimSuffix(resultApi.BaseUrl, "/")
     apiName := resultApi.Swagger.Info.Title
+    basePath := resultApi.Swagger.BasePath
     if (resultApi.Swagger != nil && resultApi.Swagger.Paths != nil) {
         for path, _ := range resultApi.Swagger.Paths {
-            whisk.Debug(whisk.DbgInfo, "printFilteredListRow: comparing api relpath: %s\n", path)
+            whisk.Debug(whisk.DbgInfo, "genFilteredRow: comparing api relpath: %s\n", path)
             if ( len(apiPath) == 0 || path == apiPath) {
-                whisk.Debug(whisk.DbgInfo, "printFilteredListRow: relpath matches\n")
+                whisk.Debug(whisk.DbgInfo, "genFilteredRow: relpath matches\n")
                 for op, opv  := range resultApi.Swagger.Paths[path] {
-                    whisk.Debug(whisk.DbgInfo, "printFilteredListRow: comparing operation: '%s'\n", op)
+                    whisk.Debug(whisk.DbgInfo, "genFilteredRow: comparing operation: '%s'\n", op)
                     if ( len(apiVerb) == 0 || strings.ToLower(op) == strings.ToLower(apiVerb)) {
-                        whisk.Debug(whisk.DbgInfo, "printFilteredListRow: operation matches: %#v\n", opv)
+                        whisk.Debug(whisk.DbgInfo, "genFilteredRow: operation matches: %#v\n", opv)
                         var actionName string
                         if (opv.XOpenWhisk == nil) {
                             actionName = ""
@@ -626,21 +627,51 @@ func printFilteredListRow(resultApi *whisk.RetApi, apiPath string, apiVerb strin
                         } else {
                             actionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.ActionName
                         }
-                        fmt.Printf(fmtString,
-                            actionName[0 : min(len(actionName), maxActionNameSize)],
-                            op,
-                            apiName[0 : min(len(apiName), maxApiNameSize)],
-                            baseUrl+path)
+                        orderInfo = AssignRowInfo(actionName[0 : min(len(actionName), maxActionNameSize)], op, apiName[0 : min(len(apiName), maxApiNameSize)], basePath, path, baseUrl+path)
+                        orderInfo.FmtString = fmtString
+                        whisk.Debug(whisk.DbgInfo, "Appening to orderInfoArr: %s %s\n", orderInfo.RelPath)
+                        orderInfoArr = append(orderInfoArr, orderInfo)
                     }
                 }
             }
         }
     }
+    return orderInfoArr
+}
+
+// AssignRowInfo(actionName, verb, apiName, basePath, relPath, url) assigns
+//      the given vaules to and initializes an ApiFilteredRow struct, then returns it.
+func AssignRowInfo(actionName string, verb string, apiName string, basePath string, relPath string, url string) whisk.ApiFilteredRow {
+    var orderInfo whisk.ApiFilteredRow
+
+    orderInfo.ActionName = actionName
+    orderInfo.Verb = verb
+    orderInfo.ApiName = apiName
+    orderInfo.BasePath = basePath
+    orderInfo.RelPath = relPath
+    orderInfo.Url = url
+
+    return orderInfo
+}
+
+// AssignListInfo(actionName, verb, apiName, basePath, relPath, url) assigns
+//      the given vaules to and initializes an ApiFilteredList struct, then returns it.
+func AssignListInfo(actionName string, verb string, apiName string, basePath string, relPath string, url string) whisk.ApiFilteredList {
+    var orderInfo whisk.ApiFilteredList
+
+    orderInfo.ActionName = actionName
+    orderInfo.Verb = verb
+    orderInfo.ApiName = apiName
+    orderInfo.BasePath = basePath
+    orderInfo.RelPath = relPath
+    orderInfo.Url = url
+
+    return orderInfo
 }
 
 func getLargestActionNameSize(retApiArray *whisk.RetApiArray, apiPath string, apiVerb string) int {
     var maxNameSize = 0
-    for i:=0; i<len(retApiArray.Apis); i++ {
+    for i := 0; i < len(retApiArray.Apis); i++ {
         var resultApi = retApiArray.Apis[i].ApiValue
         if (resultApi.Swagger != nil && resultApi.Swagger.Paths != nil) {
             for path, _ := range resultApi.Swagger.Paths {
@@ -673,7 +704,7 @@ func getLargestActionNameSize(retApiArray *whisk.RetApiArray, apiPath string, ap
 
 func getLargestApiNameSize(retApiArray *whisk.RetApiArray, apiPath string, apiVerb string) int {
     var maxNameSize = 0
-    for i:=0; i<len(retApiArray.Apis); i++ {
+    for i := 0; i < len(retApiArray.Apis); i++ {
         var resultApi = retApiArray.Apis[i].ApiValue
         apiName := resultApi.Swagger.Info.Title
         if (resultApi.Swagger != nil && resultApi.Swagger.Paths != nil) {
@@ -920,6 +951,7 @@ func init() {
     apiGetCmd.Flags().StringVarP(&flags.common.format, "format", "", formatOptionJson, wski18n.T("Specify the API output `TYPE`, either json or yaml"))
     apiListCmd.Flags().IntVarP(&flags.common.skip, "skip", "s", 0, wski18n.T("exclude the first `SKIP` number of actions from the result"))
     apiListCmd.Flags().IntVarP(&flags.common.limit, "limit", "l", 30, wski18n.T("only return `LIMIT` number of actions from the collection"))
+    apiListCmd.Flags().BoolVarP(&flags.common.nameSort, "name-sort", "n", false, wski18n.T("sorts a list alphabetically by order of [BASE_PATH | API_NAME], API_PATH, then API_VERB; only applicable within the limit/skip returned entity block"))
     apiListCmd.Flags().BoolVarP(&flags.common.full, "full", "f", false, wski18n.T("display full description of each API"))
     apiCmd.AddCommand(
         apiCreateCmd,
diff --git a/tools/cli/go-whisk-cli/commands/flags.go b/tools/cli/go-whisk-cli/commands/flags.go
index b535af5..15f9ed4 100644
--- a/tools/cli/go-whisk-cli/commands/flags.go
+++ b/tools/cli/go-whisk-cli/commands/flags.go
@@ -61,6 +61,7 @@ type Flags struct {
         feed        string  // name of feed
         detail      bool
         format      string
+        nameSort   bool    // sorts list alphabetically by entity name
     }
 
     property struct {
diff --git a/tools/cli/go-whisk-cli/commands/namespace.go b/tools/cli/go-whisk-cli/commands/namespace.go
index f132913..4bb76f0 100644
--- a/tools/cli/go-whisk-cli/commands/namespace.go
+++ b/tools/cli/go-whisk-cli/commands/namespace.go
@@ -55,7 +55,7 @@ var namespaceListCmd = &cobra.Command{
             werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_NETWORK, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
             return werr
         }
-        printList(namespaces)
+        printList(namespaces, false) // `-n` flag applies to `namespace get`, not list, so must pass value false for printList here
         return nil
     },
 }
@@ -99,16 +99,31 @@ var namespaceGetCmd = &cobra.Command{
 
         fmt.Fprintf(color.Output, wski18n.T("Entities in namespace: {{.namespace}}\n",
             map[string]interface{}{"namespace": boldString(getClientNamespace())}))
-        printList(namespace.Contents.Packages)
-        printList(namespace.Contents.Actions)
-        printList(namespace.Contents.Triggers)
-        printList(namespace.Contents.Rules)
+        sortByName := flags.common.nameSort
+        printList(namespace.Contents.Packages, sortByName)
+        printList(namespace.Contents.Actions, sortByName)
+        printList(namespace.Contents.Triggers, sortByName)
+        //No errors, lets attempt to retrieve the status of each rule #312
+        for index, rule := range namespace.Contents.Rules {
+            ruleStatus, _, err := client.Rules.Get(rule.Name)
+            if err != nil {
+                errStr := wski18n.T("Unable to get status of rule '{{.name}}': {{.err}}",
+                    map[string]interface{}{"name": rule.Name, "err": err})
+                fmt.Println(errStr)
+                werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
+                return werr
+            }
+            namespace.Contents.Rules[index].Status = ruleStatus.Status
+        }
+        printList(namespace.Contents.Rules, sortByName)
 
         return nil
     },
 }
 
 func init() {
+    namespaceGetCmd.Flags().BoolVarP(&flags.common.nameSort, "name-sort", "n", false, wski18n.T("sorts a list alphabetically by entity name; only applicable within the limit/skip returned entity block"))
+
     namespaceCmd.AddCommand(
         namespaceListCmd,
         namespaceGetCmd,
diff --git a/tools/cli/go-whisk-cli/commands/package.go b/tools/cli/go-whisk-cli/commands/package.go
index 6721f34..1696330 100644
--- a/tools/cli/go-whisk-cli/commands/package.go
+++ b/tools/cli/go-whisk-cli/commands/package.go
@@ -412,7 +412,8 @@ var packageListCmd = &cobra.Command{
       return werr
     }
 
-    printList(packages)
+    sortByName := flags.common.nameSort
+    printList(packages, sortByName)
 
     return nil
   },
@@ -524,6 +525,7 @@ func init() {
 
   packageListCmd.Flags().IntVarP(&flags.common.skip, "skip", "s", 0, wski18n.T("exclude the first `SKIP` number of packages from the result"))
   packageListCmd.Flags().IntVarP(&flags.common.limit, "limit", "l", 30, wski18n.T("only return `LIMIT` number of packages from the collection"))
+  packageListCmd.Flags().BoolVarP(&flags.common.nameSort, "name-sort", "n", false, wski18n.T("sorts a list alphabetically by entity name; only applicable within the limit/skip returned entity block"))
 
   packageCmd.AddCommand(
     packageBindCmd,
diff --git a/tools/cli/go-whisk-cli/commands/rule.go b/tools/cli/go-whisk-cli/commands/rule.go
index 47607cd..f7e0044 100644
--- a/tools/cli/go-whisk-cli/commands/rule.go
+++ b/tools/cli/go-whisk-cli/commands/rule.go
@@ -402,7 +402,9 @@ var ruleListCmd = &cobra.Command{
                 rules[index].Status = ruleStatus.Status
             }
         }
-        printList(rules)
+
+        sortByName := flags.common.nameSort
+        printList(rules, sortByName)
         return nil
     },
 }
@@ -414,6 +416,7 @@ func init() {
 
     ruleListCmd.Flags().IntVarP(&flags.common.skip, "skip", "s", 0, wski18n.T("exclude the first `SKIP` number of rules from the result"))
     ruleListCmd.Flags().IntVarP(&flags.common.limit, "limit", "l", 30, wski18n.T("only return `LIMIT` number of rules from the collection"))
+    ruleListCmd.Flags().BoolVarP(&flags.common.nameSort, "name-sort", "n", false, wski18n.T("sorts a list alphabetically by entity name; only applicable within the limit/skip returned entity block"))
 
     ruleCmd.AddCommand(
         ruleCreateCmd,
diff --git a/tools/cli/go-whisk-cli/commands/trigger.go b/tools/cli/go-whisk-cli/commands/trigger.go
index b6f8b0b..ef9dc10 100644
--- a/tools/cli/go-whisk-cli/commands/trigger.go
+++ b/tools/cli/go-whisk-cli/commands/trigger.go
@@ -457,7 +457,8 @@ var triggerListCmd = &cobra.Command{
             werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
             return werr
         }
-        printList(triggers)
+        sortByName := flags.common.nameSort
+        printList(triggers, sortByName)
         return nil
     },
 }
@@ -510,6 +511,7 @@ func init() {
 
     triggerListCmd.Flags().IntVarP(&flags.common.skip, "skip", "s", 0, wski18n.T("exclude the first `SKIP` number of triggers from the result"))
     triggerListCmd.Flags().IntVarP(&flags.common.limit, "limit", "l", 30, wski18n.T("only return `LIMIT` number of triggers from the collection"))
+    triggerListCmd.Flags().BoolVarP(&flags.common.nameSort, "name-sort", "n", false, wski18n.T("sorts a list alphabetically by entity name; only applicable within the limit/skip returned entity block"))
 
     triggerCmd.AddCommand(
         triggerFireCmd,
diff --git a/tools/cli/go-whisk-cli/commands/util.go b/tools/cli/go-whisk-cli/commands/util.go
index afe7cbe..fed9f59 100644
--- a/tools/cli/go-whisk-cli/commands/util.go
+++ b/tools/cli/go-whisk-cli/commands/util.go
@@ -121,28 +121,99 @@ func getEscapedJSON(value string) (string) {
 func isValidJSON(value string) (bool) {
     var jsonInterface interface{}
     err := json.Unmarshal([]byte(value), &jsonInterface)
+
     return err == nil
 }
 
 var boldString = color.New(color.Bold).SprintFunc()
 
-func printList(collection interface{}) {
-    switch collection := collection.(type) {
+type Sortables []whisk.Sortable
+// Uses quickSort to sort commands based on their compare methods
+// Param: Takes in a array of Sortable interfaces which contains a specific command
+func Swap(sortables Sortables, i, j int) { sortables[i], sortables[j] = sortables[j], sortables[i] }
+
+func toPrintable(sortable []whisk.Sortable) []whisk.Printable{
+    sortedPrintable := make([]whisk.Printable, len(sortable), len(sortable))
+    for i := range sortable {
+        sortedPrintable[i] = sortable[i].(whisk.Printable)
+    }
+    return sortedPrintable
+}
+
+// Prints the parameters/list for wsk xxxx list
+// Identifies type and then copies array into an array of interfaces(Sortable) to be sorted and printed
+// Param: Takes in an interace which contains an array of a command(Ex: []Action)
+func printList(collection interface{}, sortByName bool) {
+    var commandToSort []whisk.Sortable
+    switch collection := collection.(type){
     case []whisk.Action:
-        printActionList(collection)
+        for i := range collection {
+            commandToSort = append(commandToSort, collection[i])
+        }
     case []whisk.Trigger:
-        printTriggerList(collection)
+        for i := range collection {
+            commandToSort = append(commandToSort, collection[i])
+        }
     case []whisk.Package:
-        printPackageList(collection)
+        for i := range collection {
+            commandToSort = append(commandToSort, collection[i])
+        }
     case []whisk.Rule:
-        printRuleList(collection)
+        for i := range collection {
+            commandToSort = append(commandToSort, collection[i])
+        }
     case []whisk.Namespace:
-        printNamespaceList(collection)
+        for i := range collection {
+            commandToSort = append(commandToSort, collection[i])
+        }
     case []whisk.Activation:
-        printActivationList(collection)
-    case []whisk.Api:
-        printApiList(collection)
+        for i := range collection {
+            commandToSort = append(commandToSort, collection[i])
+        }
+    case []whisk.ApiFilteredList:
+        for i := range collection {
+            commandToSort = append(commandToSort, collection[i])
+        }
+    case []whisk.ApiFilteredRow:
+        for i := range collection {
+            commandToSort = append(commandToSort, collection[i])
+        }
+    }
+    if sortByName && len(commandToSort) > 0 {
+        quickSort(commandToSort, 0, len(commandToSort)-1)
     }
+    printCommandsList(toPrintable(commandToSort), makeDefaultHeader(collection))
+}
+
+func quickSort(toSort Sortables, left int, right int) {
+    low := left
+    high := right
+    pivot := toSort[(left + right) / 2]
+
+    for low <= high {
+        for toSort[low].Compare(pivot) { low++ }
+        for pivot.Compare(toSort[high]) { high-- }
+        if low <= high {
+            Swap(toSort, low, high)
+            low++
+            high--
+        }
+    }
+    if left < high { quickSort(toSort, left, high) }
+    if low < right { quickSort(toSort, low, right) }
+}
+
+// makeDefaultHeader(collection) returns the default header to be used in case
+//      the list to be printed is empty.
+func makeDefaultHeader(collection interface{}) string {
+    defaultHeader := reflect.TypeOf(collection).String()
+    defaultHeader = strings.ToLower(defaultHeader[8:] + "s")    // Removes '[]whisk.' from `[]whisk.ENTITY_TYPE`
+    if defaultHeader == "apifilteredrows" {
+        defaultHeader = fmt.Sprintf("%-30s %7s %20s  %s", "Action", "Verb", "API Name", "URL")
+    } else if defaultHeader == "apifilteredlists" {
+        defaultHeader = ""
+    }
+    return defaultHeader
 }
 
 func printFullList(collection interface{}) {
@@ -178,53 +249,18 @@ func printSummary(collection interface{}) {
     }
 }
 
-func printActionList(actions []whisk.Action) {
-    fmt.Fprintf(color.Output, "%s\n", boldString("actions"))
-    for _, action := range actions {
-        publishState := wski18n.T("private")
-        kind := getValueString(action.Annotations, "exec")
-        fmt.Printf("%-70s %s %s\n", fmt.Sprintf("/%s/%s", action.Namespace, action.Name), publishState, kind)
-    }
-}
-
-func printTriggerList(triggers []whisk.Trigger) {
-    fmt.Fprintf(color.Output, "%s\n", boldString("triggers"))
-    for _, trigger := range triggers {
-        publishState := wski18n.T("private")
-        fmt.Printf("%-70s %s\n", fmt.Sprintf("/%s/%s", trigger.Namespace, trigger.Name), publishState)
-    }
-}
-
-func printPackageList(packages []whisk.Package) {
-    fmt.Fprintf(color.Output, "%s\n", boldString("packages"))
-    for _, xPackage := range packages {
-        publishState := wski18n.T("private")
-        if xPackage.Publish != nil && *xPackage.Publish {
-            publishState = wski18n.T("shared")
+// Used to print Action, Tigger, Package, and Rule lists
+// Param: Takes in a array of Printable interface, and the name of the command
+//          being sent to it
+// **Note**: The name sould be an empty string for APIs.
+func printCommandsList(commands []whisk.Printable, defaultHeader string) {
+    if len(commands) != 0 {
+        fmt.Fprint(color.Output, boldString(commands[0].ToHeaderString()))
+        for i := range commands {
+            fmt.Print(commands[i].ToSummaryRowString())
         }
-        fmt.Printf("%-70s %s\n", fmt.Sprintf("/%s/%s", xPackage.Namespace, xPackage.Name), publishState)
-    }
-}
-
-func printRuleList(rules []whisk.Rule) {
-    fmt.Fprintf(color.Output, "%s\n", boldString("rules"))
-    for _, rule := range rules {
-        publishState := wski18n.T("private")
-        fmt.Printf("%-70s %-20s %s\n", fmt.Sprintf("/%s/%s", rule.Namespace, rule.Name), publishState, rule.Status)
-    }
-}
-
-func printNamespaceList(namespaces []whisk.Namespace) {
-    fmt.Fprintf(color.Output, "%s\n", boldString("namespaces"))
-    for _, namespace := range namespaces {
-        fmt.Printf("%s\n", namespace.Name)
-    }
-}
-
-func printActivationList(activations []whisk.Activation) {
-    fmt.Fprintf(color.Output, "%s\n", boldString("activations"))
-    for _, activation := range activations {
-        fmt.Printf("%s %-20s\n", activation.ActivationID, activation.Name)
+    } else {
+        fmt.Fprintf(color.Output, "%s\n", boldString(defaultHeader))
     }
 }
 
@@ -235,20 +271,6 @@ func printFullActivationList(activations []whisk.Activation) {
     }
 }
 
-func printApiList(apis []whisk.Api) {
-    fmt.Fprintf(color.Output, "%s\n", boldString("apis"))
-    for _, api := range apis {
-        fmt.Printf("%s %20s %20s\n", api.ApiName, api.GatewayBasePath, api.GatewayFullPath)
-    }
-}
-
-func printFullApiList(apis []whisk.Api) {
-    fmt.Fprintf(color.Output, "%s\n", boldString("apis"))
-    for _, api := range apis {
-        printJSON(api)
-    }
-}
-
 func printActivationLogs(logs []string) {
     for _, log := range logs {
         fmt.Printf("%s\n", log)
diff --git a/tools/cli/go-whisk-cli/commands/wsk.go b/tools/cli/go-whisk-cli/commands/wsk.go
index b4bbc5b..8d520e9 100644
--- a/tools/cli/go-whisk-cli/commands/wsk.go
+++ b/tools/cli/go-whisk-cli/commands/wsk.go
@@ -44,6 +44,8 @@ func init() {
     WskCmd.SetHelpTemplate(`{{with or .Long .Short }}{{.}}
 {{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`)
 
+listCmd.Flags().BoolVarP(&flags.common.nameSort, "name-sort", "n", false, wski18n.T("sorts a list alphabetically by entity name; only applicable within the limit/skip returned entity block"))
+
     WskCmd.AddCommand(
         actionCmd,
         activationCmd,
diff --git a/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json b/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json
index 78acda0..39551d0 100644
--- a/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json
+++ b/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json
@@ -1400,8 +1400,8 @@
     "translation": "display full description of each API"
   },
   {
-    "id": "An API host must be provided.",
-    "translation": "An API host must be provided."
+    "id": "order API list by action name first followed by base-path/rel-path/verb",
+    "translation": "order API list by action name first followed by base-path/rel-path/verb."
   },
   {
     "id": "Request accepted, but processing not completed yet.",
@@ -1510,9 +1510,16 @@
   {
     "id": "Unable to output bash command completion {{.err}}",
     "translation": "Unable to output bash command completion {{.err}}"
-},
-{
+  },
+  {
     "id": "prints bash command completion script to stdout",
     "translation": "prints bash command completion script to stdout"
-}
-]
+  },
+  {
+    "id": "sorts a list alphabetically by entity name; only applicable within the limit/skip returned entity block",
+    "translation": "sorts a list alphabetically by entity name; only applicable within the limit/skip returned entity block"
+  },
+  {
+    "id": "sorts a list alphabetically by order of [BASE_PATH | API_NAME], API_PATH, then API_VERB; only applicable within the limit/skip returned entity block",
+    "translation": "sorts a list alphabetically by order of [BASE_PATH | API_NAME], API_PATH, then API_VERB; only applicable within the limit/skip returned entity block"
+  }]
diff --git a/tools/cli/go-whisk/whisk/action.go b/tools/cli/go-whisk/whisk/action.go
index a129048..8b17d23 100644
--- a/tools/cli/go-whisk/whisk/action.go
+++ b/tools/cli/go-whisk/whisk/action.go
@@ -59,6 +59,51 @@ type ActionListOptions struct {
     Docs        bool        `url:"docs,omitempty"`
 }
 
+// Compare(sortable) compares action to sortable for the purpose of sorting.
+// REQUIRED: sortable must also be of type Action.
+// ***Method of type Sortable***
+func(action Action) Compare(sortable Sortable) (bool) {
+    // Sorts alphabetically by NAMESPACE -> PACKAGE_NAME -> ACTION_NAME, with
+    //    actions under default package at the top.
+    var actionString string
+    var compareString string
+    actionToCompare := sortable.(Action)
+
+    actionString = strings.ToLower(fmt.Sprintf("%s%s", action.Namespace, action.Name))
+    compareString = strings.ToLower(fmt.Sprintf("%s%s", actionToCompare.Namespace,
+    actionToCompare.Name))
+    if strings.Contains(action.Namespace, "/") && !strings.Contains(actionToCompare.Namespace, "/") {
+        return false
+    } else if !strings.Contains(action.Namespace, "/") && strings.Contains(actionToCompare.Namespace, "/") {
+        return true
+    } else if strings.Contains(action.Namespace, "/") && strings.Contains(actionToCompare.Namespace, "/") {
+        return actionString < compareString
+    } else {
+        return action.Name < actionToCompare.Name
+    }
+}
+
+// ToHeaderString() returns the header for a list of actions
+func(action Action) ToHeaderString() string {
+    return fmt.Sprintf("%s\n", "actions")
+}
+
+// ToSummaryRowString() returns a compound string of required parameters for printing
+//   from CLI command `wsk action list`.
+// ***Method of type Sortable***
+func(action Action) ToSummaryRowString() string{
+    var kind string
+    publishState := wski18n.T("private")
+
+    for i := range action.Annotations {
+        if (action.Annotations[i].Key == "exec") {
+            kind = action.Annotations[i].Value.(string)
+            break
+        }
+    }
+    return fmt.Sprintf("%-70s %s %s\n", fmt.Sprintf("/%s/%s", action.Namespace, action.Name), publishState, kind)
+}
+
 /*
 Determines if an action is a web action by examining the action's annotations. A value of true is returned if the
 action's annotations contains a "web-export" key and its associated value is a boolean value of "true". Otherwise, false
diff --git a/tools/cli/go-whisk/whisk/activation.go b/tools/cli/go-whisk/whisk/activation.go
index 84308f3..c36cdbe 100644
--- a/tools/cli/go-whisk/whisk/activation.go
+++ b/tools/cli/go-whisk/whisk/activation.go
@@ -70,6 +70,26 @@ type Log struct {
     Time   string `json:"time,omitempty"`
 }
 
+// Compare(sortable) compares activation to sortable for the purpose of sorting.
+// REQUIRED: sortable must also be of type Activation.
+// ***Method of type Sortable***
+// ***Currently, no method of sorting defined***
+func(activation Activation) Compare(sortable Sortable) (bool) {
+    return true
+}
+
+// ToHeaderString() returns the header for a list of activations
+func(activation Activation) ToHeaderString() string {
+    return fmt.Sprintf("%s\n", "activations")
+}
+
+// ToSummaryRowString() returns a compound string of required parameters for printing
+//   from CLI command `wsk activation list`.
+// ***Method of type Sortable***
+func(activation Activation) ToSummaryRowString() string {
+    return fmt.Sprintf("%s %-20s\n", activation.ActivationID, activation.Name)
+}
+
 func (s *ActivationService) List(options *ActivationListOptions) ([]Activation, *http.Response, error) {
     // TODO :: for some reason /activations only works with "_" as namespace
     s.client.Namespace = "_"
diff --git a/tools/cli/go-whisk/whisk/api.go b/tools/cli/go-whisk/whisk/api.go
index 474ad2a..cc11118 100644
--- a/tools/cli/go-whisk/whisk/api.go
+++ b/tools/cli/go-whisk/whisk/api.go
@@ -21,6 +21,8 @@ import (
     "net/http"
     "errors"
     "../wski18n"
+    "strings"
+    "fmt"
 )
 
 type ApiService struct {
@@ -143,6 +145,27 @@ type ApiSwaggerOpXOpenWhisk struct {
     ApiUrl          string    `json:"url"`
 }
 
+// Used for printing individual APIs in non-truncated form
+type ApiFilteredList struct {
+    ActionName      string
+    ApiName         string
+    BasePath        string
+    RelPath         string
+    Verb            string
+    Url             string
+}
+
+// Used for printing individual APIs in truncated form
+type ApiFilteredRow struct {
+    ActionName      string
+    ApiName         string
+    BasePath        string
+    RelPath         string
+    Verb            string
+    Url             string
+    FmtString       string
+}
+
 var ApiVerbs map[string]bool = map[string]bool {
     "GET": true,
     "PUT": true,
@@ -162,6 +185,70 @@ const (
 // Api Methods //
 /////////////////
 
+// Compare(sortable) compares api to sortable for the purpose of sorting.
+// REQUIRED: sortable must also be of type ApiFilteredList.
+// ***Method of type Sortable***
+func(api ApiFilteredList) Compare(sortable Sortable) (bool) {
+    // Sorts alphabetically by [BASE_PATH | API_NAME] -> REL_PATH -> API_VERB
+    apiToCompare := sortable.(ApiFilteredList)
+    var apiString string
+    var compareString string
+
+    apiString = strings.ToLower(fmt.Sprintf("%s%s%s",api.BasePath, api.RelPath,
+        api.Verb))
+    compareString = strings.ToLower(fmt.Sprintf("%s%s%s", apiToCompare.BasePath,
+        apiToCompare.RelPath, apiToCompare.Verb))
+
+    return apiString < compareString
+}
+
+// ToHeaderString() returns the header for a list of apis
+func(api ApiFilteredList) ToHeaderString() string {
+    return ""
+}
+
+// ToSummaryRowString() returns a compound string of required parameters for printing
+//   from CLI command `wsk api list` or `wsk api-experimental list`.
+// ***Method of type Sortable***
+func(api ApiFilteredList) ToSummaryRowString() string {
+    return fmt.Sprintf("%s %s %s %s %s %s",
+        fmt.Sprintf("%s: %s\n", wski18n.T("Action"), api.ActionName),
+        fmt.Sprintf("  %s: %s\n", wski18n.T("API Name"), api.ApiName),
+        fmt.Sprintf("  %s: %s\n", wski18n.T("Base path"), api.BasePath),
+        fmt.Sprintf("  %s: %s\n", wski18n.T("Path"), api.RelPath),
+        fmt.Sprintf("  %s: %s\n", wski18n.T("Verb"), api.Verb),
+        fmt.Sprintf("  %s: %s\n", wski18n.T("URL"), api.Url))
+}
+
+// Compare(sortable) compares api to sortable for the purpose of sorting.
+// REQUIRED: sortable must also be of type ApiFilteredRow.
+// ***Method of type Sortable***
+func(api ApiFilteredRow) Compare(sortable Sortable) (bool) {
+    // Sorts alphabetically by [BASE_PATH | API_NAME] -> REL_PATH -> API_VERB
+    var apiString string
+    var compareString string
+    apiToCompare := sortable.(ApiFilteredRow)
+
+    apiString = strings.ToLower(fmt.Sprintf("%s%s%s",api.BasePath, api.RelPath,
+        api.Verb))
+    compareString = strings.ToLower(fmt.Sprintf("%s%s%s", apiToCompare.BasePath,
+        apiToCompare.RelPath, apiToCompare.Verb))
+
+    return apiString < compareString
+}
+
+// ToHeaderString() returns the header for a list of apis
+func(api ApiFilteredRow) ToHeaderString() string {
+    return fmt.Sprintf("%s", fmt.Sprintf(api.FmtString, "Action", "Verb", "API Name", "URL"))
+}
+
+// ToSummaryRowString() returns a compound string of required parameters for printing
+//   from CLI command `wsk api list -f` or `wsk api-experimental list -f`.
+// ***Method of type Sortable***
+func(api ApiFilteredRow) ToSummaryRowString() string {
+  return fmt.Sprintf(api.FmtString, api.ActionName, api.Verb, api.ApiName, api.Url)
+}
+
 func (s *ApiService) List(apiListOptions *ApiListRequestOptions) (*ApiListResponse, *http.Response, error) {
     route := "web/whisk.system/apimgmt/getApi.http"
 
@@ -315,7 +402,7 @@ func (s *ApiService) Delete(api *ApiDeleteRequest, options *ApiDeleteRequestOpti
 
 
 func validateApiListResponse(apiList *ApiListResponse) error {
-    for i:=0; i<len(apiList.Apis); i++ {
+    for i := 0; i < len(apiList.Apis); i++ {
         if apiList.Apis[i].ApiValue == nil {
             Debug(DbgError, "validateApiResponse: No value stanza in api %v\n", apiList.Apis[i])
             errMsg := wski18n.T("Internal error. Missing value stanza in API configuration response")
diff --git a/tools/cli/go-whisk/whisk/namespace.go b/tools/cli/go-whisk/whisk/namespace.go
index 9583f01..af99d79 100644
--- a/tools/cli/go-whisk/whisk/namespace.go
+++ b/tools/cli/go-whisk/whisk/namespace.go
@@ -21,6 +21,8 @@ import (
     "net/http"
     "errors"
     "../wski18n"
+    "strings"
+    "fmt"
 )
 
 type Namespace struct {
@@ -39,6 +41,33 @@ type NamespaceService struct {
     client *Client
 }
 
+// Compare(sortable) compares namespace to sortable for the purpose of sorting.
+// REQUIRED: sortable must also be of type Namespace.
+// ***Method of type Sortable***
+func(namespace Namespace) Compare(sortable Sortable) (bool) {
+    // Sorts alphabetically
+    namespaceToCompare := sortable.(Namespace)
+    var namespaceString string
+    var compareString string
+
+    namespaceString = strings.ToLower(namespace.Name)
+    compareString = strings.ToLower(namespaceToCompare.Name)
+
+    return namespaceString < compareString
+}
+
+// ToHeaderString() returns the header for a list of namespaces
+func(namespace Namespace) ToHeaderString() string {
+    return fmt.Sprintf("%s\n", "namespaces")
+}
+
+// ToSummaryRowString() returns a compound string of required parameters for printing
+//   from CLI command `wsk namespace list`.
+// ***Method of type Sortable***
+func(namespace Namespace) ToSummaryRowString() string {
+    return fmt.Sprintf("%s\n", namespace.Name)
+}
+
 // get a list of available namespaces
 func (s *NamespaceService) List() ([]Namespace, *http.Response, error) {
     // make a request to c.BaseURL / namespaces
diff --git a/tools/cli/go-whisk/whisk/package.go b/tools/cli/go-whisk/whisk/package.go
index 05247b1..280807c 100644
--- a/tools/cli/go-whisk/whisk/package.go
+++ b/tools/cli/go-whisk/whisk/package.go
@@ -23,6 +23,7 @@ import (
     "net/url"
     "errors"
     "../wski18n"
+    "strings"
 )
 
 type PackageService struct {
@@ -46,6 +47,7 @@ type Package struct {
     Actions     []Action            `json:"actions,omitempty"`
     Feeds       []Action            `json:"feeds,omitempty"`
 }
+
 func (p *Package) GetName() string {
     return p.Name
 }
@@ -84,6 +86,43 @@ type PackageListOptions struct {
     Docs        bool                `url:"docs,omitempty"`
 }
 
+// Compare(sortable) compares xPackage to sortable for the purpose of sorting.
+// REQUIRED: sortable must also be of type Package.
+// ***Method of type Sortable***
+func(xPackage Package) Compare(sortable Sortable) (bool) {
+    // Sorts alphabetically by NAMESPACE -> PACKAGE_NAME
+    packageToCompare := sortable.(Package)
+
+    var packageString string
+    var compareString string
+
+    packageString = strings.ToLower(fmt.Sprintf("%s%s",xPackage.Namespace,
+        xPackage.Name))
+    compareString = strings.ToLower(fmt.Sprintf("%s%s", packageToCompare.Namespace,
+        packageToCompare.Name))
+
+    return packageString < compareString
+}
+
+// ToHeaderString() returns the header for a list of actions
+func(pkg Package) ToHeaderString() string {
+    return fmt.Sprintf("%s\n", "packages")
+}
+
+// ToSummaryRowString() returns a compound string of required parameters for printing
+//   from CLI command `wsk package list`.
+// ***Method of type Sortable***
+func(xPackage Package) ToSummaryRowString() string{
+    publishState := wski18n.T("private")
+
+    if xPackage.Publish != nil && *xPackage.Publish {
+        publishState = wski18n.T("shared")
+    }
+
+    return fmt.Sprintf("%-70s %s\n", fmt.Sprintf("/%s/%s", xPackage.Namespace,
+        xPackage.Name), publishState)
+}
+
 func (s *PackageService) List(options *PackageListOptions) ([]Package, *http.Response, error) {
     route := fmt.Sprintf("packages")
     routeUrl, err := addRouteOptions(route, options)
diff --git a/tools/cli/go-whisk/whisk/rule.go b/tools/cli/go-whisk/whisk/rule.go
index 59d41c2..0180f2a 100644
--- a/tools/cli/go-whisk/whisk/rule.go
+++ b/tools/cli/go-whisk/whisk/rule.go
@@ -38,7 +38,6 @@ type Rule struct {
     Trigger interface{} `json:"trigger"`
     Action  interface{} `json:"action"`
     Publish *bool       `json:"publish,omitempty"`
-
 }
 
 type RuleListOptions struct {
@@ -47,6 +46,37 @@ type RuleListOptions struct {
     Docs        bool    `url:"docs,omitempty"`
 }
 
+// Compare(sortable) compares rule to sortable for the purpose of sorting.
+// REQUIRED: sortable must also be of type Rule.
+// ***Method of type Sortable***
+func(rule Rule) Compare(sortable Sortable) (bool) {
+    // Sorts alphabetically by NAMESPACE -> PACKAGE_NAME
+    ruleToCompare := sortable.(Rule)
+    var ruleString string
+    var compareString string
+
+    ruleString = strings.ToLower(fmt.Sprintf("%s%s",rule.Namespace, rule.Name))
+    compareString = strings.ToLower(fmt.Sprintf("%s%s", ruleToCompare.Namespace,
+        ruleToCompare.Name))
+
+    return ruleString < compareString
+}
+
+// ToHeaderString() returns the header for a list of rules
+func(rule Rule) ToHeaderString() string {
+    return fmt.Sprintf("%s\n", "rules")
+}
+
+// ToSummaryRowString() returns a compound string of required parameters for printing
+//   from CLI command `wsk rule list`.
+// ***Method of type Sortable***
+func(rule Rule) ToSummaryRowString() string{
+    publishState := wski18n.T("private")
+
+    return fmt.Sprintf("%-70s %-20s %s\n", fmt.Sprintf("/%s/%s", rule.Namespace,
+        rule.Name), publishState, rule.Status)
+}
+
 func (s *RuleService) List(options *RuleListOptions) ([]Rule, *http.Response, error) {
     route := "rules"
     routeUrl, err := addRouteOptions(route, options)
diff --git a/tools/cli/go-whisk/whisk/trigger.go b/tools/cli/go-whisk/whisk/trigger.go
index 9540398..26b115b 100644
--- a/tools/cli/go-whisk/whisk/trigger.go
+++ b/tools/cli/go-whisk/whisk/trigger.go
@@ -23,6 +23,7 @@ import (
     "errors"
     "net/url"
     "../wski18n"
+    "strings"
 )
 
 type TriggerService struct {
@@ -38,7 +39,6 @@ type Trigger struct {
     Parameters      KeyValueArr     `json:"parameters,omitempty"`
     Limits          *Limits         `json:"limits,omitempty"`
     Publish         *bool           `json:"publish,omitempty"`
-
 }
 
 type TriggerListOptions struct {
@@ -47,6 +47,38 @@ type TriggerListOptions struct {
     Docs            bool            `url:"docs,omitempty"`
 }
 
+// Compare(sortable) compares trigger to sortable for the purpose of sorting.
+// REQUIRED: sortable must also be of type Trigger.
+// ***Method of type Sortable***
+func(trigger Trigger) Compare(sortable Sortable) (bool) {
+    // Sorts alphabetically by NAMESPACE -> TRIGGER_NAME
+    triggerToCompare := sortable.(Trigger)
+    var triggerString string
+    var compareString string
+
+    triggerString = strings.ToLower(fmt.Sprintf("%s%s",trigger.Namespace,
+        trigger.Name))
+    compareString = strings.ToLower(fmt.Sprintf("%s%s", triggerToCompare.Namespace,
+        triggerToCompare.Name))
+
+    return triggerString < compareString
+}
+
+// ToHeaderString() returns the header for a list of triggers
+func(trigger Trigger) ToHeaderString() string {
+    return fmt.Sprintf("%s\n", "triggers")
+}
+
+// ToSummaryRowString() returns a compound string of required parameters for printing
+//   from CLI command `wsk trigger list`.
+// ***Method of type Sortable***
+func(trigger Trigger) ToSummaryRowString() string {
+    publishState := wski18n.T("private")
+
+    return fmt.Sprintf("%-70s %s\n", fmt.Sprintf("/%s/%s", trigger.Namespace,
+        trigger.Name), publishState)
+}
+
 func (s *TriggerService) List(options *TriggerListOptions) ([]Trigger, *http.Response, error) {
     route := "triggers"
     routeUrl, err := addRouteOptions(route, options)
diff --git a/tools/cli/go-whisk/whisk/util.go b/tools/cli/go-whisk/whisk/util.go
index 0757864..f29f1e0 100644
--- a/tools/cli/go-whisk/whisk/util.go
+++ b/tools/cli/go-whisk/whisk/util.go
@@ -29,6 +29,21 @@ import (
     "../wski18n"
 )
 
+// Sortable items are anything that needs to be sorted for listing purposes.
+type Sortable interface {
+    // Compare(sortable) compares an two sortables and returns true
+    //      if the item calling the Compare method is less than toBeCompared.
+    //      Sorts alphabetically by default, can have other parameters to sort by
+    //      passed by sortByName.
+    Compare(toBeCompared Sortable) (bool)
+}
+
+// Printable items are anything that need to be printed for listing purposes.
+type Printable interface {
+    ToHeaderString() string   // Prints header information of a Printable
+    ToSummaryRowString() string     // Prints summary info of one Printable
+}
+
 // addOptions adds the parameters in opt as URL query parameters to s.  opt
 // must be a struct whose fields may contain "url" tags.
 func addRouteOptions(route string, options interface{}) (*url.URL, error) {

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