Merge the repositories of openwhisk-cli and openwhisk/go-whisk-cli
diff --git a/commands/action.go b/commands/action.go
index d269377..2393a51 100644
--- a/commands/action.go
+++ b/commands/action.go
@@ -144,6 +144,7 @@
return getJSONFromStringsParamError(paramArgs, false, err)
}
}
+ if flags.action.result {flags.common.blocking = true}
res, _, err := client.Actions.Invoke(
qualifiedName.entityName,
@@ -592,8 +593,9 @@
whisk.Debug(whisk.DbgError, "client.Actions.Insert(%#v, false) error: %s\n", action, err)
errMsg := wski18n.T(
- "Unable to create action: {{.err}}",
+ "Unable to create action '{{.name}}': {{.err}}",
map[string]interface{}{
+ "name": action.Name,
"err": err,
})
@@ -653,8 +655,9 @@
whisk.Debug(whisk.DbgError, "client.Actions.Delete(%s) error: %s\n", entityName, err)
errMsg := wski18n.T(
- "Unable to delete action: {{.err}}",
+ "Unable to delete action '{{.name}}': {{.err}}",
map[string]interface{}{
+ "name": entityName,
"err": err,
})
@@ -665,7 +668,7 @@
whisk.Debug(whisk.DbgError, "client.Actions.Get(%s) error: %s\n", entityName, err)
errMsg := wski18n.T(
- "Unable to obtain action '{{.name}}' to copy: {{.err}}",
+ "Unable to get action '{{.name}}': {{.err}}",
map[string]interface{}{
"name": entityName,
"err": err,
@@ -833,6 +836,42 @@
}))
}
+// Check if the specified action is a web-action
+func isWebAction(client *whisk.Client, qname QualifiedName) error {
+ var err error = nil
+
+ savedNs := client.Namespace
+ client.Namespace = qname.namespace
+ 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)
+ err = errors.New(wski18n.T("API action does not exist"))
+ } else {
+ err = errors.New(wski18n.T("API 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
+ }
+ }
+ }
+
+ client.Namespace = savedNs
+ return err
+}
+
func init() {
actionCreateCmd.Flags().BoolVar(&flags.action.docker, "docker", false, wski18n.T("treat ACTION as docker image path on dockerhub"))
actionCreateCmd.Flags().BoolVar(&flags.action.copy, "copy", false, wski18n.T("treat ACTION as the name of an existing action"))
@@ -865,7 +904,7 @@
actionInvokeCmd.Flags().StringSliceVarP(&flags.common.param, "param", "p", []string{}, wski18n.T("parameter values in `KEY VALUE` format"))
actionInvokeCmd.Flags().StringVarP(&flags.common.paramFile, "param-file", "P", "", wski18n.T("`FILE` containing parameter values in JSON format"))
actionInvokeCmd.Flags().BoolVarP(&flags.common.blocking, "blocking", "b", false, wski18n.T("blocking invoke"))
- actionInvokeCmd.Flags().BoolVarP(&flags.action.result, "result", "r", false, wski18n.T("show only activation result if a blocking activation (unless there is a failure)"))
+ 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"))
diff --git a/commands/activation.go b/commands/activation.go
index 575de2e..df0b2d5 100644
--- a/commands/activation.go
+++ b/commands/activation.go
@@ -135,7 +135,7 @@
activation, _, err := client.Activations.Get(id)
if err != nil {
whisk.Debug(whisk.DbgError, "client.Activations.Get(%s) failed: %s\n", id, err)
- errStr := wski18n.T("Unable to obtain activation record for '{{.id}}': {{.err}}",
+ errStr := wski18n.T("Unable to get activation '{{.id}}': {{.err}}",
map[string]interface{}{"id": id, "err": err})
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
@@ -186,7 +186,7 @@
activation, _, err := client.Activations.Logs(id)
if err != nil {
whisk.Debug(whisk.DbgError, "client.Activations.Logs(%s) failed: %s\n", id, err)
- errStr := wski18n.T("Unable to obtain logs for activation '{{.id}}': {{.err}}",
+ errStr := wski18n.T("Unable to get logs for activation '{{.id}}': {{.err}}",
map[string]interface{}{"id": id, "err": err})
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
@@ -214,7 +214,7 @@
result, _, err := client.Activations.Result(id)
if err != nil {
whisk.Debug(whisk.DbgError, "client.Activations.result(%s) failed: %s\n", id, err)
- errStr := wski18n.T("Unable to obtain result information for activation '{{.id}}': {{.err}}",
+ errStr := wski18n.T("Unable to get result for activation '{{.id}}': {{.err}}",
map[string]interface{}{"id": id, "err": err})
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
diff --git a/commands/api.go b/commands/api.go
index 3fa1a79..430dc38 100644
--- a/commands/api.go
+++ b/commands/api.go
@@ -40,6 +40,11 @@
Short: wski18n.T("work with APIs (experimental)"),
}
+var apiCmd = &cobra.Command{
+ Use: "api",
+ Short: wski18n.T("work with APIs"),
+}
+
var apiCreateCmd = &cobra.Command{
Use: "create ([BASE_PATH] API_PATH API_VERB ACTION] | --config-file CFG_FILE) ",
Short: wski18n.T("create a new API"),
@@ -82,10 +87,10 @@
}
}
- sendApi := new(whisk.SendApi)
- sendApi.ApiDoc = api
-
- retApi, _, err := client.Apis.Insert(sendApi, false)
+ apiCreateReq := new(whisk.ApiCreateRequest)
+ apiCreateReq.ApiDoc = api
+ apiCreateReqOptions := new(whisk.ApiCreateRequestOptions)
+ retApi, _, err := client.Apis.Insert(apiCreateReq, apiCreateReqOptions, whisk.DoNotOverwrite)
if err != nil {
whisk.Debug(whisk.DbgError, "client.Apis.Insert(%#v, false) error: %s\n", api, err)
errMsg := wski18n.T("Unable to create API: {{.err}}", map[string]interface{}{"err": err})
@@ -111,7 +116,7 @@
for path, _ := range retApi.Swagger.Paths {
managedUrl := strings.TrimSuffix(baseUrl, "/")+path
whisk.Debug(whisk.DbgInfo, "Managed path: %s\n",managedUrl)
- for op, _ := range retApi.Swagger.Paths[path] {
+ for op, opv := range retApi.Swagger.Paths[path] {
whisk.Debug(whisk.DbgInfo, "Path operation: %s\n", op)
fmt.Fprintf(color.Output,
wski18n.T("{{.ok}} created API {{.path}} {{.verb}} for action {{.name}}\n{{.fullpath}}\n",
@@ -119,7 +124,7 @@
"ok": color.GreenString("ok:"),
"path": path,
"verb": op,
- "name": boldString(retApi.Swagger.Paths[path][op]["x-ibm-op-ext"]["actionName"]),
+ "name": boldString(opv.XOpenWhisk.ActionName),
"fullpath": managedUrl,
}))
}
@@ -131,51 +136,51 @@
},
}
-var apiUpdateCmd = &cobra.Command{
- Use: "update API_PATH API_VERB ACTION",
- Short: wski18n.T("update an existing API"),
- SilenceUsage: true,
- SilenceErrors: true,
- PreRunE: setupClientConfig,
- RunE: func(cmd *cobra.Command, args []string) error {
-
- if whiskErr := checkArgs(args, 3, 3, "Api update",
- wski18n.T("An API path, an API verb, and an action name are required.")); whiskErr != nil {
- return whiskErr
- }
-
- api, err := parseApi(cmd, args)
- if err != nil {
- whisk.Debug(whisk.DbgError, "parseApi(%s, %s) error: %s\n", cmd, args, err)
- errMsg := wski18n.T("Unable to parse API command arguments: {{.err}}", map[string]interface{}{"err": err})
- whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
- whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
- return whiskErr
- }
- sendApi := new(whisk.SendApi)
- sendApi.ApiDoc = api
-
- retApi, _, err := client.Apis.Insert(sendApi, true)
- if err != nil {
- whisk.Debug(whisk.DbgError, "client.Apis.Insert(%#v, %t, false) error: %s\n", api, err)
- errMsg := wski18n.T("Unable to update API: {{.err}}", map[string]interface{}{"err": err})
- whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_NETWORK,
- whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
- return whiskErr
- }
-
- fmt.Fprintf(color.Output,
- wski18n.T("{{.ok}} updated API {{.path}} {{.verb}} for action {{.name}}\n{{.fullpath}}\n",
- map[string]interface{}{
- "ok": color.GreenString("ok:"),
- "path": api.GatewayRelPath,
- "verb": api.GatewayMethod,
- "name": boldString("/"+api.Action.Name),
- "fullpath": getManagedUrl(retApi, api.GatewayRelPath, api.GatewayMethod),
- }))
- return nil
- },
-}
+//var apiUpdateCmd = &cobra.Command{
+// Use: "update API_PATH API_VERB ACTION",
+// Short: wski18n.T("update an existing API"),
+// SilenceUsage: true,
+// SilenceErrors: true,
+// PreRunE: setupClientConfig,
+// RunE: func(cmd *cobra.Command, args []string) error {
+//
+// if whiskErr := checkArgs(args, 3, 3, "Api update",
+// wski18n.T("An API path, an API verb, and an action name are required.")); whiskErr != nil {
+// return whiskErr
+// }
+//
+// api, err := parseApi(cmd, args)
+// if err != nil {
+// whisk.Debug(whisk.DbgError, "parseApi(%s, %s) error: %s\n", cmd, args, err)
+// errMsg := wski18n.T("Unable to parse API command arguments: {{.err}}", map[string]interface{}{"err": err})
+// whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
+// whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+// return whiskErr
+// }
+// sendApi := new(whisk.ApiCreateRequest)
+// sendApi.ApiDoc = api
+//
+// retApi, _, err := client.Apis.Insert(sendApi, true)
+// if err != nil {
+// whisk.Debug(whisk.DbgError, "client.Apis.Insert(%#v, %t, false) error: %s\n", api, err)
+// errMsg := wski18n.T("Unable to update API: {{.err}}", map[string]interface{}{"err": err})
+// whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_NETWORK,
+// whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
+// return whiskErr
+// }
+//
+// fmt.Fprintf(color.Output,
+// wski18n.T("{{.ok}} updated API {{.path}} {{.verb}} for action {{.name}}\n{{.fullpath}}\n",
+// map[string]interface{}{
+// "ok": color.GreenString("ok:"),
+// "path": api.GatewayRelPath,
+// "verb": api.GatewayMethod,
+// "name": boldString("/"+api.Action.Name),
+// "fullpath": getManagedUrl(retApi, api.GatewayRelPath, api.GatewayMethod),
+// }))
+// return nil
+// },
+//}
var apiGetCmd = &cobra.Command{
Use: "get BASE_PATH | API_NAME",
@@ -192,15 +197,13 @@
return whiskErr
}
- api := new(whisk.Api)
- options := new(whisk.ApiListOptions)
-
- options.ApiBasePath = args[0]
-
- retApi, _, err := client.Apis.Get(api, options)
+ apiGetReq := new(whisk.ApiGetRequest)
+ apiGetReqOptions := new(whisk.ApiGetRequestOptions)
+ apiGetReqOptions.ApiBasePath = args[0]
+ retApi, _, err := client.Apis.Get(apiGetReq, apiGetReqOptions)
if err != nil {
- whisk.Debug(whisk.DbgError, "client.Apis.Get(%s) error: %s\n", api.Id, err)
- errMsg := wski18n.T("Unable to get API: {{.err}}", map[string]interface{}{"err": err})
+ whisk.Debug(whisk.DbgError, "client.Apis.Get(%#v, %#v) error: %s\n", apiGetReq, apiGetReqOptions, err)
+ errMsg := wski18n.T("Unable to get API '{{.name}}': {{.err}}", map[string]interface{}{"name": args[0], "err": err})
whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return whiskErr
@@ -256,16 +259,14 @@
return whiskErr
}
- api := new(whisk.Api)
- options := new(whisk.ApiOptions)
- options.Force = true
-
+ apiDeleteReq := new(whisk.ApiDeleteRequest)
+ apiDeleteReqOptions := new(whisk.ApiDeleteRequestOptions)
// Is the argument a basepath (must start with /) or an API name
if _, ok := isValidBasepath(args[0]); !ok {
whisk.Debug(whisk.DbgInfo, "Treating '%s' as an API name; as it does not begin with '/'\n", args[0])
- options.ApiBasePath = args[0]
+ apiDeleteReqOptions.ApiBasePath = args[0]
} else {
- options.ApiBasePath = args[0]
+ apiDeleteReqOptions.ApiBasePath = args[0]
}
if (len(args) > 1) {
@@ -273,20 +274,20 @@
if whiskErr, ok := isValidRelpath(args[1]); !ok {
return whiskErr
}
- options.ApiRelPath = args[1]
+ apiDeleteReqOptions.ApiRelPath = args[1]
}
if (len(args) > 2) {
// Is the API verb valid?
if whiskErr, ok := IsValidApiVerb(args[2]); !ok {
return whiskErr
}
- options.ApiVerb = strings.ToUpper(args[2])
+ apiDeleteReqOptions.ApiVerb = strings.ToUpper(args[2])
}
- _, err := client.Apis.Delete(api, options)
+ _, err := client.Apis.Delete(apiDeleteReq, apiDeleteReqOptions)
if err != nil {
- whisk.Debug(whisk.DbgError, "client.Apis.Delete(%s) error: %s\n", api.Id, err)
- errMsg := wski18n.T("Unable to delete action: {{.err}}", map[string]interface{}{"err": err})
+ whisk.Debug(whisk.DbgError, "client.Apis.Delete(%#v, %#v) error: %s\n", apiDeleteReq, apiDeleteReqOptions, err)
+ errMsg := wski18n.T("Unable to delete API: {{.err}}", map[string]interface{}{"err": err})
whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return whiskErr
@@ -297,24 +298,24 @@
wski18n.T("{{.ok}} deleted API {{.basepath}}\n",
map[string]interface{}{
"ok": color.GreenString("ok:"),
- "basepath": options.ApiBasePath,
+ "basepath": apiDeleteReqOptions.ApiBasePath,
}))
} else if (len(args) == 2 ) {
fmt.Fprintf(color.Output,
wski18n.T("{{.ok}} deleted {{.path}} from {{.basepath}}\n",
map[string]interface{}{
"ok": color.GreenString("ok:"),
- "path": options.ApiRelPath,
- "basepath": options.ApiBasePath,
+ "path": apiDeleteReqOptions.ApiRelPath,
+ "basepath": apiDeleteReqOptions.ApiBasePath,
}))
} else {
fmt.Fprintf(color.Output,
wski18n.T("{{.ok}} deleted {{.path}} {{.verb}} from {{.basepath}}\n",
map[string]interface{}{
"ok": color.GreenString("ok:"),
- "path": options.ApiRelPath,
- "verb": options.ApiVerb,
- "basepath": options.ApiBasePath,
+ "path": apiDeleteReqOptions.ApiRelPath,
+ "verb": apiDeleteReqOptions.ApiVerb,
+ "basepath": apiDeleteReqOptions.ApiBasePath,
}))
}
@@ -331,6 +332,8 @@
PreRunE: setupClientConfig,
RunE: func(cmd *cobra.Command, args []string) error {
var err error
+ var retApiList *whisk.ApiListResponse
+ var retApi *whisk.ApiGetResponse
var retApiArray *whisk.RetApiArray
if whiskErr := checkArgs(args, 0, 3, "Api list",
@@ -338,50 +341,59 @@
return whiskErr
}
- api := new(whisk.Api)
- api.Namespace = client.Config.Namespace
+ // Get API request body
+ apiGetReq := new(whisk.ApiGetRequest)
+ apiGetReq.Namespace = client.Config.Namespace
- options := new(whisk.ApiListOptions)
- options.Limit = flags.common.limit
- options.Skip = flags.common.skip
+ // Get API request options
+ apiGetReqOptions := new(whisk.ApiGetRequestOptions)
+
+ // List API request query parameters
+ apiListReqOptions := new(whisk.ApiListRequestOptions)
+ apiListReqOptions.Limit = flags.common.limit
+ apiListReqOptions.Skip = flags.common.skip
if (len(args) == 0) {
- retApiArray, _, err = client.Apis.List(options)
+ retApiList, _, err = client.Apis.List(apiListReqOptions)
if err != nil {
- whisk.Debug(whisk.DbgError, "client.Apis.List(%s) error: %s\n", options, err)
- errMsg := wski18n.T("Unable to get API: {{.err}}", map[string]interface{}{"err": err})
+ whisk.Debug(whisk.DbgError, "client.Apis.List(%#v) error: %s\n", apiListReqOptions, err)
+ errMsg := wski18n.T("Unable to obtain the API list: {{.err}}", map[string]interface{}{"err": err})
whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return whiskErr
}
- whisk.Debug(whisk.DbgInfo, "client.Apis.List returned: %#v (%+v)\n", retApiArray, retApiArray)
+ whisk.Debug(whisk.DbgInfo, "client.Apis.List returned: %#v (%+v)\n", retApiList, retApiList)
+ // Cast to a common type to allow for code to print out apilist response or apiget response
+ retApiArray = (*whisk.RetApiArray)(retApiList)
} else {
// The first argument is either a basepath (must start with /) or an API name
- options.ApiBasePath = args[0]
+ apiGetReqOptions.ApiBasePath = args[0]
if (len(args) > 1) {
// Is the API path valid?
if whiskErr, ok := isValidRelpath(args[1]); !ok {
return whiskErr
}
- options.ApiRelPath = args[1]
+ apiGetReqOptions.ApiRelPath = args[1]
}
if (len(args) > 2) {
// Is the API verb valid?
if whiskErr, ok := IsValidApiVerb(args[2]); !ok {
return whiskErr
}
- options.ApiVerb = strings.ToUpper(args[2])
+ apiGetReqOptions.ApiVerb = strings.ToUpper(args[2])
}
- retApiArray, _, err = client.Apis.Get(api, options)
+ retApi, _, err = client.Apis.Get(apiGetReq, apiGetReqOptions)
if err != nil {
- whisk.Debug(whisk.DbgError, "client.Apis.Get(%s) error: %s\n", api.Id, err)
+ whisk.Debug(whisk.DbgError, "client.Apis.Get(%#v, %#v) error: %s\n", apiGetReq, apiGetReqOptions, err)
errMsg := wski18n.T("Unable to obtain the API list: {{.err}}", map[string]interface{}{"err": err})
whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return whiskErr
}
- whisk.Debug(whisk.DbgInfo, "client.Apis.Get returned: %#v\n", retApiArray)
+ whisk.Debug(whisk.DbgInfo, "client.Apis.Get returned: %#v\n", retApi)
+ // Cast to a common type to allow for code to print out apilist response or apiget response
+ retApiArray = (*whisk.RetApiArray)(retApi)
}
// Display the APIs - applying any specified filtering
@@ -393,13 +405,13 @@
}))
for i:=0; i<len(retApiArray.Apis); i++ {
- printFilteredListApi(retApiArray.Apis[i].ApiValue, api)
+ printFilteredListApi(retApiArray.Apis[i].ApiValue, (*whisk.ApiOptions)(apiGetReqOptions))
}
} else {
// Dynamically create the output format string based on the maximum size of the
// fully qualified action name and the API Name.
- maxActionNameSize := min(40, max(len("Action"), getLargestActionNameSize(retApiArray, api)))
- maxApiNameSize := min(30, max(len("API Name"), getLargestApiNameSize(retApiArray, api)))
+ maxActionNameSize := min(40, max(len("Action"), getLargestActionNameSize(retApiArray, (*whisk.ApiOptions)(apiGetReqOptions))))
+ maxApiNameSize := min(30, max(len("API Name"), getLargestApiNameSize(retApiArray, (*whisk.ApiOptions)(apiGetReqOptions))))
fmtString = "%-"+strconv.Itoa(maxActionNameSize)+"s %7s %"+strconv.Itoa(maxApiNameSize+1)+"s %s\n"
fmt.Fprintf(color.Output,
@@ -410,7 +422,7 @@
fmt.Printf(fmtString, "Action", "Verb", "API Name", "URL")
for i:=0; i<len(retApiArray.Apis); i++ {
- printFilteredListRow(retApiArray.Apis[i].ApiValue, api, maxActionNameSize, maxApiNameSize)
+ printFilteredListRow(retApiArray.Apis[i].ApiValue, (*whisk.ApiOptions)(apiGetReqOptions), maxActionNameSize, maxApiNameSize)
}
}
@@ -423,20 +435,20 @@
* 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, api *whisk.Api) {
+func printFilteredListApi(resultApi *whisk.RetApi, api *whisk.ApiOptions) {
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, "apiGetCmd: comparing api relpath: %s\n", path)
- if ( len(api.GatewayRelPath) == 0 || path == api.GatewayRelPath) {
+ if ( len(api.ApiRelPath) == 0 || path == api.ApiRelPath) {
whisk.Debug(whisk.DbgInfo, "apiGetCmd: relpath matches\n")
for op, opv := range resultApi.Swagger.Paths[path] {
whisk.Debug(whisk.DbgInfo, "apiGetCmd: comparing operation: '%s'\n", op)
- if ( len(api.GatewayMethod) == 0 || strings.ToLower(op) == strings.ToLower(api.GatewayMethod)) {
+ if ( len(api.ApiVerb) == 0 || strings.ToLower(op) == strings.ToLower(api.ApiVerb)) {
whisk.Debug(whisk.DbgInfo, "apiGetCmd: operation matches: %#v\n", opv)
- var actionName = "/"+opv["x-ibm-op-ext"]["actionNamespace"].(string)+"/"+opv["x-ibm-op-ext"]["actionName"].(string)
+ var 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)
@@ -457,19 +469,19 @@
*
* NOTE: Large action name and api name value will be truncated by their associated max size parameters.
*/
-func printFilteredListRow(resultApi *whisk.RetApi, api *whisk.Api, maxActionNameSize int, maxApiNameSize int) {
+func printFilteredListRow(resultApi *whisk.RetApi, api *whisk.ApiOptions, maxActionNameSize int, maxApiNameSize int) {
baseUrl := strings.TrimSuffix(resultApi.BaseUrl, "/")
apiName := resultApi.Swagger.Info.Title
if (resultApi.Swagger != nil && resultApi.Swagger.Paths != nil) {
for path, _ := range resultApi.Swagger.Paths {
whisk.Debug(whisk.DbgInfo, "apiGetCmd: comparing api relpath: %s\n", path)
- if ( len(api.GatewayRelPath) == 0 || path == api.GatewayRelPath) {
+ if ( len(api.ApiRelPath) == 0 || path == api.ApiRelPath) {
whisk.Debug(whisk.DbgInfo, "apiGetCmd: relpath matches\n")
for op, opv := range resultApi.Swagger.Paths[path] {
whisk.Debug(whisk.DbgInfo, "apiGetCmd: comparing operation: '%s'\n", op)
- if ( len(api.GatewayMethod) == 0 || strings.ToLower(op) == strings.ToLower(api.GatewayMethod)) {
+ if ( len(api.ApiVerb) == 0 || strings.ToLower(op) == strings.ToLower(api.ApiVerb)) {
whisk.Debug(whisk.DbgInfo, "apiGetCmd: operation matches: %#v\n", opv)
- var actionName = "/"+opv["x-ibm-op-ext"]["actionNamespace"].(string)+"/"+opv["x-ibm-op-ext"]["actionName"].(string)
+ var actionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.ActionName
fmt.Printf(fmtString,
actionName[0 : min(len(actionName), maxActionNameSize)],
op,
@@ -482,20 +494,20 @@
}
}
-func getLargestActionNameSize(retApiArray *whisk.RetApiArray, api *whisk.Api) int {
+func getLargestActionNameSize(retApiArray *whisk.RetApiArray, api *whisk.ApiOptions) int {
var maxNameSize = 0
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 {
whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: comparing api relpath: %s\n", path)
- if ( len(api.GatewayRelPath) == 0 || path == api.GatewayRelPath) {
+ if ( len(api.ApiRelPath) == 0 || path == api.ApiRelPath) {
whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: relpath matches\n")
for op, opv := range resultApi.Swagger.Paths[path] {
whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: comparing operation: '%s'\n", op)
- if ( len(api.GatewayMethod) == 0 || strings.ToLower(op) == strings.ToLower(api.GatewayMethod)) {
+ if ( len(api.ApiVerb) == 0 || strings.ToLower(op) == strings.ToLower(api.ApiVerb)) {
whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: operation matches: %#v\n", opv)
- var fullActionName = "/"+opv["x-ibm-op-ext"]["actionNamespace"].(string)+"/"+opv["x-ibm-op-ext"]["actionName"].(string)
+ var fullActionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.ActionName
if (len(fullActionName) > maxNameSize) {
maxNameSize = len(fullActionName)
}
@@ -508,7 +520,7 @@
return maxNameSize
}
-func getLargestApiNameSize(retApiArray *whisk.RetApiArray, api *whisk.Api) int {
+func getLargestApiNameSize(retApiArray *whisk.RetApiArray, api *whisk.ApiOptions) int {
var maxNameSize = 0
for i:=0; i<len(retApiArray.Apis); i++ {
var resultApi = retApiArray.Apis[i].ApiValue
@@ -516,11 +528,11 @@
if (resultApi.Swagger != nil && resultApi.Swagger.Paths != nil) {
for path, _ := range resultApi.Swagger.Paths {
whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: comparing api relpath: %s\n", path)
- if ( len(api.GatewayRelPath) == 0 || path == api.GatewayRelPath) {
+ if ( len(api.ApiRelPath) == 0 || path == api.ApiRelPath) {
whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: relpath matches\n")
for op, opv := range resultApi.Swagger.Paths[path] {
whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: comparing operation: '%s'\n", op)
- if ( len(api.GatewayMethod) == 0 || strings.ToLower(op) == strings.ToLower(api.GatewayMethod)) {
+ if ( len(api.ApiVerb) == 0 || strings.ToLower(op) == strings.ToLower(api.ApiVerb)) {
whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: operation matches: %#v\n", opv)
if (len(apiName) > maxNameSize) {
maxNameSize = len(apiName)
@@ -727,6 +739,7 @@
return nil, true
}
+
/*
* Pull the managedUrl (external API URL) from the API configuration
*/
@@ -749,6 +762,693 @@
return url
}
+/////////////
+// V2 Cmds //
+/////////////
+var apiCreateCmdV2 = &cobra.Command{
+ Use: "create ([BASE_PATH] API_PATH API_VERB ACTION] | --config-file CFG_FILE) ",
+ Short: wski18n.T("create a new API"),
+ SilenceUsage: true,
+ SilenceErrors: true,
+ PreRunE: setupClientConfig,
+ RunE: func(cmd *cobra.Command, args []string) error {
+
+ var api *whisk.Api
+ var err error
+ var qname *QualifiedName
+
+ if (len(args) == 0 && flags.api.configfile == "") {
+ whisk.Debug(whisk.DbgError, "No swagger file and no arguments\n")
+ errMsg := wski18n.T("Invalid argument(s). Specify a swagger file or specify an API base path with an API path, an API verb, and an action name.")
+ whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return whiskErr
+ } else if (len(args) == 0 && flags.api.configfile != "") {
+ api, err = parseSwaggerApiV2()
+ if err != nil {
+ whisk.Debug(whisk.DbgError, "parseSwaggerApi() error: %s\n", err)
+ errMsg := wski18n.T("Unable to parse swagger file: {{.err}}", map[string]interface{}{"err": err})
+ whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return whiskErr
+ }
+ } else {
+ if whiskErr := checkArgs(args, 3, 4, "Api create",
+ wski18n.T("Specify a swagger file or specify an API base path with an API path, an API verb, and an action name.")); whiskErr != nil {
+ return whiskErr
+ }
+ api, qname, err = parseApiV2(cmd, args)
+ if err != nil {
+ whisk.Debug(whisk.DbgError, "parseApiV2(%s, %s) error: %s\n", cmd, args, err)
+ errMsg := wski18n.T("Unable to parse api command arguments: {{.err}}",
+ map[string]interface{}{"err": err})
+ whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return whiskErr
+ }
+
+ // Confirm that the specified action is a web-action
+ err = isWebAction(client, *qname)
+ if err != nil {
+ whisk.Debug(whisk.DbgError, "isWebAction(%v) is false: %s\n", qname, err)
+ whiskErr := whisk.MakeWskError(err, whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return whiskErr
+ }
+ }
+
+ apiCreateReq := new(whisk.ApiCreateRequest)
+ apiCreateReq.ApiDoc = api
+
+ apiCreateReqOptions := new(whisk.ApiCreateRequestOptions)
+ props, _ := readProps(Properties.PropsFile)
+ apiCreateReqOptions.SpaceGuid = strings.Split(props["AUTH"], ":")[0]
+ apiCreateReqOptions.AccessToken = "DUMMY_TOKEN"
+ if len(props["APIGW_ACCESS_TOKEN"]) > 0 {
+ apiCreateReqOptions.AccessToken = props["APIGW_ACCESS_TOKEN"]
+ }
+ apiCreateReqOptions.ResponseType = flags.api.resptype
+ whisk.Debug(whisk.DbgInfo, "AccessToken: %s\nSpaceGuid: %s\nResponsType: %s",
+ apiCreateReqOptions.AccessToken, apiCreateReqOptions.SpaceGuid, apiCreateReqOptions.ResponseType)
+
+ retApi, _, err := client.Apis.InsertV2(apiCreateReq, apiCreateReqOptions, whisk.DoNotOverwrite)
+ if err != nil {
+ whisk.Debug(whisk.DbgError, "client.Apis.InsertV2(%#v, false) error: %s\n", api, err)
+ errMsg := wski18n.T("Unable to create API: {{.err}}", map[string]interface{}{"err": err})
+ whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
+ return whiskErr
+ }
+
+ if (api.Swagger == "") {
+ baseUrl := retApi.BaseUrl
+ fmt.Fprintf(color.Output,
+ wski18n.T("{{.ok}} created API {{.path}} {{.verb}} for action {{.name}}\n{{.fullpath}}\n",
+ map[string]interface{}{
+ "ok": color.GreenString("ok:"),
+ "path": strings.TrimSuffix(api.GatewayBasePath, "/")+api.GatewayRelPath,
+ "verb": api.GatewayMethod,
+ "name": boldString("/"+api.Action.Namespace+"/"+api.Action.Name),
+ "fullpath": strings.TrimSuffix(baseUrl, "/")+api.GatewayRelPath,
+ }))
+ } else {
+ whisk.Debug(whisk.DbgInfo, "Processing swagger based create API response\n")
+ baseUrl := retApi.BaseUrl
+ for path, _ := range retApi.Swagger.Paths {
+ managedUrl := strings.TrimSuffix(baseUrl, "/")+path
+ whisk.Debug(whisk.DbgInfo, "Managed path: %s\n",managedUrl)
+ for op, opv := range retApi.Swagger.Paths[path] {
+ whisk.Debug(whisk.DbgInfo, "Path operation: %s\n", op)
+ var fqActionName string
+ if (len(opv.XOpenWhisk.Package) > 0) {
+ fqActionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.Package+"/"+opv.XOpenWhisk.ActionName
+ } else {
+ fqActionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.ActionName
+ }
+ whisk.Debug(whisk.DbgInfo, "baseUrl %s Path %s Path obj %+v\n", baseUrl, path, opv)
+ fmt.Fprintf(color.Output,
+ wski18n.T("{{.ok}} created API {{.path}} {{.verb}} for action {{.name}}\n{{.fullpath}}\n",
+ map[string]interface{}{
+ "ok": color.GreenString("ok:"),
+ "path": strings.TrimSuffix(retApi.Swagger.BasePath, "/") + path,
+ "verb": op,
+ "name": boldString(fqActionName),
+ "fullpath": managedUrl,
+ }))
+ }
+ }
+ }
+
+
+ return nil
+ },
+}
+
+var apiGetCmdV2 = &cobra.Command{
+ Use: "get BASE_PATH | API_NAME",
+ Short: wski18n.T("get API details"),
+ SilenceUsage: true,
+ SilenceErrors: true,
+ PreRunE: setupClientConfig,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ var err error
+ var isBasePathArg bool = true
+
+ if whiskErr := checkArgs(args, 1, 1, "Api get",
+ wski18n.T("An API base path or API name is required.")); whiskErr != nil {
+ return whiskErr
+ }
+
+ apiGetReq := new(whisk.ApiGetRequest)
+ apiGetReqOptions := new(whisk.ApiGetRequestOptions)
+ apiGetReqOptions.ApiBasePath = args[0]
+ props, _ := readProps(Properties.PropsFile)
+ apiGetReqOptions.SpaceGuid = strings.Split(props["AUTH"], ":")[0]
+ apiGetReqOptions.AccessToken = "DUMMY_TOKEN"
+ if len(props["APIGW_ACCESS_TOKEN"]) > 0 {
+ apiGetReqOptions.AccessToken = props["APIGW_ACCESS_TOKEN"]
+ }
+
+ retApi, _, err := client.Apis.GetV2(apiGetReq, apiGetReqOptions)
+ if err != nil {
+ whisk.Debug(whisk.DbgError, "client.Apis.GetV2(%#v, %#v) error: %s\n", apiGetReq, apiGetReqOptions, err)
+ errMsg := wski18n.T("Unable to get API '{{.name}}': {{.err}}", map[string]interface{}{"name": args[0], "err": err})
+ whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
+ return whiskErr
+ }
+ whisk.Debug(whisk.DbgInfo, "client.Apis.GetV2 returned: %#v\n", retApi)
+
+ var displayResult interface{} = nil
+ if (flags.common.detail) {
+ if (retApi.Apis != nil && len(retApi.Apis) > 0 &&
+ retApi.Apis[0].ApiValue != nil) {
+ displayResult = retApi.Apis[0].ApiValue
+ } else {
+ whisk.Debug(whisk.DbgError, "No result object returned\n")
+ }
+ } else {
+ if (retApi.Apis != nil && len(retApi.Apis) > 0 &&
+ retApi.Apis[0].ApiValue != nil &&
+ retApi.Apis[0].ApiValue.Swagger != nil) {
+ displayResult = retApi.Apis[0].ApiValue.Swagger
+ } else {
+ whisk.Debug(whisk.DbgError, "No swagger returned\n")
+ }
+ }
+ if (displayResult == nil) {
+ var errMsg string
+ if (isBasePathArg) {
+ errMsg = wski18n.T("API does not exist for basepath {{.basepath}}",
+ map[string]interface{}{"basepath": args[0]})
+ } else {
+ errMsg = wski18n.T("API does not exist for API name {{.apiname}}",
+ map[string]interface{}{"apiname": args[0]})
+ }
+
+ whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
+ return whiskErr
+ }
+ printJSON(displayResult)
+
+ return nil
+ },
+}
+
+var apiDeleteCmdV2 = &cobra.Command{
+ Use: "delete BASE_PATH | API_NAME [API_PATH [API_VERB]]",
+ Short: wski18n.T("delete an API"),
+ SilenceUsage: true,
+ SilenceErrors: true,
+ PreRunE: setupClientConfig,
+ RunE: func(cmd *cobra.Command, args []string) error {
+
+ if whiskErr := checkArgs(args, 1, 3, "Api delete",
+ wski18n.T("An API base path or API name is required. An optional API relative path and operation may also be provided.")); whiskErr != nil {
+ return whiskErr
+ }
+
+ apiDeleteReq := new(whisk.ApiDeleteRequest)
+ apiDeleteReqOptions := new(whisk.ApiDeleteRequestOptions)
+ props, _ := readProps(Properties.PropsFile)
+ apiDeleteReqOptions.SpaceGuid = strings.Split(props["AUTH"], ":")[0]
+ apiDeleteReqOptions.AccessToken = "DUMMY_TOKEN"
+ if len(props["APIGW_ACCESS_TOKEN"]) > 0 {
+ apiDeleteReqOptions.AccessToken = props["APIGW_ACCESS_TOKEN"]
+ }
+
+ // Is the argument a basepath (must start with /) or an API name
+ if _, ok := isValidBasepath(args[0]); !ok {
+ whisk.Debug(whisk.DbgInfo, "Treating '%s' as an API name; as it does not begin with '/'\n", args[0])
+ apiDeleteReqOptions.ApiBasePath = args[0]
+ } else {
+ apiDeleteReqOptions.ApiBasePath = args[0]
+ }
+
+ if (len(args) > 1) {
+ // Is the API path valid?
+ if whiskErr, ok := isValidRelpath(args[1]); !ok {
+ return whiskErr
+ }
+ apiDeleteReqOptions.ApiRelPath = args[1]
+ }
+ if (len(args) > 2) {
+ // Is the API verb valid?
+ if whiskErr, ok := IsValidApiVerb(args[2]); !ok {
+ return whiskErr
+ }
+ apiDeleteReqOptions.ApiVerb = strings.ToUpper(args[2])
+ }
+
+ _, err := client.Apis.DeleteV2(apiDeleteReq, apiDeleteReqOptions)
+ if err != nil {
+ whisk.Debug(whisk.DbgError, "client.Apis.DeleteV2(%#v, %#v) error: %s\n", apiDeleteReq, apiDeleteReqOptions, err)
+ errMsg := wski18n.T("Unable to delete API: {{.err}}", map[string]interface{}{"err": err})
+ whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
+ return whiskErr
+ }
+
+ if (len(args) == 1) {
+ fmt.Fprintf(color.Output,
+ wski18n.T("{{.ok}} deleted API {{.basepath}}\n",
+ map[string]interface{}{
+ "ok": color.GreenString("ok:"),
+ "basepath": apiDeleteReqOptions.ApiBasePath,
+ }))
+ } else if (len(args) == 2 ) {
+ fmt.Fprintf(color.Output,
+ wski18n.T("{{.ok}} deleted {{.path}} from {{.basepath}}\n",
+ map[string]interface{}{
+ "ok": color.GreenString("ok:"),
+ "path": apiDeleteReqOptions.ApiRelPath,
+ "basepath": apiDeleteReqOptions.ApiBasePath,
+ }))
+ } else {
+ fmt.Fprintf(color.Output,
+ wski18n.T("{{.ok}} deleted {{.path}} {{.verb}} from {{.basepath}}\n",
+ map[string]interface{}{
+ "ok": color.GreenString("ok:"),
+ "path": apiDeleteReqOptions.ApiRelPath,
+ "verb": apiDeleteReqOptions.ApiVerb,
+ "basepath": apiDeleteReqOptions.ApiBasePath,
+ }))
+ }
+
+ return nil
+ },
+}
+
+var apiListCmdV2 = &cobra.Command{
+ Use: "list [[BASE_PATH | API_NAME] [API_PATH [API_VERB]]",
+ Short: wski18n.T("list APIs"),
+ SilenceUsage: true,
+ SilenceErrors: true,
+ PreRunE: setupClientConfig,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ var err error
+ var retApiList *whisk.ApiListResponseV2
+ var retApi *whisk.ApiGetResponseV2
+ var retApiArray *whisk.RetApiArrayV2
+
+ 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 {
+ return whiskErr
+ }
+
+ props, _ := readProps(Properties.PropsFile)
+ spaceguid := strings.Split(props["AUTH"], ":")[0]
+ var accesstoken string = "DUMMY_TOKEN"
+ if len(props["APIGW_ACCESS_TOKEN"]) > 0 {
+ accesstoken = props["APIGW_ACCESS_TOKEN"]
+ }
+
+ // Get API request body
+ apiGetReq := new(whisk.ApiGetRequest)
+ apiGetReq.Namespace = client.Config.Namespace
+ // Get API request options
+ apiGetReqOptions := new(whisk.ApiGetRequestOptions)
+ apiGetReqOptions.AccessToken = accesstoken
+ apiGetReqOptions.SpaceGuid = spaceguid
+
+ // List API request query parameters
+ apiListReqOptions := new(whisk.ApiListRequestOptions)
+ apiListReqOptions.Limit = flags.common.limit
+ apiListReqOptions.Skip = flags.common.skip
+ apiListReqOptions.AccessToken = accesstoken
+ apiListReqOptions.SpaceGuid = spaceguid
+
+ if (len(args) == 0) {
+ retApiList, _, err = client.Apis.ListV2(apiListReqOptions)
+ if err != nil {
+ whisk.Debug(whisk.DbgError, "client.Apis.ListV2(%#v) error: %s\n", apiListReqOptions, err)
+ errMsg := wski18n.T("Unable to obtain the API list: {{.err}}", map[string]interface{}{"err": err})
+ whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
+ return whiskErr
+ }
+ whisk.Debug(whisk.DbgInfo, "client.Apis.ListV2 returned: %#v (%+v)\n", retApiList, retApiList)
+ // Cast to a common type to allow for code to print out apilist response or apiget response
+ retApiArray = (*whisk.RetApiArrayV2)(retApiList)
+ } else {
+ // The first argument is either a basepath (must start with /) or an API name
+ apiGetReqOptions.ApiBasePath = args[0]
+ if (len(args) > 1) {
+ // Is the API path valid?
+ if whiskErr, ok := isValidRelpath(args[1]); !ok {
+ return whiskErr
+ }
+ apiGetReqOptions.ApiRelPath = args[1]
+ }
+ if (len(args) > 2) {
+ // Is the API verb valid?
+ if whiskErr, ok := IsValidApiVerb(args[2]); !ok {
+ return whiskErr
+ }
+ apiGetReqOptions.ApiVerb = strings.ToUpper(args[2])
+ }
+
+ retApi, _, err = client.Apis.GetV2(apiGetReq, apiGetReqOptions)
+ if err != nil {
+ whisk.Debug(whisk.DbgError, "client.Apis.GetV2(%#v, %#v) error: %s\n", apiGetReq, apiGetReqOptions, err)
+ errMsg := wski18n.T("Unable to obtain the API list: {{.err}}", map[string]interface{}{"err": err})
+ whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
+ return whiskErr
+ }
+ whisk.Debug(whisk.DbgInfo, "client.Apis.GetV2 returned: %#v\n", retApi)
+ // Cast to a common type to allow for code to print out apilist response or apiget response
+ retApiArray = (*whisk.RetApiArrayV2)(retApi)
+ }
+
+ // Display the APIs - applying any specified filtering
+ if (flags.common.full) {
+ fmt.Fprintf(color.Output,
+ wski18n.T("{{.ok}} APIs\n",
+ map[string]interface{}{
+ "ok": color.GreenString("ok:"),
+ }))
+
+ for i:=0; i<len(retApiArray.Apis); i++ {
+ printFilteredListApiV2(retApiArray.Apis[i].ApiValue, (*whisk.ApiOptions)(apiGetReqOptions))
+ }
+ } else {
+ if (len(retApiArray.Apis) > 0) {
+ // Dynamically create the output format string based on the maximum size of the
+ // fully qualified action name and the API Name.
+ maxActionNameSize := min(40, max(len("Action"), getLargestActionNameSizeV2(retApiArray, (*whisk.ApiOptions)(apiGetReqOptions))))
+ maxApiNameSize := min(30, max(len("API Name"), getLargestApiNameSizeV2(retApiArray, (*whisk.ApiOptions)(apiGetReqOptions))))
+ fmtString = "%-"+strconv.Itoa(maxActionNameSize)+"s %7s %"+strconv.Itoa(maxApiNameSize+1)+"s %s\n"
+ fmt.Fprintf(color.Output,
+ wski18n.T("{{.ok}} APIs\n",
+ map[string]interface{}{
+ "ok": color.GreenString("ok:"),
+ }))
+ fmt.Printf(fmtString, "Action", "Verb", "API Name", "URL")
+ for i:=0; i<len(retApiArray.Apis); i++ {
+ printFilteredListRowV2(retApiArray.Apis[i].ApiValue, (*whisk.ApiOptions)(apiGetReqOptions), maxActionNameSize, maxApiNameSize)
+ }
+ } 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")
+ }
+ }
+
+ return nil
+ },
+}
+
+/*
+ * 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 printFilteredListApiV2(resultApi *whisk.RetApiV2, api *whisk.ApiOptions) {
+ 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, "printFilteredListApiV2: comparing api relpath: %s\n", path)
+ if ( len(api.ApiRelPath) == 0 || path == api.ApiRelPath) {
+ whisk.Debug(whisk.DbgInfo, "printFilteredListApiV2: relpath matches\n")
+ for op, opv := range resultApi.Swagger.Paths[path] {
+ whisk.Debug(whisk.DbgInfo, "printFilteredListApiV2: comparing operation: '%s'\n", op)
+ if ( len(api.ApiVerb) == 0 || strings.ToLower(op) == strings.ToLower(api.ApiVerb)) {
+ whisk.Debug(whisk.DbgInfo, "printFilteredListApiV2: operation matches: %#v\n", opv)
+ var actionName string
+ if (len(opv.XOpenWhisk.Package) > 0) {
+ actionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.Package+"/"+opv.XOpenWhisk.ActionName
+ } 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)
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * 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 printFilteredListRowV2(resultApi *whisk.RetApiV2, api *whisk.ApiOptions, maxActionNameSize int, maxApiNameSize int) {
+ baseUrl := strings.TrimSuffix(resultApi.BaseUrl, "/")
+ apiName := resultApi.Swagger.Info.Title
+ if (resultApi.Swagger != nil && resultApi.Swagger.Paths != nil) {
+ for path, _ := range resultApi.Swagger.Paths {
+ whisk.Debug(whisk.DbgInfo, "printFilteredListRowV2: comparing api relpath: %s\n", path)
+ if ( len(api.ApiRelPath) == 0 || path == api.ApiRelPath) {
+ whisk.Debug(whisk.DbgInfo, "printFilteredListRowV2: relpath matches\n")
+ for op, opv := range resultApi.Swagger.Paths[path] {
+ whisk.Debug(whisk.DbgInfo, "printFilteredListRowV2: comparing operation: '%s'\n", op)
+ if ( len(api.ApiVerb) == 0 || strings.ToLower(op) == strings.ToLower(api.ApiVerb)) {
+ whisk.Debug(whisk.DbgInfo, "printFilteredListRowV2: operation matches: %#v\n", opv)
+ var actionName string
+ if (len(opv.XOpenWhisk.Package) > 0) {
+ actionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.Package+"/"+opv.XOpenWhisk.ActionName
+ } 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)
+ }
+ }
+ }
+ }
+ }
+}
+
+func getLargestActionNameSizeV2(retApiArray *whisk.RetApiArrayV2, api *whisk.ApiOptions) int {
+ var maxNameSize = 0
+ 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 {
+ whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: comparing api relpath: %s\n", path)
+ if ( len(api.ApiRelPath) == 0 || path == api.ApiRelPath) {
+ whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: relpath matches\n")
+ for op, opv := range resultApi.Swagger.Paths[path] {
+ whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: comparing operation: '%s'\n", op)
+ if ( len(api.ApiVerb) == 0 || strings.ToLower(op) == strings.ToLower(api.ApiVerb)) {
+ whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: operation matches: %#v\n", opv)
+ var fullActionName string
+ if (len(opv.XOpenWhisk.Package) > 0) {
+ fullActionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.Package+"/"+opv.XOpenWhisk.ActionName
+ } else {
+ fullActionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.ActionName
+ }
+ if (len(fullActionName) > maxNameSize) {
+ maxNameSize = len(fullActionName)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return maxNameSize
+}
+
+func getLargestApiNameSizeV2(retApiArray *whisk.RetApiArrayV2, api *whisk.ApiOptions) int {
+ var maxNameSize = 0
+ 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) {
+ for path, _ := range resultApi.Swagger.Paths {
+ whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: comparing api relpath: %s\n", path)
+ if ( len(api.ApiRelPath) == 0 || path == api.ApiRelPath) {
+ whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: relpath matches\n")
+ for op, opv := range resultApi.Swagger.Paths[path] {
+ whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: comparing operation: '%s'\n", op)
+ if ( len(api.ApiVerb) == 0 || strings.ToLower(op) == strings.ToLower(api.ApiVerb)) {
+ whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: operation matches: %#v\n", opv)
+ if (len(apiName) > maxNameSize) {
+ maxNameSize = len(apiName)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return maxNameSize
+}
+
+/*
+ * if # args = 4
+ * args[0] = API base path
+ * args[0] = API relative path
+ * args[1] = API verb
+ * args[2] = Optional. Action name (may or may not be qualified with namespace and package name)
+ *
+ * if # args = 3
+ * args[0] = API relative path
+ * args[1] = API verb
+ * args[2] = Optional. Action name (may or may not be qualified with namespace and package name)
+ */
+func parseApiV2(cmd *cobra.Command, args []string) (*whisk.Api, *QualifiedName, error) {
+ var err error
+ var basepath string = "/"
+ var apiname string
+ var basepathArgIsApiName = false;
+
+ api := new(whisk.Api)
+
+ if (len(args) > 3) {
+ // Is the argument a basepath (must start with /) or an API name
+ if _, ok := isValidBasepath(args[0]); !ok {
+ whisk.Debug(whisk.DbgInfo, "Treating '%s' as an API name; as it does not begin with '/'\n", args[0])
+ basepathArgIsApiName = true;
+ }
+ basepath = args[0]
+
+ // Shift the args so the remaining code works with or without the explicit base path arg
+ args = args[1:]
+ }
+
+ // Is the API path valid?
+ if (len(args) > 0) {
+ if whiskErr, ok := isValidRelpath(args[0]); !ok {
+ return nil, nil, whiskErr
+ }
+ api.GatewayRelPath = args[0] // Maintain case as URLs may be case-sensitive
+ }
+
+ // Is the API verb valid?
+ if (len(args) > 1) {
+ if whiskErr, ok := IsValidApiVerb(args[1]); !ok {
+ return nil, nil, whiskErr
+ }
+ api.GatewayMethod = strings.ToUpper(args[1])
+ }
+
+ // Is the specified action name valid?
+ var qName QualifiedName
+ if (len(args) == 3) {
+ qName = QualifiedName{}
+ qName, err = parseQualifiedName(args[2])
+ if err != nil {
+ whisk.Debug(whisk.DbgError, "parseQualifiedName(%s) failed: %s\n", args[2], err)
+ errMsg := wski18n.T("'{{.name}}' is not a valid action name: {{.err}}",
+ map[string]interface{}{"name": args[2], "err": err})
+ whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return nil, nil, whiskErr
+ }
+ if (qName.entityName == "") {
+ whisk.Debug(whisk.DbgError, "Action name '%s' is invalid\n", args[2])
+ errMsg := wski18n.T("'{{.name}}' is not a valid action name.", map[string]interface{}{"name": args[2]})
+ whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return nil, nil, whiskErr
+ }
+ }
+
+ if ( len(flags.api.apiname) > 0 ) {
+ if (basepathArgIsApiName) {
+ // Specifying API name as argument AND as a --apiname option value is invalid
+ whisk.Debug(whisk.DbgError, "API is specified as an argument '%s' and as a flag '%s'\n", basepath, flags.api.apiname)
+ errMsg := wski18n.T("An API name can only be specified once.")
+ whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return nil, nil, whiskErr
+ }
+ apiname = flags.api.apiname
+ }
+
+ api.Namespace = client.Config.Namespace
+ api.Action = new(whisk.ApiAction)
+ var urlActionPackage string
+ if (len(qName.packageName) > 0) {
+ urlActionPackage = qName.packageName
+ } else {
+ urlActionPackage = "default"
+ }
+ api.Action.BackendUrl = "https://" + client.Config.Host + "/api/v1/web/" + qName.namespace + "/" + urlActionPackage + "/" + qName.entity + ".http"
+ api.Action.BackendMethod = api.GatewayMethod
+ api.Action.Name = qName.entityName
+ api.Action.Namespace = qName.namespace
+ api.Action.Auth = client.Config.AuthToken
+ api.ApiName = apiname
+ api.GatewayBasePath = basepath
+ if (!basepathArgIsApiName) { api.Id = "API:"+api.Namespace+":"+api.GatewayBasePath }
+
+ whisk.Debug(whisk.DbgInfo, "Parsed api struct: %#v\n", api)
+ return api, &qName, nil
+}
+
+func parseSwaggerApiV2() (*whisk.Api, error) {
+ // Test is for completeness, but this situation should only arise due to an internal error
+ if ( len(flags.api.configfile) == 0 ) {
+ whisk.Debug(whisk.DbgError, "No swagger file is specified\n")
+ errMsg := wski18n.T("A configuration file was not specified.")
+ whiskErr := whisk.MakeWskError(errors.New(errMsg),whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return nil, whiskErr
+ }
+
+ swagger, err:= readFile(flags.api.configfile)
+ if ( err != nil ) {
+ whisk.Debug(whisk.DbgError, "readFile(%s) error: %s\n", flags.api.configfile, err)
+ errMsg := wski18n.T("Error reading swagger file '{{.name}}': {{.err}}",
+ map[string]interface{}{"name": flags.api.configfile, "err": err})
+ whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return nil, whiskErr
+ }
+
+ // Parse the JSON into a swagger object
+ swaggerObj := new(whisk.ApiSwaggerV2)
+ err = json.Unmarshal([]byte(swagger), swaggerObj)
+ if ( err != nil ) {
+ whisk.Debug(whisk.DbgError, "JSON parse of `%s' error: %s\n", flags.api.configfile, err)
+ errMsg := wski18n.T("Error parsing swagger file '{{.name}}': {{.err}}",
+ map[string]interface{}{"name": flags.api.configfile, "err": err})
+ whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return nil, whiskErr
+ }
+ if (swaggerObj.BasePath == "" || swaggerObj.SwaggerName == "" || swaggerObj.Info == nil || swaggerObj.Paths == nil) {
+ whisk.Debug(whisk.DbgError, "Swagger file is invalid.\n", flags.api.configfile, err)
+ errMsg := wski18n.T("Swagger file is invalid (missing basePath, info, paths, or swagger fields)")
+ whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return nil, whiskErr
+ }
+ if _, ok := isValidBasepath(swaggerObj.BasePath); !ok {
+ whisk.Debug(whisk.DbgError, "Swagger file basePath is invalid.\n", flags.api.configfile, err)
+ errMsg := wski18n.T("Swagger file basePath must start with a leading slash (/)")
+ whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
+ whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+ return nil, whiskErr
+ }
+
+ api := new(whisk.Api)
+ api.Namespace = client.Config.Namespace
+ api.Swagger = swagger
+
+ return api, nil
+}
+
///////////
// Flags //
///////////
@@ -756,17 +1456,13 @@
func init() {
apiCreateCmd.Flags().StringVarP(&flags.api.apiname, "apiname", "n", "", wski18n.T("Friendly name of the API; ignored when CFG_FILE is specified (default BASE_PATH)"))
apiCreateCmd.Flags().StringVarP(&flags.api.configfile, "config-file", "c", "", wski18n.T("`CFG_FILE` containing API configuration in swagger JSON format"))
-
//apiUpdateCmd.Flags().StringVarP(&flags.api.action, "action", "a", "", wski18n.T("`ACTION` to invoke when API is called"))
//apiUpdateCmd.Flags().StringVarP(&flags.api.path, "path", "p", "", wski18n.T("relative `PATH` of API"))
//apiUpdateCmd.Flags().StringVarP(&flags.api.verb, "method", "m", "", wski18n.T("API `VERB`"))
-
apiGetCmd.Flags().BoolVarP(&flags.common.detail, "full", "f", false, wski18n.T("display full API configuration details"))
-
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.full, "full", "f", false, wski18n.T("display full description of each API"))
-
apiExperimentalCmd.AddCommand(
apiCreateCmd,
//apiUpdateCmd,
@@ -774,4 +1470,18 @@
apiDeleteCmd,
apiListCmd,
)
+
+ apiCreateCmdV2.Flags().StringVarP(&flags.api.apiname, "apiname", "n", "", wski18n.T("Friendly name of the API; ignored when CFG_FILE is specified (default BASE_PATH)"))
+ apiCreateCmdV2.Flags().StringVarP(&flags.api.configfile, "config-file", "c", "", wski18n.T("`CFG_FILE` containing API configuration in swagger JSON format"))
+ apiCreateCmdV2.Flags().StringVar(&flags.api.resptype, "response-type", "json", wski18n.T("Set the web action response `TYPE`. Possible values are html, http, json, text, svg"))
+ apiGetCmdV2.Flags().BoolVarP(&flags.common.detail, "full", "f", false, wski18n.T("display full API configuration details"))
+ apiListCmdV2.Flags().IntVarP(&flags.common.skip, "skip", "s", 0, wski18n.T("exclude the first `SKIP` number of actions from the result"))
+ apiListCmdV2.Flags().IntVarP(&flags.common.limit, "limit", "l", 30, wski18n.T("only return `LIMIT` number of actions from the collection"))
+ apiListCmdV2.Flags().BoolVarP(&flags.common.full, "full", "f", false, wski18n.T("display full description of each API"))
+ apiCmd.AddCommand(
+ apiCreateCmdV2,
+ apiGetCmdV2,
+ apiDeleteCmdV2,
+ apiListCmdV2,
+ )
}
diff --git a/commands/flags.go b/commands/flags.go
index 9f632bd..e48ac93 100644
--- a/commands/flags.go
+++ b/commands/flags.go
@@ -117,6 +117,7 @@
basepath string
apiname string
configfile string
+ resptype string
}
}
diff --git a/commands/package.go b/commands/package.go
index b8eda24..939eff5 100644
--- a/commands/package.go
+++ b/commands/package.go
@@ -192,7 +192,12 @@
p, _, err = client.Packages.Insert(p, false)
if err != nil {
whisk.Debug(whisk.DbgError, "client.Packages.Insert(%#v, false) failed: %s\n", p, err)
- errStr := wski18n.T("Package creation failed: {{.err}}", map[string]interface{}{"err":err})
+ errStr := wski18n.T(
+ "Unable to create package '{{.name}}': {{.err}}",
+ map[string]interface{}{
+ "name": qName.entityName,
+ "err": err,
+ })
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
@@ -318,7 +323,12 @@
xPackage, _, err := client.Packages.Get(qName.entityName)
if err != nil {
whisk.Debug(whisk.DbgError, "client.Packages.Get(%s) failed: %s\n", qName.entityName, err)
- errStr := wski18n.T("Package get failed: {{.err}}", map[string]interface{}{"err":err})
+ errStr := wski18n.T(
+ "Unable to get package '{{.name}}': {{.err}}",
+ map[string]interface{}{
+ "name": qName.entityName,
+ "err":err,
+ })
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
@@ -370,7 +380,12 @@
_, err = client.Packages.Delete(qName.entityName)
if err != nil {
whisk.Debug(whisk.DbgError, "client.Packages.Delete(%s) failed: %s\n", qName.entityName, err)
- errStr := wski18n.T("Package delete failed: {{.err}}", map[string]interface{}{"err":err})
+ errStr := wski18n.T(
+ "Unable to delete package '{{.name}}': {{.err}}",
+ map[string]interface{}{
+ "name": qName.entityName,
+ "err": err,
+ })
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
diff --git a/commands/rule.go b/commands/rule.go
index 8454807..b537aff 100644
--- a/commands/rule.go
+++ b/commands/rule.go
@@ -146,7 +146,7 @@
rule, _, err := client.Rules.Get(ruleName)
if err != nil {
whisk.Debug(whisk.DbgError, "client.Rules.Get(%s) failed: %s\n", ruleName, err)
- errStr := wski18n.T("Unable to retrieve rule '{{.name}}': {{.err}}",
+ errStr := wski18n.T("Unable to get status of rule '{{.name}}': {{.err}}",
map[string]interface{}{"name": ruleName, "err": err})
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
return werr
@@ -305,7 +305,7 @@
rule, _, err := client.Rules.Get(ruleName)
if err != nil {
whisk.Debug(whisk.DbgError, "client.Rules.Get(%s) failed: %s\n", ruleName, err)
- errStr := wski18n.T("Unable to retrieve rule '{{.name}}': {{.err}}",
+ errStr := wski18n.T("Unable to get rule '{{.name}}': {{.err}}",
map[string]interface{}{"name": ruleName, "err": err})
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
return werr
diff --git a/commands/util.go b/commands/util.go
index ad51ac2..ccabcd1 100644
--- a/commands/util.go
+++ b/commands/util.go
@@ -42,9 +42,10 @@
)
type QualifiedName struct {
- namespace string
- packageName string
- entityName string
+ namespace string // namespace. does not include leading '/'. may be "" (i.e. default namespace)
+ packageName string // package. may be "". does not include leading/trailing '/'
+ entity string // entity. should not be ""
+ entityName string // pkg+entity
}
func (qName QualifiedName) String() string {
@@ -100,6 +101,10 @@
}
qualifiedName.entityName = strings.Join(parts[2:], "/")
+ if len(parts) == 4 {
+ qualifiedName.packageName = parts[2]
+ }
+ qualifiedName.entity = parts[len(parts)-1]
} else {
if len(name) == 0 || name == "." {
whisk.Debug(whisk.DbgError, "A valid qualified name was not detected\n")
@@ -108,12 +113,19 @@
return qualifiedName, err
}
+ parts := strings.Split(name, "/")
+ qualifiedName.entity = parts[len(parts)-1]
+ if len(parts) == 2 {
+ qualifiedName.packageName = parts[0]
+ }
qualifiedName.entityName = name
qualifiedName.namespace = getNamespace()
}
- whisk.Debug(whisk.DbgInfo, "Qualified entityName: %s\n", qualifiedName.entityName)
- whisk.Debug(whisk.DbgInfo, "Qaulified namespace: %s\n", qualifiedName.namespace)
+ whisk.Debug(whisk.DbgInfo, "Qualified pkg+entity (EntityName): %s\n", qualifiedName.entityName)
+ whisk.Debug(whisk.DbgInfo, "Qualified namespace: %s\n", qualifiedName.namespace)
+ whisk.Debug(whisk.DbgInfo, "Qualified package: %s\n", qualifiedName.packageName)
+ whisk.Debug(whisk.DbgInfo, "Qualified entity: %s\n", qualifiedName.entity)
return qualifiedName, nil
}
@@ -168,9 +180,11 @@
whisk.Debug(whisk.DbgInfo, "Convert content to JSON: %#v\n", content)
for i := 0; i < len(content); i++ {
- if err := json.Unmarshal([]byte(content[i]), &data); err != nil {
- whisk.Debug(whisk.DbgError, "Invalid JSON detected for '%s'\n", content[i])
- return whisk.KeyValueArr{}, err
+ dc := json.NewDecoder(strings.NewReader(content[i]))
+ dc.UseNumber()
+ if err := dc.Decode(&data); err!=nil {
+ whisk.Debug(whisk.DbgError, "Invalid JSON detected for '%s' \n", content[i])
+ return whisk.KeyValueArr{} , err
}
whisk.Debug(whisk.DbgInfo, "Created map '%v' from '%v'\n", data, content[i])
diff --git a/commands/wsk.go b/commands/wsk.go
index 870a510..beca322 100644
--- a/commands/wsk.go
+++ b/commands/wsk.go
@@ -48,6 +48,7 @@
namespaceCmd,
listCmd,
apiExperimentalCmd,
+ apiCmd,
)
WskCmd.PersistentFlags().BoolVarP(&flags.global.verbose, "verbose", "v", false, wski18n.T("verbose output"))
diff --git a/wski18n/resources/en_US.all.json b/wski18n/resources/en_US.all.json
index 8a60560..2a0deb4 100644
--- a/wski18n/resources/en_US.all.json
+++ b/wski18n/resources/en_US.all.json
@@ -120,8 +120,8 @@
"translation": "create a new package"
},
{
- "id": "Package creation failed: {{.err}}",
- "translation": "Package creation failed: {{.err}}"
+ "id": "Unable to create package '{{.name}}': {{.err}}",
+ "translation": "Unable to create package '{{.name}}': {{.err}}"
},
{
"id": "{{.ok}} created package {{.name}}\n",
@@ -144,8 +144,8 @@
"translation": "get package"
},
{
- "id": "Package get failed: {{.err}}",
- "translation": "Package get failed: {{.err}}"
+ "id": "Unable to get package '{{.name}}': {{.err}}",
+ "translation": "Unable to get package '{{.name}}': {{.err}}"
},
{
"id": "{{.ok}} got package {{.name}}\n",
@@ -156,8 +156,8 @@
"translation": "delete package"
},
{
- "id": "Package delete failed: {{.err}}",
- "translation": "Package delete failed: {{.err}}"
+ "id": "Unable to delete package '{{.name}}': {{.err}}",
+ "translation": "Unable to delete package '{{.name}}': {{.err}}"
},
{
"id": "{{.ok}} deleted package {{.name}}\n",
@@ -421,8 +421,12 @@
"translation": "get rule status"
},
{
- "id": "Unable to retrieve rule '{{.name}}': {{.err}}",
- "translation": "Unable to retrieve rule '{{.name}}': {{.err}}"
+ "id": "Unable to get rule '{{.name}}': {{.err}}",
+ "translation": "Unable to get rule '{{.name}}': {{.err}}"
+ },
+ {
+ "id": "Unable to get status of rule '{{.name}}': {{.err}}",
+ "translation": "Unable to get status of rule '{{.name}}': {{.err}}"
},
{
"id": "{{.ok}} rule {{.name}} is {{.status}}\n",
@@ -772,8 +776,8 @@
"translation": "Unable to parse action command arguments: {{.err}}"
},
{
- "id": "Unable to create action: {{.err}}",
- "translation": "Unable to create action: {{.err}}"
+ "id": "Unable to create action '{{.name}}': {{.err}}",
+ "translation": "Unable to create action '{{.name}}': {{.err}}"
},
{
"id": "{{.ok}} created action {{.name}}\n",
@@ -824,8 +828,8 @@
"translation": "delete action"
},
{
- "id": "Unable to delete action: {{.err}}",
- "translation": "Unable to delete action: {{.err}}"
+ "id": "Unable to delete action '{{.name}}': {{.err}}",
+ "translation": "Unable to delete action '{{.name}}': {{.err}}"
},
{
"id": "{{.ok}} deleted action {{.name}}\n",
@@ -844,8 +848,8 @@
"translation": "Could not find 'main' method in '{{.name}}'"
},
{
- "id": "Unable to obtain action '{{.name}}' to copy: {{.err}}",
- "translation": "Unable to obtain action '{{.name}}' to copy: {{.err}}"
+ "id": "Unable to get action '{{.name}}': {{.err}}",
+ "translation": "Unable to get action '{{.name}}': {{.err}}"
},
{
"id": "File '{{.name}}' is not a valid file or it does not exist: {{.err}}",
@@ -908,8 +912,8 @@
"translation": "blocking invoke"
},
{
- "id": "show only activation result if a blocking activation (unless there is a failure)",
- "translation": "show only activation result if a blocking activation (unless there is a failure)"
+ "id": "blocking invoke; show only activation result (unless there is a failure)",
+ "translation": "blocking invoke; show only activation result (unless there is a failure)"
},
{
"id": "exclude the first `SKIP` number of actions from the result",
@@ -944,8 +948,8 @@
"translation": "get activation"
},
{
- "id": "Unable to obtain activation record for '{{.id}}': {{.err}}",
- "translation": "Unable to obtain activation record for '{{.id}}': {{.err}}"
+ "id": "Unable to get activation '{{.id}}': {{.err}}",
+ "translation": "Unable to get activation '{{.id}}': {{.err}}"
},
{
"id": "activation result for /{{.namespace}}/{{.name}} ({{.status}} at {{.time}})\n",
@@ -960,12 +964,12 @@
"translation": "get the logs of an activation"
},
{
- "id": "Unable to obtain logs for activation '{{.id}}': {{.err}}",
- "translation": "Unable to obtain logs for activation '{{.id}}': {{.err}}"
+ "id": "Unable to get logs for activation '{{.id}}': {{.err}}",
+ "translation": "Unable to get logs for activation '{{.id}}': {{.err}}"
},
{
- "id": "Unable to obtain result information for activation '{{.id}}': {{.err}}",
- "translation": "Unable to obtain result information for activation '{{.id}}': {{.err}}"
+ "id": "Unable to get result for activation '{{.id}}': {{.err}}",
+ "translation": "Unable to get result for activation '{{.id}}': {{.err}}"
},
{
"id": "poll continuously for log messages from currently running actions",
@@ -1208,16 +1212,16 @@
"translation": "get API"
},
{
- "id": "Unable to get API: {{.err}}",
- "translation": "Unable to get API: {{.err}}"
+ "id": "Unable to get API '{{.name}}': {{.err}}",
+ "translation": "Unable to get API '{{.name}}': {{.err}}"
},
{
"id": "delete an API",
"translation": "delete an API"
},
{
- "id": "Unable to delete action: {{.err}}",
- "translation": "Unable to delete action: {{.err}}"
+ "id": "Unable to delete API: {{.err}}",
+ "translation": "Unable to delete API: {{.err}}"
},
{
"id": "{{.ok}} deleted API {{.basepath}}\n",
@@ -1374,5 +1378,69 @@
{
"id": "Invalid argument '{{.arg}}' for --web flag. Valid input consist of 'yes', 'true', 'raw', 'false', or 'no'.",
"translation": "Invalid argument '{{.arg}}' for --web flag. Valid input consist of 'yes', 'true', 'raw', 'false', or 'no'."
+ },
+ {
+ "id": "API action does not exist",
+ "translation": "API action does not exist"
+ },
+ {
+ "id": "API action '{{.name}}' is not a web action. Issue 'wsk action update {{.name}} --web true' to convert the action to a web action.",
+ "translation": "API action '{{.name}}' is not a web action. Issue 'wsk action update {{.name}} --web true' to convert the action to a web action."
+ },
+ {
+ "id": "Invalid configuration. The x-openwhisk stanza is missing.",
+ "translation": "Invalid configuration. The x-openwhisk stanza is missing."
+ },
+ {
+ "id": "Internal error. Missing value stanza in API configuration response",
+ "translation": "Internal error. Missing value stanza in API configuration response"
+ },
+ {
+ "id": "Internal error. Missing apidoc stanza in API configuration",
+ "translation": "Internal error. Missing apidoc stanza in API configuration"
+ },
+ {
+ "id": "Missing operationId field in API configuration for operation {{.op}}",
+ "translation": "Missing operationId field in API configuration for operation {{.op}}"
+ },
+ {
+ "id": "Missing x-openwhisk stanza in API configuration for operation {{.op}}",
+ "translation": "Missing x-openwhisk stanza in API configuration for operation {{.op}}"
+ },
+ {
+ "id": "Missing x-openwhisk.namespace field in API configuration for operation {{.op}}",
+ "translation": "Missing x-openwhisk.namespace field in API configuration for operation {{.op}}"
+ },
+ {
+ "id": "Missing x-openwhisk.action field in API configuration for operation {{.op}}",
+ "translation": "Missing x-openwhisk.action field in API configuration for operation {{.op}}"
+ },
+ {
+ "id": "Missing x-openwhisk.url field in API configuration for operation {{.op}}",
+ "translation": "Missing x-openwhisk.url field in API configuration for operation {{.op}}"
+ },
+ {
+ "id": "work with APIs",
+ "translation": "work with APIs"
+ },
+ {
+ "id": "create a new API",
+ "translation": "create a new API"
+ },
+ {
+ "id": "get API details",
+ "translation": "get API details"
+ },
+ {
+ "id": "delete an API",
+ "translation": "delete an API"
+ },
+ {
+ "id": "list APIs",
+ "translation": "list APIs"
+ },
+ {
+ "id": "Set the web action response TYPE. Possible values are html, http, json, text, svg",
+ "translation": "Set the web action response TYPE. Possible values are html, http, json, text, svg"
}
]