You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ho...@apache.org on 2017/07/07 18:40:22 UTC

[incubator-openwhisk] branch master updated: wsk CLI should tolerate APIs that do not yet have a mapped action (#2458)

This is an automated email from the ASF dual-hosted git repository.

houshengbo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git


The following commit(s) were added to refs/heads/master by this push:
     new 9c4ff87  wsk CLI should tolerate APIs that do not yet have a mapped action (#2458)
9c4ff87 is described below

commit 9c4ff87da2658f25bcb7ea6fc920fc54d806fb0d
Author: Mark Deuser <md...@us.ibm.com>
AuthorDate: Fri Jul 7 14:40:19 2017 -0400

    wsk CLI should tolerate APIs that do not yet have a mapped action (#2458)
    
    * wsk CLI should tolerate APIs that do not yet have a mapped action
    - bump API GW version so that action-less APIs can be created via cli/swagger
    
    * Add `wsk api list --full` test for action-less apis
---
 ansible/group_vars/all                             |  2 +-
 ansible/roles/apigateway/tasks/deploy.yml          |  2 +-
 .../apigw/endpoints.without.action.swagger.json    | 76 ++++++++++++++++++++++
 .../scala/whisk/core/cli/test/ApiGwTests.scala     | 33 ++++++++++
 tools/cli/go-whisk-cli/commands/api.go             | 45 +++++++++----
 .../go-whisk-cli/wski18n/resources/en_US.all.json  |  4 ++
 tools/cli/go-whisk/whisk/api.go                    | 18 ++---
 7 files changed, 153 insertions(+), 27 deletions(-)

diff --git a/ansible/group_vars/all b/ansible/group_vars/all
index 5fa8eed..91e3fa7 100644
--- a/ansible/group_vars/all
+++ b/ansible/group_vars/all
@@ -192,7 +192,7 @@ apigateway:
     api: 9000
     api_secure: 443
     mgmt: 9001
-  version: 0.7.0
+  version: 0.8.2
 
 redis:
   version: 3.2
diff --git a/ansible/roles/apigateway/tasks/deploy.yml b/ansible/roles/apigateway/tasks/deploy.yml
index a2dd2be..9d18488 100644
--- a/ansible/roles/apigateway/tasks/deploy.yml
+++ b/ansible/roles/apigateway/tasks/deploy.yml
@@ -1,7 +1,7 @@
 ---
 # This role will install apigateway
 
-- name: "pull the openwhisk/apigateway image"
+- name: "pull the openwhisk/apigateway:{{ apigateway.version }} image"
   shell: "docker pull openwhisk/apigateway:{{ apigateway.version }}"
   when: apigateway_local_build is undefined
   retries: 3
diff --git a/tests/dat/apigw/endpoints.without.action.swagger.json b/tests/dat/apigw/endpoints.without.action.swagger.json
new file mode 100644
index 0000000..2305643
--- /dev/null
+++ b/tests/dat/apigw/endpoints.without.action.swagger.json
@@ -0,0 +1,76 @@
+{
+    "swagger": "2.0",
+    "basePath": "/NoActions",
+    "info": {
+        "title": "A descriptive name",
+        "version": "1.0"
+    },
+    "paths": {
+        "/": {
+            "delete": {
+                "operationId": "",
+                "responses": {
+                    "200": {
+                        "description": "A successful invocation response"
+                    }
+                }
+            },
+            "get": {
+                "operationId": "",
+                "responses": {
+                    "200": {
+                        "description": "A successful invocation response"
+                    }
+                }
+            },
+            "head": {
+                "operationId": "",
+                "responses": {
+                    "200": {
+                        "description": "A successful invocation response"
+                    }
+                }
+            },
+            "options": {
+                "operationId": "",
+                "responses": {
+                    "200": {
+                        "description": "A successful invocation response"
+                    }
+                }
+            },
+            "patch": {
+                "operationId": "",
+                "responses": {
+                    "200": {
+                        "description": "A successful invocation response"
+                    }
+                }
+            },
+            "post": {
+                "operationId": "",
+                "responses": {
+                    "200": {
+                        "description": "A successful invocation response"
+                    }
+                }
+            },
+            "put": {
+                "operationId": "",
+                "responses": {
+                    "200": {
+                        "description": "A successful invocation response"
+                    }
+                }
+            }
+        }
+    },
+    "x-ibm-configuration": {
+        "assembly": {
+            "execute": []
+        },
+        "cors": {
+            "enabled": true
+        }
+    }
+}
diff --git a/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala b/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
index 5894bcd..9c8e239 100644
--- a/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
@@ -1212,4 +1212,37 @@ class ApiGwTests
         var rr = apiDelete(basepathOrApiName = nonexistentApi, expectedExitCode = ANY_ERROR_EXIT)
         rr.stderr should include (s"API '${nonexistentApi}' does not exist")
     }
+
+    it should "successfully list an API whose endpoints are not mapped to actions" in {
+        val testName = "CLI_APIGWTEST23"
+        var testapiname = "A descriptive name"
+        val testbasepath = "/NoActions"
+        val testrelpath = "/"
+        val testops: Seq[String] = Seq("put", "delete", "get", "head", "options", "patch", "post")
+        val swaggerPath = TestUtils.getTestApiGwFilename(s"endpoints.without.action.swagger.json")
+
+        try {
+            var rr = apiCreate(swagger = Some(swaggerPath))
+            println("api create stdout: " + rr.stdout)
+            println("api create stderror: " + rr.stderr)
+            rr.stdout should include("ok: created API")
+
+            rr = apiList(basepathOrApiName = Some(testbasepath))
+            println("api list:\n" + rr.stdout)
+            testops foreach { testurlop =>
+                rr.stdout should include regex (s"\\s+${testurlop}\\s+${testapiname}\\s+")
+            }
+            rr.stdout should include(testbasepath + testrelpath)
+
+            rr = apiList(basepathOrApiName = Some(testbasepath), full = Some(true))
+            println("api full list:\n" + rr.stdout)
+            testops foreach { testurlop =>
+                rr.stdout should include regex (s"Verb:\\s+${testurlop}")
+            }
+            rr.stdout should include(testbasepath + testrelpath)
+
+        } finally {
+            val deleteresult = apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
+        }
+    }
 }
diff --git a/tools/cli/go-whisk-cli/commands/api.go b/tools/cli/go-whisk-cli/commands/api.go
index 00167eb..d27a42e 100644
--- a/tools/cli/go-whisk-cli/commands/api.go
+++ b/tools/cli/go-whisk-cli/commands/api.go
@@ -864,21 +864,34 @@ var apiCreateCmdV2 = &cobra.Command{
                 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) {
+                    if (opv.XOpenWhisk == nil) {
+                        fqActionName = ""
+                    } else 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,
-                            }))
+                    if len(fqActionName) > 0 {
+                        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,
+                                }))
+                    } else {
+                        fmt.Fprintf(color.Output,
+                            wski18n.T("{{.ok}} created API {{.path}} {{.verb}}\n{{.fullpath}}\n",
+                                map[string]interface{}{
+                                    "ok": color.GreenString("ok:"),
+                                    "path": strings.TrimSuffix(retApi.Swagger.BasePath, "/") + path,
+                                    "verb": op,
+                                    "fullpath": managedUrl,
+                                }))
+                    }
                 }
             }
         }
