util.dataforms: Add support for XEP-0122: Data Forms Validation
authorKim Alvefur <zash@zash.se>
Sat, 01 Sep 2018 03:10:09 +0200
changeset 9246 a4c52e304e6f
parent 9245 68694c1bd960
child 9247 b3b156bd9914
util.dataforms: Add support for XEP-0122: Data Forms Validation Initially only basic validation of xs:integer
spec/util_dataforms_spec.lua
util/dataforms.lua
--- a/spec/util_dataforms_spec.lua	Sat Sep 01 01:24:46 2018 +0200
+++ b/spec/util_dataforms_spec.lua	Sat Sep 01 03:10:09 2018 +0200
@@ -401,5 +401,27 @@
 			assert.equal("hello", x:find"field/value#");
 		end);
 	end);
+
+	describe("validation", function ()
+		local f = dataforms.new {
+			{
+				name = "number",
+				type = "text-single",
+				datatype = "xs:integer",
+			},
+		};
+
+		it("works", function ()
+			local d = f:data(f:form({number = 1}));
+			assert.equal(1, d.number);
+		end);
+
+		it("works", function ()
+			local d,e = f:data(f:form({number = "nan"}));
+			assert.not_equal(1, d.number);
+			assert.table(e);
+			assert.string(e.number);
+		end);
+	end);
 end);
 
--- a/util/dataforms.lua	Sat Sep 01 01:24:46 2018 +0200
+++ b/util/dataforms.lua	Sat Sep 01 03:10:09 2018 +0200
@@ -9,6 +9,7 @@
 local setmetatable = setmetatable;
 local ipairs = ipairs;
 local type, next = type, next;
+local tonumber = tonumber;
 local t_concat = table.concat;
 local st = require "util.stanza";
 local jid_prep = require "util.jid".prep;
@@ -17,6 +18,7 @@
 -- luacheck: std none
 
 local xmlns_forms = 'jabber:x:data';
+local xmlns_validate = 'http://jabber.org/protocol/xdata-validate';
 
 local form_t = {};
 local form_mt = { __index = form_t };
@@ -50,6 +52,13 @@
 			end
 		end
 
+		if formtype == "form" and field.datatype then
+			form:tag("validate", { xmlns = xmlns_validate, datatype = field.datatype });
+			-- <basic/> assumed
+			form:up();
+		end
+
+
 		local value = field.value;
 		local options = field.options;
 
@@ -85,6 +94,10 @@
 		end
 
 		if value ~= nil then
+			if type(value) == "number" then
+				-- TODO validate that this is ok somehow, eg check field.datatype
+				value = ("%g"):format(value);
+			end
 			-- Add value, depending on type
 			if field_type == "hidden" then
 				if type(value) == "table" then
@@ -141,6 +154,7 @@
 end
 
 local field_readers = {};
+local data_validators = {};
 
 function form_t.data(layout, stanza, current)
 	local data = {};
@@ -166,7 +180,17 @@
 			present[field.name] = true;
 			local reader = field_readers[field.type];
 			if reader then
-				data[field.name], errors[field.name] = reader(tag, field.required);
+				local value, err = reader(tag, field.required);
+				local validator = field.datatype and data_validators[field.datatype];
+				if value ~= nil and validator then
+					local valid, ret = validator(value, field);
+					if valid then
+						value = ret;
+					else
+						value, err = nil, ret or field.datatype;
+					end
+				end
+				data[field.name], errors[field.name] = value, err;
 			end
 		end
 	end
@@ -265,6 +289,17 @@
 		return field_tag:get_child_text("value");
 	end
 
+data_validators["xs:integer"] =
+	function (data)
+		local n = tonumber(data);
+		if not n then
+			return false, "not a number";
+		elseif n % 1 ~= 0 then
+			return false, "not an integer";
+		end
+		return true, n;
+	end
+
 
 local function get_form_type(form)
 	if not st.is_stanza(form) then