Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL subscriptions for stanzas #3830

Merged
merged 17 commits into from
Nov 8, 2022
Merged

Conversation

chrzaszcz
Copy link
Member

@chrzaszcz chrzaszcz commented Oct 28, 2022

The goal of this PR is to deliver incoming messages with the use of GraphQL subscriptions.
Such functionality existed for REST API, and it used SSE (Server-Sent Events) with Lasse.
This GraphQL solution is using SSE as well.

Subscription execution

According to GraphQL specification, executing a subscription should return a Response Stream, that will deliver the events to the subscriber. As the graphql library executes subscriptions the same way as queries, the subscription needs to be executed several times:

  1. The first execution should return null in data, and a new Stream in aux, which corresponds to the Source Stream from the GraphQL docs.
  2. Then, whenever an Event is received, the already processed and prepared GraphQL AST is executed again, this time with the Stream and the Event in the context. The resolver should then return either null or the processed Event to send to the client. This step is roughly equivalent to MapSourceToResponseEvent - to implement it fully according to the specs, one would need to reimplement query execution in the graphql library, which seems too complicated for now.
  3. Upon termination, the request is executed one last time with the terminate event. This is an opportunity to clean up all stream resources. It implements unsubscribe from the specs.

SSE Handler

The new mongoose_graphql_sse_handler module handles requests to the /sse sub-path. It prepares and executes the requested subscription, and then listens for incoming messages. Each received message triggers delivery of the event. Only GET requests are accepted, which follows the specs, and is enforced by lasse_handler.

subscribeForMessages

This subscription is added to both Client and Admin API. It returns messages in the same format as getLastMessages. It skips stanzas other than messages, just like the REST API. We could extend it when needed.

Data structures:

  • Stream represents an open SM session, and it is a map with the User JID, Session ID, and the Host Type as keys.
  • Events are received as {route, From, To, Acc}, just like in the REST API. We could amend it later.

Other changes

GET and POST requests are now accepted for queries and mutations, and they accept the parameters in a query string and request body, respectively. This follows the GraphQL recommendation.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@codecov
Copy link

codecov bot commented Oct 28, 2022

Codecov Report

Base: 83.04% // Head: 83.10% // Increases project coverage by +0.05% 🎉

Coverage data is based on head (95cfce5) compared to base (011217f).
Patch coverage: 93.75% of modified lines in pull request are covered.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3830      +/-   ##
==========================================
+ Coverage   83.04%   83.10%   +0.05%     
==========================================
  Files         528      534       +6     
  Lines       33926    34020      +94     
==========================================
+ Hits        28174    28271      +97     
+ Misses       5752     5749       -3     
Impacted Files Coverage Δ
...rc/mongoose_client_api/mongoose_client_api_sse.erl 58.06% <40.00%> (-0.91%) ⬇️
src/graphql/mongoose_graphql_stanza_helper.erl 87.50% <77.77%> (-12.50%) ⬇️
src/graphql/mongoose_graphql_sse_handler.erl 90.00% <90.00%> (ø)
...phql/admin/mongoose_graphql_admin_subscription.erl 100.00% <100.00%> (ø)
...min/mongoose_graphql_stanza_admin_subscription.erl 100.00% <100.00%> (ø)
src/graphql/mongoose_graphql.erl 92.72% <100.00%> (+0.89%) ⬆️
src/graphql/mongoose_graphql_commands.erl 98.27% <100.00%> (+0.03%) ⬆️
src/graphql/mongoose_graphql_cowboy_handler.erl 95.50% <100.00%> (+1.31%) ⬆️
src/graphql/mongoose_graphql_errors.erl 82.35% <100.00%> (+1.10%) ⬆️
src/graphql/mongoose_graphql_operations.erl 100.00% <100.00%> (ø)
... and 12 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

@mongoose-im

This comment was marked as outdated.

- Subscriptions can be executed only with SSE.
- Queries and mutations can be executed with HTTP or CLI.
This makes it possible to the execute a prepared subscription operation
upon receiving each event.
Also: make it possible to return 'aux' data.
@mongoose-im

This comment was marked as outdated.

It uses lasse, just like the REST SSE handler
- Handle SSE requests under the '/sse' subpath
- Export utils used by the SSE handler
- Allow passing parameters with GET (qs) or POST (body).
  This follows the GraphQL specs, and allows to handle SSE.
