Subscription support#220
Subscription support#220martijnwalraven merged 25 commits intoapollographql:masterfrom knutaa:master
Conversation
|
@knutaa: Thank you for submitting a pull request! Before we can merge it, you'll need to sign the Meteor Contributor Agreement here: https://contribute.meteor.com/ |
| /// - queue: A dispatch queue on which the result handler will be called. Defaults to the main queue. | ||
| /// - resultHandler: An optional closure that is called when mutation results are available or when an error occurs. | ||
| /// - Returns: An object that can be used to cancel an in progress mutation. | ||
| public func subscribe<Query: GraphQLSubscription>(subscription: Query, queue: DispatchQueue = DispatchQueue.main, resultHandler: @escaping OperationResultHandler<Query>) -> GraphQLSubscriptionWatcher<Query> { |
There was a problem hiding this comment.
Query type parameter should be Subscription I think.
There was a problem hiding this comment.
Possible issue: We will try to send subscription request also through the existing HTTPNetworkTransport if the SplitNetworkTransport is not used
There was a problem hiding this comment.
Yeah, HTTPNetworkTransport should probably check for supported operation types and throw an error for subscriptions.
| @@ -0,0 +1,34 @@ | |||
| import Dispatch | |||
|
|
|||
| public final class GraphQLSubscriptionWatcher<Subscription: GraphQLSubscription>: Cancellable | |||
There was a problem hiding this comment.
What is the function of GraphQLSubscriptionWatcher? Couldn't subscribe be _subscribe and return the result of send directly?
There was a problem hiding this comment.
GraphQLSubscriptionWatcher started out based on the query watcher.
Right now the only value add compared just returning the Cancellable is the "unsubscribe" function that may be more intuitive than "cancel" for terminating an active subscription. Apart from that it can be removed
Updates to ApolloClient based on review comments made the Subscription watcher redundant
| private var subscribers = [String: (JSONObject?, Error?) -> Void]() | ||
| private var subscriptions : [String: String] = [:] | ||
|
|
||
| private let sendOperationIdentifiers: Bool |
There was a problem hiding this comment.
Some formatting inconsistencies above with spaces before/after colons. Perhaps it's time to include SwiftLint.
| } | ||
| } else { | ||
| print("WebSocketTransport::unprocessed event \(text)") | ||
| } |
There was a problem hiding this comment.
This function is quite big. It seems to do two things: Parse a message object from a string, and act depending on what message it is.
I think it would make sense to have the parsing logic in OperationMessage and make id, error and payload associated values to the different types to avoid if let logic here (what happens if you get a COMPLETE message but id is nil?).
It would then make the flow much easier to test and understand.
There was a problem hiding this comment.
Something like this?
private func processMessage(socket: WebSocketClient, text: String) {
OperationMessage.parseMessage(text) { (type,id,payload,error) in
if let type = type {
switch(type) {
case OperationMessage.Types.DATA.rawValue,
OperationMessage.Types.ERROR.rawValue:
if let id = id {
if let responseHandler = subscribers[id] {
responseHandler(payload,error)
}
} else {
print("WebSocketTransport::unprocessed message \(text)")
}
case OperationMessage.Types.COMPLETE.rawValue:
if let id = id {
// remove the callback if NOT a subscription
if subscriptions[id] == nil {
subscribers.removeValue(forKey: id)
}
} else {
print("WebSocketTransport::unprocessed message \(text)")
}
case OperationMessage.Types.CONNECTION_ACK.rawValue,
OperationMessage.Types.CONNECTION_KEEP_ALIVE.rawValue:
acked = (type == OperationMessage.Types.CONNECTION_ACK.rawValue)
writeQueue()
default:
print("WebSocketTransport::unprocessed message \(text)")
}
} else {
print("WebSocketTransport::unprocessed message \(text)")
}
}
}
Moved files to - Sources/ApolloWebsocket - Tests/ApolloWebsocket Added separate framework for websocket / subscription support
|
Has anyone tested an instance of I've managed to get it working on the web side of our project using apollo-link but no luck using this PR on IOS. |
|
Would it be possible to share some more details? Believe others (and myself) has this working based on the merged apollo-ios master. |
|
@knutaa - thanks for the response! Here's how i'm instantiating my ApolloClient and setting up my subscribe call. Performing the mutation should technically trigger the subscription call (which i've tested on the web and it works) But no luck here. Am I missing anything? |
|
Have the same problem with no callbacks. |
|
Make sure your |
|
It is not the local variable. In my case it is a global one for now 🙃 |
|
@crabman448 @mikengyn Could you check with the test set-up used, i.e. the graphql server https://github.com/apollographql/starwars-server ? Also make sure correct URI scheme (ws or wss). |
|
@mikengyn @crabman448 were you able to fix your issue? I'm in the same situation. |
|
@crabman448 @mikengyn @Kiesco08 Please note that the configuration used for the HTTP network transport is not shared with the WebSocket transport! In this code the webSocket transport will not use any header or payload parameters. You would need to use either or both of the params or connectingParams from the WebSocket transport: public init(url: URL, sendOperationIdentifiers: Bool = false, params: [String:String]? = nil, connectingParams: [String:String]? = [:]) |
|
hey guys how did you install apollo websocket ? |
|
Is there documentation yet for this feature? Couldn't find any on the Apollo site. |
|
What will be the underlying server to test this. Is this going to work with default websocket server on Apollo 2.0 ? |
|
@wtrocki. Suggest you have a look at the testcases (and https://github.com/apollographql/starwars-server). Regarding Apollo 2.0, I believe you should be able to do something like this (see below). The subscription path is by default the same as for httpx. const express = require('express'); const server = new ApolloServer({ const app = express(); const httpServer = http.createServer(app); httpServer.listen(PORT, () => { |
|
@knutaa Thank you so much. I will check htat |
|
Hi, you could also try out a simple test server running on https://paneon.no:4004/graphql. This is basically the same as I shared on github but running …
Br, Knut
From: Wojciech Trocki [mailto:notifications@github.com]
Sent: 30. august 2018 10:49
To: apollographql/apollo-ios
Cc: knutaa; Mention
Subject: Re: [apollographql/apollo-ios] Subscription support (#220)
@knutaa<https://github.com/knutaa> Thank you so much. I will check htat
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<#220 (comment)>, or mute the thread<https://github.com/notifications/unsubscribe-auth/AVkh1w-cGDoorEYhul-AbjsdAybwuGoTks5uV6cNgaJpZM4ScuBH>.
|
|
I'm also not able to recieve callbacks.... I'm assigning my auth headers in the urlRequest thats used to configure the Websocket: When trying the subscription, I get no error and it seems to be connected, but no call backs. Its Friday night so I'm going to throw in the towel for now, but i'll also try and spin up the server and give that example a go on Monday to make sure its not something on the server end. |
|
bumping here...still not able to see subscription callbacks getting called. It would seem as though my websocket is certainly connected and I can even log the keep alive messages coming through, but I never see any actual data show up. Would this point to something wrong with my server? is there a more solid way to test this with a working server? Thanks |
|
I finally got this figured out... In order to get my server to properly authenticate, I had to pass the headers in the connection payload under a "headers" key. I was passing them in the URLRequest object but that wasn't what my server was checking for. I use Hasura as my backend btw so check and see how your authentication needs to be passed in. |
Adding subscription support
NOTE: Earlier GraphQLSubscriptionWatcher removed as not really necessary (as of now)
It is possible to instantiate the WebSocketTransport without initial connect, i.e. the request can be added in a later connect call. This allow for relevant information to be collected using the HTTP transport (query or mutation) before being added to the web socket request.
The WebSocketTransport can be used for all operations, queries and mutations as well as subscriptions.