mirror of
https://github.com/bjc/prosody.git
synced 2025-04-01 20:27:39 +03:00
util.datamapper: Improve handling of schemas with non-obvious "type"
The JSON Schema specification says that schemas are objects or booleans, and that the 'type' property is optional and can be an array. This module previously allowed bare type names as schemas and did not really handle booleans. It now handles missing 'type' properties and boolean 'true' as a schema. Objects and arrays are guessed based on the presence of 'properties' or 'items' field.
This commit is contained in:
parent
e700edc50f
commit
89359b70dc
3 changed files with 63 additions and 25 deletions
|
@ -25,7 +25,7 @@ describe("util.datamapper", function()
|
|||
from = attr();
|
||||
type = attr();
|
||||
id = attr();
|
||||
body = "string";
|
||||
body = true; -- should be assumed to be a string
|
||||
lang = {type = "string"; xml = {attribute = true; prefix = "xml"}};
|
||||
delay = {
|
||||
type = "object";
|
||||
|
@ -56,7 +56,8 @@ describe("util.datamapper", function()
|
|||
xml = {namespace = "urn:xmpp:reactions:0"; name = "reactions"};
|
||||
properties = {
|
||||
to = {type = "string"; xml = {attribute = true; name = "id"}};
|
||||
reactions = {type = "array"; items = {type = "string"; xml = {name = "reaction"}}};
|
||||
-- should be assumed to be array since it has 'items'
|
||||
reactions = { items = { xml = { name = "reaction" } } };
|
||||
};
|
||||
};
|
||||
stanza_ids = {
|
||||
|
@ -190,7 +191,8 @@ describe("util.datamapper", function()
|
|||
version = {
|
||||
type = "object";
|
||||
xml = {name = "query"; namespace = "jabber:iq:version"};
|
||||
properties = {name = "string"; version = "string"; os = "string"};
|
||||
-- properties should be assumed to be strings
|
||||
properties = {name = true; version = {}; os = {}};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ local pointer = require"util.jsonpointer";
|
|||
|
||||
local json_type_name = json.json_type_name;
|
||||
local json_schema_object = require "util.jsonschema"
|
||||
local type schema_t = boolean | json_type_name | json_schema_object
|
||||
local type schema_t = boolean | json_schema_object
|
||||
|
||||
local function toboolean ( s : string ) : boolean
|
||||
if s == "true" or s == "1" then
|
||||
|
@ -59,15 +59,28 @@ local enum value_goes
|
|||
end
|
||||
|
||||
local function resolve_schema(schema : schema_t, root : json_schema_object) : schema_t
|
||||
if schema is json_schema_object and schema["$ref"] and schema["$ref"]:sub(1, 1) == "#" then
|
||||
local referenced = pointer.resolve(root as table, schema["$ref"]:sub(2)) as schema_t;
|
||||
if referenced ~= nil then
|
||||
return referenced
|
||||
if schema is json_schema_object then
|
||||
if schema["$ref"] and schema["$ref"]:sub(1, 1) == "#" then
|
||||
return pointer.resolve(root as table, schema["$ref"]:sub(2)) as schema_t;
|
||||
end
|
||||
end
|
||||
return schema;
|
||||
end
|
||||
|
||||
local function guess_schema_type(schema : json_schema_object) : json_type_name
|
||||
local schema_types = schema.type
|
||||
if schema_types is json_type_name then
|
||||
return schema_types
|
||||
elseif schema_types ~= nil then
|
||||
error "schema has unsupported 'type' property"
|
||||
elseif schema.properties then
|
||||
return "object"
|
||||
elseif schema.items then
|
||||
return "array"
|
||||
end
|
||||
return "string" -- default assumption
|
||||
end
|
||||
|
||||
local function unpack_propschema( propschema : schema_t, propname : string, current_ns : string )
|
||||
: json_type_name, value_goes, string, string, string, string, { any }
|
||||
local proptype : json_type_name = "string"
|
||||
|
@ -79,9 +92,9 @@ local function unpack_propschema( propschema : schema_t, propname : string, curr
|
|||
local enums : { any }
|
||||
|
||||
if propschema is json_schema_object then
|
||||
proptype = propschema.type
|
||||
elseif propschema is json_type_name then
|
||||
proptype = propschema
|
||||
proptype = guess_schema_type(propschema);
|
||||
elseif propschema is string then -- Teal says this can never be a string, but it could before so best be sure
|
||||
error("schema as string is not supported: "..propschema.." {"..current_ns.."}"..propname)
|
||||
end
|
||||
|
||||
if proptype == "object" or proptype == "array" then
|
||||
|
@ -120,6 +133,10 @@ local function unpack_propschema( propschema : schema_t, propname : string, curr
|
|||
end
|
||||
end
|
||||
|
||||
if current_ns == "urn:xmpp:reactions:0" and name == "reactions" then
|
||||
assert(proptype=="array")
|
||||
end
|
||||
|
||||
return proptype, value_where, name, namespace, prefix, single_attribute, enums
|
||||
end
|
||||
|
||||
|
@ -239,9 +256,10 @@ function parse_array (schema : json_schema_object, s : st.stanza_t, root : json_
|
|||
end
|
||||
|
||||
local function parse (schema : json_schema_object, s : st.stanza_t) : table
|
||||
if schema.type == "object" then
|
||||
local s_type = guess_schema_type(schema)
|
||||
if s_type == "object" then
|
||||
return parse_object(schema, s, schema)
|
||||
elseif schema.type == "array" then
|
||||
elseif s_type == "array" then
|
||||
return parse_array(schema, s, schema)
|
||||
else
|
||||
error "top-level scalars unsupported"
|
||||
|
@ -333,7 +351,8 @@ function unparse ( schema : json_schema_object, t : table, current_name : string
|
|||
|
||||
local out = ctx or st.stanza(current_name, { xmlns = current_ns })
|
||||
|
||||
if schema.type == "object" then
|
||||
local s_type = guess_schema_type(schema)
|
||||
if s_type == "object" then
|
||||
|
||||
for prop, propschema in pairs(schema.properties) do
|
||||
propschema = resolve_schema(propschema, root)
|
||||
|
@ -346,7 +365,7 @@ function unparse ( schema : json_schema_object, t : table, current_name : string
|
|||
end
|
||||
return out;
|
||||
|
||||
elseif schema.type == "array" then
|
||||
elseif s_type == "array" then
|
||||
local itemschema = resolve_schema(schema.items, root)
|
||||
local proptype, value_where, name, namespace, prefix, single_attribute = unpack_propschema(itemschema, current_name, current_ns)
|
||||
for _, item in ipairs(t as { string }) do
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
-- This file is generated from teal-src/util/datamapper.lua
|
||||
|
||||
local st = require("util.stanza");
|
||||
local pointer = require("util.jsonpointer");
|
||||
|
||||
|
@ -29,15 +31,28 @@ end
|
|||
local value_goes = {}
|
||||
|
||||
local function resolve_schema(schema, root)
|
||||
if type(schema) == "table" and schema["$ref"] and schema["$ref"]:sub(1, 1) == "#" then
|
||||
local referenced = pointer.resolve(root, schema["$ref"]:sub(2));
|
||||
if referenced ~= nil then
|
||||
return referenced
|
||||
if type(schema) == "table" then
|
||||
if schema["$ref"] and schema["$ref"]:sub(1, 1) == "#" then
|
||||
return pointer.resolve(root, schema["$ref"]:sub(2))
|
||||
end
|
||||
end
|
||||
return schema
|
||||
end
|
||||
|
||||
local function guess_schema_type(schema)
|
||||
local schema_types = schema.type
|
||||
if type(schema_types) == "string" then
|
||||
return schema_types
|
||||
elseif schema_types ~= nil then
|
||||
error("schema has unsupported 'type' property")
|
||||
elseif schema.properties then
|
||||
return "object"
|
||||
elseif schema.items then
|
||||
return "array"
|
||||
end
|
||||
return "string"
|
||||
end
|
||||
|
||||
local function unpack_propschema(propschema, propname, current_ns)
|
||||
|
||||
local proptype = "string"
|
||||
|
@ -49,9 +64,9 @@ local function unpack_propschema(propschema, propname, current_ns)
|
|||
local enums
|
||||
|
||||
if type(propschema) == "table" then
|
||||
proptype = propschema.type
|
||||
proptype = guess_schema_type(propschema);
|
||||
elseif type(propschema) == "string" then
|
||||
proptype = propschema
|
||||
error("schema as string is not supported: " .. propschema .. " {" .. current_ns .. "}" .. propname)
|
||||
end
|
||||
|
||||
if proptype == "object" or proptype == "array" then
|
||||
|
@ -209,9 +224,10 @@ function parse_array(schema, s, root)
|
|||
end
|
||||
|
||||
local function parse(schema, s)
|
||||
if schema.type == "object" then
|
||||
local s_type = guess_schema_type(schema)
|
||||
if s_type == "object" then
|
||||
return parse_object(schema, s, schema)
|
||||
elseif schema.type == "array" then
|
||||
elseif s_type == "array" then
|
||||
return parse_array(schema, s, schema)
|
||||
else
|
||||
error("top-level scalars unsupported")
|
||||
|
@ -306,7 +322,8 @@ function unparse(schema, t, current_name, current_ns, ctx, root)
|
|||
|
||||
local out = ctx or st.stanza(current_name, {xmlns = current_ns})
|
||||
|
||||
if schema.type == "object" then
|
||||
local s_type = guess_schema_type(schema)
|
||||
if s_type == "object" then
|
||||
|
||||
for prop, propschema in pairs(schema.properties) do
|
||||
propschema = resolve_schema(propschema, root)
|
||||
|
@ -319,7 +336,7 @@ function unparse(schema, t, current_name, current_ns, ctx, root)
|
|||
end
|
||||
return out
|
||||
|
||||
elseif schema.type == "array" then
|
||||
elseif s_type == "array" then
|
||||
local itemschema = resolve_schema(schema.items, root)
|
||||
local proptype, value_where, name, namespace, prefix, single_attribute = unpack_propschema(itemschema, current_name, current_ns)
|
||||
for _, item in ipairs(t) do
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue