Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] new array type for schema fields #277

Merged
merged 1 commit into from
May 29, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 56 additions & 41 deletions kong/dao/schemas_validation.lua
Original file line number Diff line number Diff line change
@@ -1,71 +1,85 @@
local utils = require "kong.tools.utils"
local stringy = require "stringy"
local constants = require "kong.constants"

local LUA_TYPES = {
boolean = true,
local POSSIBLE_TYPES = {
id = true,
table = true,
array = true,
string = true,
number = true,
table = true
boolean = true,
timestamp = true
}

local LUA_TYPE_ALIASES = {
[constants.DATABASE_TYPES.ID] = "string",
[constants.DATABASE_TYPES.TIMESTAMP] = "number"
local types_validation = {
[constants.DATABASE_TYPES.ID] = function(v) return type(v) == "string" end,
[constants.DATABASE_TYPES.TIMESTAMP] = function(v) return type(v) == "number" end,
["array"] = function(v) return utils.is_array(v) end
}

local _M = {}

-- Returns the proper Lua type from a schema type, handling aliases
-- @param {string} type_val The type of the schema property
-- @return {string} A valid Lua type
function _M.get_type(type_val)
local alias = LUA_TYPE_ALIASES[type_val]
return alias and alias or type_val
local function validate_type(field_type, value)
if types_validation[field_type] then
return types_validation[field_type](value)
end
return type(value) == field_type
end

local _M = {}

-- Validate a table against a given schema
-- @param {table} t Table to validate
-- @param {table} schema Schema against which to validate the table
-- @param {boolean} is_update For an entity update, we might want a slightly different behaviour
-- @return {boolean} Success of validation
-- @return {table} A list of encountered errors during the validation
-- @param `t` Entity to validate, as a table.
-- @param `schema` Schema against which to validate the entity.
-- @param `is_update` For an entity update, check immutable fields. Set to true.
-- @return `valid` Success of validation. True or false.
-- @return `errors` A list of encountered errors during the validation.
function _M.validate(t, schema, is_update)
local errors

-- Check the given table against a given schema
for column, v in pairs(schema) do

-- Set default value for the field if given
-- [DEFAULT] Set default value for the field if given
if t[column] == nil and v.default ~= nil then
if type(v.default) == "function" then
t[column] = v.default(t)
else
t[column] = v.default
end
-- [IMMUTABLE] check immutability of a field if updating
elseif is_update and t[column] ~= nil and v.immutable and not v.required then
-- is_update check immutability of a field
errors = utils.add_error(errors, column, column.." cannot be updated")
end

-- Check if type is valid boolean and numbers as strings are accepted and converted
-- [TYPE] Check if type is valid. Boolean and Numbers as strings are accepted and converted
if v.type ~= nil and t[column] ~= nil then
local valid
if _M.get_type(v.type) == "number" and type(t[column]) == "string" then -- a number can also be sent as a string
t[column] = tonumber(t[column])
valid = t[column] ~= nil
elseif _M.get_type(v.type) == "boolean" and type(t[column]) == "string" then
local bool = t[column]:lower()
valid = bool == "true" or bool == "false"
t[column] = bool == "true"
local is_valid_type

-- ALIASES: number, boolean and array can be passed as strings and will be converted
if type(t[column]) == "string" then
if v.type == "number" then
t[column] = tonumber(t[column])
is_valid_type = t[column] ~= nil
elseif v.type == "boolean" then
local bool = t[column]:lower()
is_valid_type = bool == "true" or bool == "false"
t[column] = bool == "true"
elseif v.type == "array" then
t[column] = stringy.split(t[column], ",")
is_valid_type = validate_type(v.type, t[column])
else -- if string
is_valid_type = validate_type(v.type, t[column])
end
else
valid = type(t[column]) == _M.get_type(v.type)
is_valid_type = validate_type(v.type, t[column])
end
if not valid and LUA_TYPES[v.type] then

if not is_valid_type and POSSIBLE_TYPES[v.type] then
errors = utils.add_error(errors, column, column.." is not a "..v.type)
end
end

-- Check type if value is allowed in the enum
-- [ENUM] Check if the value is allowed in the enum.
if v.enum and t[column] ~= nil then
local found = false
for _, allowed in ipairs(v.enum) do
Expand All @@ -80,14 +94,14 @@ function _M.validate(t, schema, is_update)
end
end

-- Check field against a regex if specified
-- [REGEX] Check field against a regex if specified
if t[column] ~= nil and v.regex then
if not ngx.re.match(t[column], v.regex) then
errors = utils.add_error(errors, column, column.." has an invalid value")
end
end

-- validate a subschema
-- [SCHEMA] Validate a sub-schema from a table or retrived by a function
if v.schema then
local sub_schema, err
if type(v.schema) == "function" then
Expand All @@ -102,19 +116,19 @@ function _M.validate(t, schema, is_update)
end

if sub_schema then
-- Check for sub-schema defaults and required properties
-- Check for sub-schema defaults and required properties in advance
for sub_field_k, sub_field in pairs(sub_schema) do
if t[column] == nil then
if sub_field.default then
if sub_field.default then -- Sub-value has a default, be polite and pre-assign the sub-value
t[column] = {}
elseif sub_field.required then -- only check required if field doesn't have a default
elseif sub_field.required then -- Only check required if field doesn't have a default
errors = utils.add_error(errors, column, column.."."..sub_field_k.." is required")
end
end
end

if t[column] and type(t[column]) == "table" then
-- validating subschema
-- Actually validating the sub-schema
local s_ok, s_errors = _M.validate(t[column], sub_schema, is_update)
if not s_ok then
for s_k, s_v in pairs(s_errors) do
Expand All @@ -125,12 +139,13 @@ function _M.validate(t, schema, is_update)
end
end

-- Check required fields are set
-- [REQUIRED] Check that required fields are set. Now that default and most other checks
-- have been run.
if v.required and (t[column] == nil or t[column] == "") then
errors = utils.add_error(errors, column, column.." is required")
end

-- Check field against a custom function only if there is no error on that field already
-- [FUNC] 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
Expand Down
12 changes: 1 addition & 11 deletions kong/plugins/keyauth/schema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,7 @@ local function default_key_names(t)
end
end

local function validate_key_names(t)
if type(t) == "table" and not utils.is_array(t) then
local printable_mt = require "kong.tools.printable"
setmetatable(t, printable_mt)
return false, "key_names must be an array. '"..t.."' is a table. Lua tables must have integer indexes starting at 1."
end

return true
end

return {
key_names = { required = true, type = "table", default = default_key_names, func = validate_key_names },
key_names = { required = true, type = "array", default = default_key_names },
hide_credentials = { type = "boolean", default = false }
}
1 change: 0 additions & 1 deletion kong/plugins/request_transformer/access.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ local function get_content_type(request)
if header_value then
return stringy.strip(header_value)
end
return nil
end

function _M.execute(conf)
Expand Down
18 changes: 10 additions & 8 deletions kong/plugins/request_transformer/schema.lua
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
return {
add = { type = "table", schema = {
form = { type = "table" },
headers = { type = "table" },
querystring = { type = "table" }
add = { type = "table",
schema = {
form = { type = "array" },
headers = { type = "array" },
querystring = { type = "array" }
}
},
remove = { type = "table", schema = {
form = { type = "table" },
headers = { type = "table" },
querystring = { type = "table" }
remove = { type = "table",
schema = {
form = { type = "array" },
headers = { type = "array" },
querystring = { type = "array" }
}
}
}
Loading