| /* |
| * Copyright 2015-2016 IBM Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package commands |
| |
| import ( |
| "errors" |
| "fmt" |
| "reflect" |
| "strconv" |
| "strings" |
| |
| "github.com/openwhisk/openwhisk-client-go/whisk" |
| "github.com/openwhisk/openwhisk-cli/wski18n" |
| |
| "github.com/fatih/color" |
| "github.com/spf13/cobra" |
| "encoding/json" |
| ) |
| |
| ////////////// |
| // Commands // |
| ////////////// |
| |
| var apiExperimentalCmd = &cobra.Command{ |
| Use: "api-experimental", |
| 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"), |
| SilenceUsage: true, |
| SilenceErrors: true, |
| PreRunE: setupClientConfig, |
| RunE: func(cmd *cobra.Command, args []string) error { |
| |
| var api *whisk.Api |
| var err error |
| |
| 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 = parseSwaggerApi() |
| 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, 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 |
| } |
| } |
| |
| 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}) |
| whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_NETWORK, |
| 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) |
| 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": path, |
| "verb": op, |
| "name": boldString(opv.XOpenWhisk.ActionName), |
| "fullpath": managedUrl, |
| })) |
| } |
| } |
| } |
| |
| |
| 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", |
| 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] |
| retApi, _, err := client.Apis.Get(apiGetReq, apiGetReqOptions) |
| if err != nil { |
| 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 |
| } |
| whisk.Debug(whisk.DbgInfo, "client.Apis.Get 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 apiDeleteCmd = &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) |
| // 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.Delete(apiDeleteReq, apiDeleteReqOptions) |
| if err != nil { |
| 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 |
| } |
| |
| 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 fmtString = "%-30s %7s %20s %s\n" |
| var apiListCmd = &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.ApiListResponse |
| var retApi *whisk.ApiGetResponse |
| var retApiArray *whisk.RetApiArray |
| |
| 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 |
| } |
| |
| // Get API request body |
| apiGetReq := new(whisk.ApiGetRequest) |
| apiGetReq.Namespace = client.Config.Namespace |
| |
| // 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) { |
| retApiList, _, err = client.Apis.List(apiListReqOptions) |
| if err != nil { |
| 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", 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 |
| 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.Get(apiGetReq, apiGetReqOptions) |
| if err != nil { |
| 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", 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 |
| 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++ { |
| 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, (*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, |
| 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++ { |
| printFilteredListRow(retApiArray.Apis[i].ApiValue, (*whisk.ApiOptions)(apiGetReqOptions), maxActionNameSize, maxApiNameSize) |
| } |
| } |
| |
| 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 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.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.ApiVerb) == 0 || strings.ToLower(op) == strings.ToLower(api.ApiVerb)) { |
| whisk.Debug(whisk.DbgInfo, "apiGetCmd: operation matches: %#v\n", opv) |
| 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) |
| 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 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.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.ApiVerb) == 0 || strings.ToLower(op) == strings.ToLower(api.ApiVerb)) { |
| whisk.Debug(whisk.DbgInfo, "apiGetCmd: operation matches: %#v\n", opv) |
| var 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 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.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 = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.ActionName |
| if (len(fullActionName) > maxNameSize) { |
| maxNameSize = len(fullActionName) |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return maxNameSize |
| } |
| |
| 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 |
| 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 parseApi(cmd *cobra.Command, args []string) (*whisk.Api, 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, 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, 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, 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, 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, whiskErr |
| } |
| apiname = flags.api.apiname |
| } |
| |
| api.Namespace = client.Config.Namespace |
| api.Action = new(whisk.ApiAction) |
| api.Action.BackendUrl = "https://" + client.Config.Host + "/api/v1/namespaces/" + qName.namespace + "/actions/" + qName.entityName |
| api.Action.BackendMethod = "POST" |
| 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, nil |
| } |
| |
| func parseSwaggerApi() (*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.ApiSwagger) |
| 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 |
| } |
| |
| func IsValidApiVerb(verb string) (error, bool) { |
| // Is the API verb valid? |
| if _, ok := whisk.ApiVerbs[strings.ToUpper(verb)]; !ok { |
| whisk.Debug(whisk.DbgError, "Invalid API verb: %s\n", verb) |
| errMsg := wski18n.T("'{{.verb}}' is not a valid API verb. Valid values are: {{.verbs}}", |
| map[string]interface{}{ |
| "verb": verb, |
| "verbs": reflect.ValueOf(whisk.ApiVerbs).MapKeys()}) |
| whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL, |
| whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE) |
| return whiskErr, false |
| } |
| return nil, true |
| } |
| |
| func hasPathPrefix(path string) (error, bool) { |
| if (! strings.HasPrefix(path, "/")) { |
| whisk.Debug(whisk.DbgError, "path does not begin with '/': %s\n", path) |
| errMsg := wski18n.T("'{{.path}}' must begin with '/'.", |
| map[string]interface{}{ |
| "path": path, |
| }) |
| whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL, |
| whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE) |
| return whiskErr, false |
| } |
| return nil, true |
| } |
| |
| func isValidBasepath(basepath string) (error, bool) { |
| if whiskerr, ok := hasPathPrefix(basepath); !ok { |
| return whiskerr, false |
| } |
| return nil, true |
| } |
| |
| func isValidRelpath(relpath string) (error, bool) { |
| if whiskerr, ok := hasPathPrefix(relpath); !ok { |
| return whiskerr, false |
| } |
| return nil, true |
| } |
| |
| |
| /* |
| * Pull the managedUrl (external API URL) from the API configuration |
| */ |
| func getManagedUrl(api *whisk.RetApi, relpath string, operation string) (url string) { |
| baseUrl := strings.TrimSuffix(api.BaseUrl, "/") |
| whisk.Debug(whisk.DbgInfo, "getManagedUrl: baseUrl = %s, relpath = %s, operation = %s\n", baseUrl, relpath, operation) |
| for path, _ := range api.Swagger.Paths { |
| whisk.Debug(whisk.DbgInfo, "getManagedUrl: comparing api relpath: %s\n", path) |
| if (path == relpath) { |
| whisk.Debug(whisk.DbgInfo, "getManagedUrl: relpath matches '%s'\n", relpath) |
| for op, _ := range api.Swagger.Paths[path] { |
| whisk.Debug(whisk.DbgInfo, "getManagedUrl: comparing operation: '%s'\n", op) |
| if (strings.ToLower(op) == strings.ToLower(operation)) { |
| whisk.Debug(whisk.DbgInfo, "getManagedUrl: operation matches: %s\n", operation) |
| url = baseUrl+path |
| } |
| } |
| } |
| } |
| 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 // |
| /////////// |
| |
| 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, |
| apiGetCmd, |
| 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, |
| ) |
| } |