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>'].