Skip to content

Commit

Permalink
plugin dev quickstart: 3rd party service draft
Browse files Browse the repository at this point in the history
  • Loading branch information
rspurgeon committed Apr 16, 2024
1 parent b5e6503 commit 417fd09
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 17 deletions.
139 changes: 123 additions & 16 deletions app/_src/gateway/plugin-development/get-started/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ local cjson = require("cjson.safe")

### 2. Invoke 3rd party http request

The `lua-rest-htpp` library provides a simple "single shot" http request
The `lua-rest-http` library provides a simple "single shot" http request
function we can use to reach out to our 3rd party service. Here we show
invoking a `GET` request to the _httpbin.org/anything_ API which will
echo back various information in the response.

Add the following to the `MyPluginHandler:response` function inside the
Add the following to the top of the `MyPluginHandler:response` function inside the
`handler.lua` module:

```lua
Expand All @@ -63,24 +63,92 @@ local res, err = httpc:request_uri("http://httpbin.org/anything", {
})
```

If the request to the 3rd party service is successful, the `res`
variable will contain the response. Before showing how to process the
successful response, what about error events?
Errors will be provided in the `err` return value, let's see what
options there are for handling them.

### 3. Handle response errors

handle errors
The {{site.base_gateway}}
[Plugin Development Kit](/gateway/{{page.release_version}}/plugin-development/pdk/)
provides you with various functions to help you handle error conditions.

In this example you are processing responses from the upstream service
and decorating your client response with values from the 3rd party service.
If the request to the 3rd party service fails, you have a choice to make.
You can terminate processing of the response and return to the client with
an error, or you could continue processing the response and not complete the
custom header logic. In this example, we show how to terminate the
response processing and return a `500` internal server error to the client.

Add the following to the `MyPluginHandler:response` function immediately
after the `httpc:request_uri` call:

```lua
if err then
return kong.response.error(500,
"Error when trying to access 3rd party service: " .. err,
{ ["Content-Type"] = "text/html" })
end
```

If you choose to continue processing instead, you could log an
error message and `return` from the `MyPluginHandler:response` function,
similar to this:

```lua
if err then
kong.log("Error when trying to access 3rd party service:", err)
return
end
```

### 4. Process JSON data from 3rd party response

introduce CJSON library
process response
handle errors
This 3rd party service returns a JSON object in the
response body. Here we are going to show how to parse and extract a
single value from the JSON body.

Use the `decode` function in the `lua-cjson` library passing in the
`res.body` value received from the `request_uri` function:

```lua
local body_table, err = cjson.decode(res.body)
```

The `decode` function returns a tuple of values. The first value contains the
result of a successful decoding and represents the JSON as a table containing
the parsed data. If an error has occurred, the second value will contain
error information (or `nil` on success).

Same as during the HTTP processing above, in the event of an error return an
error response to the client and stop processing. Add the following to
the `MyPluginHandler:response` function after the previous line:

### 5. Decorate upstream response
```lua
if err then
return kong.response.error(500,
"Error while decoding 3rd party service response: " .. err,
{ ["Content-Type"] = "text/html" })
end
```

take JSON field and add it to response before completing
return response
At this point, given no error conditions, you have a valid response from the
3rd party you can use to decorate your client response. The _httpbin_ service returns
a variety of fields including a `url` field which is an echo of the URL requested.
For this example pass the `url` field to the client response by adding the following
after the previous error handling:

### 6. Full code listing
```lua
kong.response.set_header(conf.response_header_name, body_table.url)
```

This is now the full code listing for the `handler.lua` file:
We've broken down each section of the code. The following is a full code
listing for the `handler.lua` file.

### 5. Full code listing

```lua
local http = require("resty.http")
Expand All @@ -93,29 +161,68 @@ local MyPluginHandler = {

function MyPluginHandler:response(conf)

kong.log("response handler")

local httpc = http.new()

local res, err = httpc:request_uri("http://httpbin.org/anything", {
method = "GET",
})

if err then
kong.log("request failed: ", err)
return
return kong.response.error(500,
"Error when trying to access 3rd party service: " .. err,
{ ["Content-Type"] = "text/html" })
end

local body_table, err = cjson.decode(res.body)

if err then
kong.log("failed to decode response body: ", err)
return
return kong.response.error(500,
"Error when decoding 3rd party service response: " .. err,
{ ["Content-Type"] = "text/html" })
end

kong.response.set_header(conf.response_header_name, body_table.origin)
kong.response.set_header(
conf.response_header_name,
body_table.url)

end

return MyPluginHandler
```

### 6. Update testing code

At this stage, if you re-run the `pongo run` command to execute
the integration tests previously built, you will receive errors. The
expected value in the header has changed from `response` to
`http://httpbin.org/anything`. Update the
`spec/my-plugin/01-integration_spec.lua` file to assert the new
value in the header.

```lua
-- validate the value of that header
assert.equal("http://httpbin.org/anything", header_value)
```

Re-run the test with `pongo run` and verify success:

```sh
...
[----------] Global test environment teardown.
[==========] 2 tests from 1 test file ran. (23171.28 ms total)
[ PASSED ] 2 tests.
```

This guide provides examples to get you started. In a real plugin development
scenario, you would want to build integration tests for 3rd party services by
providing a test dependency using a mock service instead
of making network calls to the actual 3rd party service used. Pongo supports
test dependencies for this purpose. See the
[Pongo documentation](https://github.com/Kong/kong-pongo?tab=readme-ov-file#test-dependencies)
for details on setting test dependencies.

## What's Next

Deploying {{site.base_gateway}} plugins is the final step in building an end to end
Expand Down
2 changes: 1 addition & 1 deletion app/_src/gateway/plugin-development/get-started/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Once the dependencies are running successfully, we run a {{site.base_gateway}} c
shell within it so we can interact with the gateway. Pongo runs a {{site.base_gateway}}
container with various CLI tools pre-installed to help with testing.

Launch the gateway and open shell with:
Launch the gateway and open a shell with:

```sh
pongo shell
Expand Down

0 comments on commit 417fd09

Please sign in to comment.