| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 whisk |
| |
| import ( |
| "errors" |
| "fmt" |
| "github.com/apache/openwhisk-client-go/wski18n" |
| "net/http" |
| "strings" |
| ) |
| |
| type ApiService struct { |
| client *Client |
| } |
| |
| // wsk api create : Request, Response |
| type ApiCreateRequest struct { |
| ApiDoc *Api `json:"apidoc,omitempty"` |
| } |
| type ApiCreateRequestOptions ApiOptions |
| type ApiCreateResponse RetApi |
| |
| // wsk api list : Request, Response |
| type ApiListRequest struct { |
| } |
| type ApiListRequestOptions struct { |
| ApiOptions |
| Limit int `url:"limit"` |
| Skip int `url:"skip"` |
| Docs bool `url:"docs,omitempty"` |
| } |
| type ApiListResponse RetApiArray |
| |
| // wsk api get : Request, Response |
| type ApiGetRequest struct { |
| Api |
| } |
| type ApiGetRequestOptions ApiOptions |
| type ApiGetResponse RetApiArray |
| |
| // wsk api delete : Request, Response |
| type ApiDeleteRequest struct { |
| Api |
| } |
| type ApiDeleteRequestOptions ApiOptions |
| type ApiDeleteResponse struct{} |
| |
| type Api struct { |
| Namespace string `json:"namespace,omitempty"` |
| ApiName string `json:"apiName,omitempty"` |
| GatewayBasePath string `json:"gatewayBasePath,omitempty"` |
| GatewayRelPath string `json:"gatewayPath,omitempty"` |
| GatewayMethod string `json:"gatewayMethod,omitempty"` |
| Id string `json:"id,omitempty"` |
| GatewayFullPath string `json:"gatewayFullPath,omitempty"` |
| Swagger string `json:"swagger,omitempty"` |
| Action *ApiAction `json:"action,omitempty"` |
| PathParameters []ApiParameter `json:"pathParameters,omitempty"` |
| } |
| |
| type ApiParameter struct { |
| Name string `json:"name"` |
| In string `json:"in"` |
| Description string `json:"description,omitempty"` |
| Required bool `json:"required,omitempty"` |
| Type string `json:"type,omitempty"` |
| Format string `json:"format,omitempty"` |
| AllowEmptyValue bool `json:"allowEmptyValue,omitempty"` |
| Items map[string]interface{} `json:"items,omitempty"` |
| CollectionFormat string `json:"collectionFormat,omitempty"` |
| Default interface{} `json:"default,omitempty"` |
| Maximum int `json:"maximum,omitempty"` |
| ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` |
| Minimum int `json:"minimum,omitempty"` |
| ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` |
| MaxLength int `json:"maxLength,omitempty"` |
| MinLength int `json:"minLength,omitempty"` |
| Pattern string `json:"pattern,omitempty"` |
| MaxItems int `json:"maxItems,omitempty"` |
| MinItems int `json:"minItems,omitempty"` |
| UniqueItems bool `json:"uniqueItems,omitempty"` |
| MultipleOf int `json:"multipleOf,omitempty"` |
| Enum interface{} `json:"enum,omitempty"` |
| Ref string `json:"$ref,omitempty"` |
| } |
| |
| type ApiAction struct { |
| Name string `json:"name,omitempty"` |
| Namespace string `json:"namespace,omitempty"` |
| BackendMethod string `json:"backendMethod,omitempty"` |
| BackendUrl string `json:"backendUrl,omitempty"` |
| Auth string `json:"authkey,omitempty"` |
| SecureKey interface{} `json:"secureKey,omitempty"` |
| } |
| |
| type ApiOptions struct { |
| ActionName string `url:"action,omitempty"` |
| ApiBasePath string `url:"basepath,omitempty"` |
| ApiRelPath string `url:"relpath,omitempty"` |
| ApiVerb string `url:"operation,omitempty"` |
| ApiName string `url:"apiname,omitempty"` |
| SpaceGuid string `url:"spaceguid,omitempty"` |
| AccessToken string `url:"accesstoken,omitempty"` |
| ResponseType string `url:"responsetype,omitempty"` |
| } |
| |
| type ApiUserAuth struct { |
| SpaceGuid string `json:"spaceguid,omitempty"` |
| AccessToken string `json:"accesstoken,omitempty"` |
| } |
| |
| type RetApiArray struct { |
| Apis []ApiItem `json:"apis,omitempty"` |
| } |
| |
| type ApiItem struct { |
| ApiId string `json:"id,omitempty"` |
| QueryKey string `json:"key,omitempty"` |
| ApiValue *RetApi `json:"value,omitempty"` |
| } |
| |
| type RetApi struct { |
| Namespace string `json:"namespace"` |
| BaseUrl string `json:"gwApiUrl"` |
| Activated bool `json:"gwApiActivated"` |
| TenantId string `json:"tenantId"` |
| Swagger *ApiSwagger `json:"apidoc,omitempty"` |
| } |
| |
| type ApiSwagger struct { |
| SwaggerName string `json:"swagger,omitempty"` |
| BasePath string `json:"basePath,omitempty"` |
| Info *ApiSwaggerInfo `json:"info,omitempty"` |
| Paths map[string]*ApiSwaggerPath `json:"paths,omitempty"` |
| SecurityDef interface{} `json:"securityDefinitions,omitempty"` |
| Security interface{} `json:"security,omitempty"` |
| XConfig interface{} `json:"x-ibm-configuration,omitempty"` |
| XRateLimit interface{} `json:"x-ibm-rate-limit,omitempty"` |
| } |
| |
| type ApiSwaggerPath struct { |
| Get *ApiSwaggerOperation `json:"get,omitempty"` |
| Put *ApiSwaggerOperation `json:"put,omitempty"` |
| Post *ApiSwaggerOperation `json:"post,omitempty"` |
| Delete *ApiSwaggerOperation `json:"delete,omitempty"` |
| Options *ApiSwaggerOperation `json:"options,omitempty"` |
| Head *ApiSwaggerOperation `json:"head,omitempty"` |
| Patch *ApiSwaggerOperation `json:"patch,omitempty"` |
| Parameters []ApiParameter `json:"parameters,omitempty"` |
| } |
| |
| func (asp *ApiSwaggerPath) MakeOperationMap() map[string]*ApiSwaggerOperation { |
| var opMap map[string]*ApiSwaggerOperation = make(map[string]*ApiSwaggerOperation) |
| if asp.Get != nil { |
| opMap["get"] = asp.Get |
| } |
| if asp.Put != nil { |
| opMap["put"] = asp.Put |
| } |
| if asp.Post != nil { |
| opMap["post"] = asp.Post |
| } |
| if asp.Delete != nil { |
| opMap["delete"] = asp.Delete |
| } |
| if asp.Options != nil { |
| opMap["options"] = asp.Options |
| } |
| if asp.Head != nil { |
| opMap["head"] = asp.Head |
| } |
| if asp.Patch != nil { |
| opMap["patch"] = asp.Patch |
| } |
| return opMap |
| } |
| |
| type ApiSwaggerInfo struct { |
| Title string `json:"title,omitempty"` |
| Version string `json:"version,omitempty"` |
| } |
| |
| type ApiSwaggerOperation struct { |
| OperationId string `json:"operationId"` |
| Parameters []ApiParameter `json:"parameters,omitempty"` |
| Responses interface{} `json:"responses"` |
| XOpenWhisk *ApiSwaggerOpXOpenWhisk `json:"x-openwhisk,omitempty"` |
| } |
| |
| type ApiSwaggerOpXOpenWhisk struct { |
| ActionName string `json:"action"` |
| Namespace string `json:"namespace"` |
| Package string `json:"package"` |
| ApiUrl string `json:"url"` |
| } |
| |
| // Used for printing individual APIs in non-truncated form |
| type ApiFilteredList struct { |
| ActionName string |
| ApiName string |
| BasePath string |
| RelPath string |
| Verb string |
| Url string |
| } |
| |
| // Used for printing individual APIs in truncated form |
| type ApiFilteredRow struct { |
| ActionName string |
| ApiName string |
| BasePath string |
| RelPath string |
| Verb string |
| Url string |
| FmtString string |
| } |
| |
| var ApiVerbs map[string]bool = map[string]bool{ |
| "GET": true, |
| "PUT": true, |
| "POST": true, |
| "DELETE": true, |
| "PATCH": true, |
| "HEAD": true, |
| "OPTIONS": true, |
| } |
| |
| const ( |
| Overwrite = true |
| DoNotOverwrite = false |
| ) |
| |
| ///////////////// |
| // Api Methods // |
| ///////////////// |
| |
| // Compare(sortable) compares api to sortable for the purpose of sorting. |
| // REQUIRED: sortable must also be of type ApiFilteredList. |
| // ***Method of type Sortable*** |
| func (api ApiFilteredList) Compare(sortable Sortable) bool { |
| // Sorts alphabetically by [BASE_PATH | API_NAME] -> REL_PATH -> API_VERB |
| apiToCompare := sortable.(ApiFilteredList) |
| var apiString string |
| var compareString string |
| |
| apiString = strings.ToLower(fmt.Sprintf("%s%s%s", api.BasePath, api.RelPath, |
| api.Verb)) |
| compareString = strings.ToLower(fmt.Sprintf("%s%s%s", apiToCompare.BasePath, |
| apiToCompare.RelPath, apiToCompare.Verb)) |
| |
| return apiString < compareString |
| } |
| |
| // ToHeaderString() returns the header for a list of apis |
| func (api ApiFilteredList) ToHeaderString() string { |
| return "" |
| } |
| |
| // ToSummaryRowString() returns a compound string of required parameters for printing |
| // from CLI command `wsk api list` or `wsk api-experimental list`. |
| // ***Method of type Sortable*** |
| func (api ApiFilteredList) ToSummaryRowString() string { |
| return fmt.Sprintf("%s %s %s %s %s %s", |
| fmt.Sprintf("%s: %s\n", wski18n.T("Action"), api.ActionName), |
| fmt.Sprintf(" %s: %s\n", wski18n.T("API Name"), api.ApiName), |
| fmt.Sprintf(" %s: %s\n", wski18n.T("Base path"), api.BasePath), |
| fmt.Sprintf(" %s: %s\n", wski18n.T("Path"), api.RelPath), |
| fmt.Sprintf(" %s: %s\n", wski18n.T("Verb"), api.Verb), |
| fmt.Sprintf(" %s: %s\n", wski18n.T("URL"), api.Url)) |
| } |
| |
| // Compare(sortable) compares api to sortable for the purpose of sorting. |
| // REQUIRED: sortable must also be of type ApiFilteredRow. |
| // ***Method of type Sortable*** |
| func (api ApiFilteredRow) Compare(sortable Sortable) bool { |
| // Sorts alphabetically by [BASE_PATH | API_NAME] -> REL_PATH -> API_VERB |
| var apiString string |
| var compareString string |
| apiToCompare := sortable.(ApiFilteredRow) |
| |
| apiString = strings.ToLower(fmt.Sprintf("%s%s%s", api.BasePath, api.RelPath, |
| api.Verb)) |
| compareString = strings.ToLower(fmt.Sprintf("%s%s%s", apiToCompare.BasePath, |
| apiToCompare.RelPath, apiToCompare.Verb)) |
| |
| return apiString < compareString |
| } |
| |
| // ToHeaderString() returns the header for a list of apis |
| func (api ApiFilteredRow) ToHeaderString() string { |
| return fmt.Sprintf("%s", fmt.Sprintf(api.FmtString, "Action", "Verb", "API Name", "URL")) |
| } |
| |
| // ToSummaryRowString() returns a compound string of required parameters for printing |
| // from CLI command `wsk api list -f` or `wsk api-experimental list -f`. |
| // ***Method of type Sortable*** |
| func (api ApiFilteredRow) ToSummaryRowString() string { |
| return fmt.Sprintf(api.FmtString, api.ActionName, api.Verb, api.ApiName, api.Url) |
| } |
| |
| func (s *ApiService) List(apiListOptions *ApiListRequestOptions) (*ApiListResponse, *http.Response, error) { |
| route := "web/whisk.system/apimgmt/getApi.http" |
| |
| routeUrl, err := addRouteOptions(route, apiListOptions) |
| if err != nil { |
| Debug(DbgError, "addRouteOptions(%s, %#v) error: '%s'\n", route, apiListOptions, err) |
| errMsg := wski18n.T("Unable to add route options '{{.options}}'", |
| map[string]interface{}{"options": apiListOptions}) |
| whiskErr := MakeWskErrorFromWskError(errors.New(errMsg), err, EXIT_CODE_ERR_GENERAL, DISPLAY_MSG, |
| NO_DISPLAY_USAGE) |
| return nil, nil, whiskErr |
| } |
| Debug(DbgInfo, "Api GET/list route with api options: %s\n", routeUrl) |
| |
| req, err := s.client.NewRequestUrl("GET", routeUrl, nil, DoNotIncludeNamespaceInUrl, AppendOpenWhiskPathPrefix, EncodeBodyAsJson, AuthRequired) |
| if err != nil { |
| Debug(DbgError, "http.NewRequestUrl(GET, %s, nil, DoNotIncludeNamespaceInUrl, AppendOpenWhiskPathPrefix, EncodeBodyAsJson) error: '%s'\n", routeUrl, err) |
| errMsg := wski18n.T("Unable to create HTTP request for GET '{{.route}}': {{.err}}", |
| map[string]interface{}{"route": routeUrl, "err": err}) |
| whiskErr := MakeWskErrorFromWskError(errors.New(errMsg), err, EXIT_CODE_ERR_NETWORK, DISPLAY_MSG, |
| NO_DISPLAY_USAGE) |
| return nil, nil, whiskErr |
| } |
| |
| apiArray := new(ApiListResponse) |
| resp, err := s.client.Do(req, &apiArray, ExitWithErrorOnTimeout) |
| if err != nil { |
| Debug(DbgError, "s.client.Do() error - HTTP req %s; error '%s'\n", req.URL.String(), err) |
| return nil, resp, err |
| } |
| |
| err = validateApiListResponse(apiArray) |
| if err != nil { |
| Debug(DbgError, "Not a valid ApiListResponse object\n") |
| return nil, resp, err |
| } |
| |
| return apiArray, resp, err |
| } |
| |
| func (s *ApiService) Insert(api *ApiCreateRequest, options *ApiCreateRequestOptions, overwrite bool) (*ApiCreateResponse, *http.Response, error) { |
| route := "web/whisk.system/apimgmt/createApi.http" |
| Debug(DbgInfo, "Api PUT route: %s\n", route) |
| |
| routeUrl, err := addRouteOptions(route, options) |
| if err != nil { |
| Debug(DbgError, "addRouteOptions(%s, %#v) error: '%s'\n", route, options, err) |
| errMsg := wski18n.T("Unable to add route options '{{.options}}'", |
| map[string]interface{}{"options": options}) |
| whiskErr := MakeWskErrorFromWskError(errors.New(errMsg), err, EXIT_CODE_ERR_GENERAL, DISPLAY_MSG, |
| NO_DISPLAY_USAGE) |
| return nil, nil, whiskErr |
| } |
| Debug(DbgError, "Api create route with options: %s\n", routeUrl) |
| |
| req, err := s.client.NewRequestUrl("POST", routeUrl, api, DoNotIncludeNamespaceInUrl, AppendOpenWhiskPathPrefix, EncodeBodyAsJson, AuthRequired) |
| if err != nil { |
| Debug(DbgError, "http.NewRequestUrl(POST, %s, nil, DoNotIncludeNamespaceInUrl, AppendOpenWhiskPathPrefix, EncodeBodyAsJson) error: '%s'\n", route, err) |
| errMsg := wski18n.T("Unable to create HTTP request for POST '{{.route}}': {{.err}}", |
| map[string]interface{}{"route": route, "err": err}) |
| whiskErr := MakeWskErrorFromWskError(errors.New(errMsg), err, EXIT_CODE_ERR_NETWORK, DISPLAY_MSG, |
| NO_DISPLAY_USAGE) |
| return nil, nil, whiskErr |
| } |
| |
| retApi := new(ApiCreateResponse) |
| resp, err := s.client.Do(req, &retApi, ExitWithErrorOnTimeout) |
| if err != nil { |
| Debug(DbgError, "s.client.Do() error - HTTP req %s; error '%s'\n", req.URL.String(), err) |
| return nil, resp, err |
| } |
| |
| err = validateApiSwaggerResponse(retApi.Swagger) |
| if err != nil { |
| Debug(DbgError, "Not a valid API creation response\n") |
| return nil, resp, err |
| } |
| |
| return retApi, resp, nil |
| } |
| |
| func (s *ApiService) Get(api *ApiGetRequest, options *ApiGetRequestOptions) (*ApiGetResponse, *http.Response, error) { |
| route := "web/whisk.system/apimgmt/getApi.http" |
| Debug(DbgInfo, "Api GET route: %s\n", route) |
| |
| routeUrl, err := addRouteOptions(route, options) |
| if err != nil { |
| Debug(DbgError, "addRouteOptions(%s, %#v) error: '%s'\n", route, options, err) |
| errMsg := wski18n.T("Unable to add route options '{{.options}}'", |
| map[string]interface{}{"options": options}) |
| whiskErr := MakeWskErrorFromWskError(errors.New(errMsg), err, EXIT_CODE_ERR_GENERAL, DISPLAY_MSG, |
| NO_DISPLAY_USAGE) |
| return nil, nil, whiskErr |
| } |
| Debug(DbgError, "Api get route with options: %s\n", routeUrl) |
| |
| req, err := s.client.NewRequestUrl("GET", routeUrl, nil, DoNotIncludeNamespaceInUrl, AppendOpenWhiskPathPrefix, EncodeBodyAsJson, AuthRequired) |
| if err != nil { |
| Debug(DbgError, "http.NewRequestUrl(GET, %s, nil, DoNotIncludeNamespaceInUrl, AppendOpenWhiskPathPrefix, EncodeBodyAsJson) error: '%s'\n", route, err) |
| errMsg := wski18n.T("Unable to create HTTP request for GET '{{.route}}': {{.err}}", |
| map[string]interface{}{"route": route, "err": err}) |
| whiskErr := MakeWskErrorFromWskError(errors.New(errMsg), err, EXIT_CODE_ERR_NETWORK, DISPLAY_MSG, |
| NO_DISPLAY_USAGE) |
| return nil, nil, whiskErr |
| } |
| |
| retApi := new(ApiGetResponse) |
| resp, err := s.client.Do(req, &retApi, ExitWithErrorOnTimeout) |
| if err != nil { |
| Debug(DbgError, "s.client.Do() error - HTTP req %s; error '%s'\n", req.URL.String(), err) |
| return nil, resp, err |
| } |
| |
| return retApi, resp, nil |
| } |
| |
| func (s *ApiService) Delete(api *ApiDeleteRequest, options *ApiDeleteRequestOptions) (*http.Response, error) { |
| route := "web/whisk.system/apimgmt/deleteApi.http" |
| Debug(DbgInfo, "Api DELETE route: %s\n", route) |
| |
| routeUrl, err := addRouteOptions(route, options) |
| if err != nil { |
| Debug(DbgError, "addRouteOptions(%s, %#v) error: '%s'\n", route, options, err) |
| errMsg := wski18n.T("Unable to add route options '{{.options}}'", |
| map[string]interface{}{"options": options}) |
| whiskErr := MakeWskErrorFromWskError(errors.New(errMsg), err, EXIT_CODE_ERR_GENERAL, DISPLAY_MSG, |
| NO_DISPLAY_USAGE) |
| return nil, whiskErr |
| } |
| Debug(DbgError, "Api DELETE route with options: %s\n", routeUrl) |
| |
| req, err := s.client.NewRequestUrl("DELETE", routeUrl, nil, DoNotIncludeNamespaceInUrl, AppendOpenWhiskPathPrefix, EncodeBodyAsJson, AuthRequired) |
| if err != nil { |
| Debug(DbgError, "http.NewRequestUrl(DELETE, %s, nil, DoNotIncludeNamespaceInUrl, AppendOpenWhiskPathPrefix, EncodeBodyAsJson) error: '%s'\n", route, err) |
| errMsg := wski18n.T("Unable to create HTTP request for DELETE '{{.route}}': {{.err}}", |
| map[string]interface{}{"route": route, "err": err}) |
| whiskErr := MakeWskErrorFromWskError(errors.New(errMsg), err, EXIT_CODE_ERR_NETWORK, DISPLAY_MSG, |
| NO_DISPLAY_USAGE) |
| return nil, whiskErr |
| } |
| |
| retApi := new(ApiDeleteResponse) |
| resp, err := s.client.Do(req, &retApi, ExitWithErrorOnTimeout) |
| if err != nil { |
| Debug(DbgError, "s.client.Do() error - HTTP req %s; error '%s'\n", req.URL.String(), err) |
| return resp, err |
| } |
| |
| return nil, nil |
| } |
| |
| func validateApiListResponse(apiList *ApiListResponse) error { |
| for i := 0; i < len(apiList.Apis); i++ { |
| if apiList.Apis[i].ApiValue == nil { |
| Debug(DbgError, "validateApiResponse: No value stanza in api %v\n", apiList.Apis[i]) |
| errMsg := wski18n.T("Internal error. Missing value stanza in API configuration response") |
| whiskErr := MakeWskError(errors.New(errMsg), EXIT_CODE_ERR_NETWORK, DISPLAY_MSG, NO_DISPLAY_USAGE) |
| return whiskErr |
| } |
| err := validateApiSwaggerResponse(apiList.Apis[i].ApiValue.Swagger) |
| if err != nil { |
| Debug(DbgError, "validateApiListResponse: Invalid Api: %v\n", apiList.Apis[i]) |
| return err |
| } |
| } |
| return nil |
| } |
| |
| func validateApiSwaggerResponse(swagger *ApiSwagger) error { |
| if swagger == nil { |
| Debug(DbgError, "validateApiSwaggerResponse: No apidoc stanza in api\n") |
| errMsg := wski18n.T("Internal error. Missing apidoc stanza in API configuration") |
| whiskErr := MakeWskError(errors.New(errMsg), EXIT_CODE_ERR_NETWORK, DISPLAY_MSG, NO_DISPLAY_USAGE) |
| return whiskErr |
| } |
| for path := range swagger.Paths { |
| err := validateApiPath(swagger.Paths[path]) |
| if err != nil { |
| Debug(DbgError, "validateApiResponse: Invalid Api Path object: %v\n", swagger.Paths[path]) |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func validateApiPath(path *ApiSwaggerPath) error { |
| for op, opv := range path.MakeOperationMap() { |
| err := validateApiOperation(op, opv) |
| if err != nil { |
| Debug(DbgError, "validateApiPath: Invalid Api operation object: %v\n", opv) |
| return err |
| } |
| } |
| return nil |
| } |
| |
| func validateApiOperation(opName string, op *ApiSwaggerOperation) error { |
| if op.XOpenWhisk != nil && len(op.OperationId) == 0 { |
| Debug(DbgError, "validateApiOperation: No operationId field in operation %v\n", op) |
| errMsg := wski18n.T("Missing operationId field in API configuration for operation {{.op}}", |
| map[string]interface{}{"op": opName}) |
| whiskErr := MakeWskError(errors.New(errMsg), EXIT_CODE_ERR_NETWORK, DISPLAY_MSG, NO_DISPLAY_USAGE) |
| return whiskErr |
| } |
| |
| if op.XOpenWhisk != nil && len(op.XOpenWhisk.Namespace) == 0 { |
| Debug(DbgError, "validateApiOperation: no x-openwhisk.namespace stanza in operation %v\n", op) |
| errMsg := wski18n.T("Missing x-openwhisk.namespace field in API configuration for operation {{.op}}", |
| map[string]interface{}{"op": opName}) |
| whiskErr := MakeWskError(errors.New(errMsg), EXIT_CODE_ERR_NETWORK, DISPLAY_MSG, NO_DISPLAY_USAGE) |
| return whiskErr |
| } |
| |
| // Note: The op.XOpenWhisk.Package field can have a value of "", so don't enforce a value |
| |
| if op.XOpenWhisk != nil && len(op.XOpenWhisk.ActionName) == 0 { |
| Debug(DbgError, "validateApiOperation: no x-openwhisk.action stanza in operation %v\n", op) |
| errMsg := wski18n.T("Missing x-openwhisk.action field in API configuration for operation {{.op}}", |
| map[string]interface{}{"op": opName}) |
| whiskErr := MakeWskError(errors.New(errMsg), EXIT_CODE_ERR_NETWORK, DISPLAY_MSG, NO_DISPLAY_USAGE) |
| return whiskErr |
| } |
| if op.XOpenWhisk != nil && len(op.XOpenWhisk.ApiUrl) == 0 { |
| Debug(DbgError, "validateApiOperation: no x-openwhisk.url stanza in operation %v\n", op) |
| errMsg := wski18n.T("Missing x-openwhisk.url field in API configuration for operation {{.op}}", |
| map[string]interface{}{"op": opName}) |
| whiskErr := MakeWskError(errors.New(errMsg), EXIT_CODE_ERR_NETWORK, DISPLAY_MSG, NO_DISPLAY_USAGE) |
| return whiskErr |
| } |
| return nil |
| } |