zino-hofmann / graphql-flutter

A GraphQL client for Flutter, bringing all the features from a modern GraphQL client to one easy to use package.
https://zino-hofmann.github.io/graphql-flutter
MIT License
3.25k stars 622 forks source link

Subscription not working, only connected #1361

Closed rrifafauzikomara closed 1 year ago

rrifafauzikomara commented 1 year ago

I am creating a subscription with GraphQL, and I need to consume that subscription with Flutter, but I don't know how to do that on the Flutter side. But I can understand doing that on Postman.

Here are the steps to test a subscription on Postman:

  1. Connect to wss
  2. Send a Message by clicking Send button.
  3. Trigger any API, then the subscription will listen new data.

And here's the result for above steps. enter image description here

And if I do these steps, the subscription will not work.

  1. Connect to wss
  2. Trigger any API, then the subscription will not listen any data.

And here's the result for above steps. enter image description here

As u can see, even though connected to the wss, if i dont send the Message by clicked the Send button, the subscription will not work.

When I try to implement the subscription in Flutter side, seems only connected to the wss, here's the result.

flutter: Initialising connection

So my question is, how to send the Message like from Postman but in Flutter side?

Here's the Message data.

{
  "id": "{{$randomUUID}}",
  "payload": {
    "data": "{\"query\":\"subscription updateCheckout { onUpdateCheckout(userId: \\\"{{userId}}\\\") { userId createdAt amount checkoutId maxAllowedAmount maxAvailableAmount message state trancheId }}\"}",
    "extensions": {
      "authorization": {
        "Authorization": "{{cognitoIdToken}}",
        "host": "{{apiHost}}"
      }
    }
  },
  "type": "start"
}

Here's the Schema from AWS.

subscription MySubscription {
  onUpdateCheckout(userId: $userId) {
    createdAt
    checkoutId
    maxAllowedAmount
    maxAvailableAmount
    message
    state
    trancheId
  }
}

And here's my Flutter code to impl the subscription.

Future<Map<String, dynamic>> subscription() async {
    try {
      final graphqlEndpoint = 'https://BASEURL/graphql';
      final subscriptionEndpoint =
          'wss://BASEURL/graphql/realtime?header=<EncodedHeader>&payload=e30=';
      var currentToken = _token();

      final HttpLink httpLink = HttpLink(
        graphqlEndpoint,
        defaultHeaders: {
          'Authorization': "Bearer $currentToken",
          'Sec-WebSocket-Protocol': GraphQLProtocol.graphqlWs,
        },
      );

      final WebSocketLink webSocketLink = WebSocketLink(
        subscriptionEndpoint,
        config: SocketClientConfig(
          autoReconnect: true,
          serializer: AppSyncRequest(uuid: 'uuid', token: currentToken),
          inactivityTimeout: const Duration(seconds: 60),
          headers: kIsWeb
              ? null
              : {
                  'Authorization': "Bearer $currentToken",
                  'Sec-WebSocket-Protocol': GraphQLProtocol.graphqlWs,
                },
          initialPayload: {
            'Authorization': currentToken,
          },
        ),
        subProtocol: GraphQLProtocol.graphqlWs,
      );

      final AuthLink authLink =
          AuthLink(getToken: () => 'Bearer $currentToken');

      final Link linkSplitted = authLink.split(
        (request) => request.isSubscription,
        webSocketLink,
        httpLink,
      );

      final graphQLCache = GraphQLCache();

      final client = GraphQLClient(
        link: linkSplitted,
        cache: graphQLCache,
        defaultPolicies: DefaultPolicies(
          watchQuery: Policies(fetch: FetchPolicy.noCache),
          query: Policies(fetch: FetchPolicy.noCache),
          mutate: Policies(fetch: FetchPolicy.noCache),
          subscribe: Policies(fetch: FetchPolicy.noCache),
        ),
        alwaysRebroadcast: true,
      );

      const checkOutSubscription = r'''
subscription MySubscription {
  onUpdateCheckout(userId: $userId) {
    createdAt
    checkoutId
    maxAllowedAmount
    maxAvailableAmount
    message
    state
    trancheId
  }
}
''';

      final options = SubscriptionOptions(
        document: gql(checkOutSubscription),
        variables: {
          "userId": "userId",
        },
      );

      final result = client.subscribe(options);
      Map<String, dynamic> data = {};
      result.listen(
        (event) {
          // not triggered
          data = event.data ?? {};
          if (data.isNotEmpty) {
            // not triggered
          }
        },
        cancelOnError: true,
      );

      return Future.value(data);
    } on Exception {
      rethrow;
    }
  }
vincenzopalazzo commented 1 year ago

