You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by be...@apache.org on 2017/07/12 17:20:42 UTC
[incubator-openwhisk] branch master updated: Get Action URL from
CLI (#2461)
This is an automated email from the ASF dual-hosted git repository.
berstler 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 b5b47d8 Get Action URL from CLI (#2461)
b5b47d8 is described below
commit b5b47d81638a821e930301628ca38afe909b7d71
Author: James Dubee <jw...@us.ibm.com>
AuthorDate: Wed Jul 12 13:20:40 2017 -0400
Get Action URL from CLI (#2461)
---
tests/src/test/scala/common/Wsk.scala | 6 ++-
.../whisk/core/cli/test/WskBasicUsageTests.scala | 58 ++++++++++++++++++++++
tools/cli/go-whisk-cli/commands/action.go | 58 +++++++++++++---------
tools/cli/go-whisk-cli/commands/flags.go | 1 +
tools/cli/go-whisk-cli/commands/util.go | 19 +------
.../go-whisk-cli/wski18n/resources/en_US.all.json | 4 ++
tools/cli/go-whisk/whisk/action.go | 49 ++++++++++++++++++
tools/cli/go-whisk/whisk/shared.go | 21 +++++++-
8 files changed, 171 insertions(+), 45 deletions(-)
diff --git a/tests/src/test/scala/common/Wsk.scala b/tests/src/test/scala/common/Wsk.scala
index df632db..70d2066 100644
--- a/tests/src/test/scala/common/Wsk.scala
+++ b/tests/src/test/scala/common/Wsk.scala
@@ -168,12 +168,14 @@ trait ListOrGetFromCollection extends FullyQualifiedNames {
name: String,
expectedExitCode: Int = SUCCESS_EXIT,
summary: Boolean = false,
- fieldFilter: Option[String] = None)(
+ fieldFilter: Option[String] = None,
+ url: Option[Boolean] = None)(
implicit wp: WskProps): RunResult = {
val params = Seq(noun, "get", "--auth", wp.authKey) ++
Seq(fqn(name)) ++
{ if (summary) Seq("--summary") else Seq() } ++
- { fieldFilter map { f => Seq(f) } getOrElse Seq() }
+ { fieldFilter map { f => Seq(f) } getOrElse Seq() } ++
+ { url map { u => Seq("--url") } getOrElse Seq() }
cli(wp.overrides ++ params, expectedExitCode)
}
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 a5cf39c..6e06f71 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
@@ -21,6 +21,7 @@ import java.io.File
import java.io.BufferedWriter
import java.io.FileWriter
import java.time.Instant
+import java.net.URLEncoder
import scala.language.postfixOps
import scala.concurrent.duration.Duration
@@ -695,6 +696,63 @@ class WskBasicUsageTests
}
}
+ it should "get an action URL" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val actionName = "action name@_-."
+ val packageName = "package name@_-."
+ val defaultPackageName = "default"
+ val webActionName = "web action name@_-."
+ val nonExistentActionName = "non-existence action"
+ val packagedAction = s"$packageName/$actionName"
+ val packagedWebAction = s"$packageName/$webActionName"
+ val (user, namespace) = WskAdmin.getUser(wskprops.authKey)
+ val encodedActionName = URLEncoder.encode(actionName, "UTF-8").replace("+", "%20")
+ val encodedPackageName = URLEncoder.encode(packageName, "UTF-8").replace("+", "%20")
+ val encodedWebActionName = URLEncoder.encode(webActionName, "UTF-8").replace("+", "%20")
+ val encodedNamespace = URLEncoder.encode(namespace, "UTF-8").replace("+", "%20")
+ val actionPath = "https://%s/api/%s/namespaces/%s/actions/%s"
+ val packagedActionPath = s"$actionPath/%s"
+ val webActionPath = "https://%s/api/%s/web/%s/%s/%s"
+
+ assetHelper.withCleaner(wsk.action, actionName) {
+ (action, _) => action.create(actionName, defaultAction)
+ }
+
+ assetHelper.withCleaner(wsk.action, webActionName) {
+ (action, _) => action.create(webActionName, defaultAction, web = Some("true"))
+ }
+
+ assetHelper.withCleaner(wsk.pkg, packageName) {
+ (pkg, _) => pkg.create(packageName)
+ }
+
+ assetHelper.withCleaner(wsk.action, packagedAction) {
+ (action, _) => action.create(packagedAction, defaultAction)
+ }
+
+ assetHelper.withCleaner(wsk.action, packagedWebAction) {
+ (action, _) => action.create(packagedWebAction, defaultAction, web = Some("true"))
+ }
+
+ wsk.action.get(actionName, url = Some(true)).
+ stdout should include(actionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedActionName))
+
+ // Ensure url flag works when a field filter and summary flag are specified
+ wsk.action.get(actionName, url = Some(true), fieldFilter = Some("field"), summary = true).
+ stdout should include(actionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedActionName))
+
+ wsk.action.get(webActionName, url = Some(true)).
+ stdout should include(webActionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, defaultPackageName, encodedWebActionName))
+
+ wsk.action.get(packagedAction, url = Some(true)).
+ stdout should include(packagedActionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedPackageName, encodedActionName))
+
+ wsk.action.get(packagedWebAction, url = Some(true)).
+ stdout should include(webActionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedPackageName, encodedWebActionName))
+
+ wsk.action.get(nonExistentActionName, url = Some(true), expectedExitCode = NOT_FOUND)
+ }
+
behavior of "Wsk packages"
it should "create, and delete a package" in {
diff --git a/tools/cli/go-whisk-cli/commands/action.go b/tools/cli/go-whisk-cli/commands/action.go
index 20a7926..97e361f 100644
--- a/tools/cli/go-whisk-cli/commands/action.go
+++ b/tools/cli/go-whisk-cli/commands/action.go
@@ -33,13 +33,13 @@ import (
"github.com/mattn/go-colorable"
)
-const MEMORY_LIMIT = 256
-const TIMEOUT_LIMIT = 60000
-const LOGSIZE_LIMIT = 10
-const ACTIVATION_ID = "activationId"
-const WEB_EXPORT_ANNOT = "web-export"
-const RAW_HTTP_ANNOT = "raw-http"
-const FINAL_ANNOT = "final"
+const MEMORY_LIMIT = 256
+const TIMEOUT_LIMIT = 60000
+const LOGSIZE_LIMIT = 10
+const ACTIVATION_ID = "activationId"
+const WEB_EXPORT_ANNOT = "web-export"
+const RAW_HTTP_ANNOT = "raw-http"
+const FINAL_ANNOT = "final"
var actionCmd = &cobra.Command{
Use: "action",
@@ -195,7 +195,7 @@ func handleInvocationResponse(
}
var actionGetCmd = &cobra.Command{
- Use: "get ACTION_NAME [FIELD_FILTER]",
+ Use: "get ACTION_NAME [FIELD_FILTER | --summary | --url]",
Short: wski18n.T("get action"),
SilenceUsage: true,
SilenceErrors: true,
@@ -210,7 +210,7 @@ var actionGetCmd = &cobra.Command{
return whiskErr
}
- if len(args) > 1 {
+ if !flags.action.url && !flags.common.summary && len(args) > 1 {
field = args[1]
if !fieldExists(&whisk.Action{}, field) {
@@ -228,7 +228,13 @@ var actionGetCmd = &cobra.Command{
return actionGetError(qualifiedName.entityName, err)
}
- if flags.common.summary {
+ if flags.action.url {
+ actionURL := action.ActionURL(Properties.APIHost,
+ DefaultOpenWhiskApiPath,
+ Properties.APIVersion,
+ qualifiedName.packageName)
+ printActionGetWithURL(qualifiedName.entity, actionURL)
+ } else if flags.common.summary {
printSummary(action)
} else {
if len(field) > 0 {
@@ -822,6 +828,17 @@ func printActionGetWithField(entityName string, field string, action *whisk.Acti
printField(action, field)
}
+func printActionGetWithURL(entityName string, actionURL string) {
+ fmt.Fprintf(
+ color.Output,
+ wski18n.T("{{.ok}} got action {{.name}}\n",
+ map[string]interface{}{
+ "ok": color.GreenString("ok:"),
+ "name": boldString(entityName),
+ }))
+ fmt.Println(actionURL)
+}
+
func printActionGet(entityName string, action *whisk.Action) {
fmt.Fprintf(
color.Output,
@@ -846,7 +863,7 @@ func printActionDeleted(entityName string) {
}
// Check if the specified action is a web-action
-func isWebAction(client *whisk.Client, qname QualifiedName) error {
+func isWebAction(client *whisk.Client, qname QualifiedName) (error) {
var err error = nil
savedNs := client.Namespace
@@ -854,6 +871,7 @@ func isWebAction(client *whisk.Client, qname QualifiedName) error {
fullActionName := "/" + qname.namespace + "/" + qname.entityName
action, _, err := client.Actions.Get(qname.entityName)
+
if err != nil {
whisk.Debug(whisk.DbgError, "client.Actions.Get(%s) error: %s\n", fullActionName, err)
whisk.Debug(whisk.DbgError, "Unable to obtain action '%s' for web action validation\n", fullActionName)
@@ -864,23 +882,14 @@ func isWebAction(client *whisk.Client, qname QualifiedName) error {
} else {
err = errors.New(wski18n.T("Action '{{.name}}' is not a web action. Issue 'wsk action update {{.name}} --web true' to convert the action to a web action.",
map[string]interface{}{"name": fullActionName}))
- weVal := getValue(action.Annotations, "web-export")
- if (weVal == nil) {
- whisk.Debug(whisk.DbgError, "getValue(annotations, web-export) for action %s found no value\n", fullActionName)
- } else {
- var webExport bool
- var ok bool
- if webExport, ok = weVal.(bool); !ok {
- whisk.Debug(whisk.DbgError, "web-export annotation value (%v) is not a boolean\n", weVal)
- } else if !webExport {
- whisk.Debug(whisk.DbgError, "web-export annotation value is false\n", weVal)
- } else {
- err = nil
- }
+
+ if action.WebAction() {
+ err = nil
}
}
client.Namespace = savedNs
+
return err
}
@@ -921,6 +930,7 @@ func init() {
actionInvokeCmd.Flags().BoolVarP(&flags.action.result, "result", "r", false, wski18n.T("blocking invoke; show only activation result (unless there is a failure)"))
actionGetCmd.Flags().BoolVarP(&flags.common.summary, "summary", "s", false, wski18n.T("summarize action details"))
+ actionGetCmd.Flags().BoolVarP(&flags.action.url, "url", "r", false, wski18n.T("get action url"))
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"))
diff --git a/tools/cli/go-whisk-cli/commands/flags.go b/tools/cli/go-whisk-cli/commands/flags.go
index 19c9cbc..dd0645b 100644
--- a/tools/cli/go-whisk-cli/commands/flags.go
+++ b/tools/cli/go-whisk-cli/commands/flags.go
@@ -126,6 +126,7 @@ type ActionFlags struct {
result bool
kind string
main string
+ url bool
}
func IsVerbose() bool {
diff --git a/tools/cli/go-whisk-cli/commands/util.go b/tools/cli/go-whisk-cli/commands/util.go
index 1871727..d8cea19 100644
--- a/tools/cli/go-whisk-cli/commands/util.go
+++ b/tools/cli/go-whisk-cli/commands/util.go
@@ -487,26 +487,11 @@ func getKeys(keyValueArr whisk.KeyValueArr) ([]string) {
return res
}
-func getValue(keyValueArr whisk.KeyValueArr, key string) (interface{}) {
- var res interface{}
-
- for i := 0; i < len(keyValueArr); i++ {
- if keyValueArr[i].Key == key {
- res = keyValueArr[i].Value
- break;
- }
- }
-
- whisk.Debug(whisk.DbgInfo, "Got value '%v' from '%v' for key '%s'\n", res, keyValueArr, key)
-
- return res
-}
-
func getValueString(keyValueArr whisk.KeyValueArr, key string) (string) {
var value interface{}
var res string
- value = getValue(keyValueArr, key)
+ value = keyValueArr.GetValue(key)
castedValue, canCast := value.(string)
if (canCast) {
@@ -522,7 +507,7 @@ func getChildValues(keyValueArr whisk.KeyValueArr, key string, childKey string)
var value interface{}
var res []interface{}
- value = getValue(keyValueArr, key)
+ value = keyValueArr.GetValue(key)
castedValue, canCast := value.([]interface{})
if canCast {
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 ec409ad..bcc776d 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
@@ -1466,5 +1466,9 @@
{
"id": "An entity name, '{{.name}}', was provided instead of a namespace. Valid namespaces are of the following format: /NAMESPACE.",
"translation": "An entity name, '{{.name}}', was provided instead of a namespace. Valid namespaces are of the following format: /NAMESPACE."
+ },
+ {
+ "id": "get action url",
+ "translation": "get action url"
}
]
diff --git a/tools/cli/go-whisk/whisk/action.go b/tools/cli/go-whisk/whisk/action.go
index 101f6d8..d370acf 100644
--- a/tools/cli/go-whisk/whisk/action.go
+++ b/tools/cli/go-whisk/whisk/action.go
@@ -22,6 +22,8 @@ import (
"net/http"
"errors"
"net/url"
+ "strings"
+
"../wski18n"
)
@@ -57,6 +59,53 @@ type ActionListOptions struct {
Docs bool `url:"docs,omitempty"`
}
+/*
+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
+is returned.
+ */
+func (action Action) WebAction() (webExportValue bool) {
+ webExport := action.Annotations.GetValue("web-export")
+ webExportValue, _ = webExport.(bool)
+
+ Debug(DbgInfo, "Web export value is '%t'\n", webExportValue)
+
+ return webExportValue
+}
+
+/*
+Returns the URL of an action as a string. A valid API host, path and version must be passed. A package that contains the
+action must be passed as well. An empty string must be passed if the action is not packaged.
+ */
+func (action Action) ActionURL(apiHost string, apiPath string, apiVersion string, pkg string) (actionURL string) {
+ webActionPath := "https://%s%s/%s/web/%s/%s/%s"
+ actionPath := "https://%s%s/%s/namespaces/%s/actions/%s"
+ packagedActionPath := actionPath + "/%s"
+ namespace := strings.Split(action.Namespace, "/")[0]
+ namespace = strings.Replace(url.QueryEscape(namespace), "+", "%20", -1)
+ name := strings.Replace(url.QueryEscape(action.Name), "+", "%20", -1)
+ pkg = strings.Replace(url.QueryEscape(pkg), "+", "%20", -1)
+
+ if action.WebAction() {
+ if len(pkg) == 0 {
+ pkg = "default"
+ }
+
+ actionURL = fmt.Sprintf(webActionPath, apiHost, apiPath, apiVersion, namespace, pkg, name)
+ Debug(DbgInfo, "Web action URL: %s\n", actionURL)
+ } else {
+ if len(pkg) == 0 {
+ actionURL = fmt.Sprintf(actionPath, apiHost, apiPath, apiVersion, namespace, name)
+ Debug(DbgInfo, "Packaged action URL: %s\n", actionURL)
+ } else {
+ actionURL = fmt.Sprintf(packagedActionPath, apiHost, apiPath, apiVersion, namespace, pkg, name)
+ Debug(DbgInfo, "Action URL: %s\n", actionURL)
+ }
+ }
+
+ return actionURL
+}
+
////////////////////
// Action Methods //
////////////////////
diff --git a/tools/cli/go-whisk/whisk/shared.go b/tools/cli/go-whisk/whisk/shared.go
index a39722f..f91787d 100644
--- a/tools/cli/go-whisk/whisk/shared.go
+++ b/tools/cli/go-whisk/whisk/shared.go
@@ -20,12 +20,29 @@ package whisk
import "encoding/json"
type KeyValue struct {
- Key string `json:"key"`
- Value interface{} `json:"value"`
+ Key string `json:"key"`
+ Value interface{} `json:"value"`
}
type KeyValueArr []KeyValue
+/*
+Retrieves a value associated with a given key from a KeyValueArr. A key of type string must be passed to the method.
+An interface will be returned containing the found value. If a key could not be found, a nil value will be returned.
+ */
+func (keyValueArr KeyValueArr) GetValue(key string) (res interface{}) {
+ for i := 0; i < len(keyValueArr); i++ {
+ if keyValueArr[i].Key == key {
+ res = keyValueArr[i].Value
+ break;
+ }
+ }
+
+ Debug(DbgInfo, "Got value '%v' for key '%s' from '%v'\n", res, key, keyValueArr)
+
+ return res
+}
+
type Annotations []map[string]interface{}
type Parameters *json.RawMessage
--
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <co...@openwhisk.apache.org>'].