Skip to content

Commit

Permalink
validate schema with kong.db.schema.validate()
Browse files Browse the repository at this point in the history
  • Loading branch information
chobits committed Jan 20, 2025
1 parent bfc35c3 commit d20ebeb
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 52 deletions.
34 changes: 19 additions & 15 deletions kong/clustering/services/sync/validate.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,51 @@ local declarative_config = require("kong.db.schema.others.declarative_config")


local null = ngx.null
local tb_insert = table.insert
local validate_schema = declarative_config.validate_schema
local pk_string = declarative_config.pk_string
local validate_references_sync = declarative_config.validate_references_sync
local pretty_print_error = declarative.pretty_print_error


local function validate_deltas(deltas, is_full_sync)

local errs = {}

-- generate deltas table mapping primary key string to entity item
local deltas_map = {}

-- generate declarative config table
local dc_table = { _format_version = "3.0", }

local db = kong.db

for _, delta in ipairs(deltas) do
local delta_type = delta.type
local delta_entity = delta.entity

if delta_entity ~= nil and delta_entity ~= null then
dc_table[delta_type] = dc_table[delta_type] or {}

tb_insert(dc_table[delta_type], delta_entity)

-- table: primary key string -> entity
local schema = db[delta_type].schema
local pk = schema:extract_pk_values(delta_entity)
local pks = pk_string(schema, pk)

deltas_map[pks] = delta_entity

-- validate entity
local dao = kong.db[delta_type]
if dao then
local ws_id = delta_entity.ws_id -- bypass ws_id field for validation
delta_entity.ws_id = nil

local ok, err_t = dao.schema:validate(delta_entity)

delta_entity.ws_id = ws_id

if not ok then
errs[#errs + 1] = { [delta_type] = err_t }
end
end
end
end

-- validate schema (same logic as the sync v1 full-sync schema validation)
local dc_schema = db.declarative_config.schema

local ok, err_t = validate_schema(dc_schema, dc_table)
if not ok then
return nil, pretty_print_error(err_t)
if next(errs) then
return nil, pretty_print_error(errs, "deltas")
end

-- validate references
Expand Down
4 changes: 1 addition & 3 deletions kong/db/declarative/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ local _MT = { __index = _M, }
-- of the database (e.g. for db_import)
-- @treturn table A Config schema adjusted for this configuration
function _M.new_config(kong_config, partial, include_foreign)
local schema, err = declarative_config.load(kong_config.loaded_plugins,
kong_config.loaded_vaults,
include_foreign, kong.sync ~= nil)
local schema, err = declarative_config.load(kong_config.loaded_plugins, kong_config.loaded_vaults, include_foreign)
if not schema then
return nil, err
end
Expand Down
39 changes: 5 additions & 34 deletions kong/db/schema/others/declarative_config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -980,9 +980,7 @@ local function load_entity_subschemas(entity_name, entity)
end


-- @tparam sync_v2_enabled It generates full schema and foreign references to
-- validate schema and references for sync.v2
function DeclarativeConfig.load(plugin_set, vault_set, include_foreign, sync_v2_enabled)
function DeclarativeConfig.load(plugin_set, vault_set, include_foreign)
all_schemas = {}
local schemas_array = {}
for _, entity in ipairs(constants.CORE_ENTITIES) do
Expand Down Expand Up @@ -1017,7 +1015,7 @@ function DeclarativeConfig.load(plugin_set, vault_set, include_foreign, sync_v2_
known_entities[i] = schema.name
end

local fields, records = build_fields(known_entities, include_foreign or sync_v2_enabled)
local fields, records = build_fields(known_entities, include_foreign)
-- assert(no_foreign(fields))

local ok, err = load_plugin_subschemas(fields, plugin_set)
Expand All @@ -1030,33 +1028,10 @@ function DeclarativeConfig.load(plugin_set, vault_set, include_foreign, sync_v2_
return nil, err
end

-- Pre-load the full schema to validate the schema for sync.v2. Lazy loading
-- the full schema with DeclarativeConfig.load() will consume a lot of time
-- due to load_plugin_subschemas().
local full_schema

if sync_v2_enabled then
local def = {
name = "declarative_config",
primary_key = {},
-- copy fields to avoid its "foreign"-type fields from being cleared by
-- reference_foreign_by_name()
fields = kong_table.cycle_aware_deep_copy(fields, true),
}

full_schema = Schema.new(def)

full_schema.known_entities = known_entities
full_schema.flatten = flatten
full_schema.insert_default_workspace_if_not_given = insert_default_workspace_if_not_given
full_schema.plugin_set = plugin_set
full_schema.vault_set = vault_set
end

-- we replace the "foreign"-type fields at the top-level
-- with "string"-type fields only after the subschemas have been loaded,
-- otherwise they will detect the mismatch.
if not include_foreign then
-- we replace the "foreign"-type fields at the top-level
-- with "string"-type fields only after the subschemas have been loaded,
-- otherwise they will detect the mismatch.
reference_foreign_by_name(known_entities, records)
end

Expand All @@ -1074,10 +1049,6 @@ function DeclarativeConfig.load(plugin_set, vault_set, include_foreign, sync_v2_
schema.plugin_set = plugin_set
schema.vault_set = vault_set

if sync_v2_enabled then
schema.full_schema = full_schema
end

return schema, nil, def
end

Expand Down
28 changes: 28 additions & 0 deletions spec/02-integration/18-hybrid_rpc/06-validate_deltas_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,34 @@ describe("[delta validations]",function()
end
end)

it("route has unknown field", function()
local bp = setup_bp()

-- add entities
db_insert(bp, "workspaces", { name = "ws-001" })
local service = db_insert(bp, "services", { name = "service-001", })
db_insert(bp, "routes", {
name = "route-001",
paths = { "/mock" },
service = { id = service.id },
})

local deltas = declarative.export_config_sync()

for _, delta in ipairs(deltas) do
if delta.type == "routes" then
delta.entity.foo = "invalid_field_value"
break
end
end

local _, err = validate_deltas(deltas)
assert.matches([[- in entry 1 of 'deltas':
in 'routes':
in 'foo': unknown field]],
err)
end)

it("route has foreign service", function()
local bp = setup_bp()

Expand Down

0 comments on commit d20ebeb

Please sign in to comment.