Rocket.chat: GraphQL - Message subscription doesn't return feedback

Created on 22 Feb 2019  路  6Comments  路  Source: RocketChat/Rocket.Chat

TL;DR

I cannot get GraphQL subscriptions running. Queries do work (e.g. messages), the only available subscriptions (chatMessageAdded) gives neither error feedback or informs about new messages in a channel.

Intro

My goal is to get live updates of messages of a certain channel, where the user is not part of. In a perfect world, the user may be anonymous - but I guess, that'a whole other story.

In my example, consider it a list of several 'small room previews' (read-only) rather than an actual 'chat frontend'.

  • As soon as a room container (where the messages of a room shall be displayed) is rendered initially, the GraphQL query messages is used to fetch the latest x messages
  • Simultaneously, a chatMessageAdded subscription is established for this room in order to get informed about new messages.

I tried to debug this as much as I can. Could you give advice on what could be wrong?

Description:

In order to achieve this goal, I see GraphQL subscriptions as the only suitable way. Why?

  • The keywords (from above) are: live update & not part of the channel.
  • If the user is not part of the channel, there's no way the Realtime API can push us new messages (-> RealtimeAPI subscription)

Steps to reproduce:

  1. Activate GraphQL in Rocket.Chat
  2. Create small dummy implementation (query & subscription)
  3. Inspect WebSocket frames and find the outgoing frame for the subscription

Expected behavior:

  • After the outgoing frame, the subscription gets affirmed (some kind of 'ACK' / 'OK') response.
  • Sending a message in the observed channel triggers the subscription.
  • The WebSocket shows an incoming frame with the new message

Actual behavior:

  • No response after creating the subscription

    • To be clear: When invalidating the subscription on purpose (like remove the required params or change the subscription name) in fact trigger an error response (cool :-).

    • Just for the valid subscription, there's no feedback at all

  • New messages are not propagated through the WebSocket

Server Setup Information:

  • Version of Rocket.Chat Server: 0.73.1
  • Operating System: Ubuntu
  • Deployment Method: Just a local dev VM (vagrant)
  • Number of Running Instances: 1
  • NodeJS Version: v8.11.4
  • MongoDB Version: v4.0.6

Additional context

I tried to inspect & debug the entire process, here's the outcome:

  • The Code for managing the chatMessageAdded subscription is located here

    • PubSub in this context holds the subscription in-memory (see graphql-subscriptions)

  • My user is authenticated correctly
  • The generic hook afterSaveMessage is executed correctly
  • Then, publishMessage gets executed with passing the correct message object to PubSub
  • In the resolve, the pubsub.asyncIterator will not be triggered, thus none of the actual subscription code gets executed

    • pubsub.subscriptions is empty -> {}

Just for the sake of completeness, the two GraphQL documents I use as example:

const listMessagesDocument = gql`
    query listMessages {
        messages(channelId: "DveDZbQDQWcT7wPRd", count: 5) {
            messagesArray {
                author {
                    username
                }
                content
                id
            }
        }
    }
`;

const messageAdded = gql`
    subscription {
        chatMessageAdded(channelId: "DveDZbQDQWcT7wPRd") {
            content
        }
    }
`;

Relevant logs:

WebSocket frames

{"type":"connection_init","payload":{}}
{"type":"connection_ack"}
{"id":"1","type":"start","payload":{"variables":{},"extensions":{},"operationName":null,"query":"subscription {\n  chatMessageAdded(channelId: \"DveDZbQDQWcT7wPRd\") {\n    content\n    __typename\n  }\n}\n"}}

Rocket.Chat Server logs

  • Even in debug mode, it doesn't display any useful information

Webserver logs

  • Connection was established correctly
api waiting response

All 6 comments

@winterstefan first of all, thanks for the very detailed explanation of the problem. I tested the situation that you are reporting, and I found the problem. Basically the problem is with authentication, the authenticated function does not receive the token, because the connection is different from the normal that you send the authToken. As we can see here, the websocket connection expect a parameter that contain the authToken, but the problem is that we CAN'T sent headers over websocket connection, and the subscriptions-transport-ws from apollo-graphql can't handle with this, as you can see in this doc. There is no correct way to authenticate a connection over websocket and graphql(at least until now). Apollo graphql says that we should use the apollo-client to be able to set this connectionParams with the expected authToken.

See apollo explanation: https://www.apollographql.com/docs/graphql-subscriptions/authentication.html
and possible solution to this: https://www.apollographql.com/docs/react/advanced/subscriptions.html#authentication

Please let me know if I can help you with this.

@MarcosSpessatto Thanks for the explanation!

I'm not sure whether I fully understood it, but I played around a bit after reading your answer and the associated docs.

By just passing the token as connectionParams.Authorization, I managed to get my use-case running:

    new WebSocketLink({
        uri: 'myuri.example.com',
        options: {
            reconnect: true,
            connectionParams: {
                Authorization: 'MY-TOKEN-HERE',
            },
        },
        ...config,
    });

With that, the token gets sent in the initial connect frame of the WebSocket.

  • Inspecting / debugging the Node process that the Authorization param was handled correctly
  • Subscription established successfully
  • After sending a message to the chat, it correctly got propagated

So for me, I'd consider it working. Is there anything I left out / that's wrong with my solution? Otherwise, I would mark this issue als solved.

@winterstefan Your solution looks good! Unfortunately is the only way to send the token when the socket connects.

Okay, thank you very much for your help!

_For me, the case is solved -> closing issue_

Hi @winterstefan,

We are planning to remove the support for GrapgQL on the next release and work a new implementation later.

Are you using the GraphQL API in production?

Can you please give us your opinion on the issue?

https://github.com/RocketChat/Rocket.Chat/issues/14959

@engelgabriel Thank you very much for mention + hint about GraphQL. See my latest comment with #14959 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

brendanheywood picture brendanheywood  路  3Comments

Buzzele picture Buzzele  路  3Comments

mddvul22 picture mddvul22  路  3Comments

sta-szek picture sta-szek  路  3Comments

Buzzele picture Buzzele  路  3Comments