I did not check the code but do you try to use the correct ws protocol? Check the ws link dart docs about the protocol name

rrifafauzikomara commented 1 year ago

I copy the ws link from console log (IDE) when I running that code, then paste it to Postman, it's success connected. Then I send the Message by clicked Send button, it's success get the data as well.

Seems the problem is I need to send the Message as well like in the Postman, but not sure how to do it on ur pacakge, any suggestion?

For the ws protocol, yea i use the correct one, which is 'Sec-WebSocket-Protocol': GraphQLProtocol.graphqlWs, same like in the Postman, which is Sec-WebSocket-Protocol:graphql-ws

vincenzopalazzo commented 1 year ago

I am not able to see what is the ws server protocol, can you post it?

In addition, I am having a hard time with the postman image because I do not use it, so with postman you are able to get the result in the correct way? If yes we need a reproducer for this

rrifafauzikomara commented 1 year ago

how i can get/see the ws server protocol? I do not understand what u mean, basically, I just follow ur example from here: https://github.com/zino-hofmann/graphql-flutter/tree/main/examples/starwars

but adding some extra code like header, token, etc. I already share all my code above.

Yea from the postman I can get the data because I can send the Message by clicked the Send button, but how to do it in ur package?

vincenzopalazzo commented 1 year ago

The demo is not working, so you should follow this https://github.com/zino-hofmann/graphql-flutter/pull/1204 I should merge it, too also, if needs some work.

In addition, you should know what protocol the server speaks, I can not know. Try to change the protocol to the last one and see what happens

Yea from the postman I can get the data because I can send the Message by clicked the Send button, but how to do it in ur package?

Sorry I do not know what Send a message means!

rrifafauzikomara commented 1 year ago

in my example code above, I already share the ws protocol, which is graphql-ws.

Screenshot 2023-07-20 at 7 07 00 PM

Sorry I do not know what Send a message means!

How can u understand it if u never see my Postman image, lol.

Here's the image, pls see the red line!

aaa
vincenzopalazzo commented 1 year ago

No please no screenshot, try to change the protocol with the second one and see the server has the new protocol enabled

rrifafauzikomara commented 1 year ago

So you hope to understand other developer's problems that use ur package without wanting to see the problem? lol

Change protocol to what? in postman, we use that on the header. In my example code above, I use it as well. And for the ws link we use the same for both flutter code and also postman.

ws link: wss://BASEURL/graphql/realtime?header=<EncodedHeader>&payload=e30=
ws protocol: graphql-ws
vincenzopalazzo commented 1 year ago

So you hope to understand other developer's problems that use ur package without wanting to see the problem? lol

You are asking to me to spend time on your problem without know what ws protocol your server speak, by putting a couple of picture together and without know that the library support another protocol (and this mean that you never see the docs) https://github.com/zino-hofmann/graphql-flutter/blob/71d7502dec9336fa72d60d44780294ba01fde7ae/packages/graphql/lib/src/links/websocket_link/websocket_client.dart#L175

every time I regret spending some time on an issue without reproducer. So if you want me to see the problem you need to reproduce an issue with an example that I can run.

P.S: It is missing also the library version, so this is not a bug report, but a consultancy request it you want it you should consider using the email.

Cheers

rrifafauzikomara commented 1 year ago

Sorry about that, prev I don't understand what ws meant, but if u see my code and also I already share it with u right, the ws protocol u mean was graphql-ws or GraphQLProtocol.graphqlWs.

by putting a couple of picture together

Like I said before right, the subscription working on Postman, then I want to explain it to u so I put a couple of pictures together with some description about that pictures, but u say Sorry I do not know what Send a message means! :(. Then how I can explaint the Postman and my case to u? coz the Message and Send what I mean before is only available on the screenshot :(.

But yeah nvm, thanks for spending ur time to take a look on my issue. The problem before it's not because ws protocol i think, but because I don't add connectFn in the WebSocketLink. After I add this, now I can get the data from subs. But seems got another error from that.

flutter: Rifa 3: {"type":"connection_ack","payload":{"connectionTimeoutMs":300000}}
flutter: Rifa 3: {"type":"ka"}
flutter: Rifa 3: {"type":"error","payload":{"errors":[{"errorType":"UnsupportedOperation","message":"unknown not supported through the realtime channel"}]}}

Is this error coming from my server or from the package (need more setup)?

vincenzopalazzo commented 1 year ago

Just checking the error on stack overflow https://stackoverflow.com/questions/74293606/websocket-with-appsync-error-unsupportedoperation-unknown-not-supported-throug

It is the server, please see the PR that are open to check how to do authentication

vincenzopalazzo commented 1 year ago

I am closing this because you solved the problem by looking at your code. feel free to if you had a reproducer