diff --git a/examples/Cargo.toml b/examples/Cargo.toml index e1e89db..ade281a 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -9,11 +9,12 @@ publish = false [dependencies] async-std = { version = "1.9", features = ["attributes"] } +async-tungstenite = { version = "0.24", features = ["async-std-runtime", "tokio-runtime"] } async_executors = { version = "0.7", features = ["async_std"] } cynic = { version = "3" } futures = { version = "0.3"} - -async-tungstenite = { version = "0.24", features = ["async-std-runtime", "tokio-runtime"] } +graphql_client = { version = "0.13" } +serde = "1" tokio = { version = "1.15", features = ["rt-multi-thread", "macros"] } [dependencies.graphql-ws-client] diff --git a/examples/examples/mulitiple-subscriptions.rs b/examples/examples/cynic-mulitiple-subscriptions.rs similarity index 97% rename from examples/examples/mulitiple-subscriptions.rs rename to examples/examples/cynic-mulitiple-subscriptions.rs index 315c6c4..7f288d5 100644 --- a/examples/examples/mulitiple-subscriptions.rs +++ b/examples/examples/cynic-mulitiple-subscriptions.rs @@ -1,5 +1,5 @@ //! An example of running a multiple subscriptions on a single connection -//! using `graphql-ws-client`, `async-tungstenite` & the `async_std` +//! using `cynic`, `async-tungstenite` & the `async_std` //! executor. //! //! Talks to the the tide subscription example in `async-graphql` diff --git a/examples/examples/single-subscription.rs b/examples/examples/cynic-single-subscription.rs similarity index 97% rename from examples/examples/single-subscription.rs rename to examples/examples/cynic-single-subscription.rs index 1a3388b..a0ba83c 100644 --- a/examples/examples/single-subscription.rs +++ b/examples/examples/cynic-single-subscription.rs @@ -1,5 +1,5 @@ //! An example of creating a connection and running a single subscription on it -//! using `graphql-ws-client` and `async-tungstenite` +//! using `cynic` and `async-tungstenite` //! //! Talks to the the tide subscription example in `async-graphql` diff --git a/examples/examples/graphql-client-single-subscription.rs b/examples/examples/graphql-client-single-subscription.rs new file mode 100644 index 0000000..b83ac01 --- /dev/null +++ b/examples/examples/graphql-client-single-subscription.rs @@ -0,0 +1,45 @@ +//! An example of creating a connection and running a single subscription on it +//! using `graphql-client` and `async-tungstenite` +//! +//! Talks to the the tide subscription example in `async-graphql` + +use futures::StreamExt; +use graphql_client::GraphQLQuery; +use graphql_ws_client::graphql::StreamingOperation; + +#[derive(GraphQLQuery)] +#[graphql( + query_path = "examples/graphql-client-subscription.graphql", + schema_path = "../schemas/books.graphql", + response_derives = "Debug" +)] +struct BooksChanged; + +#[async_std::main] +async fn main() { + use async_tungstenite::tungstenite::{client::IntoClientRequest, http::HeaderValue}; + use graphql_ws_client::Client; + + let mut request = "ws://localhost:8000/graphql".into_client_request().unwrap(); + request.headers_mut().insert( + "Sec-WebSocket-Protocol", + HeaderValue::from_str("graphql-transport-ws").unwrap(), + ); + + let (connection, _) = async_tungstenite::async_std::connect_async(request) + .await + .unwrap(); + + println!("Connected"); + + let mut subscription = Client::build(connection) + .subscribe(StreamingOperation::::new( + books_changed::Variables, + )) + .await + .unwrap(); + + while let Some(item) = subscription.next().await { + println!("{:?}", item); + } +} diff --git a/examples/examples/graphql-client-subscription.graphql b/examples/examples/graphql-client-subscription.graphql new file mode 100644 index 0000000..c0e2af4 --- /dev/null +++ b/examples/examples/graphql-client-subscription.graphql @@ -0,0 +1,10 @@ +subscription BooksChanged { + books(mutationType: CREATED) { + id + book { + id + name + author + } + } +} diff --git a/tests/graphql-client-subscription.graphql b/tests/graphql-client-subscription.graphql new file mode 100644 index 0000000..c0e2af4 --- /dev/null +++ b/tests/graphql-client-subscription.graphql @@ -0,0 +1,10 @@ +subscription BooksChanged { + books(mutationType: CREATED) { + id + book { + id + name + author + } + } +} diff --git a/tests/graphql-client-tests.rs b/tests/graphql-client-tests.rs new file mode 100644 index 0000000..5de44d2 --- /dev/null +++ b/tests/graphql-client-tests.rs @@ -0,0 +1,151 @@ +use std::{future::IntoFuture, time::Duration}; + +use assert_matches::assert_matches; +use graphql_client::GraphQLQuery; +use graphql_ws_client::graphql::StreamingOperation; +use subscription_server::SubscriptionServer; +use tokio::time::sleep; + +mod subscription_server; + +#[derive(GraphQLQuery)] +#[graphql( + query_path = "tests/graphql-client-subscription.graphql", + schema_path = "schemas/books.graphql", + response_derives = "Debug" +)] +struct BooksChanged; + +#[tokio::test] +async fn main_test() { + use async_tungstenite::tungstenite::{client::IntoClientRequest, http::HeaderValue}; + use futures::StreamExt; + + let server = SubscriptionServer::start().await; + + sleep(Duration::from_millis(20)).await; + + let mut request = server.websocket_url().into_client_request().unwrap(); + request.headers_mut().insert( + "Sec-WebSocket-Protocol", + HeaderValue::from_str("graphql-transport-ws").unwrap(), + ); + + let (connection, _) = async_tungstenite::tokio::connect_async(request) + .await + .unwrap(); + + println!("Connected"); + + let (mut client, actor) = graphql_ws_client::Client::build(connection).await.unwrap(); + + tokio::spawn(actor.into_future()); + + let stream = client.subscribe(build_query()).await.unwrap(); + + sleep(Duration::from_millis(100)).await; + + let updates = [ + subscription_server::BookChanged { + id: "123".into(), + book: None, + }, + subscription_server::BookChanged { + id: "456".into(), + book: None, + }, + subscription_server::BookChanged { + id: "789".into(), + book: None, + }, + ]; + + futures::join!( + async { + for update in &updates { + server.send(update.to_owned()).unwrap(); + } + }, + async { + let received_updates = stream.take(updates.len()).collect::>().await; + + for (expected, update) in updates.iter().zip(received_updates) { + let update = update.unwrap(); + assert_matches!(update.errors, None); + let data = update.data.unwrap(); + assert_eq!(data.books.id, expected.id.0); + } + } + ); +} + +#[tokio::test] +async fn oneshot_operation_test() { + use async_tungstenite::tungstenite::{client::IntoClientRequest, http::HeaderValue}; + use futures::StreamExt; + + let server = SubscriptionServer::start().await; + + sleep(Duration::from_millis(20)).await; + + let mut request = server.websocket_url().into_client_request().unwrap(); + request.headers_mut().insert( + "Sec-WebSocket-Protocol", + HeaderValue::from_str("graphql-transport-ws").unwrap(), + ); + + let (connection, _) = async_tungstenite::tokio::connect_async(request) + .await + .unwrap(); + + println!("Connected"); + + let stream = graphql_ws_client::Client::build(connection) + .subscribe(build_query()) + .await + .unwrap(); + + let updates = [ + subscription_server::BookChanged { + id: "123".into(), + book: None, + }, + subscription_server::BookChanged { + id: "456".into(), + book: None, + }, + subscription_server::BookChanged { + id: "789".into(), + book: None, + }, + ]; + + futures::join!( + async { + sleep(Duration::from_millis(10)).await; + for update in &updates { + server.send(update.to_owned()).unwrap(); + } + }, + async { + let received_updates = stream.take(updates.len()).collect::>().await; + + for (expected, update) in updates.iter().zip(received_updates) { + let update = update.unwrap(); + assert_matches!(update.errors, None); + let data = update.data.unwrap(); + assert_eq!(data.books.id, expected.id.0); + } + } + ); +} + +fn build_query() -> graphql_ws_client::graphql::StreamingOperation { + StreamingOperation::new(books_changed::Variables) +} + +impl PartialEq for books_changed::BooksChangedBooksBook { + fn eq(&self, other: &subscription_server::Book) -> bool { + self.id == other.id.0 && self.name == other.name && self.author == other.author + } +}