- unsupported operation
- invalid query string
This common code is used by REST and GraphQL SSE handlers.
This is actually used only for building the queries for tests.
The commands will be displayed in the CLI help - we can change this
later if needed.
@mongoose-im

This comment was marked as outdated.

Put the more generic tests (mostly error handling) in a separate suite.
@mongoose-im
Copy link
Collaborator

mongoose-im commented Nov 4, 2022

small_tests_24 / small_tests / 856b379
Reports root / small


small_tests_25 / small_tests / 856b379
Reports root / small


ldap_mnesia_24 / ldap_mnesia / 856b379
Reports root/ big
OK: 2118 / Failed: 0 / User-skipped: 784 / Auto-skipped: 0


dynamic_domains_pgsql_mnesia_24 / pgsql_mnesia / 856b379
Reports root/ big
OK: 4009 / Failed: 0 / User-skipped: 88 / Auto-skipped: 0


dynamic_domains_mysql_redis_25 / mysql_redis / 856b379
Reports root/ big
OK: 3983 / Failed: 0 / User-skipped: 114 / Auto-skipped: 0


ldap_mnesia_25 / ldap_mnesia / 856b379
Reports root/ big
OK: 2118 / Failed: 0 / User-skipped: 784 / Auto-skipped: 0


dynamic_domains_pgsql_mnesia_25 / pgsql_mnesia / 856b379
Reports root/ big
OK: 4008 / Failed: 1 / User-skipped: 88 / Auto-skipped: 0

graphql_last_SUITE:admin_cli:admin_last_configured:admin_last:admin_count_active_users
{error,{{assertEqual,[{module,graphql_last_SUITE},
            {line,284},
            {expression,"get_ok_value ( p ( countActiveUsers ) , Res )"},
            {expected,2},
            {value,1}]},
    [{graphql_last_SUITE,admin_count_active_users_story,3,
               [{file,"/home/circleci/project/big_tests/tests/graphql_last_SUITE.erl"},
                {line,284}]},
     {escalus_story,story,4,
            [{file,"/home/circleci/project/big_tests/_build/default/lib/escalus/src/escalus_story.erl"},
             {line,72}]},
     {test_server,ts_tc,3,[{file,"test_server.erl"},{line,1782}]},
     {test_server,run_test_case_eval1,6,
            [{file,"test_server.erl"},{line,1291}]},
     {test_server,run_test_case_eval,9,
            [{file,"test_server.erl"},{line,1223}]}]}}

Report log


dynamic_domains_mssql_mnesia_25 / odbc_mssql_mnesia / 856b379
Reports root/ big
OK: 4009 / Failed: 0 / User-skipped: 88 / Auto-skipped: 0


elasticsearch_and_cassandra_25 / elasticsearch_and_cassandra_mnesia / 856b379
Reports root/ big
OK: 2596 / Failed: 0 / User-skipped: 641 / Auto-skipped: 0


internal_mnesia_25 / internal_mnesia / 856b379
Reports root/ big
OK: 2254 / Failed: 0 / User-skipped: 648 / Auto-skipped: 0


pgsql_mnesia_24 / pgsql_mnesia / 856b379
Reports root/ big
OK: 4383 / Failed: 0 / User-skipped: 97 / Auto-skipped: 0


pgsql_mnesia_25 / pgsql_mnesia / 856b379
Reports root/ big
OK: 4383 / Failed: 0 / User-skipped: 97 / Auto-skipped: 0


mysql_redis_25 / mysql_redis / 856b379
Reports root/ big
OK: 4369 / Failed: 0 / User-skipped: 111 / Auto-skipped: 0


mssql_mnesia_25 / odbc_mssql_mnesia / 856b379
Reports root/ big
OK: 4383 / Failed: 0 / User-skipped: 97 / Auto-skipped: 0


riak_mnesia_24 / riak_mnesia / 856b379
Reports root/ big
OK: 2442 / Failed: 0 / User-skipped: 627 / Auto-skipped: 0


dynamic_domains_pgsql_mnesia_25 / pgsql_mnesia / 856b379
Reports root/ big
OK: 4009 / Failed: 0 / User-skipped: 88 / Auto-skipped: 0

@mongoose-im
Copy link
Collaborator

