From fafd9704a43fa8c43c16fb72a121a0a03677c48d Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Wed, 19 Nov 2025 16:20:57 +0000 Subject: [PATCH] [Fix] Fix lua_shape registry to recursively resolve nested schemas The registry's resolve_schema function was not recursively resolving field schemas and opts.extra in table nodes, causing mixins in nested one_of variants to never be expanded. This broke external_relay plugin validation where rule variants with mixins were reported as having unknown fields. Now recursively resolves all nested schemas including field schemas, opts.extra, and ensures mixins are properly expanded throughout the entire schema tree. --- lualib/lua_shape/registry.lua | 48 ++++++++++++++++++++++++++---- src/plugins/lua/external_relay.lua | 3 +- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lualib/lua_shape/registry.lua b/lualib/lua_shape/registry.lua index e5118a92a8..63b40b4488 100644 --- a/lualib/lua_shape/registry.lua +++ b/lualib/lua_shape/registry.lua @@ -114,14 +114,40 @@ function Registry:resolve_schema(schema) return target.resolved end - -- Handle table nodes with mixins + -- Handle table nodes with mixins and/or extra schema if tag == "table" then local opts = schema.opts or {} local mixins = opts.mixins or {} + local has_mixins = #mixins > 0 + local has_extra = opts.extra ~= nil + + -- First, recursively resolve all field schemas + local fields = schema.fields or {} + local resolved_fields = nil + local fields_changed = false + + for field_name, field_spec in pairs(fields) do + local field_schema = field_spec.schema + local resolved_field_schema = self:resolve_schema(field_schema) + if resolved_field_schema ~= field_schema then + if not resolved_fields then + resolved_fields = shallowcopy(fields) + end + local resolved_field_spec = shallowcopy(field_spec) + resolved_field_spec.schema = resolved_field_schema + resolved_fields[field_name] = resolved_field_spec + fields_changed = true + end + end - if #mixins > 0 then - -- Merge mixin fields into table - local merged_fields = shallowcopy(schema.fields or {}) + local merged_fields = resolved_fields or fields + local resolved_extra = opts.extra + + -- Merge mixin fields if present + if has_mixins then + if not resolved_fields then + merged_fields = shallowcopy(fields) + end for _, mixin_def in ipairs(mixins) do if mixin_def._is_mixin then @@ -157,10 +183,22 @@ function Registry:resolve_schema(schema) end end end + end + + -- Resolve extra schema if present + if has_extra then + resolved_extra = self:resolve_schema(opts.extra) + end - -- Create new table schema with merged fields + -- Create new table schema if anything changed + if fields_changed or has_mixins or (has_extra and resolved_extra ~= opts.extra) then local resolved = shallowcopy(schema) resolved.fields = merged_fields + if resolved_extra ~= opts.extra then + local resolved_opts = shallowcopy(opts) + resolved_opts.extra = resolved_extra + resolved.opts = resolved_opts + end self.resolved_cache[schema] = resolved return resolved end diff --git a/src/plugins/lua/external_relay.lua b/src/plugins/lua/external_relay.lua index b40dd076b2..e4860bd944 100644 --- a/src/plugins/lua/external_relay.lua +++ b/src/plugins/lua/external_relay.lua @@ -92,7 +92,8 @@ local config_schema = T.table({ }):doc({ summary = "External relay rules keyed by name" }), }):doc({ summary = "External relay plugin configuration" }) -PluginSchema.register("plugins.external_relay", config_schema) +-- Register and get the resolved schema (with mixins expanded) +config_schema = PluginSchema.register("plugins.external_relay", config_schema) if confighelp then return -- 2.47.3