I am creating a subscription with GraphQL, and I need to consume that subscription with Flutter, I can connect to the ws and get the basic data, but seems my code still not working coz got the error, here's the error.
{"type":"error","payload":{"errors":[{"errorType":"UnsupportedOperation","message":"unknown not supported through the realtime channel"}]}}
I already googling the error, seems it's related auth error. But when I compare my flutter code with Postman setup, seems all already same setup.
Here's the Postman.
Here's the Message data from Postman.
{
"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 my flutter code.
class AppSyncRequest extends RequestSerializer {
final String uuid;
final Map webSocketHeader;
AppSyncRequest({
required this.uuid,
required this.webSocketHeader,
});
@override
Map<String, dynamic> serializeRequest(Request request) {
String query = printNode(request.operation.document);
String data = jsonEncode({"query": query});
final message = <String, dynamic>{
"id": uuid,
"payload": {
"data": data,
"extensions": {
"authorization": webSocketHeader,
}
},
"type": "start"
};
String str = jsonEncode(message);
EpLogger.d('WebSocket Message Request: $str');
return message;
}
}
void _onErrorSubscription(Object error, StackTrace stackTrace) {
EpLogger.d('Listen Subscription Error: $error & $stackTrace');
}
void _onDoneSubscription() {
EpLogger.d('Listen Subscription Done');
}
void _onErrorWebSocket(Object error, StackTrace stackTrace) {
EpLogger.d('Listen WebSocket Error: $error & $stackTrace');
}
void _onDoneWebSocket() {
EpLogger.d('Listen WebSocket Done');
}
@override
Future<Map<String, dynamic>> subscription() async {
try {
var currentToken = "";
final graphqlEndpoint = 'https://BASEURL/graphql/realtime';
final subscriptionEndpoint =
'wss://BASEURL/graphql/realtime?header=<EncodedHeader>&payload=e30=';
const uuid = Uuid();
final webSocketHeader = <String, dynamic>{
"Authorization": currentToken,
"host": ApiConstant.apiHost,
};
final HttpLink httpLink = HttpLink(
graphqlEndpoint,
defaultHeaders: {
"Authorization": "Bearer $currentToken",
'Sec-WebSocket-Protocol': "graphql-ws",
},
);
final WebSocketLink webSocketLink = WebSocketLink(
subscriptionEndpoint,
config: SocketClientConfig(
autoReconnect: true,
serializer: AppSyncRequest(
uuid: uuid.v1(),
webSocketHeader: webSocketHeader,
),
inactivityTimeout: const Duration(seconds: 60),
connectFn: (url, protocol) {
WebSocketChannel channel = WebSocketChannel.connect(
url,
protocols: protocol,
);
channel = channel.forGraphQL();
channel.stream.listen(
(data) {
EpLogger.d('Listen WebSocket Active: $data');
},
onDone: _onDoneWebSocket,
onError: _onErrorWebSocket,
cancelOnError: true,
);
return channel;
},
),
subProtocol: ApiConstant.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 updateCheckout {
onUpdateCheckout(userId: $userId) {
userId
createdAt
amount
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) {
data = event.data ?? {};
EpLogger.d('Listen Subscription: $event');
EpLogger.d('Listen Subscription Data: $data');
},
onDone: _onDoneSubscription,
onError: _onErrorSubscription,
cancelOnError: true,
);
return Future.value(data);
} on Exception {
rethrow;
}
}