mongoose-im commented Nov 4, 2022

small_tests_24 / small_tests / 0407461
Reports root / small


small_tests_25 / small_tests / 0407461
Reports root / small


dynamic_domains_pgsql_mnesia_24 / pgsql_mnesia / 0407461
Reports root/ big
OK: 4009 / Failed: 0 / User-skipped: 88 / Auto-skipped: 0


ldap_mnesia_24 / ldap_mnesia / 0407461
Reports root/ big
OK: 2118 / Failed: 0 / User-skipped: 784 / Auto-skipped: 0


dynamic_domains_pgsql_mnesia_25 / pgsql_mnesia / 0407461
Reports root/ big
OK: 4009 / Failed: 0 / User-skipped: 88 / Auto-skipped: 0


ldap_mnesia_25 / ldap_mnesia / 0407461
Reports root/ big
OK: 2118 / Failed: 0 / User-skipped: 784 / Auto-skipped: 0


dynamic_domains_mysql_redis_25 / mysql_redis / 0407461
Reports root/ big
OK: 3983 / Failed: 0 / User-skipped: 114 / Auto-skipped: 0


pgsql_mnesia_24 / pgsql_mnesia / 0407461
Reports root/ big
OK: 4383 / Failed: 0 / User-skipped: 97 / Auto-skipped: 0


elasticsearch_and_cassandra_25 / elasticsearch_and_cassandra_mnesia / 0407461
Reports root/ big
OK: 2596 / Failed: 0 / User-skipped: 641 / Auto-skipped: 0


riak_mnesia_24 / riak_mnesia / 0407461
Reports root/ big
OK: 2442 / Failed: 0 / User-skipped: 627 / Auto-skipped: 0


mysql_redis_25 / mysql_redis / 0407461
Reports root/ big
OK: 4369 / Failed: 0 / User-skipped: 111 / Auto-skipped: 0


pgsql_mnesia_25 / pgsql_mnesia / 0407461
Reports root/ big
OK: 4383 / Failed: 0 / User-skipped: 97 / Auto-skipped: 0


mssql_mnesia_25 / odbc_mssql_mnesia / 0407461
Reports root/ big
OK: 4383 / Failed: 0 / User-skipped: 97 / Auto-skipped: 0


dynamic_domains_mssql_mnesia_25 / odbc_mssql_mnesia / 0407461
Reports root/ big
OK: 4008 / Failed: 1 / User-skipped: 88 / Auto-skipped: 0

bosh_SUITE:essential:accept_higher_hold_value
{error,
  {{assertEqual,
     [{module,bosh_SUITE},
      {line,251},
      {expression,"get_bosh_sessions ( )"},
      {expected,[]},
      {value,
        [{bosh_session,<<"67f4451b903a90e3f84952c5e890345be82bf311">>,
           <9147.5996.0>}]}]},
   [{bosh_SUITE,accept_higher_hold_value,1,
      [{file,"/home/circleci/project/big_tests/tests/bosh_SUITE.erl"},
       {line,251}]},
    {test_server,ts_tc,3,[{file,"test_server.erl"},{line,1782}]},
    {test_server,run_test_case_eval1,6,
      [{file,"test_server.erl"},{line,1291}]},
    {test_server,run_test_case_eval,9,
      [{file,"test_server.erl"},{line,1223}]}]}}

Report log


dynamic_domains_mssql_mnesia_25 / odbc_mssql_mnesia / 0407461
Reports root/ big
OK: 4009 / Failed: 0 / User-skipped: 88 / Auto-skipped: 0

@chrzaszcz chrzaszcz marked this pull request as ready for review November 4, 2022 11:46
Copy link
Contributor

@JanuszJakubiec JanuszJakubiec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, minor typos in docs

@mongoose-im
Copy link
Collaborator

mongoose-im commented Nov 8, 2022

small_tests_24 / small_tests / 95cfce5
Reports root / small


small_tests_25 / small_tests / 95cfce5
Reports root / small


ldap_mnesia_24 / ldap_mnesia / 95cfce5
Reports root/ big
OK: 2118 / Failed: 0 / User-skipped: 784 / Auto-skipped: 0


dynamic_domains_pgsql_mnesia_24 / pgsql_mnesia / 95cfce5
Reports root/ big
OK: 4009 / Failed: 0 / User-skipped: 88 / Auto-skipped: 0