@@ -1218,7 +1231,9 @@ func printFilteredListApiV2(resultApi *whisk.RetApiV2, apiPath string, apiVerb s
                     if ( len(apiVerb) == 0 || strings.ToLower(op) == strings.ToLower(apiVerb)) {
                         whisk.Debug(whisk.DbgInfo, "printFilteredListApiV2: operation matches: %#v\n", opv)
                         var actionName string
-                        if (len(opv.XOpenWhisk.Package) > 0) {
+                        if (opv.XOpenWhisk == nil) {
+                            actionName = ""
+                        } else if (len(opv.XOpenWhisk.Package) > 0) {
                             actionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.Package+"/"+opv.XOpenWhisk.ActionName
                         } else {
                             actionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.ActionName
@@ -1256,7 +1271,9 @@ func printFilteredListRowV2(resultApi *whisk.RetApiV2, apiPath string, apiVerb s
                     if ( len(apiVerb) == 0 || strings.ToLower(op) == strings.ToLower(apiVerb)) {
                         whisk.Debug(whisk.DbgInfo, "printFilteredListRowV2: operation matches: %#v\n", opv)
                         var actionName string
-                        if (len(opv.XOpenWhisk.Package) > 0) {
+                        if (opv.XOpenWhisk == nil) {
+                            actionName = ""
+                        } else if (len(opv.XOpenWhisk.Package) > 0) {
                             actionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.Package+"/"+opv.XOpenWhisk.ActionName
                         } else {
                             actionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.ActionName
@@ -1287,7 +1304,9 @@ func getLargestActionNameSizeV2(retApiArray *whisk.RetApiArrayV2, apiPath string
                         if ( len(apiVerb) == 0 || strings.ToLower(op) == strings.ToLower(apiVerb)) {
                             whisk.Debug(whisk.DbgInfo, "getLargestActionNameSize: operation matches: %#v\n", opv)
                             var fullActionName string
-                            if (len(opv.XOpenWhisk.Package) > 0) {
+                            if (opv.XOpenWhisk == nil) {
+                                fullActionName = ""
+                            } else if (len(opv.XOpenWhisk.Package) > 0) {
                                 fullActionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.Package+"/"+opv.XOpenWhisk.ActionName
                             } else {
                                 fullActionName = "/"+opv.XOpenWhisk.Namespace+"/"+opv.XOpenWhisk.ActionName
diff --git a/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json b/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json
index 7d3a9bd..765f961 100644
--- a/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json
+++ b/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json
@@ -1188,6 +1188,10 @@
     "translation": "{{.ok}} created API {{.path}} {{.verb}} for action {{.name}}\n{{.fullpath}}\n"
   },
   {
+    "id": "{{.ok}} created API {{.path}} {{.verb}}\n{{.fullpath}}\n",
+    "translation": "{{.ok}} created API {{.path}} {{.verb}}\n{{.fullpath}}\n"
+  },
+  {
     "id": "Unable to parse api command arguments: {{.err}}",
     "translation": "Unable to parse api command arguments: {{.err}}"
   },
diff --git a/tools/cli/go-whisk/whisk/api.go b/tools/cli/go-whisk/whisk/api.go
index baeb0c5..6b9d4a6 100644
--- a/tools/cli/go-whisk/whisk/api.go
+++ b/tools/cli/go-whisk/whisk/api.go
@@ -545,21 +545,15 @@ func validateApiPath(path map[string]*ApiSwaggerOperationV2) error {
 }
 
 func validateApiOperation(opName string, op *ApiSwaggerOperationV2) error {
-    if len(op.OperationId) == 0 {
-        Debug(DbgError, "validateApiResponse: No operationId field in operation %v\n", op)
+    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), EXITCODE_ERR_NETWORK, DISPLAY_MSG, NO_DISPLAY_USAGE)
         return whiskErr
     }
-    if op.XOpenWhisk == nil {
-        Debug(DbgError, "validateApiResponse: No x-openwhisk stanza in operation %v\n", op)
-        errMsg := wski18n.T("Missing x-openwhisk stanza in API configuration for operation {{.op}}",
-            map[string]interface{}{"op": opName})
-        whiskErr := MakeWskError(errors.New(errMsg), EXITCODE_ERR_NETWORK, DISPLAY_MSG, NO_DISPLAY_USAGE)
-        return whiskErr
-    }
-    if len(op.XOpenWhisk.Namespace) == 0 {
+
+    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})
@@ -569,14 +563,14 @@ func validateApiOperation(opName string, op *ApiSwaggerOperationV2) error {
 
     // Note: The op.XOpenWhisk.Package field can have a value of "", so don't enforce a value
 
-    if len(op.XOpenWhisk.ActionName) == 0 {
+    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), EXITCODE_ERR_NETWORK, DISPLAY_MSG, NO_DISPLAY_USAGE)
         return whiskErr
     }
-    if len(op.XOpenWhisk.ApiUrl) == 0 {
+    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})

-- 
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <co...@openwhisk.apache.org>'].