comparison mod_rest/jsonmap.lib.lua @ 3922:ea59c9455f93

mod_rest: Move dataforms into structure for more logical code order Needs that `local x; x = ...` trick to make the mapping table be in scope for all the inner functions.
author Kim Alvefur <zash@zash.se>
date Fri, 28 Feb 2020 22:24:49 +0100
parents 1df4900bbd29
children 3c3d216c6f6d
comparison
equal deleted inserted replaced
3921:9eabd68b8e48 3922:ea59c9455f93
2 local jid = require "util.jid"; 2 local jid = require "util.jid";
3 local json = require "util.json"; 3 local json = require "util.json";
4 local st = require "util.stanza"; 4 local st = require "util.stanza";
5 local xml = require "util.xml"; 5 local xml = require "util.xml";
6 6
7 -- Reused in many XEPs so declared up here 7 local field_mappings; -- in scope for "func" mappings
8 local dataform = { 8 field_mappings = {
9 -- Generic and complete dataforms mapping
10 type = "func", xmlns = "jabber:x:data", tagname = "x",
11 st2json = function (s)
12 local fields = array();
13 local form = {
14 type = s.attr.type;
15 title = s:get_child_text("title");
16 instructions = s:get_child_text("instructions");
17 fields = fields;
18 };
19 for field in s:childtags("field") do
20 local i = {
21 var = field.attr.var;
22 type = field.attr.type;
23 label = field.attr.label;
24 desc = field:get_child_text("desc");
25 required = field:get_child("required") and true or nil;
26 value = field:get_child_text("value");
27 };
28 if field.attr.type == "jid-multi" or field.attr.type == "list-multi" or field.attr.type == "text-multi" then
29 local value = array();
30 for v in field:childtags("value") do
31 value:push(v:get_text());
32 end
33 if field.attr.type == "text-multi" then
34 i.value = value:concat("\n");
35 else
36 i.value = value;
37 end
38 end
39 if field.attr.type == "list-single" or field.attr.type == "list-multi" then
40 local options = array();
41 for o in field:childtags("option") do
42 options:push({ label = o.attr.label, value = o:get_child_text("value") });
43 end
44 i.options = options;
45 end
46 fields:push(i);
47 end
48 return form;
49 end;
50 json2st = function (x)
51 if type(x) == "table" and x ~= json.null then
52 local form = st.stanza("x", { xmlns = "jabber:x:data", type = x.type });
53 if x.title then
54 form:text_tag("title", x.title);
55 end
56 if x.instructions then
57 form:text_tag("instructions", x.instructions);
58 end
59 if type(x.fields) == "table" then
60 for _, f in ipairs(x.fields) do
61 if type(f) == "table" then
62 form:tag("field", { var = f.var, type = f.type, label = f.label });
63 if f.desc then
64 form:text_tag("desc", f.desc);
65 end
66 if f.required == true then
67 form:tag("required"):up();
68 end
69 if type(f.value) == "string" then
70 form:text_tag("value", f.value);
71 elseif type(f.value) == "table" then
72 for _, v in ipairs(f.value) do
73 form:text_tag("value", v);
74 end
75 end
76 if type(f.options) == "table" then
77 for _, o in ipairs(f.value) do
78 if type(o) == "table" then
79 form:tag("option", { label = o.label });
80 form:text_tag("value", o.value);
81 form:up();
82 end
83 end
84 end
85 end
86 end
87 end
88 return form;
89 end
90 end;
91 };
92
93 local function formdata(s, t)
94 local form = st.stanza("x", { xmlns = "jabber:x:data", type = t });
95 for k, v in pairs(s) do
96 form:tag("field", { var = k });
97 if type(v) == "string" then
98 form:text_tag("value", v);
99 elseif type(v) == "table" then
100 for _, v_ in ipairs(v) do
101 form:text_tag("value", v_);
102 end
103 end
104 end
105 return form;
106 end
107
108 local field_mappings = {
109 -- top level stanza attributes 9 -- top level stanza attributes
110 -- needed here to mark them as known fields 10 -- needed here to mark them as known fields
111 kind = "attr", 11 kind = "attr",
112 type = "attr", 12 type = "attr",
113 to = "attr", 13 to = "attr",
259 type = note.attr.type; 159 type = note.attr.type;
260 text = note:get_text(); 160 text = note:get_text();
261 }; 161 };
262 end 162 end
263 if form then 163 if form then
264 cmd.form = dataform.st2json(form); 164 cmd.form = field_mappings.dataform.st2json(form);
265 end 165 end
266 return cmd; 166 return cmd;
267 end; 167 end;
268 json2st = function (s) 168 json2st = function (s)
269 if type(s) == "table" and s ~= json.null then 169 if type(s) == "table" and s ~= json.null then
290 cmd:up(); 190 cmd:up();
291 elseif type(s.note) == "table" then 191 elseif type(s.note) == "table" then
292 cmd:text_tag("note", s.note.text, { type = s.note.type }); 192 cmd:text_tag("note", s.note.text, { type = s.note.type });
293 end 193 end
294 if s.form then 194 if s.form then
295 cmd:add_child(dataform.json2st(s.form)); 195 cmd:add_child(field_mappings.dataform.json2st(s.form));
296 elseif s.data then 196 elseif s.data then
297 cmd:add_child(formdata(s.data)); 197 cmd:add_child(field_mappings.formdata.json2st(s.data));
298 end 198 end
299 return cmd; 199 return cmd;
300 elseif type(s) == "string" then -- assume node 200 elseif type(s) == "string" then -- assume node
301 return st.stanza("command", { xmlns = "http://jabber.org/protocol/commands", node = s }); 201 return st.stanza("command", { xmlns = "http://jabber.org/protocol/commands", node = s });
302 end 202 end
335 end; 235 end;
336 end 236 end
337 }; 237 };
338 238
339 -- XEP-0004: Data Forms 239 -- XEP-0004: Data Forms
340 dataform = dataform; 240 dataform = {
341 241 -- Generic and complete dataforms mapping
342 -- Simpler mapping from JSON map 242 type = "func", xmlns = "jabber:x:data", tagname = "x",
243 st2json = function (s)
244 local fields = array();
245 local form = {
246 type = s.attr.type;
247 title = s:get_child_text("title");
248 instructions = s:get_child_text("instructions");
249 fields = fields;
250 };
251 for field in s:childtags("field") do
252 local i = {
253 var = field.attr.var;
254 type = field.attr.type;
255 label = field.attr.label;
256 desc = field:get_child_text("desc");
257 required = field:get_child("required") and true or nil;
258 value = field:get_child_text("value");
259 };
260 if field.attr.type == "jid-multi" or field.attr.type == "list-multi" or field.attr.type == "text-multi" then
261 local value = array();
262 for v in field:childtags("value") do
263 value:push(v:get_text());
264 end
265 if field.attr.type == "text-multi" then
266 i.value = value:concat("\n");
267 else
268 i.value = value;
269 end
270 end
271 if field.attr.type == "list-single" or field.attr.type == "list-multi" then
272 local options = array();
273 for o in field:childtags("option") do
274 options:push({ label = o.attr.label, value = o:get_child_text("value") });
275 end
276 i.options = options;
277 end
278 fields:push(i);
279 end
280 return form;
281 end;
282 json2st = function (x)
283 if type(x) == "table" and x ~= json.null then
284 local form = st.stanza("x", { xmlns = "jabber:x:data", type = x.type });
285 if x.title then
286 form:text_tag("title", x.title);
287 end
288 if x.instructions then
289 form:text_tag("instructions", x.instructions);
290 end
291 if type(x.fields) == "table" then
292 for _, f in ipairs(x.fields) do
293 if type(f) == "table" then
294 form:tag("field", { var = f.var, type = f.type, label = f.label });
295 if f.desc then
296 form:text_tag("desc", f.desc);
297 end
298 if f.required == true then
299 form:tag("required"):up();
300 end
301 if type(f.value) == "string" then
302 form:text_tag("value", f.value);
303 elseif type(f.value) == "table" then
304 for _, v in ipairs(f.value) do
305 form:text_tag("value", v);
306 end
307 end
308 if type(f.options) == "table" then
309 for _, o in ipairs(f.value) do
310 if type(o) == "table" then
311 form:tag("option", { label = o.label });
312 form:text_tag("value", o.value);
313 form:up();
314 end
315 end
316 end
317 end
318 end
319 end
320 return form;
321 end
322 end;
323 };
324
325 -- Simpler mapping of dataform from JSON map
343 formdata = { type = "func", xmlns = "jabber:x:data", tagname = "", 326 formdata = { type = "func", xmlns = "jabber:x:data", tagname = "",
344 st2json = function () 327 st2json = function ()
345 -- Tricky to do in a generic way without each form layout 328 -- Tricky to do in a generic way without each form layout
346 -- In the future, some well-known layouts might be understood 329 -- In the future, some well-known layouts might be understood
347 return nil, "not-implemented"; 330 return nil, "not-implemented";
348 end, 331 end,
349 json2st = formdata, 332 json2st = function (s, t)
333 local form = st.stanza("x", { xmlns = "jabber:x:data", type = t });
334 for k, v in pairs(s) do
335 form:tag("field", { var = k });
336 if type(v) == "string" then
337 form:text_tag("value", v);
338 elseif type(v) == "table" then
339 for _, v_ in ipairs(v) do
340 form:text_tag("value", v_);
341 end
342 end
343 end
344 return form;
345 end
350 }; 346 };
351 }; 347 };
352 348
353 local implied_kinds = { 349 local implied_kinds = {
354 disco = "iq", 350 disco = "iq",