Skip to content

Commit

Permalink
Allow FQN with 3 parts (namespace/package/action) to be input with/wi…
Browse files Browse the repository at this point in the history
…thout leading slash in CLI (apache#2424)
  • Loading branch information
underwoodb-sd-ibm authored and rabbah committed Aug 11, 2017
1 parent 93f6fdc commit 5de0820
Show file tree
Hide file tree
Showing 14 changed files with 482 additions and 371 deletions.
19 changes: 11 additions & 8 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ You can create your own namespaces if you're entitled to do so. The `/whisk.syst
### Fully qualified names

The fully qualified name of an entity is
`/namespaceName[/packageName]/entityName`. Notice that `/` is used to delimit namespaces, packages, and entities. Also, namespaces must be prefixed with a `/`.
`/namespaceName[/packageName]/entityName`. Notice that `/` is used to delimit namespaces, packages, and entities.

If the fully qualified name has three parts:
`/namespaceName/packageName/entityName`, then the namespace can be entered without a prefixed `/`; otherwise, namespaces must be prefixed with a `/`.

For convenience, the namespace can be left off if it is the user's *default namespace*.

Expand Down Expand Up @@ -219,23 +222,23 @@ The following packages are available to be used in the Node.js 6.9.1 environment
- [glob v7.1.1](https://www.npmjs.com/package/glob) - Match files using the patterns the shell uses, like stars and stuff.
- [gm v1.23.0](https://www.npmjs.com/package/gm) - GraphicsMagick and ImageMagick for Node.
- [lodash v4.17.2](https://www.npmjs.com/package/lodash) - The Lodash library exported as Node.js modules.
- [log4js v0.6.38](https://www.npmjs.com/package/log4js) - This is a conversion of the log4js framework to work with Node.
- [log4js v0.6.38](https://www.npmjs.com/package/log4js) - This is a conversion of the log4js framework to work with Node.
- [iconv-lite v0.4.15](https://www.npmjs.com/package/iconv-lite) - Pure JS character encoding conversion
- [marked v0.3.6](https://www.npmjs.com/package/marked) - A full-featured markdown parser and compiler, written in JavaScript. Built for speed.
- [merge v1.2.0](https://www.npmjs.com/package/merge) - Merge multiple objects into one, optionally creating a new cloned object.
- [merge v1.2.0](https://www.npmjs.com/package/merge) - Merge multiple objects into one, optionally creating a new cloned object.
- [moment v2.17.0](https://www.npmjs.com/package/moment) - A lightweight JavaScript date library for parsing, validating, manipulating, and formatting dates.
- [mongodb v2.2.11](https://www.npmjs.com/package/mongodb) - The official MongoDB driver for Node.js.
- [mustache v2.3.0](https://www.npmjs.com/package/mustache) - mustache.js is an implementation of the mustache template system in JavaScript.
- [nano v6.2.0](https://www.npmjs.com/package/nano) - minimalistic couchdb driver for Node.js.
- [node-uuid v1.4.7](https://www.npmjs.com/package/node-uuid) - Deprecated UUID packaged.
- [node-uuid v1.4.7](https://www.npmjs.com/package/node-uuid) - Deprecated UUID packaged.
- [nodemailer v2.6.4](https://www.npmjs.com/package/nodemailer) - Send e-mails from Node.js – easy as cake!
- [oauth2-server v2.4.1](https://www.npmjs.com/package/oauth2-server) - Complete, compliant and well tested module for implementing an OAuth2 Server/Provider with express in Node.js.
- [openwhisk v3.3.2](https://www.npmjs.com/package/openwhisk) - JavaScript client library for the OpenWhisk platform. Provides a wrapper around the OpenWhisk APIs.
- [pkgcloud v1.4.0](https://www.npmjs.com/package/pkgcloud) - pkgcloud is a standard library for Node.js that abstracts away differences among multiple cloud providers.
- [process v0.11.9](https://www.npmjs.com/package/process) - require('process'); just like any other module.
- [pug v2.0.0-beta6](https://www.npmjs.com/package/pug) - Implements the Pug templating language.
- [redis v2.6.3](https://www.npmjs.com/package/redis) - This is a complete and feature rich Redis client for Node.js.
- [request v2.79.0](https://www.npmjs.com/package/request) - Request is designed to be the simplest way possible to make HTTP calls.
- [redis v2.6.3](https://www.npmjs.com/package/redis) - This is a complete and feature rich Redis client for Node.js.
- [request v2.79.0](https://www.npmjs.com/package/request) - Request is designed to be the simplest way possible to make HTTP calls.
- [request-promise v4.1.1](https://www.npmjs.com/package/request-promise) - The simplified HTTP request client 'request' with Promise support. Powered by Bluebird.
- [rimraf v2.5.4](https://www.npmjs.com/package/rimraf) - The UNIX command rm -rf for node.
- [semver v5.3.0](https://www.npmjs.com/package/semver) - Supports semantic versioning.
Expand All @@ -251,10 +254,10 @@ The following packages are available to be used in the Node.js 6.9.1 environment
- [uuid v3.0.0](https://www.npmjs.com/package/uuid) - Simple, fast generation of RFC4122 UUIDS.
- [validator v6.1.0](https://www.npmjs.com/package/validator) - A library of string validators and sanitizers.
- [watson-developer-cloud v2.29.0](https://www.npmjs.com/package/watson-developer-cloud) - Node.js client library to use the Watson Developer Cloud services, a collection of APIs that use cognitive computing to solve complex problems.
- [when v3.7.7](https://www.npmjs.com/package/when) - When.js is a rock solid, battle-tested Promises/A+ and when() implementation, including a complete ES6 Promise shim.
- [when v3.7.7](https://www.npmjs.com/package/when) - When.js is a rock solid, battle-tested Promises/A+ and when() implementation, including a complete ES6 Promise shim.
- [winston v2.3.0](https://www.npmjs.com/package/winston) - A multi-transport async logging library for node.js. "CHILL WINSTON! ... I put it in the logs."
- [ws v1.1.1](https://www.npmjs.com/package/ws) - ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and server implementation.
- [xml2js v0.4.17](https://www.npmjs.com/package/xml2js) - Simple XML to JavaScript object converter. It supports bi-directional conversion.
- [xml2js v0.4.17](https://www.npmjs.com/package/xml2js) - Simple XML to JavaScript object converter. It supports bi-directional conversion.
- [xmlhttprequest v1.8.0](https://www.npmjs.com/package/xmlhttprequest) - node-XMLHttpRequest is a wrapper for the built-in http client to emulate the browser XMLHttpRequest object.
- [yauzl v2.7.0](https://www.npmjs.com/package/yauzl) - yet another unzip library for node. For zipping.

Expand Down
2 changes: 1 addition & 1 deletion tests/src/test/scala/common/Wsk.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ trait FullyQualifiedNames {
*/
def fqn(name: String)(implicit wp: WskProps) = {
val sep = "/" // Namespace.PATHSEP
if (name.startsWith(sep)) name
if (name.startsWith(sep) || name.count(_ == sep(0)) == 2) name
else s"$sep${wp.namespace}$sep$name"
}

Expand Down
1 change: 0 additions & 1 deletion tests/src/test/scala/system/basic/WskActionTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -355,5 +355,4 @@ class WskActionTests
activation.logs.get.mkString(" ") should include(s"hello $utf8")
}
}

}
29 changes: 29 additions & 0 deletions tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,35 @@ class WskBasicUsageTests
result.stderr should include regex ("""(?i)Run 'wsk --help' for usage""")
}

it should "allow a 3 part Fully Qualified Name (FQN) without a leading '/'" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val guestNamespace = wsk.namespace.whois()
val packageName = "packageName3ptFQN"
val actionName = "actionName3ptFQN"
val triggerName = "triggerName3ptFQN"
val ruleName = "ruleName3ptFQN"
val fullQualifiedName = s"${guestNamespace}/${packageName}/${actionName}"
// Used for action and rule creation below
assetHelper.withCleaner(wsk.pkg, packageName) {
(pkg, _) => pkg.create(packageName)
}
assetHelper.withCleaner(wsk.trigger, triggerName) {
(trigger, _) => trigger.create(triggerName)
}
// Test action and rule creation where action name is 3 part FQN w/out leading slash
assetHelper.withCleaner(wsk.action, fullQualifiedName) {
(action, _) => action.create(fullQualifiedName, defaultAction)
}
assetHelper.withCleaner(wsk.rule, ruleName) {
(rule, _) =>
rule.create(ruleName, trigger = triggerName, action = fullQualifiedName)
}

wsk.action.invoke(fullQualifiedName).stdout should include(s"ok: invoked /$fullQualifiedName")
wsk.action.get(fullQualifiedName).stdout should include(s"ok: got action ${packageName}/${actionName}")
}


behavior of "Wsk actions"

it should "reject creating entities with invalid names" in withAssetCleaner(wskprops) {
Expand Down
108 changes: 54 additions & 54 deletions tools/cli/go-whisk-cli/commands/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ var actionInvokeCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
var err error
var parameters interface{}
var qualifiedName QualifiedName
var qualifiedName = new(QualifiedName)
var paramArgs []string

if whiskErr := checkArgs(
Expand All @@ -133,11 +133,11 @@ var actionInvokeCmd = &cobra.Command{
return whiskErr
}

if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
return parseQualifiedNameError(args[0], err)
if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
return NewQualifiedNameError(args[0], err)
}

client.Namespace = qualifiedName.namespace
client.Namespace = qualifiedName.GetNamespace()
paramArgs = flags.common.param

if len(paramArgs) > 0 {
Expand All @@ -148,12 +148,12 @@ var actionInvokeCmd = &cobra.Command{
if flags.action.result {flags.common.blocking = true}

res, _, err := client.Actions.Invoke(
qualifiedName.entityName,
qualifiedName.GetEntityName(),
parameters,
flags.common.blocking,
flags.action.result)

return handleInvocationResponse(qualifiedName, parameters, res, err)
return handleInvocationResponse(*qualifiedName, parameters, res, err)
},
}

Expand All @@ -164,29 +164,29 @@ func handleInvocationResponse(
err error) (error) {
if err == nil {
printInvocationMsg(
qualifiedName.namespace,
qualifiedName.entityName,
qualifiedName.GetNamespace(),
qualifiedName.GetEntityName(),
getValueFromJSONResponse(ACTIVATION_ID, result),
result,
color.Output)
} else {
if !flags.common.blocking {
return handleInvocationError(err, qualifiedName.entityName, parameters)
return handleInvocationError(err, qualifiedName.GetEntityName(), parameters)
} else {
if isBlockingTimeout(err) {
printBlockingTimeoutMsg(
qualifiedName.namespace,
qualifiedName.entityName,
qualifiedName.GetNamespace(),
qualifiedName.GetEntityName(),
getValueFromJSONResponse(ACTIVATION_ID, result))
} else if isApplicationError(err) {
printInvocationMsg(
qualifiedName.namespace,
qualifiedName.entityName,
qualifiedName.GetNamespace(),
qualifiedName.GetEntityName(),
getValueFromJSONResponse(ACTIVATION_ID, result),
result,
colorable.NewColorableStderr())
} else {
return handleInvocationError(err, qualifiedName.entityName, parameters)
return handleInvocationError(err, qualifiedName.GetEntityName(), parameters)
}
}
}
Expand All @@ -204,7 +204,7 @@ var actionGetCmd = &cobra.Command{
var err error
var field string
var action *whisk.Action
var qualifiedName QualifiedName
var qualifiedName = new(QualifiedName)

if whiskErr := checkArgs(args, 1, 2, "Action get", wski18n.T("An action name is required.")); whiskErr != nil {
return whiskErr
Expand All @@ -218,29 +218,29 @@ var actionGetCmd = &cobra.Command{
}
}

if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
return parseQualifiedNameError(args[0], err)
if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
return NewQualifiedNameError(args[0], err)
}

client.Namespace = qualifiedName.namespace
client.Namespace = qualifiedName.GetNamespace()

if action, _, err = client.Actions.Get(qualifiedName.entityName); err != nil {
return actionGetError(qualifiedName.entityName, err)
if action, _, err = client.Actions.Get(qualifiedName.GetEntityName()); err != nil {
return actionGetError(qualifiedName.GetEntityName(), err)
}

if flags.action.url {
actionURL := action.ActionURL(Properties.APIHost,
DefaultOpenWhiskApiPath,
Properties.APIVersion,
qualifiedName.packageName)
printActionGetWithURL(qualifiedName.entity, actionURL)
qualifiedName.GetPackageName())
printActionGetWithURL(qualifiedName.GetEntity(), actionURL)
} else if flags.common.summary {
printSummary(action)
} else {
if len(field) > 0 {
printActionGetWithField(qualifiedName.entityName, field, action)
printActionGetWithField(qualifiedName.GetEntityName(), field, action)
} else {
printActionGet(qualifiedName.entityName, action)
printActionGet(qualifiedName.GetEntityName(), action)
}
}

Expand All @@ -255,7 +255,7 @@ var actionDeleteCmd = &cobra.Command{
SilenceErrors: true,
PreRunE: setupClientConfig,
RunE: func(cmd *cobra.Command, args []string) error {
var qualifiedName QualifiedName
var qualifiedName = new(QualifiedName)
var err error

if whiskErr := checkArgs(
Expand All @@ -267,17 +267,17 @@ var actionDeleteCmd = &cobra.Command{
return whiskErr
}

if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
return parseQualifiedNameError(args[0], err)
if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
return NewQualifiedNameError(args[0], err)
}

client.Namespace = qualifiedName.namespace
client.Namespace = qualifiedName.GetNamespace()

if _, err = client.Actions.Delete(qualifiedName.entityName); err != nil {
return actionDeleteError(qualifiedName.entityName, err)
if _, err = client.Actions.Delete(qualifiedName.GetEntityName()); err != nil {
return actionDeleteError(qualifiedName.GetEntityName(), err)
}

printActionDeleted(qualifiedName.entityName)
printActionDeleted(qualifiedName.GetEntityName())

return nil
},
Expand All @@ -290,7 +290,7 @@ var actionListCmd = &cobra.Command{
SilenceErrors: true,
PreRunE: setupClientConfig,
RunE: func(cmd *cobra.Command, args []string) error {
var qualifiedName QualifiedName
var qualifiedName = new(QualifiedName)
var actions []whisk.Action
var err error

Expand All @@ -304,20 +304,20 @@ var actionListCmd = &cobra.Command{
}

if len(args) == 1 {
if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
return parseQualifiedNameError(args[0], err)
if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
return NewQualifiedNameError(args[0], err)
}

client.Namespace = qualifiedName.namespace
client.Namespace = qualifiedName.GetNamespace()
}

options := &whisk.ActionListOptions{
Skip: flags.common.skip,
Limit: flags.common.limit,
}

if actions, _, err = client.Actions.List(qualifiedName.entityName, options); err != nil {
return actionListError(qualifiedName.entityName, options, err)
if actions, _, err = client.Actions.List(qualifiedName.GetEntityName(), options); err != nil {
return actionListError(qualifiedName.GetEntityName(), options, err)
}

printList(actions)
Expand All @@ -334,16 +334,16 @@ func parseAction(cmd *cobra.Command, args []string, update bool) (*whisk.Action,
var parameters interface{}
var annotations interface{}

qualifiedName := QualifiedName{}
var qualifiedName = new(QualifiedName)

if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
return nil, parseQualifiedNameError(args[0], err)
if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
return nil, NewQualifiedNameError(args[0], err)
}

client.Namespace = qualifiedName.namespace
client.Namespace = qualifiedName.GetNamespace()
action := new(whisk.Action)
action.Name = qualifiedName.entityName
action.Namespace = qualifiedName.namespace
action.Name = qualifiedName.GetEntityName()
action.Namespace = qualifiedName.GetNamespace()
action.Limits = getLimits(
cmd.LocalFlags().Changed(MEMORY_FLAG),
cmd.LocalFlags().Changed(LOG_SIZE_FLAG),
Expand Down Expand Up @@ -372,19 +372,19 @@ func parseAction(cmd *cobra.Command, args []string, update bool) (*whisk.Action,
}

if flags.action.copy {
copiedQualifiedName := QualifiedName{}
var copiedQualifiedName = new(QualifiedName)

if copiedQualifiedName, err = parseQualifiedName(args[1]); err != nil {
return nil, parseQualifiedNameError(args[1], err)
if copiedQualifiedName, err = NewQualifiedName(args[1]); err != nil {
return nil, NewQualifiedNameError(args[1], err)
}

client.Namespace = copiedQualifiedName.namespace
client.Namespace = copiedQualifiedName.GetNamespace()

if existingAction, _, err = client.Actions.Get(copiedQualifiedName.entityName); err != nil {
return nil, actionGetError(copiedQualifiedName.entityName, err)
if existingAction, _, err = client.Actions.Get(copiedQualifiedName.GetEntityName()); err != nil {
return nil, actionGetError(copiedQualifiedName.GetEntityName(), err)
}

client.Namespace = qualifiedName.namespace
client.Namespace = qualifiedName.GetNamespace()
action.Exec = existingAction.Exec
action.Parameters = append(action.Parameters, existingAction.Parameters...)
action.Annotations = append(action.Annotations, existingAction.Annotations...)
Expand All @@ -407,7 +407,7 @@ func parseAction(cmd *cobra.Command, args []string, update bool) (*whisk.Action,

if cmd.LocalFlags().Changed(WEB_FLAG) {
preserveAnnotations := action.Annotations == nil
action.Annotations, err = webAction(flags.action.web, action.Annotations, qualifiedName.entityName, preserveAnnotations)
action.Annotations, err = webAction(flags.action.web, action.Annotations, qualifiedName.GetEntityName(), preserveAnnotations)
}

whisk.Debug(whisk.DbgInfo, "Parsed action struct: %#v\n", action)
Expand Down Expand Up @@ -874,10 +874,10 @@ func isWebAction(client *whisk.Client, qname QualifiedName) (error) {
var err error = nil

savedNs := client.Namespace
client.Namespace = qname.namespace
fullActionName := "/" + qname.namespace + "/" + qname.entityName
client.Namespace = qname.GetNamespace()
fullActionName := "/" + qname.GetNamespace() + "/" + qname.GetEntityName()

action, _, err := client.Actions.Get(qname.entityName)
action, _, err := client.Actions.Get(qname.GetEntityName())

if err != nil {
whisk.Debug(whisk.DbgError, "client.Actions.Get(%s) error: %s\n", fullActionName, err)
Expand Down
Loading

0 comments on commit 5de0820

Please sign in to comment.