aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKim Alvefur <zash@zash.se>2022-07-08 17:32:48 +0200
committerKim Alvefur <zash@zash.se>2022-07-08 17:32:48 +0200
commit89359b70dc0446cdda15da19173f460504bafc3d (patch)
treece589d446940d9c5ac2ce5cf9a342c6031a94054
parente700edc50f3bd7f05d45bb4410396178811f3561 (diff)
downloadprosody-89359b70dc0446cdda15da19173f460504bafc3d.tar.gz
prosody-89359b70dc0446cdda15da19173f460504bafc3d.zip
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.
-rw-r--r--spec/util_datamapper_spec.lua8
-rw-r--r--teal-src/util/datamapper.tl43
-rw-r--r--util/datamapper.lua37
3 files changed, 63 insertions, 25 deletions
diff --git a/spec/util_datamapper_spec.lua b/spec/util_datamapper_spec.lua
index 3b0ae87e..51ccf127 100644
--- a/spec/util_datamapper_spec.lua
+++ b/spec/util_datamapper_spec.lua
@@ -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 = {}};
};
};
};
diff --git a/teal-src/util/datamapper.tl b/teal-src/util/datamapper.tl
index 78ca3035..73b1dfc0 100644
--- a/teal-src/util/datamapper.tl
+++ b/teal-src/util/datamapper.tl
@@ -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
diff --git a/util/datamapper.lua b/util/datamapper.lua
index 25c4c986..2378314c 100644
--- a/util/datamapper.lua
+++ b/util/datamapper.lua
@@ -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