Skip to content

Commit 8c1a221

Browse files
jarvis9443Copilot
andcommitted
test: port TestFilter and TestValidateRequestBody from kin-openapi
Port 19 test cases from openapi3filter/validation_test.go: - TestFilter: path/query param validation, anyOf/oneOf/allOf, content-encoded JSON query params, required param checks - TestValidateRequestBody: required/optional body, text/plain, undeclared content/schema Fix content-based parameter validation in params.lua: - Move content JSON decoding before schema nil check so params with content (no param.schema) are properly decoded - Look up schema from param.content when param.schema is nil Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e1c4afb commit 8c1a221

2 files changed

Lines changed: 449 additions & 11 deletions

File tree

lib/resty/openapi_validator/params.lua

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,40 @@ local function get_validator(schema)
4747
return nil
4848
end
4949

50+
--- Collect all possible types from a schema, including composite sub-schemas.
51+
local function collect_types(schema, seen)
52+
if not schema then return {} end
53+
seen = seen or {}
54+
if seen[schema] then return {} end
55+
seen[schema] = true
56+
57+
local types = {}
58+
local stype = schema.type
59+
if stype then
60+
if type(stype) == "table" then
61+
for _, t in ipairs(stype) do
62+
types[t] = true
63+
end
64+
else
65+
types[stype] = true
66+
end
67+
end
68+
69+
for _, key in ipairs({"anyOf", "oneOf", "allOf"}) do
70+
local composite = schema[key]
71+
if composite then
72+
for _, sub in ipairs(composite) do
73+
local sub_types = collect_types(sub, seen)
74+
for t in pairs(sub_types) do
75+
types[t] = true
76+
end
77+
end
78+
end
79+
end
80+
81+
return types
82+
end
83+
5084
--- Coerce a string value to the type declared in schema.
5185
-- @param value string raw value from request
5286
-- @param schema table parameter schema
@@ -87,6 +121,20 @@ local function coerce_value(value, schema)
87121
return value
88122
end
89123

124+
-- no direct type — check composite schemas for possible types
125+
if not stype then
126+
local possible = collect_types(schema)
127+
-- try coercion in order: boolean first (most specific), then number
128+
if possible["boolean"] then
129+
if value == "true" or value == "1" then return true end
130+
if value == "false" or value == "0" then return false end
131+
end
132+
if possible["integer"] or possible["number"] then
133+
local n = tonumber(value)
134+
if n then return n end
135+
end
136+
end
137+
90138
return value
91139
end
92140

@@ -187,6 +235,18 @@ end
187235
-- query: form, explode=true
188236
-- header: simple, explode=false
189237
local function deserialize_param(raw_value, param, query_args)
238+
-- handle content-based parameters (param.content.application/json) first,
239+
-- since these params have no param.schema (schema lives inside content)
240+
if param.content then
241+
local json_content = param.content["application/json"]
242+
if json_content and json_content.schema and has_cjson then
243+
local decoded = cjson.decode(raw_value)
244+
if decoded ~= nil then
245+
return decoded
246+
end
247+
end
248+
end
249+
190250
local schema = param.schema
191251
if not schema then
192252
return raw_value
@@ -208,17 +268,6 @@ local function deserialize_param(raw_value, param, query_args)
208268
explode = (style == "form")
209269
end
210270

211-
-- handle content-based parameters (param.content.application/json)
212-
if param.content then
213-
local json_content = param.content["application/json"]
214-
if json_content and json_content.schema and has_cjson then
215-
local decoded = cjson.decode(raw_value)
216-
if decoded ~= nil then
217-
return decoded
218-
end
219-
end
220-
end
221-
222271
local stype = schema.type
223272

224273
if stype == "array" then
@@ -351,13 +400,25 @@ function _M.validate(route, path_params, query_args, headers, skip)
351400
end
352401

353402
local schema = param.schema
403+
-- for content-based parameters, use the content schema
404+
if not schema and param.content then
405+
local json_ct = param.content["application/json"]
406+
if json_ct then
407+
schema = json_ct.schema
408+
end
409+
end
354410
if not schema then
355411
goto continue
356412
end
357413

358414
-- deserialize and coerce
359415
local value = deserialize_param(raw, param, query_args)
360416

417+
-- for deepObject, nil means no matching keys found — skip if optional
418+
if value == nil and not param.required then
419+
goto continue
420+
end
421+
361422
-- validate with jsonschema
362423
local validator = get_validator(schema)
363424
if validator then

0 commit comments

Comments
 (0)