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

feat(core) implement entity tagging #4275

Merged
merged 5 commits into from
Feb 13, 2019
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
5 changes: 5 additions & 0 deletions kong-1.0.3-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ build = {
["kong.api.routes.targets"] = "kong/api/routes/targets.lua",
["kong.api.routes.certificates"] = "kong/api/routes/certificates.lua",
["kong.api.routes.snis"] = "kong/api/routes/snis.lua",
["kong.api.routes.tags"] = "kong/api/routes/tags.lua",

["kong.tools.cluster_ca"] = "kong/tools/cluster_ca.lua",
["kong.tools.ciphers"] = "kong/tools/ciphers.lua",
Expand All @@ -126,6 +127,7 @@ build = {
["kong.db.dao.snis"] = "kong/db/dao/snis.lua",
["kong.db.dao.targets"] = "kong/db/dao/targets.lua",
["kong.db.dao.plugins"] = "kong/db/dao/plugins.lua",
["kong.db.dao.tags"] = "kong/db/dao/tags.lua",
["kong.db.schema"] = "kong/db/schema/init.lua",
["kong.db.schema.entities.cluster_ca"] = "kong/db/schema/entities/cluster_ca.lua",
["kong.db.schema.entities.consumers"] = "kong/db/schema/entities/consumers.lua",
Expand All @@ -136,6 +138,7 @@ build = {
["kong.db.schema.entities.upstreams"] = "kong/db/schema/entities/upstreams.lua",
["kong.db.schema.entities.targets"] = "kong/db/schema/entities/targets.lua",
["kong.db.schema.entities.plugins"] = "kong/db/schema/entities/plugins.lua",
["kong.db.schema.entities.tags"] = "kong/db/schema/entities/tags.lua",
["kong.db.schema.others.migrations"] = "kong/db/schema/others/migrations.lua",
["kong.db.schema.entity"] = "kong/db/schema/entity.lua",
["kong.db.schema.metaschema"] = "kong/db/schema/metaschema.lua",
Expand All @@ -146,9 +149,11 @@ build = {
["kong.db.strategies.cassandra.connector"] = "kong/db/strategies/cassandra/connector.lua",
["kong.db.strategies.cassandra.plugins"] = "kong/db/strategies/cassandra/plugins.lua",
["kong.db.strategies.cassandra.services"] = "kong/db/strategies/cassandra/services.lua",
["kong.db.strategies.cassandra.tags"] = "kong/db/strategies/cassandra/tags.lua",
["kong.db.strategies.postgres"] = "kong/db/strategies/postgres/init.lua",
["kong.db.strategies.postgres.plugins"] = "kong/db/strategies/postgres/plugins.lua",
["kong.db.strategies.postgres.connector"] = "kong/db/strategies/postgres/connector.lua",
["kong.db.strategies.postgres.tags"] = "kong/db/strategies/postgres/tags.lua",
["kong.db.migrations.state"] = "kong/db/migrations/state.lua",
["kong.db.migrations.helpers"] = "kong/db/migrations/helpers.lua",
["kong.db.migrations.core"] = "kong/db/migrations/core/init.lua",
Expand Down
4 changes: 4 additions & 0 deletions kong/api/arguments.lua
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ infer = function(args, schema)
end
end

if schema.ttl == true and args.ttl then
args.ttl = tonumber(args.ttl) or args.ttl
end

return args
end

Expand Down
48 changes: 43 additions & 5 deletions kong/api/endpoints.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ local unescape_uri = ngx.unescape_uri
local tonumber = tonumber
local tostring = tostring
local select = select
local concat = table.concat
local null = ngx.null
local type = type
local fmt = string.format
local concat = table.concat
local re_match = ngx.re.match
local split = utils.split


-- error codes http status codes
Expand Down Expand Up @@ -125,9 +127,32 @@ local function extract_options(args, schema, context)
if schema.ttl == true and args.ttl ~= nil and (context == "insert" or
context == "update" or
context == "upsert") then
options.ttl = tonumber(args.ttl) or args.ttl
options.ttl = args.ttl
args.ttl = nil
end

if schema.fields.tags and args.tags ~= nil and context == "page" then
local tags = args.tags
if type(tags) == "table" then
tags = tags[1]
end

if re_match(tags, [=[^([a-zA-Z0-9\.\-\_~]+(?:,|$))+$]=], 'jo') then
-- 'a,b,c' or 'a'
options.tags_cond = 'and'
options.tags = split(tags, ',')
elseif re_match(tags, [=[^([a-zA-Z0-9\.\-\_~]+(?:/|$))+$]=], 'jo') then
-- 'a/b/c'
options.tags_cond = 'or'
options.tags = split(tags, '/')
else
options.tags = tags
-- not setting tags_cond since we can't determine the cond
-- will raise an error in db/dao/init.lua:validate_options_value
end

args.tags = nil
end
end

return options
Expand Down Expand Up @@ -158,7 +183,11 @@ local function query_entity(context, self, db, schema, method)
args = self.args.uri
end

local opts = extract_options(args, schema, context)
local opts, err = extract_options(args, schema, context)
if err then
return nil, err, db[schema.name].errors:invalid_size(err)
end

local dao = db[schema.name]

if is_insert then
Expand Down Expand Up @@ -249,14 +278,23 @@ end
-- /services
local function get_collection_endpoint(schema, foreign_schema, foreign_field_name, method)
return not foreign_schema and function(self, db, helpers)
local next_page_tags = ""

local args = self.args.uri
if args.tags then
next_page_tags = "&tags=" .. (type(args.tags) == "table" and args.tags[1] or args.tags)
end

local data, _, err_t, offset = page_collection(self, db, schema, method)

if err_t then
return handle_error(err_t)
end

local next_page = offset and fmt("/%s?offset=%s",
local next_page = offset and fmt("/%s?offset=%s%s",
schema.name,
escape_uri(offset)) or null
escape_uri(offset),
next_page_tags) or null

return ok {
data = data,
Expand Down
32 changes: 32 additions & 0 deletions kong/api/routes/tags.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
local endpoints = require "kong.api.endpoints"

local fmt = string.format
local escape_uri = ngx.escape_uri


return {
["/tags/:tags"] = {
GET = function(self, db, helpers, parent)
local data, _, err_t, offset =
endpoints.page_collection(self, db, db.tags.schema, "page_by_tag")

if err_t then
return endpoints.handle_error(err_t)
end

local next_page
if offset then
next_page = fmt("/tags/%s?offset=%s", self.params.tags, escape_uri(offset))

else
next_page = ngx.null
end

return kong.response.exit(200, {
data = data,
offset = offset,
next = next_page,
})
end,
},
}
27 changes: 26 additions & 1 deletion kong/db/dao/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ local type = type
local next = next
local log = ngx.log
local fmt = string.format
local match = string.match


local ERR = ngx.ERR
Expand Down Expand Up @@ -131,7 +132,7 @@ local function validate_options_value(options, schema, context)
context ~= "update" and
context ~= "upsert" then
errors.ttl = fmt("option can only be used with inserts, updates and upserts, not with '%ss'",
tostring(context))
context)

elseif floor(options.ttl) ~= options.ttl or
options.ttl < 0 or
Expand All @@ -145,6 +146,30 @@ local function validate_options_value(options, schema, context)
errors.ttl = fmt("cannot be used with '%s'", schema.name)
end

if schema.fields.tags and options.tags ~= nil then
if context ~= "select" then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the options.tags filter also work in generated methods for relationships, such as db.targets:each_for_upstream({ id = U.id }, nil, { tags = T })? (i.e. "give me all targets of upstream X that have tag T") — there needs to be a test in the suite answering this question :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

each_for_* will not support tags because it will not parse "?tags=" from Admin API, and uses a different pager page_for_* other than the tag-enabled function page used by normal pagination. I'll add a unit test on each_for_upstream to ensure the functionality doesn't exist.

errors.tags = fmt("option can only be used with selects and pages, not with '%ss'",
tostring(context))
hishamhm marked this conversation as resolved.
Show resolved Hide resolved
elseif type(options.tags) ~= "table" then
if not options.tags_cond then
-- If options.tags is not a table and options.tags_cond is nil at the same time
-- it means arguments.lua gets an invalid tags arg from the Admin API
errors.tags = "invalid filter syntax"
else
errors.tags = "must be a table"
end
elseif #options.tags > 5 then
errors.tags = "cannot query more than 5 tags"
elseif not match(concat(options.tags), "^[%w%.%-%_~]+$") then
errors.tags = "must only contain alphanumeric and '., -, _, ~' characters"
elseif #options.tags > 1 and options.tags_cond ~= "and" and options.tags_cond ~= "or" then
errors.tags_cond = "must be a either 'and' or 'or' when more than one tag is specified"
end

elseif schema.fields.tags == nil and options.tags ~= nil then
errors.tags = fmt("cannot be used with '%s'", schema.name)
end

if next(errors) then
return nil, errors
end
Expand Down
27 changes: 27 additions & 0 deletions kong/db/dao/tags.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
local Tags = {}

function Tags:page_by_tag(tag, size, offset, options)
local ok, err = self.schema:validate_field(self.schema.fields.tag, tag)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

if not ok then
local err_t = self.errors:invalid_unique('tag', err)
return nil, tostring(err_t), err_t
end

local rows, err_t, offset = self.strategy:page_by_tag(tag, size or 100, offset, options)
if err_t then
return rows, tostring(err_t), err_t
end
return rows, nil, nil, offset
end

local function noop(self, ...)
local err_t = self.errors:schema_violation({ tags = 'does not support insert/upsert/update/delete operations' })
return nil, tostring(err_t), err_t
end

Tags.insert = noop
Tags.delete = noop
Tags.update = noop
Tags.upsert = noop

return Tags
1 change: 1 addition & 0 deletions kong/db/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ local CORE_ENTITIES = {
"targets",
"plugins",
"cluster_ca",
"tags",
}


Expand Down
Loading