util.datamapper: Deal with locally built stanzas missing xmlns
So the problem is that xmlns is not inherited when building a stanza,
and then :get_child(n, ns) with an explicit namespace does not find that
such child tags.
E.g.
local t = st.stanza("foo", { xmlns = "urn:example:bar" })
:text_tag("hello", "world");
assert(t:get_child("hello", "urn:example:bar"), "This fails");
Meanwhile, during parsing (util.xmppstream or util.xml) child tags do
get the parents xmlns when not overriding them.
Thus, in the above example, if the stanza is passed trough
`t = util.xml.parse(tostring(t))` then the assert succeeds.
This change makes it so that it leaves out the namespace argument to
:get_child when it is the same as the current/parent namespace, which
behaves the same for both built and parsed stanzas.
5 years ago
local st
local xml
local map
setup ( function ( )
util.datamapper: Deal with locally built stanzas missing xmlns
So the problem is that xmlns is not inherited when building a stanza,
and then :get_child(n, ns) with an explicit namespace does not find that
such child tags.
E.g.
local t = st.stanza("foo", { xmlns = "urn:example:bar" })
:text_tag("hello", "world");
assert(t:get_child("hello", "urn:example:bar"), "This fails");
Meanwhile, during parsing (util.xmppstream or util.xml) child tags do
get the parents xmlns when not overriding them.
Thus, in the above example, if the stanza is passed trough
`t = util.xml.parse(tostring(t))` then the assert succeeds.
This change makes it so that it leaves out the namespace argument to
:get_child when it is the same as the current/parent namespace, which
behaves the same for both built and parsed stanzas.
5 years ago
st = require " util.stanza " ;
xml = require " util.xml " ;
map = require " util.datamapper " ;
end ) ;
describe ( " util.datamapper " , function ( )
local s , x , d
local disco , disco_info , disco_schema
setup ( function ( )
-- a convenience function for simple attributes, there's a few of them
local attr = { [ " $ref " ] = " #/$defs/attr " } ;
s = {
[ " $defs " ] = { attr = { type = " string " ; xml = { attribute = true } } } ;
type = " object " ;
xml = { name = " message " ; namespace = " jabber:client " } ;
properties = {
to = attr ;
from = attr ;
type = attr ;
id = attr ;
body = true ; -- should be assumed to be a string
lang = { type = " string " ; xml = { attribute = true ; prefix = " xml " } } ;
delay = {
type = " object " ;
xml = { namespace = " urn:xmpp:delay " ; name = " delay " } ;
properties = { stamp = attr ; from = attr ; reason = { type = " string " ; xml = { text = true } } } ;
} ;
state = {
type = " string " ;
enum = {
" active " ,
" inactive " ,
" gone " ,
" composing " ,
" paused " ,
} ;
xml = { x_name_is_value = true ; namespace = " http://jabber.org/protocol/chatstates " } ;
} ;
fallback = {
type = " boolean " ;
xml = { x_name_is_value = true ; name = " fallback " ; namespace = " urn:xmpp:fallback:0 " } ;
} ;
origin_id = {
type = " string " ;
xml = { name = " origin-id " ; namespace = " urn:xmpp:sid:0 " ; x_single_attribute = " id " } ;
} ;
react = {
type = " object " ;
xml = { namespace = " urn:xmpp:reactions:0 " ; name = " reactions " } ;
properties = {
to = { type = " string " ; xml = { attribute = true ; name = " id " } } ;
-- should be assumed to be array since it has 'items'
reactions = { items = { xml = { name = " reaction " } } } ;
} ;
} ;
stanza_ids = {
type = " array " ;
items = {
xml = { name = " stanza-id " ; namespace = " urn:xmpp:sid:0 " } ;
type = " object " ;
properties = {
id = attr ;
by = attr ;
} ;
} ;
} ;
} ;
} ;
x = xml.parse [ [
< message xmlns = " jabber:client " xml : lang = " en " to = " a@test " from = " b@test " type = " chat " id = " 1 " >
< body > Hello </ body >
< delay xmlns = ' urn:xmpp:delay ' from = ' test ' stamp = ' 2021-03-07T15:59:08+00:00 ' > Because </ delay >
< UNRELATED xmlns = ' http://jabber.org/protocol/chatstates ' />
< active xmlns = ' http://jabber.org/protocol/chatstates ' />
< fallback xmlns = ' urn:xmpp:fallback:0 ' />
< origin - id xmlns = ' urn:xmpp:sid:0 ' id = ' qgkmMdPB ' />
< stanza - id xmlns = ' urn:xmpp:sid:0 ' id = ' abc1 ' by = ' muc ' />
< stanza - id xmlns = ' urn:xmpp:sid:0 ' id = ' xyz2 ' by = ' host ' />
< reactions id = ' 744f6e18-a57a-11e9-a656-4889e7820c76 ' xmlns = ' urn:xmpp:reactions:0 ' >
< reaction > 👋 </ reaction >
< reaction > 🐢 </ reaction >
</ reactions >
</ message >
] ] ;
d = {
to = " a@test " ;
from = " b@test " ;
type = " chat " ;
id = " 1 " ;
lang = " en " ;
body = " Hello " ;
delay = { from = " test " ; stamp = " 2021-03-07T15:59:08+00:00 " ; reason = " Because " } ;
state = " active " ;
fallback = true ;
origin_id = " qgkmMdPB " ;
stanza_ids = { { id = " abc1 " ; by = " muc " } ; { id = " xyz2 " ; by = " host " } } ;
react = {
to = " 744f6e18-a57a-11e9-a656-4889e7820c76 " ;
reactions = {
" 👋 " ,
" 🐢 " ,
} ;
} ;
} ;
disco_schema = {
[ " $defs " ] = { attr = { type = " string " ; xml = { attribute = true } } } ;
type = " object " ;
xml = {
name = " iq " ;
namespace = " jabber:client "
} ;
properties = {
to = attr ;
from = attr ;
type = attr ;
id = attr ;
disco = {
type = " object " ;
xml = {
name = " query " ;
namespace = " http://jabber.org/protocol/disco#info "
} ;
properties = {
features = {
type = " array " ;
items = {
type = " string " ;
xml = {
name = " feature " ;
x_single_attribute = " var " ;
} ;
} ;
} ;
} ;
} ;
} ;
} ;
disco_info = xml.parse [ [
< iq type = " result " id = " disco1 " from = " example.com " >
< query xmlns = " http://jabber.org/protocol/disco#info " >
< feature var = " urn:example:feature:1 " > wrong </ feature >
< feature var = " urn:example:feature:2 " />
< feature var = " urn:example:feature:3 " />
< unrelated var = " urn:example:feature:not " />
</ query >
</ iq >
] ] ;
disco = {
type = " result " ;
id = " disco1 " ;
from = " example.com " ;
disco = {
features = {
" urn:example:feature:1 " ;
" urn:example:feature:2 " ;
" urn:example:feature:3 " ;
} ;
} ;
} ;
end ) ;
describe ( " parse " , function ( )
it ( " works " , function ( )
assert.same ( d , map.parse ( s , x ) ) ;
end ) ;
it ( " handles arrays " , function ( )
assert.same ( disco , map.parse ( disco_schema , disco_info ) ) ;
end ) ;
util.datamapper: Deal with locally built stanzas missing xmlns
So the problem is that xmlns is not inherited when building a stanza,
and then :get_child(n, ns) with an explicit namespace does not find that
such child tags.
E.g.
local t = st.stanza("foo", { xmlns = "urn:example:bar" })
:text_tag("hello", "world");
assert(t:get_child("hello", "urn:example:bar"), "This fails");
Meanwhile, during parsing (util.xmppstream or util.xml) child tags do
get the parents xmlns when not overriding them.
Thus, in the above example, if the stanza is passed trough
`t = util.xml.parse(tostring(t))` then the assert succeeds.
This change makes it so that it leaves out the namespace argument to
:get_child when it is the same as the current/parent namespace, which
behaves the same for both built and parsed stanzas.
5 years ago
it ( " deals with locally built stanzas " , function ( )
-- FIXME this could also be argued to be a util.stanza problem
local ver_schema = {
type = " object " ;
xml = { name = " iq " } ;
properties = {
type = { type = " string " ; xml = { attribute = true } } ;
id = { type = " string " ; xml = { attribute = true } } ;
version = {
type = " object " ;
xml = { name = " query " ; namespace = " jabber:iq:version " } ;
-- properties should be assumed to be strings
properties = { name = true ; version = { } ; os = { } } ;
util.datamapper: Deal with locally built stanzas missing xmlns
So the problem is that xmlns is not inherited when building a stanza,
and then :get_child(n, ns) with an explicit namespace does not find that
such child tags.
E.g.
local t = st.stanza("foo", { xmlns = "urn:example:bar" })
:text_tag("hello", "world");
assert(t:get_child("hello", "urn:example:bar"), "This fails");
Meanwhile, during parsing (util.xmppstream or util.xml) child tags do
get the parents xmlns when not overriding them.
Thus, in the above example, if the stanza is passed trough
`t = util.xml.parse(tostring(t))` then the assert succeeds.
This change makes it so that it leaves out the namespace argument to
:get_child when it is the same as the current/parent namespace, which
behaves the same for both built and parsed stanzas.
5 years ago
} ;
} ;
} ;
local ver_st = st.iq ( { type = " result " ; id = " v1 " } )
: query ( " jabber:iq:version " )
: text_tag ( " name " , " Prosody " )
: text_tag ( " version " , " trunk " )
: text_tag ( " os " , " Lua 5.3 " )
: reset ( ) ;
local data = { type = " result " ; id = " v1 " ; version = { name = " Prosody " ; version = " trunk " ; os = " Lua 5.3 " } }
assert.same ( data , map.parse ( ver_schema , ver_st ) ) ;
end ) ;
end ) ;
describe ( " unparse " , function ( )
it ( " works " , function ( )
local u = map.unparse ( s , d ) ;
assert.equal ( " message " , u.name ) ;
assert.same ( x.attr , u.attr ) ;
assert.equal ( x : get_child_text ( " body " ) , u : get_child_text ( " body " ) ) ;
assert.equal ( x : get_child_text ( " delay " , " urn:xmpp:delay " ) , u : get_child_text ( " delay " , " urn:xmpp:delay " ) ) ;
assert.same ( x : get_child ( " delay " , " urn:xmpp:delay " ) . attr , u : get_child ( " delay " , " urn:xmpp:delay " ) . attr ) ;
assert.same ( x : get_child ( " origin-id " , " urn:xmpp:sid:0 " ) . attr , u : get_child ( " origin-id " , " urn:xmpp:sid:0 " ) . attr ) ;
assert.same ( x : get_child ( " reactions " , " urn:xmpp:reactions:0 " ) . attr , u : get_child ( " reactions " , " urn:xmpp:reactions:0 " ) . attr ) ;
assert.same ( 2 , # u : get_child ( " reactions " , " urn:xmpp:reactions:0 " ) . tags ) ;
for _ , tag in ipairs ( x.tags ) do
if tag.name ~= " UNRELATED " then
assert.truthy ( u : get_child ( tag.name , tag.attr . xmlns ) or u : get_child ( tag.name ) , tag : top_tag ( ) )
end
end
assert.equal ( # x.tags - 1 , # u.tags )
end ) ;
it ( " handles arrays " , function ( )
local u = map.unparse ( disco_schema , disco ) ;
assert.equal ( " urn:example:feature:1 " , u : find ( " {http://jabber.org/protocol/disco#info}query/feature/@var " ) )
local n = 0 ;
for child in u : get_child ( " query " , " http://jabber.org/protocol/disco#info " ) : childtags ( " feature " ) do
n = n + 1 ;
assert.equal ( string.format ( " urn:example:feature:%d " , n ) , child.attr . var ) ;
end
end ) ;
end ) ;
end )