processone / xmpp

Erlang/Elixir XMPP parsing and serialization library on top of Fast XML
http://process-one.net
Apache License 2.0
137 stars 88 forks source link

how to add a tag #8

Closed grizzly-monkey closed 7 years ago

grizzly-monkey commented 7 years ago

I have created below spec

-record(sync, {id = <<>> :: binary(),
               c = <<>> :: sync_type(),
               from :: undefined | jid:jid(),
               to :: undefined | jid:jid(),
               json :: <<>> :: binary(),
               sub_els = [] :: [xmpp_element() | fxml:xmlel()]
               }).

-xml(sync,
     #elem{name = <<"sync">>,
     xmlns =  [<<"jabber:client">>, <<"jabber:server">>,
               <<"jabber:component:accept">>],
     cdata = #cdata{label = '$json'},
     result = {sync, '$id','$c','$from', '$to', '$json', '$_els'},
     attrs = [ #attr{name = <<"id">>},
               #attr{name = <<"c">>,
                     default = <<"0">> },
               #attr{name = <<"from">>,
                          dec = {dec_jid, []},
                          enc = {enc_jid, []}},
               #attr{name = <<"to">>,
                          dec = {dec_jid, []},
                          enc = {enc_jid, []}}
             ]}).

Which parses below request

<<"<sync id='6383950948998274048-13' c='-902781519'>{\"t\":\"sync1\",\"id\":{\"t\":\"UserDets\",\"hash\":2102,\"user_id\":5533240}}</sync>">>

however my reponse to the result is with addtion of response tag as below

<<"<sync id='6383950948998274048-13' c='-902781519'> <response>JSON_RESPONSE </response> </sync>">>

when preparing response packet how do i add response tag ? in old version i used to

Children = [#xmlel{name = <<"response">>,
        attrs = [],
        children = [{xmlcdata, ResponseData}]}],

Please help Thanks

zinid commented 7 years ago

Add sync_response spec:

-xml(sync_response,
        #elem{name = <<"response">>,
                xmlns =  [<<"jabber:client">>, <<"jabber:server">>,
                          <<"jabber:component:accept">>],
                result = $cdata}).

