From 6a8df2385a11ebe8c1746dc1b824a836af99b532 Mon Sep 17 00:00:00 2001 From: Jordao Rosario Date: Wed, 8 Apr 2020 22:16:22 -0300 Subject: [PATCH 01/13] Initial implementation of Subscription Docs --- Cargo.toml | 1 + docs/book/content/SUMMARY.md | 1 + docs/book/content/advanced/index.md | 1 + docs/book/content/advanced/subscriptions.md | 165 ++++++++++++++++++ .../content/schema/schemas_and_mutations.md | 13 +- docs/book/tests/Cargo.toml | 5 +- examples/basic_subscriptions/.gitignore | 1 + examples/basic_subscriptions/Cargo.toml | 16 ++ examples/basic_subscriptions/src/main.rs | 65 +++++++ 9 files changed, 261 insertions(+), 7 deletions(-) create mode 100644 docs/book/content/advanced/subscriptions.md create mode 100644 examples/basic_subscriptions/.gitignore create mode 100644 examples/basic_subscriptions/Cargo.toml create mode 100644 examples/basic_subscriptions/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 6f10451ae..61a474efd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ exclude = [ "docs/book/tests", "examples/warp_async", "examples/warp_subscriptions", + "examples/basic_subscriptions", # TODO enable async tests "juniper_rocket_async", ] diff --git a/docs/book/content/SUMMARY.md b/docs/book/content/SUMMARY.md index 947d9e035..49c542f52 100644 --- a/docs/book/content/SUMMARY.md +++ b/docs/book/content/SUMMARY.md @@ -32,6 +32,7 @@ - [Objects and generics](advanced/objects_and_generics.md) - [Multiple operations per request](advanced/multiple_ops_per_request.md) - [Dataloaders](advanced/dataloaders.md) + - [Subscriptions](advanced/subscriptions.md) # - [Context switching] diff --git a/docs/book/content/advanced/index.md b/docs/book/content/advanced/index.md index a9704030a..e46978b38 100644 --- a/docs/book/content/advanced/index.md +++ b/docs/book/content/advanced/index.md @@ -7,3 +7,4 @@ The chapters below cover some more advanced scenarios. - [Objects and generics](objects_and_generics.md) - [Multiple operations per request](multiple_ops_per_request.md) - [Dataloaders](dataloaders.md) +- [Subscriptions](subscriptions.md) \ No newline at end of file diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md new file mode 100644 index 000000000..cd52d8ba4 --- /dev/null +++ b/docs/book/content/advanced/subscriptions.md @@ -0,0 +1,165 @@ +# Subscriptions +### How to achieve realtime data with GraphQL subscriptions + +GraphQL Subscriptions are a way to push data from the server to the clients that choose to listen to real time messages +from the server. Subscriptions are similar to queries in that they specify a set of fields to be delivered to the client, +but instead of immediately returning a single answer, a result is sent every time a particular event happens on the +server. + +For the usage of subscriptions you need to add [juniper_subscriptions][juniper_subscriptions] into your cargo.toml: +```toml +[dependencies] +juniper = { git = "https://github.com/graphql-rust/juniper", branch = "master" } +juniper_subscriptions = { git = "https://github.com/graphql-rust/juniper", branch = "master" } +``` + +### Schema Definition + +The Subscription is just a GraphQL object, similar to the Query root and Mutations object that you defined for the +operations in your [Schema][Schema], the difference is that all the operations defined there should be async and the return of it +should be a [Stream][Stream]. + +This example shows a subscription operation that returns two events, the strings `Hello` and `World!` +sequentially: + +```rust +# use juniper::http::GraphQLRequest; +# use juniper::{DefaultScalarValue, FieldError, SubscriptionCoordinator}; +# use juniper_subscriptions::Coordinator; +# use futures::{Stream, StreamExt}; +# use std::pin::Pin; +# #[derive(Clone)] +# pub struct Database; +# impl juniper::Context for Database {} +# impl Database { +# fn new() -> Self { +# Self {} +# } +#} +# pub struct Query; +# #[juniper::graphql_object(Context = Database)] +# impl Query { +# fn hello_world() -> &str { +# "Hello World!" +# } +# } +pub struct Subscription; + +type StringStream = Pin> + Send>>; + +#[juniper::graphql_subscription(Context = Database)] +impl Subscription { + async fn hello_world() -> StringStream { + let stream = tokio::stream::iter(vec![ + Ok(String::from("Hello")), + Ok(String::from("World!")) + ]); + Box::pin(stream) + } +} +# fn main () {} +``` + + + +### Coordinator + +The [Coordinator][Coordinator] struct is a simple implementation of the trait [SubscriptionCoordinator][SubscriptionCoordinator] +that is responsible for handling the execution of subscription operation into your schema. The execution of the `subscribe` +operation returns a [Future][Future] with a Item value of a Result<[Connection][Connection], [GraphQLError][GraphQLError]>, +where the connection is the Stream of values returned by the operation and the GraphQLError is the error that occurred in the +resolution of this connection, which means that the subscription failed. + +```rust +# use juniper::http::GraphQLRequest; +# use juniper::{DefaultScalarValue, EmptyMutation, FieldError, RootNode, SubscriptionCoordinator}; +# use juniper_subscriptions::Coordinator; +# use futures::{Stream, StreamExt}; +# use std::pin::Pin; +# use tokio::runtime::Runtime; +# use tokio::task; +# +# #[derive(Clone)] +# pub struct Database; +# +# impl juniper::Context for Database {} +# +# impl Database { +# fn new() -> Self { +# Self {} +# } +# } +# +# pub struct Query; +# +# #[juniper::graphql_object(Context = Database)] +# impl Query { +# fn hello_world() -> &str { +# "Hello World!" +# } +# } +# +# pub struct Subscription; +# +# type StringStream = Pin> + Send>>; +# +# #[juniper::graphql_subscription(Context = Database)] +# impl Subscription { +# async fn hello_world() -> StringStream { +# let stream = +# tokio::stream::iter(vec![Ok(String::from("Hello")), Ok(String::from("World!"))]); +# Box::pin(stream) +# } +# } +type Schema = RootNode<'static, Query, EmptyMutation, Subscription>; + +fn schema() -> Schema { + Schema::new(Query {}, EmptyMutation::new(), Subscription {}) +} + +async fn run_subscription() { + let schema = schema(); + let coordinator = Coordinator::new(schema); + let req: GraphQLRequest = serde_json::from_str( + r#" + { + "query": "subscription { helloWorld }" + } + "#, + ) + .unwrap(); + let ctx = Database::new(); + let mut conn = coordinator.subscribe(&req, &ctx).await.unwrap(); + while let Some(result) = conn.next().await { + println!("{}", serde_json::to_string(&result).unwrap()); + } +} + +# fn main() { } +``` + +### Web Integration and Examples + +In the moment there is a example of subscriptions with [warp][warp], but it still in a Alpha state +since the GraphQL over [WS][WS] is not fully supported yet in this implementation and in the moment is +the only integration that supports it. + +- [Warp Subscription Example](https://github.com/graphql-rust/juniper/tree/master/examples/warp_subscriptions) +- [Small Example](https://github.com/graphql-rust/juniper/tree/master/examples/basic_subscriptions) + + + + +[juniper_subscriptions]: https://github.com/graphql-rust/juniper/tree/master/juniper_subscriptions +[Stream]: https://docs.rs/futures/0.3.4/futures/stream/trait.Stream.html + +[Coordinator]: https://docs.rs/juniper_subscriptions/0.15.0/struct.Coordinator.html +[SubscriptionCoordinator]: https://docs.rs/juniper_subscriptions/0.15.0/trait.SubscriptionCoordinator.html +[Connection]: https://docs.rs/juniper_subscriptions/0.15.0/struct.Connection.html +[SubscriptionConnection]: https://docs.rs/juniper_subscriptions/0.15.0/trait.SubscriptionConnection.html + +[Future]: https://docs.rs/futures/0.3.4/futures/future/trait.Future.html +[warp]: https://github.com/graphql-rust/juniper/tree/master/juniper_warp +[WS]: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md +[GraphQLError]: https://docs.rs/juniper/0.14.2/juniper/enum.GraphQLError.html +[Schema]: ../schema/schemas_and_mutations.md \ No newline at end of file diff --git a/docs/book/content/schema/schemas_and_mutations.md b/docs/book/content/schema/schemas_and_mutations.md index b3ba9a747..e2634e9f7 100644 --- a/docs/book/content/schema/schemas_and_mutations.md +++ b/docs/book/content/schema/schemas_and_mutations.md @@ -1,12 +1,13 @@ # Schemas -A schema consists of two types: a query object and a mutation object (Juniper -does not support subscriptions yet). These two define the root query fields -and mutations of the schema, respectively. +A schema consists of three types: a query object, a mutation object and a subscription object +( The usage of subscriptions is a little different from the mutation and query objects, so there is a +specific [section][section] to handle this topic). +These three define the root query fields, mutations and subscriptions of the schema, respectively. Both query and mutation objects are regular GraphQL objects, defined like any -other object in Juniper. The mutation object, however, is optional since schemas -can be read-only. +other object in Juniper. The mutation and subscription object, however, is optional since schemas +can be read-only and without subscriptions as well. In Juniper, the `RootNode` type represents a schema. You usually don't have to create this object yourself: see the framework integrations for [Iron](../servers/iron.md) @@ -58,3 +59,5 @@ impl Mutations { # fn main() { } ``` + +[section]: ../advanced/subscriptions.md \ No newline at end of file diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml index 04f07dafa..d7c4d0bc9 100644 --- a/docs/book/tests/Cargo.toml +++ b/docs/book/tests/Cargo.toml @@ -8,8 +8,9 @@ build = "build.rs" [dependencies] juniper = { path = "../../../juniper" } juniper_iron = { path = "../../../juniper_iron" } -futures = "0.3.1" - +juniper_subscriptions = { path = "../../../juniper_subscriptions" } +futures = "0.3" +tokio = { version = "0.2", features = ["rt-core", "blocking", "stream", "rt-util"] } iron = "0.5.0" mount = "0.4.0" diff --git a/examples/basic_subscriptions/.gitignore b/examples/basic_subscriptions/.gitignore new file mode 100644 index 000000000..eb5a316cb --- /dev/null +++ b/examples/basic_subscriptions/.gitignore @@ -0,0 +1 @@ +target diff --git a/examples/basic_subscriptions/Cargo.toml b/examples/basic_subscriptions/Cargo.toml new file mode 100644 index 000000000..e6ac93a29 --- /dev/null +++ b/examples/basic_subscriptions/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "basic_subscriptions" +version = "0.1.0" +edition = "2018" +authors = ["Jordao Rosario "] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +futures = "0.3" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tokio = { version = "0.2", features = ["rt-core", "macros", "stream"] } + +juniper = { git = "https://github.com/graphql-rust/juniper" } +juniper_subscriptions = { git = "https://github.com/graphql-rust/juniper" } diff --git a/examples/basic_subscriptions/src/main.rs b/examples/basic_subscriptions/src/main.rs new file mode 100644 index 000000000..3f3cbe0d3 --- /dev/null +++ b/examples/basic_subscriptions/src/main.rs @@ -0,0 +1,65 @@ +#![deny(warnings)] + +use futures::{Stream, StreamExt}; +use juniper::http::GraphQLRequest; +use juniper::{DefaultScalarValue, EmptyMutation, FieldError, RootNode, SubscriptionCoordinator}; +use juniper_subscriptions::Coordinator; +use std::pin::Pin; + +#[derive(Clone)] +pub struct Database; + +impl juniper::Context for Database {} + +impl Database { + fn new() -> Self { + Self {} + } +} + +pub struct Query; + +#[juniper::graphql_object(Context = Database)] +impl Query { + fn hello_world() -> &str { + "Hello World!" + } +} + +pub struct Subscription; + +type StringStream = Pin> + Send>>; + +#[juniper::graphql_subscription(Context = Database)] +impl Subscription { + async fn hello_world() -> StringStream { + let stream = + tokio::stream::iter(vec![Ok(String::from("Hello")), Ok(String::from("World!"))]); + Box::pin(stream) + } +} + +type Schema = RootNode<'static, Query, EmptyMutation, Subscription>; + +fn schema() -> Schema { + Schema::new(Query {}, EmptyMutation::new(), Subscription {}) +} + +#[tokio::main] +async fn main() { + let schema = schema(); + let coordinator = Coordinator::new(schema); + let req: GraphQLRequest = serde_json::from_str( + r#" + { + "query": "subscription { helloWorld }" + } + "#, + ) + .unwrap(); + let ctx = Database::new(); + let mut conn = coordinator.subscribe(&req, &ctx).await.unwrap(); + while let Some(result) = conn.next().await { + println!("{}", serde_json::to_string(&result).unwrap()); + } +} From 6d3bb5b8178cbe6de6c72329e5a048cfbc820e9c Mon Sep 17 00:00:00 2001 From: Jordao Rosario Date: Thu, 9 Apr 2020 07:39:07 -0300 Subject: [PATCH 02/13] Fix on doc test --- docs/book/content/advanced/subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index cd52d8ba4..696ae45cb 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -35,7 +35,7 @@ sequentially: # fn new() -> Self { # Self {} # } -#} +# } # pub struct Query; # #[juniper::graphql_object(Context = Database)] # impl Query { From d34ceec34e678e485183f8ec8bd602302ffdc00c Mon Sep 17 00:00:00 2001 From: Jordao Rosario Date: Sun, 12 Apr 2020 18:51:04 -0300 Subject: [PATCH 03/13] Improvements in subscription docs --- docs/book/content/advanced/subscriptions.md | 15 ++++++++++++++- docs/book/content/schema/schemas_and_mutations.md | 7 +++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index 696ae45cb..9e1cfab9e 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -6,7 +6,11 @@ from the server. Subscriptions are similar to queries in that they specify a set but instead of immediately returning a single answer, a result is sent every time a particular event happens on the server. -For the usage of subscriptions you need to add [juniper_subscriptions][juniper_subscriptions] into your cargo.toml: +In order to execute subscriptions you need a coordinator (that spawns connections) +and a GraphQL object that can be resolved into a stream, elements of which will then +be returned to the end user. [juniper_subscriptions][juniper_subscriptions] crate +provides a default connection implementation, and you'll need to add it into +your cargo.toml: ```toml [dependencies] juniper = { git = "https://github.com/graphql-rust/juniper", branch = "master" } @@ -64,6 +68,15 @@ impl Subscription { ### Coordinator +Subscriptions require a bit more resources than regular queries, since they can provide a great vector +for DOS attacks and can bring down a server easily if not handled right. [SubscriptionCoordinator][SubscriptionCoordinator] trait provides the coordination logic. +It contains the schema and can keep track of opened connections, handle subscription +start and maintains a global subscription id. Once connection is established, subscription +coordinator spawns a [SubscriptionConnection][SubscriptionConnection], which handles a +single connection, provides resolver logic for a client stream and can provide re-connection +and shutdown logic. + + The [Coordinator][Coordinator] struct is a simple implementation of the trait [SubscriptionCoordinator][SubscriptionCoordinator] that is responsible for handling the execution of subscription operation into your schema. The execution of the `subscribe` operation returns a [Future][Future] with a Item value of a Result<[Connection][Connection], [GraphQLError][GraphQLError]>, diff --git a/docs/book/content/schema/schemas_and_mutations.md b/docs/book/content/schema/schemas_and_mutations.md index e2634e9f7..5ef18e61a 100644 --- a/docs/book/content/schema/schemas_and_mutations.md +++ b/docs/book/content/schema/schemas_and_mutations.md @@ -7,7 +7,7 @@ These three define the root query fields, mutations and subscriptions of the sch Both query and mutation objects are regular GraphQL objects, defined like any other object in Juniper. The mutation and subscription object, however, is optional since schemas -can be read-only and without subscriptions as well. +can be read-only and without subscriptions as well. If mutations/subscriptions functionality is not needed, consider using [EmptyMutation][EmptyMutation]/[EmptySubscription][EmptySubscription]. In Juniper, the `RootNode` type represents a schema. You usually don't have to create this object yourself: see the framework integrations for [Iron](../servers/iron.md) @@ -60,4 +60,7 @@ impl Mutations { # fn main() { } ``` -[section]: ../advanced/subscriptions.md \ No newline at end of file +[section]: ../advanced/subscriptions.md +[EmptyMutation]: https://docs.rs/juniper/0.14.2/juniper/struct.EmptyMutation.html + +[EmptySubscription]: https://docs.rs/juniper/0.14.2/juniper/struct.EmptySubscription.html \ No newline at end of file From 73c90c5041e358ec09b69d338207310679072318 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Thu, 16 Apr 2020 19:09:59 -1000 Subject: [PATCH 04/13] Update docs/book/content/advanced/subscriptions.md --- docs/book/content/advanced/subscriptions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index 9e1cfab9e..eef1466b1 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -1,7 +1,7 @@ # Subscriptions ### How to achieve realtime data with GraphQL subscriptions -GraphQL Subscriptions are a way to push data from the server to the clients that choose to listen to real time messages +GraphQL subscriptions are a way to push data from the server to clients requesting real-time messages from the server. Subscriptions are similar to queries in that they specify a set of fields to be delivered to the client, but instead of immediately returning a single answer, a result is sent every time a particular event happens on the server. @@ -175,4 +175,4 @@ the only integration that supports it. [warp]: https://github.com/graphql-rust/juniper/tree/master/juniper_warp [WS]: https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md [GraphQLError]: https://docs.rs/juniper/0.14.2/juniper/enum.GraphQLError.html -[Schema]: ../schema/schemas_and_mutations.md \ No newline at end of file +[Schema]: ../schema/schemas_and_mutations.md From 02600f6388cc10b5063a1b3d41d368370e8203ff Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Thu, 16 Apr 2020 19:10:09 -1000 Subject: [PATCH 05/13] Update docs/book/content/advanced/subscriptions.md --- docs/book/content/advanced/subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index eef1466b1..6afe27bba 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -7,7 +7,7 @@ but instead of immediately returning a single answer, a result is sent every tim server. In order to execute subscriptions you need a coordinator (that spawns connections) -and a GraphQL object that can be resolved into a stream, elements of which will then +and a GraphQL object that can be resolved into a stream--elements of which will then be returned to the end user. [juniper_subscriptions][juniper_subscriptions] crate provides a default connection implementation, and you'll need to add it into your cargo.toml: From fb796ce874cad454ff9b601a9c250207abd0e97d Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Thu, 16 Apr 2020 19:10:21 -1000 Subject: [PATCH 06/13] Update docs/book/content/advanced/subscriptions.md --- docs/book/content/advanced/subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index 6afe27bba..a0b5970d8 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -8,7 +8,7 @@ server. In order to execute subscriptions you need a coordinator (that spawns connections) and a GraphQL object that can be resolved into a stream--elements of which will then -be returned to the end user. [juniper_subscriptions][juniper_subscriptions] crate +be returned to the end user. The [juniper_subscriptions][juniper_subscriptions] crate provides a default connection implementation, and you'll need to add it into your cargo.toml: ```toml From 803a935ac30e7b5a7c08935bf98698a334253f18 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Thu, 16 Apr 2020 19:10:31 -1000 Subject: [PATCH 07/13] Update docs/book/content/advanced/subscriptions.md --- docs/book/content/advanced/subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index a0b5970d8..0b63ff77f 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -9,7 +9,7 @@ server. In order to execute subscriptions you need a coordinator (that spawns connections) and a GraphQL object that can be resolved into a stream--elements of which will then be returned to the end user. The [juniper_subscriptions][juniper_subscriptions] crate -provides a default connection implementation, and you'll need to add it into +provides a default connection implementation. Currently subscriptions are only supported on the `master` branch. Add the following to your `Cargo.toml`: your cargo.toml: ```toml [dependencies] From 0c3202c7f82294bffb82e99f66584ed087fc5ce5 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Thu, 16 Apr 2020 19:10:40 -1000 Subject: [PATCH 08/13] Update docs/book/content/advanced/subscriptions.md --- docs/book/content/advanced/subscriptions.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index 0b63ff77f..06e61dc63 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -10,7 +10,6 @@ In order to execute subscriptions you need a coordinator (that spawns connection and a GraphQL object that can be resolved into a stream--elements of which will then be returned to the end user. The [juniper_subscriptions][juniper_subscriptions] crate provides a default connection implementation. Currently subscriptions are only supported on the `master` branch. Add the following to your `Cargo.toml`: -your cargo.toml: ```toml [dependencies] juniper = { git = "https://github.com/graphql-rust/juniper", branch = "master" } From 0014b308e5614f6788ac5893b0ca339abb921c5d Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Thu, 16 Apr 2020 19:10:49 -1000 Subject: [PATCH 09/13] Update docs/book/content/advanced/subscriptions.md --- docs/book/content/advanced/subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index 06e61dc63..af2dacffc 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -152,7 +152,7 @@ async fn run_subscription() { ### Web Integration and Examples -In the moment there is a example of subscriptions with [warp][warp], but it still in a Alpha state +Currently there is an example of subscriptions with [warp][warp], but it still in an alpha state. since the GraphQL over [WS][WS] is not fully supported yet in this implementation and in the moment is the only integration that supports it. From 822ed3fd1743bb741ef9f09b553cb62ec5bbcd85 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Thu, 16 Apr 2020 19:10:55 -1000 Subject: [PATCH 10/13] Update docs/book/content/advanced/subscriptions.md --- docs/book/content/advanced/subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index af2dacffc..89a50761d 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -153,7 +153,7 @@ async fn run_subscription() { ### Web Integration and Examples Currently there is an example of subscriptions with [warp][warp], but it still in an alpha state. -since the GraphQL over [WS][WS] is not fully supported yet in this implementation and in the moment is +GraphQL over [WS][WS] is not fully supported yet and is non-standard. the only integration that supports it. - [Warp Subscription Example](https://github.com/graphql-rust/juniper/tree/master/examples/warp_subscriptions) From d060308ee64c3c83a36fbbd57788115818e5ee54 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Thu, 16 Apr 2020 19:11:02 -1000 Subject: [PATCH 11/13] Update docs/book/content/advanced/subscriptions.md --- docs/book/content/advanced/subscriptions.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index 89a50761d..d687c6667 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -154,7 +154,6 @@ async fn run_subscription() { Currently there is an example of subscriptions with [warp][warp], but it still in an alpha state. GraphQL over [WS][WS] is not fully supported yet and is non-standard. -the only integration that supports it. - [Warp Subscription Example](https://github.com/graphql-rust/juniper/tree/master/examples/warp_subscriptions) - [Small Example](https://github.com/graphql-rust/juniper/tree/master/examples/basic_subscriptions) From 9e07a30013cfa309b6f19f4334a0255c3523839e Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Thu, 16 Apr 2020 19:11:11 -1000 Subject: [PATCH 12/13] Update docs/book/content/schema/schemas_and_mutations.md --- docs/book/content/schema/schemas_and_mutations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/book/content/schema/schemas_and_mutations.md b/docs/book/content/schema/schemas_and_mutations.md index 5ef18e61a..acfdd8df7 100644 --- a/docs/book/content/schema/schemas_and_mutations.md +++ b/docs/book/content/schema/schemas_and_mutations.md @@ -1,6 +1,6 @@ # Schemas -A schema consists of three types: a query object, a mutation object and a subscription object +A schema consists of three types: a query object, a mutation object, and a subscription object. ( The usage of subscriptions is a little different from the mutation and query objects, so there is a specific [section][section] to handle this topic). These three define the root query fields, mutations and subscriptions of the schema, respectively. @@ -63,4 +63,4 @@ impl Mutations { [section]: ../advanced/subscriptions.md [EmptyMutation]: https://docs.rs/juniper/0.14.2/juniper/struct.EmptyMutation.html -[EmptySubscription]: https://docs.rs/juniper/0.14.2/juniper/struct.EmptySubscription.html \ No newline at end of file +[EmptySubscription]: https://docs.rs/juniper/0.14.2/juniper/struct.EmptySubscription.html From ae106ecc6e1ef78307ef7551d16db321f0e3e50f Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Thu, 16 Apr 2020 19:11:17 -1000 Subject: [PATCH 13/13] Update docs/book/content/schema/schemas_and_mutations.md --- docs/book/content/schema/schemas_and_mutations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/book/content/schema/schemas_and_mutations.md b/docs/book/content/schema/schemas_and_mutations.md index acfdd8df7..124b7a89e 100644 --- a/docs/book/content/schema/schemas_and_mutations.md +++ b/docs/book/content/schema/schemas_and_mutations.md @@ -1,10 +1,10 @@ # Schemas A schema consists of three types: a query object, a mutation object, and a subscription object. -( The usage of subscriptions is a little different from the mutation and query objects, so there is a -specific [section][section] to handle this topic). These three define the root query fields, mutations and subscriptions of the schema, respectively. +The usage of subscriptions is a little different from the mutation and query objects, so there is a specific [section][section] that discusses them. + Both query and mutation objects are regular GraphQL objects, defined like any other object in Juniper. The mutation and subscription object, however, is optional since schemas can be read-only and without subscriptions as well. If mutations/subscriptions functionality is not needed, consider using [EmptyMutation][EmptyMutation]/[EmptySubscription][EmptySubscription].