dynamic_domains_pgsql_mnesia_25 / pgsql_mnesia / 95cfce5
Reports root/ big
OK: 4009 / Failed: 0 / User-skipped: 88 / Auto-skipped: 0


dynamic_domains_mysql_redis_25 / mysql_redis / 95cfce5
Reports root/ big
OK: 3983 / Failed: 0 / User-skipped: 114 / Auto-skipped: 0


ldap_mnesia_25 / ldap_mnesia / 95cfce5
Reports root/ big
OK: 2118 / Failed: 0 / User-skipped: 784 / Auto-skipped: 0


pgsql_mnesia_24 / pgsql_mnesia / 95cfce5
Reports root/ big
OK: 4383 / Failed: 0 / User-skipped: 97 / Auto-skipped: 0


elasticsearch_and_cassandra_25 / elasticsearch_and_cassandra_mnesia / 95cfce5
Reports root/ big
OK: 2596 / Failed: 0 / User-skipped: 641 / Auto-skipped: 0


internal_mnesia_25 / internal_mnesia / 95cfce5
Reports root/ big
OK: 2254 / Failed: 0 / User-skipped: 648 / Auto-skipped: 0


dynamic_domains_mssql_mnesia_25 / odbc_mssql_mnesia / 95cfce5
Reports root/ big
OK: 4009 / Failed: 0 / User-skipped: 88 / Auto-skipped: 0


riak_mnesia_24 / riak_mnesia / 95cfce5
Reports root/ big
OK: 2440 / Failed: 2 / User-skipped: 627 / Auto-skipped: 0

graphql_server_SUITE:admin_http:clustering_http_tests:remove_node_test
{error,{{badmatch,{error,econnrefused}},
    [{rest_helper,fusco_request,7,
            [{file,"/home/circleci/project/big_tests/tests/rest_helper.erl"},
             {line,185}]},
     {rest_helper,make_request,1,
            [{file,"/home/circleci/project/big_tests/tests/rest_helper.erl"},
             {line,114}]},
     {rest_helper,make_request,1,
            [{file,"/home/circleci/project/big_tests/tests/rest_helper.erl"},
             {line,121}]},
     {graphql_server_SUITE,remove_node_test,1,
                 [{file,"/home/circleci/project/big_tests/tests/graphql_server_SUITE.erl"},
                {line,210}]},
     {test_server,ts_tc,3,[{file,"test_server.erl"},{line,1783}]},
     {test_server,run_test_case_eval1,6,
            [{file,"test_server.erl"},{line,1292}]},
     {test_server,run_test_case_eval,9,
            [{file,"test_server.erl"},{line,1224}]}]}}

Report log

graphql_server_SUITE:admin_http:clustering_http_tests:stop_node_test
{error,{{badmatch,{error,econnrefused}},
    [{rest_helper,fusco_request,7,
            [{file,"/home/circleci/project/big_tests/tests/rest_helper.erl"},
             {line,185}]},
     {rest_helper,make_request,1,
            [{file,"/home/circleci/project/big_tests/tests/rest_helper.erl"},
             {line,114}]},
     {rest_helper,make_request,1,
            [{file,"/home/circleci/project/big_tests/tests/rest_helper.erl"},
             {line,121}]},
     {graphql_server_SUITE,stop_node_test,1,
                 [{file,"/home/circleci/project/big_tests/tests/graphql_server_SUITE.erl"},
                {line,215}]},
     {test_server,ts_tc,3,[{file,"test_server.erl"},{line,1783}]},
     {test_server,run_test_case_eval1,6,
            [{file,"test_server.erl"},{line,1292}]},
     {test_server,run_test_case_eval,9,
            [{file,"test_server.erl"},{line,1224}]}]}}

Report log


pgsql_mnesia_25 / pgsql_mnesia / 95cfce5
Reports root/ big
OK: 4394 / Failed: 1 / User-skipped: 97 / Auto-skipped: 0

