From dc8cbddf154ebfc3c4cb8e8f76d7b9908ba30b8b Mon Sep 17 00:00:00 2001 From: Thibault Charbonnier Date: Thu, 28 May 2015 15:48:47 +0200 Subject: [PATCH] refactor: entities schemas structure and tests --- Makefile | 2 +- kong-0.3.0-1.rockspec | 5 +- kong/dao/cassandra/apis.lua | 30 ++--------- kong/dao/cassandra/base_dao.lua | 2 +- kong/dao/cassandra/consumers.lua | 21 ++------ kong/dao/cassandra/plugins_configurations.lua | 27 ++-------- kong/dao/schemas/apis.lua | 26 ++++++++++ kong/dao/schemas/consumers.lua | 17 ++++++ kong/dao/schemas/plugins_configurations.lua | 23 ++++++++ .../{schemas.lua => schemas_validation.lua} | 5 +- spec/integration/admin_api/admin_api_spec.lua | 52 ------------------- spec/unit/dao/cassandra_spec.lua | 2 +- spec/unit/dao/entities_schemas_spec.lua | 46 ++++++++++++++++ spec/unit/schemas_spec.lua | 2 +- 14 files changed, 131 insertions(+), 129 deletions(-) create mode 100644 kong/dao/schemas/apis.lua create mode 100644 kong/dao/schemas/consumers.lua create mode 100644 kong/dao/schemas/plugins_configurations.lua rename kong/dao/{schemas.lua => schemas_validation.lua} (96%) create mode 100644 spec/unit/dao/entities_schemas_spec.lua diff --git a/Makefile b/Makefile index f666c8627e4..6d8cb772d82 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ install: OPENSSL_LIBDIR=`find / -type f -name "libssl.so*" -print -quit | xargs dirname`; \ fi -dev: +dev: install @for rock in $(DEV_ROCKS) ; do \ if ! command -v $$rock &> /dev/null ; then \ echo $$rock not found, installing via luarocks... ; \ diff --git a/kong-0.3.0-1.rockspec b/kong-0.3.0-1.rockspec index c9051868c8a..9b32e9badbc 100644 --- a/kong-0.3.0-1.rockspec +++ b/kong-0.3.0-1.rockspec @@ -73,7 +73,10 @@ build = { ["kong.resolver.resolver_util"] = "kong/resolver/resolver_util.lua", ["kong.dao.error"] = "kong/dao/error.lua", - ["kong.dao.schemas"] = "kong/dao/schemas.lua", + ["kong.dao.schemas_validation"] = "kong/dao/schemas_validation.lua", + ["kong.dao.schemas.apis"] = "kong/dao/schemas/apis.lua", + ["kong.dao.schemas.consumers"] = "kong/dao/schemas/consumers.lua", + ["kong.dao.schemas.plugins_configurations"] = "kong/dao/schemas/plugins_configurations.lua", ["kong.dao.cassandra.factory"] = "kong/dao/cassandra/factory.lua", ["kong.dao.cassandra.base_dao"] = "kong/dao/cassandra/base_dao.lua", ["kong.dao.cassandra.apis"] = "kong/dao/cassandra/apis.lua", diff --git a/kong/dao/cassandra/apis.lua b/kong/dao/cassandra/apis.lua index dac265b356c..823982aeca3 100644 --- a/kong/dao/cassandra/apis.lua +++ b/kong/dao/cassandra/apis.lua @@ -1,36 +1,12 @@ local BaseDao = require "kong.dao.cassandra.base_dao" -local constants = require "kong.constants" +local apis_schema = require "kong.dao.schemas.apis" local PluginsConfigurations = require "kong.dao.cassandra.plugins_configurations" -local url = require "socket.url" - -local function validate_target_url(value) - local parsed_url = url.parse(value) - if parsed_url.scheme and parsed_url.host then - parsed_url.scheme = parsed_url.scheme:lower() - if parsed_url.scheme == "http" or parsed_url.scheme == "https" then - parsed_url.path = parsed_url.path or "/" - return true, nil, { target_url = url.build(parsed_url)} - else - return false, "Supported protocols are HTTP and HTTPS" - end - end - - return false, "Invalid target URL" -end - -local SCHEMA = { - id = { type = constants.DATABASE_TYPES.ID }, - name = { type = "string", unique = true, queryable = true, default = function(api_t) return api_t.public_dns end }, - public_dns = { type = "string", required = true, unique = true, queryable = true, - regex = "([a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*)" }, - target_url = { type = "string", required = true, func = validate_target_url }, - created_at = { type = constants.DATABASE_TYPES.TIMESTAMP } -} local Apis = BaseDao:extend() function Apis:new(properties) - self._schema = SCHEMA + self._entity = "API" + self._schema = apis_schema self._queries = { insert = { args_keys = { "id", "name", "public_dns", "target_url", "created_at" }, diff --git a/kong/dao/cassandra/base_dao.lua b/kong/dao/cassandra/base_dao.lua index 1d0f6b84bbe..aca5dca2cdf 100644 --- a/kong/dao/cassandra/base_dao.lua +++ b/kong/dao/cassandra/base_dao.lua @@ -8,7 +8,7 @@ local constants = require "kong.constants" local cassandra = require "cassandra" local timestamp = require "kong.tools.timestamp" -local validate = require("kong.dao.schemas").validate +local validate = require("kong.dao.schemas_validation").validate local DaoError = require "kong.dao.error" local stringy = require "stringy" local Object = require "classic" diff --git a/kong/dao/cassandra/consumers.lua b/kong/dao/cassandra/consumers.lua index 1a6c8a16caf..9b84820183d 100644 --- a/kong/dao/cassandra/consumers.lua +++ b/kong/dao/cassandra/consumers.lua @@ -1,27 +1,12 @@ local BaseDao = require "kong.dao.cassandra.base_dao" -local stringy = require "stringy" -local constants = require "kong.constants" +local consumers_schema = require "kong.dao.schemas.consumers" local PluginsConfigurations = require "kong.dao.cassandra.plugins_configurations" -local function check_custom_id_and_username(value, consumer_t) - if (consumer_t.custom_id == nil or stringy.strip(consumer_t.custom_id) == "") - and (consumer_t.username == nil or stringy.strip(consumer_t.username) == "") then - return false, "At least a 'custom_id' or a 'username' must be specified" - end - return true -end - -local SCHEMA = { - id = { type = constants.DATABASE_TYPES.ID }, - custom_id = { type = "string", unique = true, queryable = true, func = check_custom_id_and_username }, - username = { type = "string", unique = true, queryable = true, func = check_custom_id_and_username }, - created_at = { type = constants.DATABASE_TYPES.TIMESTAMP } -} - local Consumers = BaseDao:extend() function Consumers:new(properties) - self._schema = SCHEMA + self._entity = "Consumer" + self._schema = consumers_schema self._queries = { insert = { args_keys = { "id", "custom_id", "username", "created_at" }, diff --git a/kong/dao/cassandra/plugins_configurations.lua b/kong/dao/cassandra/plugins_configurations.lua index 8dc55cf5724..864fa877c8f 100644 --- a/kong/dao/cassandra/plugins_configurations.lua +++ b/kong/dao/cassandra/plugins_configurations.lua @@ -1,34 +1,13 @@ +local plugins_configurations_schema = require "kong.dao.schemas.plugins_configurations" local constants = require "kong.constants" local BaseDao = require "kong.dao.cassandra.base_dao" local cjson = require "cjson" -local utils = require "kong.tools.utils" - -local function load_value_schema(plugin_t) - if plugin_t.name then - local loaded, plugin_schema = utils.load_module_if_exists("kong.plugins."..plugin_t.name..".schema") - if loaded then - return plugin_schema - else - return nil, "Plugin \""..(plugin_t.name and plugin_t.name or "").."\" not found" - end - end -end - -local SCHEMA = { - id = { type = constants.DATABASE_TYPES.ID }, - api_id = { type = constants.DATABASE_TYPES.ID, required = true, foreign = true, queryable = true }, - consumer_id = { type = constants.DATABASE_TYPES.ID, foreign = true, queryable = true, default = constants.DATABASE_NULL_ID }, - name = { type = "string", required = true, queryable = true, immutable = true }, - value = { type = "table", schema = load_value_schema }, - enabled = { type = "boolean", default = true }, - created_at = { type = constants.DATABASE_TYPES.TIMESTAMP } -} local PluginsConfigurations = BaseDao:extend() function PluginsConfigurations:new(properties) - self._entity = "Plugin" - self._schema = SCHEMA + self._entity = "Plugin configuration" + self._schema = plugins_configurations_schema self._queries = { insert = { args_keys = { "id", "api_id", "consumer_id", "name", "value", "enabled", "created_at" }, diff --git a/kong/dao/schemas/apis.lua b/kong/dao/schemas/apis.lua new file mode 100644 index 00000000000..ff5df355771 --- /dev/null +++ b/kong/dao/schemas/apis.lua @@ -0,0 +1,26 @@ +local url = require "socket.url" +local constants = require "kong.constants" + +local function validate_target_url(value) + local parsed_url = url.parse(value) + if parsed_url.scheme and parsed_url.host then + parsed_url.scheme = parsed_url.scheme:lower() + if parsed_url.scheme == "http" or parsed_url.scheme == "https" then + parsed_url.path = parsed_url.path or "/" + return true, nil, { target_url = url.build(parsed_url)} + else + return false, "Supported protocols are HTTP and HTTPS" + end + end + + return false, "Invalid target URL" +end + +return { + id = { type = constants.DATABASE_TYPES.ID }, + name = { type = "string", unique = true, queryable = true, default = function(api_t) return api_t.public_dns end }, + public_dns = { type = "string", required = true, unique = true, queryable = true, + regex = "([a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*)" }, + target_url = { type = "string", required = true, func = validate_target_url }, + created_at = { type = constants.DATABASE_TYPES.TIMESTAMP } +} diff --git a/kong/dao/schemas/consumers.lua b/kong/dao/schemas/consumers.lua new file mode 100644 index 00000000000..32dd97bc35b --- /dev/null +++ b/kong/dao/schemas/consumers.lua @@ -0,0 +1,17 @@ +local stringy = require "stringy" +local constants = require "kong.constants" + +local function check_custom_id_and_username(value, consumer_t) + if (consumer_t.custom_id == nil or stringy.strip(consumer_t.custom_id) == "") + and (consumer_t.username == nil or stringy.strip(consumer_t.username) == "") then + return false, "At least a 'custom_id' or a 'username' must be specified" + end + return true +end + +return { + id = { type = constants.DATABASE_TYPES.ID }, + custom_id = { type = "string", unique = true, queryable = true, func = check_custom_id_and_username }, + username = { type = "string", unique = true, queryable = true, func = check_custom_id_and_username }, + created_at = { type = constants.DATABASE_TYPES.TIMESTAMP } +} diff --git a/kong/dao/schemas/plugins_configurations.lua b/kong/dao/schemas/plugins_configurations.lua new file mode 100644 index 00000000000..2ac86d2f981 --- /dev/null +++ b/kong/dao/schemas/plugins_configurations.lua @@ -0,0 +1,23 @@ +local utils = require "kong.tools.utils" +local constants = require "kong.constants" + +local function load_value_schema(plugin_t) + if plugin_t.name then + local loaded, plugin_schema = utils.load_module_if_exists("kong.plugins."..plugin_t.name..".schema") + if loaded then + return plugin_schema + else + return nil, "Plugin \""..(plugin_t.name and plugin_t.name or "").."\" not found" + end + end +end + +return { + id = { type = constants.DATABASE_TYPES.ID }, + api_id = { type = constants.DATABASE_TYPES.ID, required = true, foreign = true, queryable = true }, + consumer_id = { type = constants.DATABASE_TYPES.ID, foreign = true, queryable = true, default = constants.DATABASE_NULL_ID }, + name = { type = "string", required = true, queryable = true, immutable = true }, + value = { type = "table", schema = load_value_schema }, + enabled = { type = "boolean", default = true }, + created_at = { type = constants.DATABASE_TYPES.TIMESTAMP } +} diff --git a/kong/dao/schemas.lua b/kong/dao/schemas_validation.lua similarity index 96% rename from kong/dao/schemas.lua rename to kong/dao/schemas_validation.lua index 242774501a3..0355884fc1a 100644 --- a/kong/dao/schemas.lua +++ b/kong/dao/schemas_validation.lua @@ -23,7 +23,6 @@ function _M.get_type(type_val) return alias and alias or type_val end - -- Validate a table against a given schema -- @param {table} t Table to validate -- @param {table} schema Schema against which to validate the table @@ -131,8 +130,8 @@ function _M.validate(t, schema, is_update) errors = utils.add_error(errors, column, column.." is required") end - -- Check field against a custom function only if the value requirement has been satisfied - if v.func and type(v.func) == "function" and errors == nil then + -- Check field against a custom function only if there is no error on that field already + if v.func and type(v.func) == "function" and (errors == nil or errors[column] == nil) then local ok, err, new_fields = v.func(t[column], t) if not ok and err then errors = utils.add_error(errors, column, err) diff --git a/spec/integration/admin_api/admin_api_spec.lua b/spec/integration/admin_api/admin_api_spec.lua index 3817adb2d2b..26f5e2ed8a3 100644 --- a/spec/integration/admin_api/admin_api_spec.lua +++ b/spec/integration/admin_api/admin_api_spec.lua @@ -90,58 +90,6 @@ describe("Admin API", function() end) - describe("APIs Schema", function() - - it("should return error with wrong target_url", function() - local response, status = http_client.post(spec_helper.API_URL.."/apis", { - public_dns = "hello.com", - target_url = "asdasd" - }) - - assert.are.equal(400, status) - assert.are.equal("Invalid target URL", cjson.decode(response).target_url) - end) - - it("should return error with wrong target_url protocol", function() - local response, status = http_client.post(spec_helper.API_URL.."/apis", { - public_dns = "hello.com", - target_url = "wot://hello.com/" - }) - - assert.are.equal(400, status) - assert.are.equal("Supported protocols are HTTP and HTTPS", cjson.decode(response).target_url) - end) - - it("should work without a path", function() - local response, status = http_client.post(spec_helper.API_URL.."/apis", { - public_dns = "hello.com", - target_url = "http://hello.com" - }) - - local body = cjson.decode(response) - assert.are.equal(201, status) - assert.are.equal("http://hello.com/", body.target_url) - - -- Clean up - http_client.delete(spec_helper.API_URL.."/apis/"..body.id) - end) - - it("should work without upper case protocol", function() - local response, status = http_client.post(spec_helper.API_URL.."/apis", { - public_dns = "hello2.com", - target_url = "HTTP://hello.com/world" - }) - - local body = cjson.decode(response) - assert.are.equal(201, status) - assert.are.equal("http://hello.com/world", cjson.decode(response).target_url) - - -- Clean up - http_client.delete(spec_helper.API_URL.."/apis/"..body.id) - end) - - end) - describe("POST", function() describe("application/x-www-form-urlencoded", function() test_for_each_endpoint(function(endpoint, base_url) diff --git a/spec/unit/dao/cassandra_spec.lua b/spec/unit/dao/cassandra_spec.lua index 61544b673da..d709624ce10 100644 --- a/spec/unit/dao/cassandra_spec.lua +++ b/spec/unit/dao/cassandra_spec.lua @@ -289,7 +289,7 @@ describe("Cassandra DAO", function() assert.truthy(err) assert.is_daoError(err) assert.True(err.unique) - assert.are.same("Plugin already exists", err.message) + assert.are.same("Plugin configuration already exists", err.message) end) it("should not insert a plugin if this plugin doesn't exist (not installed)", function() diff --git a/spec/unit/dao/entities_schemas_spec.lua b/spec/unit/dao/entities_schemas_spec.lua new file mode 100644 index 00000000000..6348035465b --- /dev/null +++ b/spec/unit/dao/entities_schemas_spec.lua @@ -0,0 +1,46 @@ +local validate = require("kong.dao.schemas_validation").validate +local api_schema = require "kong.dao.schemas.apis" + +require "kong.tools.ngx_stub" + +describe("Entities Schemas", function() + describe("APIs", function() + + it("should return error with wrong target_url", function() + local valid, errors = validate({ + public_dns = "hello.com", + target_url = "asdasd" + }, api_schema) + assert.False(valid) + assert.same("Invalid target URL", errors.target_url) + end) + + it("should return error with wrong target_url protocol", function() + local valid, errors = validate({ + public_dns = "hello.com", + target_url = "wot://hello.com/" + }, api_schema) + assert.False(valid) + assert.same("Supported protocols are HTTP and HTTPS", errors.target_url) + end) + + it("should work without a path", function() + local valid, errors = validate({ + public_dns = "hello.com", + target_url = "http://hello.com" + }, api_schema) + assert.True(valid) + assert.falsy(errors) + end) + + it("should work without upper case protocol", function() + local valid, errors = validate({ + public_dns = "hello2.com", + target_url = "HTTP://hello.com/world" + }, api_schema) + assert.True(valid) + assert.falsy(errors) + end) + + end) +end) diff --git a/spec/unit/schemas_spec.lua b/spec/unit/schemas_spec.lua index c0e5ca14152..a9945d67ce7 100644 --- a/spec/unit/schemas_spec.lua +++ b/spec/unit/schemas_spec.lua @@ -1,4 +1,4 @@ -local schemas = require "kong.dao.schemas" +local schemas = require "kong.dao.schemas_validation" local constants = require "kong.constants" local validate = schemas.validate