Add a custom plugin configuration
Add configuration fields in the plugins’s schema.lua
file, and define the features using the configuration fields in handler.lua
.
Prerequisites
Series Prerequisites
This page is part of the Get started with custom plugin development series.
Complete the previous page, Add custom plugin testing before completing this page.
Add configuration fields to the schema
Let’s add some configuration fields to our schema.lua
file.
- Include the Kong Gateway
typedefs
module at the top of theschema.lua
file:local typedefs = require "kong.db.schema.typedefs"
- Add the following
header_name
type definition within thefields
array we defined earlier:{ response_header_name = typedefs.header_name { required = false, default = "X-MyPlugin" } },
This type definition defines the field to be a string that cannot be null and conforms to the rules for header names. It also indicates that the configuration value is not required, which means it’s optional for the user when configuring the plugin. We also specify a default value that will be used when a user does not specify a value.
The full schema.lua
now looks like this:
local typedefs = require "kong.db.schema.typedefs"
local PLUGIN_NAME = "my-plugin"
local schema = {
name = PLUGIN_NAME,
fields = {
{ config = {
type = "record",
fields = {
{ response_header_name = typedefs.header_name {
required = false,
default = "X-MyPlugin" } },
},
},
},
},
}
return schema
Read configuration values from plugin code
Modify the response
function in the handler.lua
file to read the configuration value from the incoming conf
parameter instead of the current hardcoded value:
function MyPluginHandler:response(conf)
kong.response.set_header(conf.response_header_name, "response")
end
Manually validate the configuration
Let’s use Pongo to test the updated configuration.
- Launch Kong Gateway and open a shell:
pongo shell
- Run the database migrations and start Kong Gateway:
kms
-
Add a test Gateway Service:
curl -X POST "http://localhost:8001/services" \ --json '{ "name": "example_service", "url": "https://httpbin.konghq.com" }'
-
Enable the plugin, this time with the configuration value:
curl -X POST "http://localhost:8001/services/example_service/plugins" \ --json '{ "name": "my-plugin", "config": { "response_header_name": "X-CustomHeaderName" } }'
-
curl -X POST "http://localhost:8001/services/example_service/routes" \ --json '{ "name": "example_route", "paths": [ "/mock" ] }'
-
Send a request to the Route:
curl -i "http://localhost:8000/mock/anything"
This time we should see the
X-CustomHeaderName
in the response. - Exit the Kong Gateway shell before proceeding to the next step:
exit
Add automated configuration testing
- Update the
setup
function inside thespec/01-integration_spec.lua
module so that themy-plugin
that is added to the database is configured with a different value for theresponse_header_name
field:-- Add the custom plugin to the test Route blue_print.plugins:insert { name = PLUGIN_NAME, route = { id = test_route.id }, config = { response_header_name = "X-CustomHeaderName", }, }
- Modify the test assertion to match the new header name:
-- now validate and retrieve the expected response header local header_value = assert.response(r).has.header("X-CustomHeaderName")
The test file should now look like this:
-- Helper functions provided by Kong Gateway, see https://github.com/Kong/kong/blob/master/spec/helpers.lua local helpers = require "spec.helpers" -- matches our plugin name defined in the plugins's schema.lua local PLUGIN_NAME = "my-plugin" -- Run the tests for each strategy. Strategies include "postgres" and "off" -- which represent the deployment topologies for Kong Gateway for _, strategy in helpers.all_strategies() do describe(PLUGIN_NAME .. ": [#" .. strategy .. "]", function() -- Will be initialized before_each nested test local client setup(function() -- A BluePrint gives us a helpful database wrapper to -- manage Kong Gateway entities directly. -- This function also truncates any existing data in an existing db. -- The custom plugin name is provided to this function so it mark as loaded local blue_print = helpers.get_db_utils(strategy, nil, { PLUGIN_NAME }) -- Using the BluePrint to create a test Route, automatically attaches it -- to the default "echo" Service that will be created by the test framework local test_route = blue_print.routes:insert({ paths = { "/mock" }, }) -- Add the custom plugin to the test Route blue_print.plugins:insert { name = PLUGIN_NAME, route = { id = test_route.id }, config = { response_header_name = "X-CustomHeaderName", }, } -- start kong assert(helpers.start_kong({ -- use the custom test template to create a local mock server nginx_conf = "spec/fixtures/custom_nginx.template", -- make sure our plugin gets loaded plugins = "bundled," .. PLUGIN_NAME, })) end) -- teardown runs after its parent describe block teardown(function() helpers.stop_kong(nil, true) end) -- before_each runs before each child describe before_each(function() client = helpers.proxy_client() end) -- after_each runs after each child describe after_each(function() if client then client:close() end end) -- a nested describe defines an actual test on the plugin behavior describe("The response", function() it("gets the expected header", function() -- invoke a test request local r = client:get("/mock/anything", {}) -- validate that the request succeeded, response status 200 assert.response(r).has.status(200) -- now validate and retrieve the expected response header local header_value = assert.response(r).has.header("X-CustomHeaderName") -- validate the value of that header assert.equal("response", header_value) end) end) end) end
- Run the tests:
pongo run
Pongo should report a successful test run.