-xml(sync,
        #elem{
           result = {sync, '$id','$c','$from', '$to', '$json', '$response', '$_els'},
           %% All your filelds go here
           refs = [#ref{name = sync_response, label = '$response', default = <<"">>}]}).

Then the reply in your code will look like:

Reply = #sync{id = ID, from = To, to = From, response = JSONBinary}
zinid commented 7 years ago

I would also like to add that using jabber:client namespace violates RFC6120, because there is no such element within this namespace defined in RFC6120 schema.

grizzly-monkey commented 7 years ago

Oh thanks for bringing it to my notice will change the namespace

Error on sync_response that you gave without xmlns

case fxml_gen:compile("specs/xmpp_codec.spec", [{add_type_specs, xmpp_element}, {erl_dir, "src"}, {hrl_dir, "include"}]) of ok -> halt(0); _ -> halt(1) end.'
failed to compile "specs/xmpp_codec.spec": {'EXIT',
                                            {{bad_spec,
                                              {empty_xmlns,<<"response">>}},
                                             [{fxml_gen,bad_spec,1,

So later i added

-xml(sync_response,
     #elem{name = <<"response">>,
           xmlns = <<"testing:xmlnss">>,
           result = '$cdata',
           cdata = #cdata{label = '$cdata'}}).

and refs as

refs = [#ref{name = sync_result, label = '$response', default = <<"">>}]}).

But after this i get below error on encode

#sync{id = <<"6383980290226270208-21">>,c = <<"-902781519">>,
      from = {jid,<<>>,<<"dev.com">>,<<>>,<<>>,<<"dev.com">>,<<>>},
      to = {jid,<<"143622">>,<<"dev.com">>,<<"A-3.13">>,<<"143622">>,
                <<"dev.com">>,<<"A-3.13">>},
      json = <<>>,
      response = <<"{\"t\":\"user\",\"id\":143622,\"first_name\":\"Gsr\",\"last_name\":\"Goos\"}">>,
      sub_els = []}

13:04:00.267 [error] Hook c2s_handle_info crashed when running ejabberd_c2s:process_info/2: Reason = {error,function_clause, [{xmpp_codec,'encodesync$result',[{file,"src/xmpp_codec.erl"},{line,2282}],[<<"{\"t\":\"user\",\"id\":143622,\"first_name\":\"Gsr\",\"last_name\":\"Goos\"}">>,<<"testing:xmlnss">>,[]]},{xmpp_codec,encode_sync,[{file,"src/xmpp_codec.erl"},{line,2271}],2},{xmpp_stream_in,socket_send,[{file,"src/xmpp_stream_in.erl"},{line,1112}],2},{xmpp_stream_in,send_pkt,[{file,"src/xmpp_stream_in.erl"},{line,1066}],2},{ejabberd_hooks,safe_apply,[{file,"src/ejabberd_hooks.erl"},{line,380}],4},{ejabberd_hooks,run_fold1,[{file,"src/ejabberd_hooks.erl"},{line,364}],4},{xmpp_stream_in,handle_info,[{file,"src/xmpp_stream_in.erl"},{line,388}],2},{gen_server,handle_msg,[{file,"gen_server.erl"},{line,599}],5}]} ** Arguments = [#{access => c2s,auth_module => ejabberd_auth_sql,conn => c2s_compressed_tls,ip => {{219,91,211,1

PS : I added response field to my sync record as well

zinid commented 7 years ago

Please show the complete spec: sync, sync_response and the record.

zinid commented 7 years ago

Ah, the reference is incorrect: you should use sync_response instead of sync_result.

zinid commented 7 years ago

btw, you don't need to write your record by hand, it's only needed if you have "underscore" label ($_) in your spec.

grizzly-monkey commented 7 years ago

This is final complete spec that i have written and is giving me the same error

-record(sync, {id = <<>> :: binary(),
               c = <<>> :: sync_type(),
               from :: undefined | jid:jid(),
               to :: undefined | jid:jid(),
               json :: <<>> :: binary(),
               result :: <<>> :: binary(),
               sub_els = [] :: [xmpp_element() | fxml:xmlel()]
               }).

-xml(sync_result,
     #elem{name = <<"result">>,
           xmlns = <<"a:c">>,
           result = '$cdata',
           cdata = #cdata{label = '$cdata'}}).

-xml(sync,
     #elem{name = <<"sync">>,
     xmlns =  [<<"a:c">>, <<"jabber:server">>,
               <<"jabber:component:accept">>],
     cdata = #cdata{label = '$json'},
     result = {sync, '$id','$c','$from', '$to', '$json', '$result', '$_els'},
     attrs = [ #attr{name = <<"id">>},
               #attr{name = <<"c">>,
                     default = <<"0">> },
               #attr{name = <<"from">>,
                          dec = {dec_jid, []},
                          enc = {enc_jid, []}},
               #attr{name = <<"to">>,
                          dec = {dec_jid, []},
                          enc = {enc_jid, []}}
             ],
     refs = [#ref{name = sync_result, label = '$result', default = <<"">>}]}).
zinid commented 7 years ago

1) Remove record definition (it's not needed in your case as I pointed above), because it will be generated automatically. 2) Add min/max attributes to the #ref{}:

#ref{name = sync_result, label = '$result', default = <<"">>, min = 0, max = 1}
zinid commented 7 years ago

Also remove 'jabber:server' and 'jabber:component:accept', because they have the same problem as 'jabber:client', i.e. just leave xmlns = <<"a:c">>.

zinid commented 7 years ago

I just checked, this spec works flawlessly for me:

-xml(sync_result,
     #elem{name = <<"result">>,
           xmlns = <<"a:c">>,
           result = '$cdata',
           cdata = #cdata{label = '$cdata'}}).

-xml(sync,
     #elem{name = <<"sync">>,
     xmlns =  <<"a:c">>,
     cdata = #cdata{label = '$json'},
     result = {sync, '$id','$c','$from', '$to', '$json', '$result', '$_els'},
     attrs = [ #attr{name = <<"id">>},
               #attr{name = <<"c">>,
                     default = <<"0">> },
               #attr{name = <<"from">>,
                          dec = {dec_jid, []},
                          enc = {enc_jid, []}},
               #attr{name = <<"to">>,
                          dec = {dec_jid, []},
                          enc = {enc_jid, []}}
             ],
     refs = [#ref{name = sync_result, label = '$result',
          min = 0, max = 1, default = <<"">>}]}).
> xmpp:encode(#sync{result = <<"some json">>}).
#xmlel{name = <<"sync">>,
       attrs = [{<<"xmlns">>,<<"a:c">>}],
       children = [#xmlel{name = <<"result">>,attrs = [],
                          children = [{xmlcdata,<<"some json">>}]}]}
grizzly-monkey commented 7 years ago

min = 0, max = 1 adding this made it work .. what is the purpose of it ?

Thanks for the help I am good now you are the man @zinid :) thanks a lot. i spent almost 6 hours i getting this corrected

zinid commented 7 years ago

min = 0, max = 1 adding this made it work .. what is the purpose of it ?

This means exactly what it says: it restricts the element to allow only 0 or 1 subelement of this kind, i.e. only zero or single <result/> element is allowed in <sync/>. The default is to allow any number of <result/> elements, which is not your case from what I understand.

zinid commented 7 years ago

i spent almost 6 hours i getting this corrected

Next time don't spend hours of your life on this, just ask a question :)

grizzly-monkey commented 7 years ago

you all do so much of work for the community i thought ill try something before i ask ... and was not sure if it was an idiotic question. definitely I owe you a beer / beers :)