# HG changeset patch # User Kim Alvefur # Date 1616367246 -3600 # Node ID 073f5397c1d2faa83a07d6cac313118df44acb10 # Parent d6a3201a65c033e8f90315b07705cac6f23cfd0a mod_rest: Replace most mappings by using util.datamapper All the stuff now goes into a JSON file that describes the mappings. diff -r d6a3201a65c0 -r 073f5397c1d2 mod_rest/README.markdown --- a/mod_rest/README.markdown Mon Mar 22 21:32:43 2021 +0100 +++ b/mod_rest/README.markdown Sun Mar 21 23:54:06 2021 +0100 @@ -8,6 +8,7 @@ mod_rest.jsonmap: jsonmap.lib.lua copy_directories: - example + - res --- # Introduction diff -r d6a3201a65c0 -r 073f5397c1d2 mod_rest/jsonmap.lib.lua --- a/mod_rest/jsonmap.lib.lua Mon Mar 22 21:32:43 2021 +0100 +++ b/mod_rest/jsonmap.lib.lua Sun Mar 21 23:54:06 2021 +0100 @@ -3,37 +3,26 @@ local json = require "util.json"; local st = require "util.stanza"; local xml = require "util.xml"; +local map = require "util.datamapper"; +local schema do + local f = assert(module:load_resource("res/schema-xmpp.json")); + schema = json.decode(f:read("*a")) + f:close(); + -- Copy common properties to all stanza kinds + if schema._common then + for key, prop in pairs(schema._common) do + for _, copyto in pairs(schema.properties) do + copyto.properties[key] = prop; + end + end + schema._common = nil; + end +end + +-- Some mappings that are still hard to do in a nice way with util.datamapper local field_mappings; -- in scope for "func" mappings field_mappings = { - -- top level stanza attributes - -- needed here to mark them as known fields - kind = "attr", - type = "attr", - to = "attr", - from = "attr", - id = "attr", - lang = "attr", - - -- basic message - body = "text_tag", - subject = "text_tag", - thread = "text_tag", - - -- basic presence - show = "text_tag", - status = "text_tag", - priority = "text_tag", - - state = { type = "name", xmlns = "http://jabber.org/protocol/chatstates" }, - nick = { type = "text_tag", xmlns = "http://jabber.org/protocol/nick", tagname = "nick" }, - delay = { type = "attr", xmlns = "urn:xmpp:delay", tagname = "delay", attr = "stamp" }, - replace = { type = "attr", xmlns = "urn:xmpp:message-correct:0", tagname = "replace", attr = "id" }, - - -- XEP-0045 MUC - -- TODO history, password, ??? - join = { type = "bool_tag", xmlns = "http://jabber.org/protocol/muc", tagname = "x" }, - -- XEP-0071 html = { type = "func", xmlns = "http://jabber.org/protocol/xhtml-im", tagname = "html", @@ -47,31 +36,6 @@ end; }; - -- XEP-0199: XMPP Ping - ping = { type = "bool_tag", xmlns = "urn:xmpp:ping", tagname = "ping" }, - - -- XEP-0092: Software Version - version = { type = "func", xmlns = "jabber:iq:version", tagname = "query", - st2json = function (s) - return { - name = s:get_child_text("name"); - version = s:get_child_text("version"); - os = s:get_child_text("os"); - } - end, - json2st = function (s) - local v = st.stanza("query", { xmlns = "jabber:iq:version" }); - if type(s) == "table" then - v:text_tag("name", s.name); - v:text_tag("version", s.version); - if s.os then - v:text_tag("os", s.os); - end - end - return v; - end - }; - -- XEP-0030 disco = { type = "func", xmlns = "http://jabber.org/protocol/disco#info", tagname = "query", @@ -228,6 +192,7 @@ }; -- XEP-0066: Out of Band Data + -- TODO Replace by oob.url in datamapper schema oob_url = { type = "func", xmlns = "jabber:x:oob", tagname = "x", -- XXX namespace depends on whether it's in an iq or message stanza st2json = function (s) @@ -416,6 +381,10 @@ local byxmlname = {}; for k, spec in pairs(field_mappings) do + for _, replace in pairs(schema.properties) do + replace.properties[k] = nil + end + if type(spec) == "table" then spec.key = k; if spec.xmlns and spec.tagname then @@ -461,14 +430,8 @@ } local function st2json(s) - local t = { - kind = s.name, - type = s.attr.type, - to = s.attr.to, - from = s.attr.from, - id = s.attr.id, - lang = s.attr["xml:lang"], - }; + local t = map.parse(schema.properties[s.name], s); + if s.name == "presence" and not s.attr.type then t.type = "available"; end @@ -501,17 +464,7 @@ mapping = byxmlname[prefix]; end - if not mapping then -- luacheck: ignore 542 - -- pass - elseif mapping.type == "text_tag" then - t[mapping.key] = tag:get_text(); - elseif mapping.type == "name" then - t[mapping.key] = tag.name; - elseif mapping.type == "attr" then - t[mapping.key] = tag.attr[mapping.attr]; - elseif mapping.type == "bool_tag" then - t[mapping.key] = true; - elseif mapping.type == "func" and mapping.st2json then + if mapping and mapping.type == "func" and mapping.st2json then t[mapping.key] = mapping.st2json(tag); end end @@ -547,27 +500,17 @@ end end - if t_type == "available" then + if kind == "presence" and t_type == "available" then t_type = nil; + elseif kind == "iq" and not t_type then + t_type = "get"; end - local s = st.stanza(kind or "message", { - type = t_type; - to = str(t.to) and jid.prep(t.to); - from = str(t.to) and jid.prep(t.from); - id = str(t.id), - ["xml:lang"] = str(t.lang), - }); + local s = map.unparse(schema.properties[kind or "message"], t); - if t.to and not s.attr.to then - return nil, "invalid-jid-to"; - end - if t.from and not s.attr.from then - return nil, "invalid-jid-from"; - end - if kind == "iq" and not s.attr.type then - s.attr.type = "get"; - end + s.attr.type = t_type; + s.attr.to = str(t.to) and jid.prep(t.to); + s.attr.from = str(t.to) and jid.prep(t.from); if type(t.error) == "table" then return st.error_reply(st.reply(s), str(t.error.type), str(t.error.condition), str(t.error.text), str(t.error.by)); @@ -578,25 +521,9 @@ for k, v in pairs(t) do local mapping = field_mappings[k]; - if mapping then - if mapping == "text_tag" then - s:text_tag(k, v); - elseif mapping == "attr" then -- luacheck: ignore 542 - -- handled already - elseif mapping.type == "text_tag" then - s:text_tag(mapping.tagname or k, v, mapping.xmlns and { xmlns = mapping.xmlns }); - elseif mapping.type == "name" then - s:tag(v, { xmlns = mapping.xmlns }):up(); - elseif mapping.type == "attr" then - s:tag(mapping.tagname or k, { xmlns = mapping.xmlns, [mapping.attr or k] = v }):up(); - elseif mapping.type == "bool_tag" then - s:tag(mapping.tagname or k, { xmlns = mapping.xmlns }):up(); - elseif mapping.type == "func" and mapping.json2st then + if mapping and mapping.type == "func" and mapping.json2st then s:add_child(mapping.json2st(v)):up(); end - else - return nil, "unknown-field"; - end end s:reset(); diff -r d6a3201a65c0 -r 073f5397c1d2 mod_rest/res/schema-xmpp.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_rest/res/schema-xmpp.json Sun Mar 21 23:54:06 2021 +0100 @@ -0,0 +1,204 @@ +{ + "_common" : { + "delay" : { + "format" : "date-time", + "title" : "XEP-0203: Delayed Delivery", + "type" : "string", + "xml" : { + "name" : "delay", + "namespace" : "urn:xmpp:delay", + "x_single_attribute" : "stamp" + } + }, + "from" : { + "description" : "the sender of the stanza", + "example" : "bob@example.net", + "format" : "xmpp-jid", + "type" : "string", + "xml" : { + "attribute" : true + } + }, + "id" : { + "description" : "Reasonably unique id. mod_rest generates one if left out.", + "type" : "string", + "xml" : { + "attribute" : true + } + }, + "lang" : { + "description" : "Language code", + "example" : "en", + "type" : "string", + "xml" : { + "attribute" : true, + "prefix" : "xml" + } + }, + "nick" : { + "type" : "string", + "xml" : { + "name" : "nick", + "namespace" : "http://jabber.org/protocol/nick" + } + }, + "to" : { + "description" : "the intended recipient for the stanza", + "example" : "alice@another.example", + "format" : "xmpp-jid", + "type" : "string", + "xml" : { + "attribute" : true + } + }, + "type" : { + "description" : "Stanza type", + "type" : "string", + "xml" : { + "attribute" : true + } + } + }, + "properties" : { + "iq" : { + "properties" : { + "ping" : { + "description" : "Test reachability of some XMPP address", + "enum" : [ + true + ], + "title" : "XEP-0199: XMPP Ping", + "type" : "boolean", + "xml" : { + "name" : "ping", + "namespace" : "urn:xmpp:ping", + "x_name_is_value" : true + } + }, + "version" : { + "description" : "Ask about software version information", + "properties" : { + "name" : { + "example" : "My Software", + "type" : "string" + }, + "os" : { + "example" : "Linux", + "type" : "string" + }, + "version" : { + "example" : "1.0.0", + "type" : "string" + } + }, + "required" : [ + "name", + "version" + ], + "title" : "XEP-0092: Software Version", + "type" : "object", + "xml" : { + "name" : "query", + "namespace" : "jabber:iq:version" + } + } + }, + "type" : "object", + "xml" : { + "name" : "iq" + } + }, + "message" : { + "properties" : { + "body" : { + "description" : "Human-readable chat message", + "example" : "Hello, World!", + "type" : "string" + }, + "replace" : { + "description" : "For indicating that a message is a correction of the last sent message.", + "title" : "XEP-0308: Last Message Correction", + "type" : "string", + "xml" : { + "name" : "replace", + "namespace" : "urn:xmpp:message-correct:0", + "x_single_attribute" : "id" + } + }, + "state" : { + "description" : "Chat state notifications, e.g. \"is typing...\"", + "enum" : [ + "active", + "inactive", + "gone", + "composing", + "paused" + ], + "type" : "string", + "xml" : { + "namespace" : "http://jabber.org/protocol/chatstates", + "x_name_is_value" : true + } + }, + "subject" : { + "description" : "Subject of message or group chat", + "example" : "Talking about stuff", + "type" : "string" + }, + "thread" : { + "description" : "Message thread identifier", + "properties" : { + "id" : { + "type" : "string", + "xml" : { + "text" : true + } + }, + "parent" : { + "type" : "string", + "xml" : { + "attribute" : true + } + } + }, + "type" : "object" + } + }, + "type" : "object", + "xml" : { + "name" : "message" + } + }, + "presence" : { + "properties" : { + "priority" : { + "description" : "Presence priority", + "type" : "string" + }, + "show" : { + "description" : "indicator of availability, ie away or not", + "enum" : [ + "away", + "chat", + "dnd", + "xa" + ], + "type" : "string" + }, + "status" : { + "description" : "Textual status message.", + "type" : "string" + } + }, + "type" : "object", + "xml" : { + "name" : "presence" + } + } + }, + "type" : "object", + "xml" : { + "name" : "xmpp", + "namespace" : "jabber:client" + } +}