pep_SUITE:pep_tests:unsubscribe_after_presence_unsubscription
{error,
  {{badmatch,
     [{xmlel,<<"message">>,
        [{<<"from">>,
        <<"alice_unsubscribe_after_presence_unsubscription_2594@localhost">>},
         {<<"to">>,
        <<"bob_unsubscribe_after_presence_unsubscription_2594@localhost/res1">>},
         {<<"type">>,<<"headline">>}],
        [{xmlel,<<"event">>,
           [{<<"xmlns">>,
           <<"http://jabber.org/protocol/pubsub#event">>}],
           [{xmlel,<<"items">>,
            [{<<"node">>,<<"ezNPIrAnnss+oZPPqlnSLg==">>}],
            [{xmlel,<<"item">>,
               [{<<"id">>,<<"salmon">>}],
               [{xmlel,<<"entry">>,
                  [{<<"xmlns">>,
                  <<"http://www.w3.org/2005/Atom">>}],
                  []}]}]}]},
         {xmlel,<<"headers">>,
           [{<<"xmlns">>,<<"http://jabber.org/protocol/shim">>}],
           []}]}]},
   [{pep_SUITE,'-unsubscribe_after_presence_unsubscription/1-fun-0-',2,
      [{file,"/home/circleci/project/big_tests/tests/pep_SUITE.erl"},
       {line,384}]},
    {escalus_story,story,4,
      [{file,
         "/home/circleci/project/big_tests/_build/default/lib/escalus/src/escalus_story.erl"},
       {line,72}]},
    {test_server,ts_tc,3,[{file,"test_server.erl"},{line,1782}]},
    {test_server,run_test_case_eval1,6,
      [{file,"test_server.erl"},{line,1291}]},
    {test_server,run_test_case_eval,9,
      [{file,"test_server.erl"},{line,1223}]}]}}

Report log


mysql_redis_25 / mysql_redis / 95cfce5
Reports root/ big
OK: 4369 / Failed: 0 / User-skipped: 111 / Auto-skipped: 0


mssql_mnesia_25 / odbc_mssql_mnesia / 95cfce5
Reports root/ big
OK: 4382 / Failed: 1 / User-skipped: 97 / Auto-skipped: 0

smart_markers_SUITE:regular:one2one:marker_for_thread_can_be_fetched
{error,
  {{fetch_marker,ok,
     [{times,50,
        {error,
          {badmatch,[]},
          [{smart_markers_SUITE,'-verify_marker_fetch/4-fun-6-',3,
             [{file,
              "/home/circleci/project/big_tests/tests/smart_markers_SUITE.erl"},
            {line,405}]},
           {mongoose_helper,do_wait_until,2,
             [{file,
              "/home/circleci/project/big_tests/tests/mongoose_helper.erl"},
            {line,374}]},
           {escalus_story,story,4,
             [{file,
              "/home/circleci/project/big_tests/_build/default/lib/escalus/src/escalus_story.erl"},
            {line,72}]},
           {test_server,ts_tc,3,
             [{file,"test_server.erl"},{line,1782}]},
           {test_server,run_test_case_eval1,6,
             [{file,"test_server.erl"},{line,1291}]},
           {test_server,run_test_case_eval,9,
             [{file,"test_server.erl"},{line,1223}]}]}}]},
   [{mongoose_helper,do_wait_until,2,
      [{file,"/home/circleci/project/big_tests/tests/mongoose_helper.erl"},
       {line,371}]},
    {escalus_story,story,4,
      [{file,
         "/home/circleci/project/big_tests/_build/default/lib/escalus/src/escalus_story.erl"},
       {line,72}]},
    {test_server,ts_tc,3,[{file,"test_server.erl"},{line,1782}]},
    {test_server,run_test_case_eval1,6,
      [{file,"test_server.erl"},{line,1291}]},
    {test_server,run_test_case_eval,9,
      [{file,"test_server.erl"},{line,1223}]}]}}

Report log


riak_mnesia_24 / riak_mnesia / 95cfce5
Reports root/ big
OK: 2442 / Failed: 0 / User-skipped: 627 / Auto-skipped: 0


mssql_mnesia_25 / odbc_mssql_mnesia / 95cfce5
Reports root/ big
OK: 4383 / Failed: 0 / User-skipped: 97 / Auto-skipped: 0

@JanuszJakubiec JanuszJakubiec merged commit 7c74198 into master Nov 8, 2022
@JanuszJakubiec JanuszJakubiec deleted the graphql-subscriptions branch November 8, 2022 08:48
@chrzaszcz chrzaszcz added this to the 6.0.0 milestone Dec 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants