You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by pd...@apache.org on 2018/08/31 19:14:31 UTC

[incubator-openwhisk-wskdeploy] branch master updated: #975 Added HTTP response documentation (#976)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new e9b75ef  #975 Added HTTP response documentation (#976)
e9b75ef is described below

commit e9b75efb177ed54020fc985ac8ea354d82baaffb
Author: Bruno Girin <br...@gmail.com>
AuthorDate: Fri Aug 31 20:14:29 2018 +0100

    #975 Added HTTP response documentation (#976)
    
    * #975 Added documentation showing simple HTTP response example.
    
    * Changed the payload from message to greeting for consistency with other examples
    
    * Added missing license headers
    
    * Fixed typo in Js example and added link to new page in programming guide
    
    * Fixed indentation
    
    * Added page on sequences and HTTP response; fixed location of previous manifest
---
 .../manifest_hello_world_apigateway_http.yaml      |  26 +++
 ...nifest_hello_world_apigateway_http_sequence.yml |  26 +++
 docs/examples/src/hello_http.js                    |  29 +++
 docs/examples/src/hello_http_promise.js            |  28 +++
 docs/examples/src/hello_http_validate.js           |  22 +++
 docs/examples/src/hello_http_wrap.js               |  14 ++
 docs/programming_guide.md                          |   2 +
 docs/wskdeploy_apigateway_http.md                  | 194 +++++++++++++++++++++
 docs/wskdeploy_apigateway_http_sequence.md         | 175 +++++++++++++++++++
 docs/wskdeploy_apigateway_sequence.md              |   4 +-
 10 files changed, 518 insertions(+), 2 deletions(-)

diff --git a/docs/examples/manifest_hello_world_apigateway_http.yaml b/docs/examples/manifest_hello_world_apigateway_http.yaml
new file mode 100644
index 0000000..7016ecc
--- /dev/null
+++ b/docs/examples/manifest_hello_world_apigateway_http.yaml
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more contributor
+# license agreements; and to You under the Apache License, Version 2.0.
+
+# Example: Hello World action with API and HTTP response
+packages:
+  hello_world_package:
+    version: 1.0
+    license: Apache-2.0
+    actions:
+      hello_world:
+        function: src/hello_http.js
+        web-export: true
+      hello_world_promise:
+        function: src/hello_http_promise.js
+        web-export: true
+    apis:
+      hello-world:
+        hello:
+          world:
+            hello_world:
+              method: GET
+              response: http
+          world2:
+            hello_world_promise:
+              method: GET
+              response: http
diff --git a/docs/examples/manifest_hello_world_apigateway_http_sequence.yml b/docs/examples/manifest_hello_world_apigateway_http_sequence.yml
new file mode 100644
index 0000000..94a2dec
--- /dev/null
+++ b/docs/examples/manifest_hello_world_apigateway_http_sequence.yml
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more contributor
+# license agreements; and to You under the Apache License, Version 2.0.
+
+# Example: Hello World sequence with API and HTTP response
+packages:
+  hello_world_package:
+    version: 1.0
+    license: Apache-2.0
+    actions:
+      hello_validate:
+        function: src/hello_http_validate.js
+      hello:
+        function: src/hello.js
+      hello_wrap:
+        function: src/hello_http_wrap.js
+    sequences:
+      hello_world:
+        actions: hello_validate, hello, hello_wrap
+        web: true
+    apis:
+      hello-world:
+        hello:
+          world:
+            hello_world:
+              method: GET
+              response: http
diff --git a/docs/examples/src/hello_http.js b/docs/examples/src/hello_http.js
new file mode 100644
index 0000000..ea547c7
--- /dev/null
+++ b/docs/examples/src/hello_http.js
@@ -0,0 +1,29 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements; and to You under the Apache License, Version 2.0.
+
+/*
+ * This action returns a greeting wrapped into a HTTP response that can be used
+ * by the API Gateway. It also checks for mandatory attributes and returns a
+ * HTTP error response when any attribute is missing.
+ */
+function main(params) {
+    if(params.name && params.place) {
+        return {
+            body: {
+                greeting: `Hello ${params.name} from ${params.place}!`
+            },
+            statusCode: 200,
+            headers: {'Content-Type': 'application/json'}
+        };
+    } else {
+        return {
+            error: {
+                body: {
+                    message: 'Attributes name and place are mandatory'
+                },
+                statusCode: 400,
+                headers: {'Content-Type': 'application/json'}
+            }
+        }
+    }
+}
diff --git a/docs/examples/src/hello_http_promise.js b/docs/examples/src/hello_http_promise.js
new file mode 100644
index 0000000..74598a5
--- /dev/null
+++ b/docs/examples/src/hello_http_promise.js
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements; and to You under the Apache License, Version 2.0.
+
+/*
+ * This action returns a greeting wrapped into a HTTP response that can be used
+ * by the API Gateway. It also checks for mandatory attributes and returns a
+ * HTTP error response when any attribute is missing. It uses Promise.resolve
+ * and Promise.reject to handle the response.
+ */
+function main(params) {
+    if(params.name && params.place) {
+        return Promise.resolve({
+            body: {
+                greeting: `Hello ${params.name} from ${params.place}!`
+            },
+            statusCode: 200,
+            headers: {'Content-Type': 'application/json'}
+        });
+    } else {
+        return Promise.reject({
+            body: {
+                message: 'Attributes name and place are mandatory'
+            },
+            statusCode: 400,
+            headers: {'Content-Type': 'application/json'}
+        });
+    }
+}
diff --git a/docs/examples/src/hello_http_validate.js b/docs/examples/src/hello_http_validate.js
new file mode 100644
index 0000000..6f56f1a
--- /dev/null
+++ b/docs/examples/src/hello_http_validate.js
@@ -0,0 +1,22 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements; and to You under the Apache License, Version 2.0.
+
+/*
+ * This action validates the parameters passed to the hello world action and
+ * returns an error if any of them is missing.
+ */
+function main(params) {
+    if(params.name && params.place) {
+        return params;
+    } else {
+        return {
+            error: {
+                body: {
+                    message: 'Attributes name and place are mandatory'
+                },
+                statusCode: 400,
+                headers: {'Content-Type': 'application/json'}
+            }
+        }
+    }
+}
diff --git a/docs/examples/src/hello_http_wrap.js b/docs/examples/src/hello_http_wrap.js
new file mode 100644
index 0000000..893afaa
--- /dev/null
+++ b/docs/examples/src/hello_http_wrap.js
@@ -0,0 +1,14 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more contributor
+// license agreements; and to You under the Apache License, Version 2.0.
+
+/*
+ * This action take a result from the previous action and wraps it into a
+ * HTTP response structure.
+ */
+function main(params) {
+    return {
+        body: params,
+        statusCode: 200,
+        headers: {'Content-Type': 'application/json'}
+    };
+}
diff --git a/docs/programming_guide.md b/docs/programming_guide.md
index 3f2b899..0f1704e 100644
--- a/docs/programming_guide.md
+++ b/docs/programming_guide.md
@@ -58,6 +58,8 @@ Each example shows the "code", that is the Package Manifest, Deployment file and
 - API Gateway examples
   - [The "Hello World" API Gateway](wskdeploy_apigateway_helloworld.md#api-gateway) - deploy a "hello world" JavaScript function with associated HTTP API.
   - [API Gateway sequence](wskdeploy_apigateway_sequence.md#api-gateway-sequence) - deploy JavaScript sequence with associated HTTP API.
+  - [API Gateway HTTP response](wskdeploy_apigateway_http.md#api-gateway-http-response) - deploy JavaScript actions that return custom HTTP responses.
+  - [API Gateway HTTP response and sequence](wskdeploy_apigateway_http_sequence.md#api-gateway-http-response-and-sequence) - deploy JavaScript sequences that return custom HTTP responses.
 
 ---
 <!--
diff --git a/docs/wskdeploy_apigateway_http.md b/docs/wskdeploy_apigateway_http.md
new file mode 100644
index 0000000..63f7e7c
--- /dev/null
+++ b/docs/wskdeploy_apigateway_http.md
@@ -0,0 +1,194 @@
+<!--
+#
+# 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.
+#
+-->
+
+# API Gateway HTTP response
+
+## HTTP status handling
+
+The API Gateway does a lot of things for you and in particular provides a default error handling behaviour that assumes that a successful result should map to a HTTP response with status code `200: OK` and an error result should map to a response with status code `400: Bad Request`. This works for simple use cases but when building a complex API, you may want to have more control over what is returned. Some of the things you may want to do include:
+- Return a more precise status code
+- Set specific response headers
+- Return data in a format other than JSON
+
+The API Gateway allows you to do this by specifying the HTTP response type on individual API endpoints. Doing this gives you more control by removing the default behaviour of the API Gateway but this means that you also need to adapt what your actions return to tell the API Gateway what do do.
+
+This example is a variation of the ["Hello World" API Gateway](wskdeploy_apigateway_helloworld.md#api-gateway) example that shows the changes that need to apply:
+- to the manifest,
+- and to the underlying actions.
+
+### Manifest file
+#### _Example: “Hello world” action with HTTP response_
+```yaml
+packages:
+  hello_world_package:
+    version: 1.0
+    license: Apache-2.0
+    actions:
+      hello_world:
+        function: src/hello_http.js
+        web-export: true
+    apis:
+      hello-world:
+        hello:
+          world:
+            hello_world:
+              method: GET
+              response: http
+```
+
+There are two key changes to this file:
+- a `response` field with value `http` was added to the API,
+- the `hello_world` action points to a different file detailed below.
+
+### Action file
+#### _Example: “Hello world” action with structured HTTP response_
+```javascript
+function main(params) {
+    if(params.name && params.place) {
+        return {
+            body: {
+                greeting: `Hello ${params.name} from ${params.place}`
+            },
+            statusCode: 200,
+            headers: {'Content-Type': 'application/json'}
+        };
+    } else {
+        return {
+            error: {
+                body: {
+                    message: 'Attributes name and place are mandatory'
+                },
+                statusCode: 400,
+                headers: {'Content-Type': 'application/json'}
+            }
+        }
+    }
+}
+```
+
+Because a HTTP response disables the API Gateway default handling, you have to provide a more complex response that fills in the blanks and provides:
+- a `statusCode` field with the required HTTP status code,
+- a `body` field that contains your normal payload,
+- an optional `headers` field that includes any HTTP header you want to set, typically `Content-Type`.
+
+If you don't provide this structure, the API Gateway will generate a HTTP response with status code `204: No Content` and an empty body. If this occurs when it shouldn't, it's probably a sign that you have a HTTP response specified with the gateway but the undelying action doesn't return this structure.
+
+When you want to return an error, you need to provide the same structure wrapped into an `error` object. If you don't wrap it into an `error` object, it will still work from an HTTP prespective but OpenWhisk will not recognise it as an error.
+
+This structure will work with any language that is supported by OpenWhisk, such as python or Java. If you are using JavaScript, you can make use of `Promise.resolve` and `Promise.reject` to make your code more readable by removing the need for the `error` wrapper:
+
+### Action file
+#### _Example: “Hello world” action with HTTP response using Promise_
+```javascript
+function main(params) {
+    if(params.name && params.place) {
+        return Promise.resolve({
+            body: {
+                greeting: `Hello ${params.name} from ${params.place}`
+            },
+            statusCode: 200,
+            headers: {'Content-Type': 'application/json'}
+        });
+    } else {
+        return Promise.reject({
+            body: {
+                message: 'Attributes name and place are mandatory'
+            },
+            statusCode: 400,
+            headers: {'Content-Type': 'application/json'}
+        });
+    }
+}
+```
+
+### Deploying
+
+You can actually deploy the "API Gateway HTTP" manifest from the incubator-openwhisk-wskdeploy project directory if you have downloaded it from GitHub:
+
+```sh
+$ wskdeploy -m docs/examples/manifest_hello_world_apigateway_http.yaml
+```
+
+### Invoking
+
+Check the full URL of your API first:
+```sh
+$ wsk api list
+```
+
+This will return some information on the API, including the full URL, which
+should end with `hello/world`. It can then be invoked:
+
+```sh
+$ curl -i <url>
+```
+
+### Result
+The invocation should return a JSON response with status code `400` that includes this result:
+
+```json
+{
+    "message": "Attributes name and place are mandatory"
+}
+```
+
+This shows our error handling code working. To get a valid response, we need to provide the `name` and `place` parameters:
+
+```sh
+$ curl -i <url>?name=World&place=Earth
+```
+
+You should then see a JSON response with status code `200` and the following result:
+
+```json
+{
+    "greeting": "Hello World from Earth!"
+}
+```
+
+### Discussion
+
+This example shows how you can have full control over the HTTP response produced by the API Gateway. This is essential when building a good REST API. Taking this example further, you can also return different payloads, such as CSV or XML files.
+
+### Source code
+The source code for the manifest and JavaScript files can be found here:
+- [manifest_hello_world_apigateway_http.yaml](examples/manifest_hello_world_apigateway_http.yaml)
+- [hello_http.js](examples/src/hello_http.js)
+- [hello_http_promise.js](examples/src/hello_http_promise.js)
+
+### Specification
+For convenience, the Packages and Actions grammar can be found here:
+- **[Packages](../specification/html/spec_packages.md#packages)**
+- **[Actions](../specification/html/spec_actions.md#actions)**
+
+---
+<!--
+ Bottom Navigation
+-->
+<html>
+<div align="center">
+<table align="center">
+  <tr>
+    <td><a href="wskdeploy_apigateway_sequence.md#api-gateway-sequence">&lt;&lt;&nbsp;previous</a></td>
+    <td><a href="programming_guide.md#guided-examples">Example Index</a></td>
+    <td><a href="wskdeploy_apigateway_http_sequence.md#api-gateway-http-response-and-sequence">next&nbsp;&gt;&gt;</a></td>
+  </tr>
+</table>
+</div>
+</html>
diff --git a/docs/wskdeploy_apigateway_http_sequence.md b/docs/wskdeploy_apigateway_http_sequence.md
new file mode 100644
index 0000000..26bb689
--- /dev/null
+++ b/docs/wskdeploy_apigateway_http_sequence.md
@@ -0,0 +1,175 @@
+<!--
+#
+# 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.
+#
+-->
+
+# API Gateway HTTP response and sequence
+
+## HTTP status handling in sequences
+
+The previous example in [API Gateway HTTP response](wskdeploy_apigateway_http.md#api-gateway-http-response) showed how to handle custom HTTP responses. The downside of the approach used in that example is that it requires the action to be updated to return a HTTP response structure. What if we want to re-use actions that have no knowledge of HTTP? One simple options is to use a sequence. The HTTP response structure needs to be included at any point where the sequence terminates. A sequen [...]
+- Wrap the final result in a HTTP response structure,
+- Wrap any error in a HTTP response structure.
+
+The first step is to create a manifest with a sequence that validates input, calls the existing `hello.js` action and wraps the result.
+
+### Manifest file
+#### _Example: “Hello world” action with HTTP response and sequence_
+```yaml
+packages:
+  hello_world_package:
+    version: 1.0
+    license: Apache-2.0
+    actions:
+      hello_validate:
+        function: src/hello_http_validate.js
+      hello:
+        function: src/hello.js
+      hello_wrap:
+        function: src/hello_http_wrap.js
+    sequences:
+      hello_world:
+        actions: hello_validate, hello, hello_wrap
+        web: true
+    apis:
+      hello-world:
+        hello:
+          world:
+            hello_world:
+              method: GET
+              response: http
+```
+
+The key points of this file are:
+- multiple actions are defined to validate input, run the main action and wrap the output,
+- a sequence is defined that combines all three actions and that has the `web` field set to `true`,
+- a `response` field with value `http` is set on the the API.
+
+### Validate action file
+#### _Example: validation action with structured HTTP error_
+```javascript
+function main(params) {
+    if(params.name && params.place) {
+        return params;
+    } else {
+        return {
+            error: {
+                body: {
+                    message: 'Attributes name and place are mandatory'
+                },
+                statusCode: 400,
+                headers: {'Content-Type': 'application/json'}
+            }
+        }
+    }
+}
+```
+
+The first action validates inputs. If everything is fine, it returns the parameters unchanged for those to be passed to the next action in the sequence. If validation fails, it create an error HTTP structure. The presence of the `error` field in the result will make OpenWhisk abort the sequence and return what is contained in that field, which in turn is transformed into an HTTP response by the API Gateway.
+
+The second action in the chain is the simple greeting action from [The "Hello World" Action](wskdeploy_action_helloworld.md#actions).
+
+### Wrap action file
+#### _Example: action that wraps a simple result into a HTTP response_
+```javascript
+function main(params) {
+    return {
+        body: params,
+        statusCode: 200,
+        headers: {'Content-Type': 'application/json'}
+    };
+}
+```
+
+The last action in the chain simply wraps the result from the previous action into a HTTP response structure with a status code and headers.
+
+### Deploying
+
+You can actually deploy the "API Gateway HTTP" manifest from the incubator-openwhisk-wskdeploy project directory if you have downloaded it from GitHub:
+
+```sh
+$ wskdeploy -m docs/examples/manifest_hello_world_apigateway_http_sequence.yaml
+```
+
+### Invoking
+
+Check the full URL of your API first:
+```sh
+$ wsk api list
+```
+
+This will return some information on the API, including the full URL, which
+should end with `hello/world`. It can then be invoked:
+
+```sh
+$ curl -i <url>
+```
+
+### Result
+The invocation should return a JSON response with status code `400` that includes this result:
+
+```json
+{
+    "message": "Attributes name and place are mandatory"
+}
+```
+
+This shows our error handling code working. To get a valid response, we need to provide the `name` and `place` parameters:
+
+```sh
+$ curl -i <url>?name=World&place=Earth
+```
+
+You should then see a JSON response with status code `200` and the following result:
+
+```json
+{
+    "greeting": "Hello World from Earth!"
+}
+```
+
+### Discussion
+
+By combining HTTP responses and sequences, you can re-use existing actions that are not designed to return HTTP responses by adding the necesary wrapper to the final result. You need to be careful how errors are handled as they will short-circuit the sequence execution and return early.
+
+### Source code
+The source code for the manifest and JavaScript files can be found here:
+- [manifest_hello_world_apigateway_http_sequence.yaml](examples/manifest_hello_world_apigateway_http_sequence.yaml)
+- [hello_http_validate.js](examples/src/hello_http_validate.js)
+- [hello.js](examples/src/hello.js)
+- [hello_http_wrap.js](examples/src/hello_http_wrap.js)
+
+### Specification
+For convenience, the Packages and Actions grammar can be found here:
+- **[Packages](../specification/html/spec_packages.md#packages)**
+- **[Actions](../specification/html/spec_actions.md#actions)**
+
+---
+<!--
+ Bottom Navigation
+-->
+<html>
+<div align="center">
+<table align="center">
+  <tr>
+    <td><a href="wskdeploy_apigateway_http.md#api-gateway-http-response">&lt;&lt;&nbsp;previous</a></td>
+    <td><a href="programming_guide.md#guided-examples">Example Index</a></td>
+    <!--<td><a href="">next&nbsp;&gt;&gt;</a></td>-->
+  </tr>
+</table>
+</div>
+</html>
diff --git a/docs/wskdeploy_apigateway_sequence.md b/docs/wskdeploy_apigateway_sequence.md
index 8c03fe5..b8a935e 100644
--- a/docs/wskdeploy_apigateway_sequence.md
+++ b/docs/wskdeploy_apigateway_sequence.md
@@ -116,9 +116,9 @@ For convenience, the Packages and Actions grammar can be found here:
 <div align="center">
 <table align="center">
   <tr>
-    <td><a href="wskdeploy_apigateway_helloworld.md#packages">&lt;&lt;&nbsp;previous</a></td>
+    <td><a href="wskdeploy_apigateway_helloworld.md#api-gateway">&lt;&lt;&nbsp;previous</a></td>
     <td><a href="programming_guide.md#guided-examples">Example Index</a></td>
-    <!--<td><a href="">next&nbsp;&gt;&gt;</a></td>-->
+    <td><a href="wskdeploy_apigateway_http.md#api-gateway-http-response">next&nbsp;&gt;&gt;</a></td>
   </tr>
 </table>
 </div>