diff --git a/integrations/warp/src/request.rs b/integrations/warp/src/request.rs index e3e13d28..882a8b00 100644 --- a/integrations/warp/src/request.rs +++ b/integrations/warp/src/request.rs @@ -31,14 +31,14 @@ use crate::{graphql_batch_opts, GraphQLBadRequest, GraphQLBatchResponse}; /// /// type MySchema = Schema; /// -/// tokio::runtime::Runtime::new().unwrap().block_on(async { -/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); -/// let filter = async_graphql_warp::graphql(schema) -/// .and_then(|(schema, request): (MySchema, async_graphql::Request)| async move { -/// Ok::<_, Infallible>(async_graphql_warp::GraphQLResponse::from(schema.execute(request).await)) -/// }); -/// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await; -/// }); +/// # tokio::runtime::Runtime::new().unwrap().block_on(async { +/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); +/// let filter = async_graphql_warp::graphql(schema) +/// .and_then(|(schema, request): (MySchema, async_graphql::Request)| async move { +/// Ok::<_, Infallible>(async_graphql_warp::GraphQLResponse::from(schema.execute(request).await)) +/// }); +/// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await; +/// # }); /// ``` pub fn graphql( schema: Schema, diff --git a/integrations/warp/src/subscription.rs b/integrations/warp/src/subscription.rs index d3a72d9f..38ca0c04 100644 --- a/integrations/warp/src/subscription.rs +++ b/integrations/warp/src/subscription.rs @@ -46,12 +46,12 @@ use warp::{Error, Filter, Rejection, Reply}; /// } /// } /// -/// tokio::runtime::Runtime::new().unwrap().block_on(async { -/// let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); -/// let filter = async_graphql_warp::graphql_subscription(schema) -/// .or(warp::any().map(|| "Hello, World!")); -/// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await; -/// }); +/// # tokio::runtime::Runtime::new().unwrap().block_on(async { +/// let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); +/// let filter = async_graphql_warp::graphql_subscription(schema) +/// .or(warp::any().map(|| "Hello, World!")); +/// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await; +/// # }); /// ``` pub fn graphql_subscription( schema: Schema, @@ -136,27 +136,27 @@ fn default_on_connection_init(_: serde_json::Value) -> Ready { sink: Sink, diff --git a/src/context.rs b/src/context.rs index e546a8d9..8fd551ee 100644 --- a/src/context.rs +++ b/src/context.rs @@ -618,12 +618,12 @@ impl<'a> ContextBase<'a, &'a Positioned> { /// } /// } /// - /// tokio::runtime::Runtime::new().unwrap().block_on(async move { - /// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); - /// assert!(schema.execute("{ obj { a b c }}").await.is_ok()); - /// assert!(schema.execute("{ obj { a ... { b c } }}").await.is_ok()); - /// assert!(schema.execute("{ obj { a ... BC }} fragment BC on MyObj { b c }").await.is_ok()); - /// }); + /// # tokio::runtime::Runtime::new().unwrap().block_on(async move { + /// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); + /// assert!(schema.execute("{ obj { a b c }}").await.is_ok()); + /// assert!(schema.execute("{ obj { a ... { b c } }}").await.is_ok()); + /// assert!(schema.execute("{ obj { a ... BC }} fragment BC on MyObj { b c }").await.is_ok()); + /// # }); /// /// ``` pub fn field(&self) -> SelectionField { diff --git a/src/dataloader/mod.rs b/src/dataloader/mod.rs index f4c95a89..414808fb 100644 --- a/src/dataloader/mod.rs +++ b/src/dataloader/mod.rs @@ -32,29 +32,28 @@ //! } //! } //! -//! tokio::runtime::Runtime::new().unwrap().block_on(async move { -//! let schema = Schema::new(Query, EmptyMutation, EmptySubscription); -//! let query = r#" -//! { -//! v1: value(n: 1) -//! v2: value(n: 2) -//! v3: value(n: 3) -//! v4: value(n: 4) -//! v5: value(n: 5) -//! } -//! "#; -//! let request = Request::new(query).data(DataLoader::new(MyLoader)); -//! let res = schema.execute(request).await.into_result().unwrap().data; -//! -//! assert_eq!(res, value!({ -//! "v1": "1", -//! "v2": "2", -//! "v3": "3", -//! "v4": "4", -//! "v5": "5", -//! })); -//! }); +//! # tokio::runtime::Runtime::new().unwrap().block_on(async move { +//! let schema = Schema::new(Query, EmptyMutation, EmptySubscription); +//! let query = r#" +//! { +//! v1: value(n: 1) +//! v2: value(n: 2) +//! v3: value(n: 3) +//! v4: value(n: 4) +//! v5: value(n: 5) +//! } +//! "#; +//! let request = Request::new(query).data(DataLoader::new(MyLoader)); +//! let res = schema.execute(request).await.into_result().unwrap().data; //! +//! assert_eq!(res, value!({ +//! "v1": "1", +//! "v2": "2", +//! "v3": "3", +//! "v4": "4", +//! "v5": "5", +//! })); +//! # }); //! ``` mod cache; diff --git a/src/docs/complex_object.md b/src/docs/complex_object.md new file mode 100644 index 00000000..c39ed431 --- /dev/null +++ b/src/docs/complex_object.md @@ -0,0 +1,92 @@ +Define a complex GraphQL object for SimpleObject's complex field resolver. + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/define_simple_object.html).* + +Sometimes most of the fields of a GraphQL object simply return the value of the structure member, but a few +fields are calculated. Usually we use the `Object` macro to define such a GraphQL object. + +But this can be done more beautifully with the `ComplexObject` macro. We can use the `SimpleObject` macro to define +some simple fields, and use the `ComplexObject` macro to define some other fields that need to be calculated. + +# Macro attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| name | Object name | string | Y | +| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | + +# Field attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| skip | Skip this field | bool | Y | +| name | Field name | string | Y | +| desc | Field description | string | Y | +| deprecation | Field deprecated | bool | Y | +| deprecation | Field deprecation reason | string | Y | +| cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y | +| external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y | +| provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | +| requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | +| guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| complexity | Custom field complexity. *[See also the Book](https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html).* | bool | Y | +| complexity | Custom field complexity. | string | Y | +| derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y | + +# Field argument attributes + +| Attribute | description | Type | Optional | +|--------------|------------------------------------------|------------ |----------| +| name | Argument name | string | Y | +| desc | Argument description | string | Y | +| default | Use `Default::default` for default value | none | Y | +| default | Argument default value | literal | Y | +| default_with | Expression to generate default value | code string | Y | +| validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | + +# Examples + +```rust +use async_graphql::*; + +#[derive(SimpleObject)] +#[graphql(complex)] // NOTE: If you want the `ComplexObject` macro to take effect, this `complex` attribute is required. +struct MyObj { + a: i32, + b: i32, +} + +#[ComplexObject] +impl MyObj { + async fn c(&self) -> i32 { + self.a + self.b + } +} + +struct Query; + +#[Object] +impl Query { + async fn obj(&self) -> MyObj { + MyObj { a: 10, b: 20 } + } +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::new(Query, EmptyMutation, EmptySubscription); +let res = schema.execute("{ obj { a b c } }").await.into_result().unwrap().data; +assert_eq!(res, value!({ + "obj": { + "a": 10, + "b": 20, + "c": 30, + }, +})); +# }); +``` diff --git a/src/docs/description.md b/src/docs/description.md new file mode 100644 index 00000000..ad4fa090 --- /dev/null +++ b/src/docs/description.md @@ -0,0 +1,40 @@ +Attach a description to `Object`, `Scalar` or `Subscription`. + +The three types above use the rustdoc on the implementation block as +the GraphQL type description, but if you want to use the rustdoc on the +type declaration as the GraphQL type description, you can use that derived macro. + +# Examples + +```rust +use async_graphql::*; + +/// This is MyObj +#[derive(Description, Default)] +struct MyObj; + +#[Object(use_type_description)] +impl MyObj { + async fn value(&self) -> i32 { + 100 + } +} + +#[derive(SimpleObject, Default)] +struct Query { + obj: MyObj, +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::new(Query::default(), EmptyMutation, EmptySubscription); +assert_eq!( + schema + .execute(r#"{ __type(name: "MyObj") { description } }"#) + .await + .data, + value!({ + "__type": { "description": "This is MyObj" } + }) +); +# }); +``` diff --git a/src/docs/directive.md b/src/docs/directive.md new file mode 100644 index 00000000..f97afccd --- /dev/null +++ b/src/docs/directive.md @@ -0,0 +1,74 @@ +Define a directive for query. + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/custom_directive.html).* + +# Macro attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| name | Object name | string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| repeatable | It means that the directive can be used multiple times in the same location. | bool | Y | +| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| locations | Specify the location where the directive is available, multiples are allowed. The possible values is "field", ... | string | N | + +# Directive attributes + +| Attribute | description | Type | Optional | +|--------------|------------------------------------------|------------ |----------| +| name | Argument name | string | Y | +| desc | Argument description | string | Y | +| default | Use `Default::default` for default value | none | Y | +| default | Argument default value | literal | Y | +| default_with | Expression to generate default value | code string | Y | +| validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | + +# Examples + +```rust +use async_graphql::*; + +struct ConcatDirective { + value: String, +} + +#[async_trait::async_trait] +impl CustomDirective for ConcatDirective { + async fn resolve_field(&self, _ctx: &Context<'_>, resolve: ResolveFut<'_>) -> ServerResult> { + resolve.await.map(|value| { + value.map(|value| match value { + Value::String(str) => Value::String(str + &self.value), + _ => value, + }) + }) + } +} + +#[Directive(location = "field")] +fn concat(value: String) -> impl CustomDirective { + ConcatDirective { value } +} + +struct Query; + +#[Object] +impl Query { + async fn value(&self) -> &'static str { + "abc" + } +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::build(Query, EmptyMutation, EmptySubscription) + .directive(concat) + .finish(); +let res = schema.execute(r#"{ value @concat(value: "def") }"#).await.into_result().unwrap().data; +assert_eq!(res, value!({ + "value": "abcdef", +})); +# }); +``` diff --git a/src/docs/enum.md b/src/docs/enum.md new file mode 100644 index 00000000..eb174d4d --- /dev/null +++ b/src/docs/enum.md @@ -0,0 +1,59 @@ +Define a GraphQL enum + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/define_enum.html).* + +# Macro attributes + +| Attribute | description | Type | Optional | +|--------------|---------------------------|----------|----------| +| name | Enum name | string | Y | +| rename_items | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| remote | Derive a remote enum | string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | + +# Item attributes + +| Attribute | description | Type | Optional | +|-------------|---------------------------|----------|----------| +| name | Item name | string | Y | +| deprecation | Item deprecated | bool | Y | +| deprecation | Item deprecation reason | string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | + +# Examples + +```rust +use async_graphql::*; + +#[derive(Enum, Copy, Clone, Eq, PartialEq)] +enum MyEnum { + A, + #[graphql(name = "b")] B, +} + +struct Query { + value1: MyEnum, + value2: MyEnum, +} + +#[Object] +impl Query { + /// value1 + async fn value1(&self) -> MyEnum { + self.value1 + } + + /// value2 + async fn value2(&self) -> MyEnum { + self.value2 + } +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::new(Query{ value1: MyEnum::A, value2: MyEnum::B }, EmptyMutation, EmptySubscription); +let res = schema.execute("{ value1 value2 }").await.into_result().unwrap().data; +assert_eq!(res, value!({ "value1": "A", "value2": "b" })); +# }); +``` diff --git a/src/docs/input_object.md b/src/docs/input_object.md new file mode 100644 index 00000000..d9b170cf --- /dev/null +++ b/src/docs/input_object.md @@ -0,0 +1,60 @@ +Define a GraphQL input object + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/define_input_object.html).* + +# Macro attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| name | Object name | string | Y | +| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | + +# Field attributes + +| Attribute | description | Type | Optional | +|--------------|------------------------------------------|-------------|----------| +| name | Field name | string | Y | +| default | Use `Default::default` for default value | none | Y | +| default | Argument default value | literal | Y | +| default_with | Expression to generate default value | code string | Y | +| validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | +| flatten | Similar to serde (flatten) | boolean | Y | +| skip | Skip this field, use `Default::default` to get a default value for this field. | bool | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | + +# Examples + +```rust +use async_graphql::*; + +#[derive(InputObject)] +struct MyInputObject { + a: i32, + #[graphql(default = 10)] + b: i32, +} + +struct Query; + +#[Object] +impl Query { + /// value + async fn value(&self, input: MyInputObject) -> i32 { + input.a * input.b + } +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::new(Query, EmptyMutation, EmptySubscription); +let res = schema.execute(r#" +{ + value1: value(input:{a:9, b:3}) + value2: value(input:{a:9}) +}"#).await.into_result().unwrap().data; +assert_eq!(res, value!({ "value1": 27, "value2": 90 })); +# }); +``` diff --git a/src/docs/interface.md b/src/docs/interface.md new file mode 100644 index 00000000..bf5a1807 --- /dev/null +++ b/src/docs/interface.md @@ -0,0 +1,140 @@ +Define a GraphQL interface + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/define_interface.html).* + +# Macro attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| name | Object name | string | Y | +| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| field | Fields of this Interface | InterfaceField | N | +| extends | Add fields to an entity that's defined in another service | bool | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | + +# Field attributes + +| Attribute | description | Type | Optional | +|-------------|---------------------------|----------|----------| +| name | Field name | string | N | +| type | Field type | string | N | +| method | Rust resolver method name. If specified, `name` will not be camelCased in schema definition | string | Y | +| desc | Field description | string | Y | +| deprecation | Field deprecated | bool | Y | +| deprecation | Field deprecation reason | string | Y | +| arg | Field arguments | InterfaceFieldArgument | Y | +| external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y | +| provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | +| requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | + +# Field argument attributes + +| Attribute | description | Type | Optional | +|--------------|------------------------------------------|-------------|----------| +| name | Argument name | string | N | +| type | Argument type | string | N | +| desc | Argument description | string | Y | +| default | Use `Default::default` for default value | none | Y | +| default | Argument default value | literal | Y | +| default_with | Expression to generate default value | code string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | + +# Define an interface + +Define TypeA, TypeB, TypeC... Implement the MyInterface + +```ignore +#[derive(Interface)] +enum MyInterface { + TypeA(TypeA), + TypeB(TypeB), + TypeC(TypeC), + ... +} +``` + +# Fields + +The type, name, and parameter fields of the interface must exactly match the type of the +implementation interface, but Result can be omitted. + +```rust +use async_graphql::*; + +struct TypeA { + value: i32, +} + +#[Object] +impl TypeA { + /// Returns data borrowed from the context + async fn value_a<'a>(&self, ctx: &'a Context<'_>) -> Result<&'a str> { + Ok(ctx.data::()?.as_str()) + } + + /// Returns data borrowed self + async fn value_b(&self) -> &i32 { + &self.value + } + + /// With parameters + async fn value_c(&self, a: i32, b: i32) -> i32 { + a + b + } + + /// Disabled name transformation, don't forget "method" argument in interface! + #[graphql(name = "value_d")] + async fn value_d(&self) -> i32 { + &self.value + 1 + } +} + +#[derive(Interface)] +#[graphql( + field(name = "value_a", type = "&'ctx str"), + field(name = "value_b", type = "&i32"), + field(name = "value_c", type = "i32", + arg(name = "a", type = "i32"), + arg(name = "b", type = "i32")), + field(name = "value_d", method = "value_d", type = "i32"), +)] +enum MyInterface { + TypeA(TypeA) +} + +struct Query; + +#[Object] +impl Query { + async fn type_a(&self) -> MyInterface { + TypeA { value: 10 }.into() + } +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish(); +let res = schema.execute(r#" +{ + typeA { + valueA + valueB + valueC(a: 3, b: 2) + value_d + } +}"#).await.into_result().unwrap().data; +assert_eq!(res, value!({ + "typeA": { + "valueA": "hello", + "valueB": 10, + "valueC": 5, + "value_d": 11 + } +})); +# }); +``` diff --git a/src/docs/merged_object.md b/src/docs/merged_object.md new file mode 100644 index 00000000..79a4f84b --- /dev/null +++ b/src/docs/merged_object.md @@ -0,0 +1,40 @@ +Define a merged object with multiple object types. + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).* + +# Macro attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| name | Object name | string | Y | +| cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y | +| extends | Add fields to an entity that's defined in another service | bool | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| serial | Resolve each field sequentially. | bool | Y | + +# Examples + +```rust +use async_graphql::*; + +#[derive(SimpleObject)] + struct Object1 { + a: i32, + } + +#[derive(SimpleObject)] +struct Object2 { + b: i32, +} + +#[derive(SimpleObject)] +struct Object3 { + c: i32, +} + +#[derive(MergedObject)] +struct MyObj(Object1, Object2, Object3); + +let obj = MyObj(Object1 { a: 10 }, Object2 { b: 20 }, Object3 { c: 30 }); +``` diff --git a/src/docs/merged_subscription.md b/src/docs/merged_subscription.md new file mode 100644 index 00000000..889a5674 --- /dev/null +++ b/src/docs/merged_subscription.md @@ -0,0 +1,42 @@ +Define a merged subscription with multiple subscription types. + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).* + +# Macro attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| name | Object name | string | Y | +| extends | Add fields to an entity that's defined in another service | bool | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | + +# Examples + +```rust +use async_graphql::*; +use futures_util::stream::Stream; + +#[derive(Default)] +struct Subscription1; + +#[Subscription] +impl Subscription1 { + async fn events1(&self) -> impl Stream { + futures_util::stream::iter(0..10) + } +} + +#[derive(Default)] +struct Subscription2; + +#[Subscription] +impl Subscription2 { + async fn events2(&self) -> impl Stream { + futures_util::stream::iter(10..20) + } +} + +#[derive(MergedSubscription, Default)] +struct Subscription(Subscription1, Subscription2); +``` diff --git a/src/docs/newtype.md b/src/docs/newtype.md new file mode 100644 index 00000000..4b803561 --- /dev/null +++ b/src/docs/newtype.md @@ -0,0 +1,132 @@ +Define a NewType Scalar + +It also implements `From` and `Into`. + +# Macro attributes + +| Attribute | description | Type | Optional | +|-------------|---------------------------|----------|----------| +| name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | string | Y | +| name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | bool | Y | +| visible(Only valid for new scalars) | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible(Only valid for new scalars) | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| specified_by_url(Only valid for new scalars) | Provide a specification URL for this scalar type, it must link to a human-readable specification of the data format, serialization and coercion rules for this scalar. | string | Y | + +# Examples + +## Use the original scalar name + +```rust +use async_graphql::*; + +#[derive(NewType)] +struct Weight(f64); + +struct Query; + +#[Object] +impl Query { + async fn value(&self) -> Weight { + Weight(1.234) + } +} + +// Test conversion +let weight: Weight = 10f64.into(); +let weight_f64: f64 = weight.into(); + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish(); + +let res = schema.execute("{ value }").await.into_result().unwrap().data; +assert_eq!(res, value!({ + "value": 1.234, +})); + +let res = schema.execute(r#" +{ + __type(name: "Query") { + fields { + name type { + kind + ofType { name } + } + } + } +}"#).await.into_result().unwrap().data; +assert_eq!(res, value!({ + "__type": { + "fields": [{ + "name": "value", + "type": { + "kind": "NON_NULL", + "ofType": { + "name": "Float" + } + } + }] + } +})); +# }); +``` + +## Define a new scalar + +```rust +use async_graphql::*; + +/// Widget NewType +#[derive(NewType)] +#[graphql(name)] // or: #[graphql(name = true)], #[graphql(name = "Weight")] +struct Weight(f64); + +struct Query; + +#[Object] +impl Query { + async fn value(&self) -> Weight { + Weight(1.234) + } +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish(); + +let res = schema.execute("{ value }").await.into_result().unwrap().data; +assert_eq!(res, value!({ + "value": 1.234, +})); + +let res = schema.execute(r#" +{ + __type(name: "Query") { + fields { + name type { + kind + ofType { name } + } + } + } +}"#).await.into_result().unwrap().data; +assert_eq!(res, value!({ + "__type": { + "fields": [{ + "name": "value", + "type": { + "kind": "NON_NULL", + "ofType": { + "name": "Weight" + } + } + }] + } +})); + +assert_eq!(schema.execute(r#"{ __type(name: "Weight") { name description } }"#). + await.into_result().unwrap().data, value!({ + "__type": { + "name": "Weight", "description": "Widget NewType" + } + })); +# }); +``` diff --git a/src/docs/object.md b/src/docs/object.md new file mode 100644 index 00000000..cdce49bf --- /dev/null +++ b/src/docs/object.md @@ -0,0 +1,189 @@ +Define a GraphQL object with methods + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/define_complex_object.html).* + +All methods are converted to camelCase. + +# Macro attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| name | Object name | string | Y | +| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y | +| extends | Add fields to an entity that's defined in another service | bool | Y | +| use_type_description | Specifies that the description of the type is on the type declaration. [`Description`]()(derive.Description.html) | bool | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| serial | Resolve each field sequentially. | bool | Y | + +# Field attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| skip | Skip this field | bool | Y | +| name | Field name | string | Y | +| desc | Field description | string | Y | +| deprecation | Field deprecated | bool | Y | +| deprecation | Field deprecation reason | string | Y | +| cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y | +| external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y | +| provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | +| requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | +| guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| complexity | Custom field complexity. *[See also the Book](https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html).* | bool | Y | +| complexity | Custom field complexity. | string | Y | +| derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y | + +# Field argument attributes + +| Attribute | description | Type | Optional | +|--------------|------------------------------------------|------------ |----------| +| name | Argument name | string | Y | +| desc | Argument description | string | Y | +| default | Use `Default::default` for default value | none | Y | +| default | Argument default value | literal | Y | +| default_with | Expression to generate default value | code string | Y | +| validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | +| key | Is entity key(for Federation) | bool | Y | + +# Derived argument attributes + +| Attribute | description | Type | Optional | +|--------------|------------------------------------------|------------ |----------| +| name | Generated derived field name | string | N | +| into | Type to derived an into | string | Y | +| with | Function to apply to manage advanced use cases | string| Y | + +# Valid field return types + +- Scalar values, such as `i32` and `bool`. `usize`, `isize`, `u128` and `i128` are not +supported +- `Vec`, such as `Vec` +- Slices, such as `&[i32]` +- `Option`, such as `Option` +- `BTree`, `HashMap`, `HashSet`, `BTreeSet`, `LinkedList`, `VecDeque` +- GraphQL objects. +- GraphQL enums. +- References to any of the above types, such as `&i32` or `&Option`. +- `Result`, such as `Result` + +# Context + +You can define a context as an argument to a method, and the context should be the first argument to the method. + +```ignore +#[Object] +impl Query { + async fn value(&self, ctx: &Context<'_>) -> { ... } +} +``` + +# Examples + +Implements GraphQL Object for struct. + +```rust +use async_graphql::*; + +struct Query { + value: i32, +} + +#[Object] +impl Query { + /// value + async fn value(&self) -> i32 { + self.value + } + + /// reference value + async fn value_ref(&self) -> &i32 { + &self.value + } + + /// value with error + async fn value_with_error(&self) -> Result { + Ok(self.value) + } + + async fn value_with_arg(&self, #[graphql(default = 1)] a: i32) -> i32 { + a + } +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::new(Query { value: 10 }, EmptyMutation, EmptySubscription); +let res = schema.execute(r#"{ + value + valueRef + valueWithError + valueWithArg1: valueWithArg + valueWithArg2: valueWithArg(a: 99) +}"#).await.into_result().unwrap().data; +assert_eq!(res, value!({ + "value": 10, + "valueRef": 10, + "valueWithError": 10, + "valueWithArg1": 1, + "valueWithArg2": 99 +})); +# }); +``` + +# Examples + +Implements GraphQL Object for trait object. + +```rust +use async_graphql::*; + +trait MyTrait: Send + Sync { + fn name(&self) -> &str; +} + +#[Object] +impl dyn MyTrait { + #[graphql(name = "name")] + async fn gql_name(&self) -> &str { + self.name() + } +} + +struct MyObj(String); + +impl MyTrait for MyObj { + fn name(&self) -> &str { + &self.0 + } +} + +struct Query; + +#[Object] +impl Query { + async fn objs(&self) -> Vec> { + vec![ + Box::new(MyObj("a".to_string())), + Box::new(MyObj("b".to_string())), + ] + } +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::new(Query, EmptyMutation, EmptySubscription); +let res = schema.execute("{ objs { name } }").await.into_result().unwrap().data; +assert_eq!(res, value!({ + "objs": [ + { "name": "a" }, + { "name": "b" }, + ] +})); +# }); +``` \ No newline at end of file diff --git a/src/docs/scalar.md b/src/docs/scalar.md new file mode 100644 index 00000000..bbe97b54 --- /dev/null +++ b/src/docs/scalar.md @@ -0,0 +1,8 @@ +Define a Scalar + +# Macro attributes + +| Attribute | description | Type | Optional | +|-------------|---------------------------|----------|----------| +| name | Scalar name | string | Y | +| specified_by_url | Provide a specification URL for this scalar type, it must link to a human-readable specification of the data format, serialization and coercion rules for this scalar. | string | Y | diff --git a/src/docs/simple_object.md b/src/docs/simple_object.md new file mode 100644 index 00000000..9236f317 --- /dev/null +++ b/src/docs/simple_object.md @@ -0,0 +1,65 @@ +Define a GraphQL object with fields + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/define_simple_object.html).* + +Similar to `Object`, but defined on a structure that automatically generates getters for all fields. For a list of valid field types, see [`Object`](attr.Object.html). All fields are converted to camelCase. + +# Macro attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| name | Object name | string | Y | +| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y | +| extends | Add fields to an entity that's defined in another service | bool | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| concretes | Specify how the concrete type of the generic SimpleObject should be implemented. *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_simple_object.html#generic-simpleobjects) | ConcreteType | Y | +| serial | Resolve each field sequentially. | bool | Y | + +# Field attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| skip | Skip this field | bool | Y | +| name | Field name | string | Y | +| deprecation | Field deprecated | bool | Y | +| deprecation | Field deprecation reason | string | Y | +| derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y | +| owned | Field resolver return a ownedship value | bool | Y | +| cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y | +| external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y | +| provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | +| requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | +| guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | + +# Derived attributes + +| Attribute | description | Type | Optional | +|--------------|------------------------------------------|------------ |----------| +| name | Generated derived field name | string | N | +| into | Type to derived an into | string | Y | +| owned | Field resolver return a ownedship value | bool | Y | +| with | Function to apply to manage advanced use cases | string| Y | + + +# Examples + +```rust +use async_graphql::*; + +#[derive(SimpleObject)] +struct Query { + value: i32, +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::new(Query{ value: 10 }, EmptyMutation, EmptySubscription); +let res = schema.execute("{ value }").await.into_result().unwrap().data; +assert_eq!(res, value!({ + "value": 10, +})); +# }); +``` diff --git a/src/docs/subscription.md b/src/docs/subscription.md new file mode 100644 index 00000000..bfbdfa6d --- /dev/null +++ b/src/docs/subscription.md @@ -0,0 +1,64 @@ +Define a GraphQL subscription + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/subscription.html).* + +The field function is a synchronization function that performs filtering. When true is returned, the message is pushed to the client. +The second parameter is the type of the field. +Starting with the third parameter is one or more filtering conditions, The filter condition is the parameter of the field. +The filter function should be synchronous. + +# Macro attributes + +| Attribute | description | Type | Optional | +|---------------|---------------------------|----------|----------| +| name | Object name | string | Y | +| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | +| extends | Add fields to an entity that's defined in another service | bool | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| use_type_description | Specifies that the description of the type is on the type declaration. [`Description`]()(derive.Description.html) | bool | Y | + +# Field attributes + +| Attribute | description | Type | Optional | +|-------------|---------------------------|----------|----------| +| name | Field name | string | Y | +| deprecation | Field deprecated | bool | Y | +| deprecation | Field deprecation reason | string | Y | +| guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| complexity | Custom field complexity. *[See also the Book](https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html).* | bool | Y | +| complexity | Custom field complexity. | string | Y | +| secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | + +# Field argument attributes + +| Attribute | description | Type | Optional | +|--------------|------------------------------------------|------------ |----------| +| name | Argument name | string | Y | +| desc | Argument description | string | Y | +| default | Use `Default::default` for default value | none | Y | +| default | Argument default value | literal | Y | +| default_with | Expression to generate default value | code string | Y | +| validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | + +# Examples + +```rust +use async_graphql::*; +use futures_util::stream::{Stream, StreamExt}; + +struct Subscription; + +#[Subscription] +impl Subscription { + async fn value(&self, condition: i32) -> impl Stream { + // Returns the number from 0 to `condition`. + futures_util::stream::iter(0..condition) + } +} +``` diff --git a/src/docs/union.md b/src/docs/union.md new file mode 100644 index 00000000..4d5131a1 --- /dev/null +++ b/src/docs/union.md @@ -0,0 +1,71 @@ +Define a GraphQL union + +*[See also the Book](https://async-graphql.github.io/async-graphql/en/define_union.html).* + +# Macro attributes + +| Attribute | description | Type | Optional | +|-------------|---------------------------|----------|----------| +| name | Object name | string | Y | +| visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | +| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | + +# Item attributes + +| Attribute | description | Type | Optional | +|--------------|------------------------------------------|----------|----------| +| flatten | Similar to serde (flatten) | boolean | Y | + +# Define a union + +Define TypeA, TypeB, ... as MyUnion + +```rust +use async_graphql::*; + +#[derive(SimpleObject)] +struct TypeA { + value_a: i32, +} + +#[derive(SimpleObject)] +struct TypeB { + value_b: i32 +} + +#[derive(Union)] +enum MyUnion { + TypeA(TypeA), + TypeB(TypeB), +} + +struct Query; + +#[Object] +impl Query { + async fn all_data(&self) -> Vec { + vec![TypeA { value_a: 10 }.into(), TypeB { value_b: 20 }.into()] + } +} + +# tokio::runtime::Runtime::new().unwrap().block_on(async move { +let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish(); +let res = schema.execute(r#" +{ + allData { + ... on TypeA { + valueA + } + ... on TypeB { + valueB + } + } +}"#).await.into_result().unwrap().data; +assert_eq!(res, value!({ + "allData": [ + { "valueA": 10 }, + { "valueB": 20 }, + ] +})); +# }); +``` diff --git a/src/extensions/tracing.rs b/src/extensions/tracing.rs index 49382f2a..fbdc0bde 100644 --- a/src/extensions/tracing.rs +++ b/src/extensions/tracing.rs @@ -33,9 +33,9 @@ use crate::{Response, ServerError, ServerResult, ValidationResult, Value, Variab /// .extension(Tracing) /// .finish(); /// -/// tokio::runtime::Runtime::new().unwrap().block_on(async { -/// schema.execute(Request::new("{ value }")).await; -/// }); +/// # tokio::runtime::Runtime::new().unwrap().block_on(async { +/// schema.execute(Request::new("{ value }")).await; +/// # }); /// ``` #[cfg_attr(docsrs, doc(cfg(feature = "tracing")))] pub struct Tracing; diff --git a/src/lib.rs b/src/lib.rs index 4b131709..1d19f66f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -243,1107 +243,31 @@ pub type FieldError = Error; /// reasons. pub type FieldResult = Result; -/// Define a GraphQL object with methods -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_complex_object.html).* -/// -/// All methods are converted to camelCase. -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y | -/// | extends | Add fields to an entity that's defined in another service | bool | Y | -/// | use_type_description | Specifies that the description of the type is on the type declaration. [`Description`]()(derive.Description.html) | bool | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | serial | Resolve each field sequentially. | bool | Y | -/// -/// # Field attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | skip | Skip this field | bool | Y | -/// | name | Field name | string | Y | -/// | desc | Field description | string | Y | -/// | deprecation | Field deprecated | bool | Y | -/// | deprecation | Field deprecation reason | string | Y | -/// | cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y | -/// | external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y | -/// | provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | -/// | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | -/// | guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | complexity | Custom field complexity. *[See also the Book](https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html).* | bool | Y | -/// | complexity | Custom field complexity. | string | Y | -/// | derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y | -/// -/// # Field argument attributes -/// -/// | Attribute | description | Type | Optional | -/// |--------------|------------------------------------------|------------ |----------| -/// | name | Argument name | string | Y | -/// | desc | Argument description | string | Y | -/// | default | Use `Default::default` for default value | none | Y | -/// | default | Argument default value | literal | Y | -/// | default_with | Expression to generate default value | code string | Y | -/// | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | -/// | key | Is entity key(for Federation) | bool | Y | -/// -/// # Derived argument attributes -/// -/// | Attribute | description | Type | Optional | -/// |--------------|------------------------------------------|------------ |----------| -/// | name | Generated derived field name | string | N | -/// | into | Type to derived an into | string | Y | -/// | with | Function to apply to manage advanced use cases | string| Y | -/// -/// # Valid field return types -/// -/// - Scalar values, such as `i32` and `bool`. `usize`, `isize`, `u128` and `i128` are not -/// supported -/// - `Vec`, such as `Vec` -/// - Slices, such as `&[i32]` -/// - `Option`, such as `Option` -/// - `BTree`, `HashMap`, `HashSet`, `BTreeSet`, `LinkedList`, `VecDeque` -/// - GraphQL objects. -/// - GraphQL enums. -/// - References to any of the above types, such as `&i32` or `&Option`. -/// - `Result`, such as `Result` -/// -/// # Context -/// -/// You can define a context as an argument to a method, and the context should be the first argument to the method. -/// -/// ```ignore -/// #[Object] -/// impl Query { -/// async fn value(&self, ctx: &Context<'_>) -> { ... } -/// } -/// ``` -/// -/// # Examples -/// -/// Implements GraphQL Object for struct. -/// -/// ```rust -/// use async_graphql::*; -/// -/// struct Query { -/// value: i32, -/// } -/// -/// #[Object] -/// impl Query { -/// /// value -/// async fn value(&self) -> i32 { -/// self.value -/// } -/// -/// /// reference value -/// async fn value_ref(&self) -> &i32 { -/// &self.value -/// } -/// -/// /// value with error -/// async fn value_with_error(&self) -> Result { -/// Ok(self.value) -/// } -/// -/// async fn value_with_arg(&self, #[graphql(default = 1)] a: i32) -> i32 { -/// a -/// } -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::new(Query { value: 10 }, EmptyMutation, EmptySubscription); -/// let res = schema.execute(r#"{ -/// value -/// valueRef -/// valueWithError -/// valueWithArg1: valueWithArg -/// valueWithArg2: valueWithArg(a: 99) -/// }"#).await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "value": 10, -/// "valueRef": 10, -/// "valueWithError": 10, -/// "valueWithArg1": 1, -/// "valueWithArg2": 99 -/// })); -/// }); -/// ``` -/// -/// # Examples -/// -/// Implements GraphQL Object for trait object. -/// -/// ```rust -/// use async_graphql::*; -/// -/// trait MyTrait: Send + Sync { -/// fn name(&self) -> &str; -/// } -/// -/// #[Object] -/// impl dyn MyTrait { -/// #[graphql(name = "name")] -/// async fn gql_name(&self) -> &str { -/// self.name() -/// } -/// } -/// -/// struct MyObj(String); -/// -/// impl MyTrait for MyObj { -/// fn name(&self) -> &str { -/// &self.0 -/// } -/// } -/// -/// struct Query; -/// -/// #[Object] -/// impl Query { -/// async fn objs(&self) -> Vec> { -/// vec![ -/// Box::new(MyObj("a".to_string())), -/// Box::new(MyObj("b".to_string())), -/// ] -/// } -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); -/// let res = schema.execute("{ objs { name } }").await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "objs": [ -/// { "name": "a" }, -/// { "name": "b" }, -/// ] -/// })); -/// }); -/// ``` -pub use async_graphql_derive::Object; - -/// Define a GraphQL object with fields -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_simple_object.html).* -/// -/// Similar to `Object`, but defined on a structure that automatically generates getters for all fields. For a list of valid field types, see [`Object`](attr.Object.html). All fields are converted to camelCase. -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y | -/// | extends | Add fields to an entity that's defined in another service | bool | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | concretes | Specify how the concrete type of the generic SimpleObject should be implemented. *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_simple_object.html#generic-simpleobjects) | ConcreteType | Y | -/// | serial | Resolve each field sequentially. | bool | Y | -/// -/// # Field attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | skip | Skip this field | bool | Y | -/// | name | Field name | string | Y | -/// | deprecation | Field deprecated | bool | Y | -/// | deprecation | Field deprecation reason | string | Y | -/// | derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y | -/// | owned | Field resolver return a ownedship value | bool | Y | -/// | cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y | -/// | external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y | -/// | provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | -/// | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | -/// | guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// -/// # Derived attributes -/// -/// | Attribute | description | Type | Optional | -/// |--------------|------------------------------------------|------------ |----------| -/// | name | Generated derived field name | string | N | -/// | into | Type to derived an into | string | Y | -/// | owned | Field resolver return a ownedship value | bool | Y | -/// | with | Function to apply to manage advanced use cases | string| Y | -/// -/// -/// # Examples -/// -/// ```rust -/// use async_graphql::*; -/// -/// #[derive(SimpleObject)] -/// struct Query { -/// value: i32, -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::new(Query{ value: 10 }, EmptyMutation, EmptySubscription); -/// let res = schema.execute("{ value }").await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "value": 10, -/// })); -/// }); -/// ``` -pub use async_graphql_derive::SimpleObject; - -/// Define a complex GraphQL object for SimpleObject's complex field resolver. -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_simple_object.html).* -/// -/// Sometimes most of the fields of a GraphQL object simply return the value of the structure member, but a few -/// fields are calculated. Usually we use the `Object` macro to define such a GraphQL object. -/// -/// But this can be done more beautifully with the `ComplexObject` macro. We can use the `SimpleObject` macro to define -/// some simple fields, and use the `ComplexObject` macro to define some other fields that need to be calculated. -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// -/// # Field attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | skip | Skip this field | bool | Y | -/// | name | Field name | string | Y | -/// | desc | Field description | string | Y | -/// | deprecation | Field deprecated | bool | Y | -/// | deprecation | Field deprecation reason | string | Y | -/// | cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y | -/// | external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y | -/// | provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | -/// | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | -/// | guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | complexity | Custom field complexity. *[See also the Book](https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html).* | bool | Y | -/// | complexity | Custom field complexity. | string | Y | -/// | derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y | -/// -/// # Field argument attributes -/// -/// | Attribute | description | Type | Optional | -/// |--------------|------------------------------------------|------------ |----------| -/// | name | Argument name | string | Y | -/// | desc | Argument description | string | Y | -/// | default | Use `Default::default` for default value | none | Y | -/// | default | Argument default value | literal | Y | -/// | default_with | Expression to generate default value | code string | Y | -/// | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | -/// -/// # Examples -/// -/// ```rust -/// use async_graphql::*; -/// -/// #[derive(SimpleObject)] -/// #[graphql(complex)] // NOTE: If you want the `ComplexObject` macro to take effect, this `complex` attribute is required. -/// struct MyObj { -/// a: i32, -/// b: i32, -/// } -/// -/// #[ComplexObject] -/// impl MyObj { -/// async fn c(&self) -> i32 { -/// self.a + self.b -/// } -/// } -/// -/// struct Query; -/// -/// #[Object] -/// impl Query { -/// async fn obj(&self) -> MyObj { -/// MyObj { a: 10, b: 20 } -/// } -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); -/// let res = schema.execute("{ obj { a b c } }").await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "obj": { -/// "a": 10, -/// "b": 20, -/// "c": 30, -/// }, -/// })); -/// }); -/// ``` +#[doc = include_str!("docs/complex_object.md")] pub use async_graphql_derive::ComplexObject; - -/// Define a GraphQL enum -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_enum.html).* -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |--------------|---------------------------|----------|----------| -/// | name | Enum name | string | Y | -/// | rename_items | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | remote | Derive a remote enum | string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// -/// # Item attributes -/// -/// | Attribute | description | Type | Optional | -/// |-------------|---------------------------|----------|----------| -/// | name | Item name | string | Y | -/// | deprecation | Item deprecated | bool | Y | -/// | deprecation | Item deprecation reason | string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// -/// # Examples -/// -/// ```rust -/// use async_graphql::*; -/// -/// #[derive(Enum, Copy, Clone, Eq, PartialEq)] -/// enum MyEnum { -/// A, -/// #[graphql(name = "b")] B, -/// } -/// -/// struct Query { -/// value1: MyEnum, -/// value2: MyEnum, -/// } -/// -/// #[Object] -/// impl Query { -/// /// value1 -/// async fn value1(&self) -> MyEnum { -/// self.value1 -/// } -/// -/// /// value2 -/// async fn value2(&self) -> MyEnum { -/// self.value2 -/// } -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::new(Query{ value1: MyEnum::A, value2: MyEnum::B }, EmptyMutation, EmptySubscription); -/// let res = schema.execute("{ value1 value2 }").await.into_result().unwrap().data; -/// assert_eq!(res, value!({ "value1": "A", "value2": "b" })); -/// }); -/// ``` -pub use async_graphql_derive::Enum; - -/// Define a GraphQL input object -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_input_object.html).* -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// -/// # Field attributes -/// -/// | Attribute | description | Type | Optional | -/// |--------------|------------------------------------------|-------------|----------| -/// | name | Field name | string | Y | -/// | default | Use `Default::default` for default value | none | Y | -/// | default | Argument default value | literal | Y | -/// | default_with | Expression to generate default value | code string | Y | -/// | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | -/// | flatten | Similar to serde (flatten) | boolean | Y | -/// | skip | Skip this field, use `Default::default` to get a default value for this field. | bool | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | -/// -/// # Examples -/// -/// ```rust -/// use async_graphql::*; -/// -/// #[derive(InputObject)] -/// struct MyInputObject { -/// a: i32, -/// #[graphql(default = 10)] -/// b: i32, -/// } -/// -/// struct Query; -/// -/// #[Object] -/// impl Query { -/// /// value -/// async fn value(&self, input: MyInputObject) -> i32 { -/// input.a * input.b -/// } -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); -/// let res = schema.execute(r#" -/// { -/// value1: value(input:{a:9, b:3}) -/// value2: value(input:{a:9}) -/// }"#).await.into_result().unwrap().data; -/// assert_eq!(res, value!({ "value1": 27, "value2": 90 })); -/// }); -/// ``` -pub use async_graphql_derive::InputObject; - -/// Define a GraphQL interface -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_interface.html).* -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | field | Fields of this Interface | InterfaceField | N | -/// | extends | Add fields to an entity that's defined in another service | bool | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// -/// # Field attributes -/// -/// | Attribute | description | Type | Optional | -/// |-------------|---------------------------|----------|----------| -/// | name | Field name | string | N | -/// | type | Field type | string | N | -/// | method | Rust resolver method name. If specified, `name` will not be camelCased in schema definition | string | Y | -/// | desc | Field description | string | Y | -/// | deprecation | Field deprecated | bool | Y | -/// | deprecation | Field deprecation reason | string | Y | -/// | arg | Field arguments | InterfaceFieldArgument | Y | -/// | external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y | -/// | provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | -/// | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// -/// # Field argument attributes -/// -/// | Attribute | description | Type | Optional | -/// |--------------|------------------------------------------|-------------|----------| -/// | name | Argument name | string | N | -/// | type | Argument type | string | N | -/// | desc | Argument description | string | Y | -/// | default | Use `Default::default` for default value | none | Y | -/// | default | Argument default value | literal | Y | -/// | default_with | Expression to generate default value | code string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | -/// -/// # Define an interface -/// -/// Define TypeA, TypeB, TypeC... Implement the MyInterface -/// -/// ```ignore -/// #[derive(Interface)] -/// enum MyInterface { -/// TypeA(TypeA), -/// TypeB(TypeB), -/// TypeC(TypeC), -/// ... -/// } -/// ``` -/// -/// # Fields -/// -/// The type, name, and parameter fields of the interface must exactly match the type of the -/// implementation interface, but Result can be omitted. -/// -/// ```rust -/// use async_graphql::*; -/// -/// struct TypeA { -/// value: i32, -/// } -/// -/// #[Object] -/// impl TypeA { -/// /// Returns data borrowed from the context -/// async fn value_a<'a>(&self, ctx: &'a Context<'_>) -> Result<&'a str> { -/// Ok(ctx.data::()?.as_str()) -/// } -/// -/// /// Returns data borrowed self -/// async fn value_b(&self) -> &i32 { -/// &self.value -/// } -/// -/// /// With parameters -/// async fn value_c(&self, a: i32, b: i32) -> i32 { -/// a + b -/// } -/// -/// /// Disabled name transformation, don't forget "method" argument in interface! -/// #[graphql(name = "value_d")] -/// async fn value_d(&self) -> i32 { -/// &self.value + 1 -/// } -/// } -/// -/// #[derive(Interface)] -/// #[graphql( -/// field(name = "value_a", type = "&'ctx str"), -/// field(name = "value_b", type = "&i32"), -/// field(name = "value_c", type = "i32", -/// arg(name = "a", type = "i32"), -/// arg(name = "b", type = "i32")), -/// field(name = "value_d", method = "value_d", type = "i32"), -/// )] -/// enum MyInterface { -/// TypeA(TypeA) -/// } -/// -/// struct Query; -/// -/// #[Object] -/// impl Query { -/// async fn type_a(&self) -> MyInterface { -/// TypeA { value: 10 }.into() -/// } -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish(); -/// let res = schema.execute(r#" -/// { -/// typeA { -/// valueA -/// valueB -/// valueC(a: 3, b: 2) -/// value_d -/// } -/// }"#).await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "typeA": { -/// "valueA": "hello", -/// "valueB": 10, -/// "valueC": 5, -/// "value_d": 11 -/// } -/// })); -/// }); -/// ``` -pub use async_graphql_derive::Interface; - -/// Define a GraphQL union -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_union.html).* -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |-------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// -/// # Item attributes -/// -/// | Attribute | description | Type | Optional | -/// |--------------|------------------------------------------|----------|----------| -/// | flatten | Similar to serde (flatten) | boolean | Y | -/// -/// # Define a union -/// -/// Define TypeA, TypeB, ... as MyUnion -/// -/// ```rust -/// use async_graphql::*; -/// -/// #[derive(SimpleObject)] -/// struct TypeA { -/// value_a: i32, -/// } -/// -/// #[derive(SimpleObject)] -/// struct TypeB { -/// value_b: i32 -/// } -/// -/// #[derive(Union)] -/// enum MyUnion { -/// TypeA(TypeA), -/// TypeB(TypeB), -/// } -/// -/// struct Query; -/// -/// #[Object] -/// impl Query { -/// async fn all_data(&self) -> Vec { -/// vec![TypeA { value_a: 10 }.into(), TypeB { value_b: 20 }.into()] -/// } -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish(); -/// let res = schema.execute(r#" -/// { -/// allData { -/// ... on TypeA { -/// valueA -/// } -/// ... on TypeB { -/// valueB -/// } -/// } -/// }"#).await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "allData": [ -/// { "valueA": 10 }, -/// { "valueB": 20 }, -/// ] -/// })); -/// }); -/// ``` -pub use async_graphql_derive::Union; - -/// Define a GraphQL subscription -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/subscription.html).* -/// -/// The field function is a synchronization function that performs filtering. When true is returned, the message is pushed to the client. -/// The second parameter is the type of the field. -/// Starting with the third parameter is one or more filtering conditions, The filter condition is the parameter of the field. -/// The filter function should be synchronous. -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | extends | Add fields to an entity that's defined in another service | bool | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | use_type_description | Specifies that the description of the type is on the type declaration. [`Description`]()(derive.Description.html) | bool | Y | -/// -/// # Field attributes -/// -/// | Attribute | description | Type | Optional | -/// |-------------|---------------------------|----------|----------| -/// | name | Field name | string | Y | -/// | deprecation | Field deprecated | bool | Y | -/// | deprecation | Field deprecation reason | string | Y | -/// | guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | complexity | Custom field complexity. *[See also the Book](https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html).* | bool | Y | -/// | complexity | Custom field complexity. | string | Y | -/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | -/// -/// # Field argument attributes -/// -/// | Attribute | description | Type | Optional | -/// |--------------|------------------------------------------|------------ |----------| -/// | name | Argument name | string | Y | -/// | desc | Argument description | string | Y | -/// | default | Use `Default::default` for default value | none | Y | -/// | default | Argument default value | literal | Y | -/// | default_with | Expression to generate default value | code string | Y | -/// | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// -/// # Examples -/// -/// ```rust -/// use async_graphql::*; -/// use futures_util::stream::{Stream, StreamExt}; -/// -/// struct SubscriptionRoot; -/// -/// #[Subscription] -/// impl SubscriptionRoot { -/// async fn value(&self, condition: i32) -> impl Stream { -/// // Returns the number from 0 to `condition`. -/// futures_util::stream::iter(0..condition) -/// } -/// } -/// ``` -pub use async_graphql_derive::Subscription; - -/// Define a Scalar -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |-------------|---------------------------|----------|----------| -/// | name | Scalar name | string | Y | -/// | specified_by_url | Provide a specification URL for this scalar type, it must link to a human-readable specification of the data format, serialization and coercion rules for this scalar. | string | Y | -/// -pub use async_graphql_derive::Scalar; - -/// Define a NewType Scalar -/// -/// It also implements `From` and `Into`. -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |-------------|---------------------------|----------|----------| -/// | name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | string | Y | -/// | name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | bool | Y | -/// | visible(Only valid for new scalars) | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible(Only valid for new scalars) | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | specified_by_url(Only valid for new scalars) | Provide a specification URL for this scalar type, it must link to a human-readable specification of the data format, serialization and coercion rules for this scalar. | string | Y | -/// -/// # Examples -/// -/// ## Use the original scalar name -/// -/// ```rust -/// use async_graphql::*; -/// -/// #[derive(NewType)] -/// struct Weight(f64); -/// -/// struct Query; -/// -/// #[Object] -/// impl Query { -/// async fn value(&self) -> Weight { -/// Weight(1.234) -/// } -/// } -/// -/// // Test conversion -/// let weight: Weight = 10f64.into(); -/// let weight_f64: f64 = weight.into(); -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish(); -/// -/// let res = schema.execute("{ value }").await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "value": 1.234, -/// })); -/// -/// let res = schema.execute(r#" -/// { -/// __type(name: "Query") { -/// fields { -/// name type { -/// kind -/// ofType { name } -/// } -/// } -/// } -/// }"#).await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "__type": { -/// "fields": [{ -/// "name": "value", -/// "type": { -/// "kind": "NON_NULL", -/// "ofType": { -/// "name": "Float" -/// } -/// } -/// }] -/// } -/// })); -/// }); -/// ``` -/// -/// ## Define a new scalar -/// -/// ```rust -/// use async_graphql::*; -/// -/// /// Widget NewType -/// #[derive(NewType)] -/// #[graphql(name)] // or: #[graphql(name = true)], #[graphql(name = "Weight")] -/// struct Weight(f64); -/// -/// struct Query; -/// -/// #[Object] -/// impl Query { -/// async fn value(&self) -> Weight { -/// Weight(1.234) -/// } -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish(); -/// -/// let res = schema.execute("{ value }").await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "value": 1.234, -/// })); -/// -/// let res = schema.execute(r#" -/// { -/// __type(name: "Query") { -/// fields { -/// name type { -/// kind -/// ofType { name } -/// } -/// } -/// } -/// }"#).await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "__type": { -/// "fields": [{ -/// "name": "value", -/// "type": { -/// "kind": "NON_NULL", -/// "ofType": { -/// "name": "Weight" -/// } -/// } -/// }] -/// } -/// })); -/// -/// assert_eq!(schema.execute(r#"{ __type(name: "Weight") { name description } }"#). -/// await.into_result().unwrap().data, value!({ -/// "__type": { -/// "name": "Weight", "description": "Widget NewType" -/// } -/// })); -/// }); -/// ``` -pub use async_graphql_derive::NewType; - -/// Define a merged object with multiple object types. -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).* -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y | -/// | extends | Add fields to an entity that's defined in another service | bool | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | serial | Resolve each field sequentially. | bool | Y | -/// -/// # Examples -/// -/// ```rust -/// use async_graphql::*; -/// -/// #[derive(SimpleObject)] -/// struct Object1 { -/// a: i32, -/// } -/// -/// #[derive(SimpleObject)] -/// struct Object2 { -/// b: i32, -/// } -/// -/// #[derive(SimpleObject)] -/// struct Object3 { -/// c: i32, -/// } -/// -/// #[derive(MergedObject)] -/// struct MyObj(Object1, Object2, Object3); -/// -/// let obj = MyObj(Object1 { a: 10 }, Object2 { b: 20 }, Object3 { c: 30 }); -/// ``` -pub use async_graphql_derive::MergedObject; - -/// Define a merged subscription with multiple subscription types. -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).* -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | extends | Add fields to an entity that's defined in another service | bool | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// -/// # Examples -/// -/// ```rust -/// use async_graphql::*; -/// use futures_util::stream::Stream; -/// -/// #[derive(Default)] -/// struct Subscription1; -/// -/// #[Subscription] -/// impl Subscription1 { -/// async fn events1(&self) -> impl Stream { -/// futures_util::stream::iter(0..10) -/// } -/// } -/// -/// #[derive(Default)] -/// struct Subscription2; -/// -/// #[Subscription] -/// impl Subscription2 { -/// async fn events2(&self) -> impl Stream { -/// futures_util::stream::iter(10..20) -/// } -/// } -/// -/// #[derive(MergedSubscription, Default)] -/// struct Subscription(Subscription1, Subscription2); -/// ``` -pub use async_graphql_derive::MergedSubscription; - -/// Attach a description to `Object`, `Scalar` or `Subscription`. -/// -/// The three types above use the rustdoc on the implementation block as -/// the GraphQL type description, but if you want to use the rustdoc on the -/// type declaration as the GraphQL type description, you can use that derived macro. -/// -/// # Examples -/// -/// ```rust -/// use async_graphql::*; -/// -/// /// This is MyObj -/// #[derive(Description, Default)] -/// struct MyObj; -/// -/// #[Object(use_type_description)] -/// impl MyObj { -/// async fn value(&self) -> i32 { -/// 100 -/// } -/// } -/// -/// #[derive(SimpleObject, Default)] -/// struct Query { -/// obj: MyObj, -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::new(Query::default(), EmptyMutation, EmptySubscription); -/// assert_eq!( -/// schema -/// .execute(r#"{ __type(name: "MyObj") { description } }"#) -/// .await -/// .data, -/// value!({ -/// "__type": { "description": "This is MyObj" } -/// }) -/// ); -/// }); -/// ``` +#[doc = include_str!("docs/description.md")] pub use async_graphql_derive::Description; - -/// Define a directive for query. -/// -/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/custom_directive.html).* -/// -/// # Macro attributes -/// -/// | Attribute | description | Type | Optional | -/// |---------------|---------------------------|----------|----------| -/// | name | Object name | string | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | repeatable | It means that the directive can be used multiple times in the same location. | bool | Y | -/// | rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE".| string | Y | -/// | locations | Specify the location where the directive is available, multiples are allowed. The possible values is "field", ... | string | N | -/// -/// # Directive attributes -/// -/// | Attribute | description | Type | Optional | -/// |--------------|------------------------------------------|------------ |----------| -/// | name | Argument name | string | Y | -/// | desc | Argument description | string | Y | -/// | default | Use `Default::default` for default value | none | Y | -/// | default | Argument default value | literal | Y | -/// | default_with | Expression to generate default value | code string | Y | -/// | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | -/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | -/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | -/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | -/// -/// # Examples -/// -/// ```rust -/// use async_graphql::*; -/// -/// struct ConcatDirective { -/// value: String, -/// } -/// -/// #[async_trait::async_trait] -/// impl CustomDirective for ConcatDirective { -/// async fn resolve_field(&self, _ctx: &Context<'_>, resolve: ResolveFut<'_>) -> ServerResult> { -/// resolve.await.map(|value| { -/// value.map(|value| match value { -/// Value::String(str) => Value::String(str + &self.value), -/// _ => value, -/// }) -/// }) -/// } -/// } -/// -/// #[Directive(location = "field")] -/// fn concat(value: String) -> impl CustomDirective { -/// ConcatDirective { value } -/// } -/// -/// struct Query; -/// -/// #[Object] -/// impl Query { -/// async fn value(&self) -> &'static str { -/// "abc" -/// } -/// } -/// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription) -/// .directive(concat) -/// .finish(); -/// let res = schema.execute(r#"{ value @concat(value: "def") }"#).await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "value": "abcdef", -/// })); -/// }); -/// ``` +#[doc = include_str!("docs/directive.md")] pub use async_graphql_derive::Directive; +#[doc = include_str!("docs/enum.md")] +pub use async_graphql_derive::Enum; +#[doc = include_str!("docs/input_object.md")] +pub use async_graphql_derive::InputObject; +#[doc = include_str!("docs/interface.md")] +pub use async_graphql_derive::Interface; +#[doc = include_str!("docs/merged_object.md")] +pub use async_graphql_derive::MergedObject; +#[doc = include_str!("docs/merged_subscription.md")] +pub use async_graphql_derive::MergedSubscription; +#[doc = include_str!("docs/newtype.md")] +pub use async_graphql_derive::NewType; +#[doc = include_str!("docs/object.md")] +pub use async_graphql_derive::Object; +#[doc = include_str!("docs/scalar.md")] +pub use async_graphql_derive::Scalar; +#[doc = include_str!("docs/simple_object.md")] +pub use async_graphql_derive::SimpleObject; +#[doc = include_str!("docs/subscription.md")] +pub use async_graphql_derive::Subscription; +#[doc = include_str!("docs/union.md")] +pub use async_graphql_derive::Union; diff --git a/src/registry/cache_control.rs b/src/registry/cache_control.rs index 8d3beac8..10a370a2 100644 --- a/src/registry/cache_control.rs +++ b/src/registry/cache_control.rs @@ -5,10 +5,10 @@ /// ```rust /// use async_graphql::*; /// -/// struct QueryRoot; +/// struct Query; /// /// #[Object(cache_control(max_age = 60))] -/// impl QueryRoot { +/// impl Query { /// #[graphql(cache_control(max_age = 30))] /// async fn value1(&self) -> i32 { /// 0 @@ -20,12 +20,12 @@ /// } /// } /// -/// tokio::runtime::Runtime::new().unwrap().block_on(async { -/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); -/// assert_eq!(schema.execute("{ value1 }").await.into_result().unwrap().cache_control, CacheControl { public: true, max_age: 30 }); -/// assert_eq!(schema.execute("{ value2 }").await.into_result().unwrap().cache_control, CacheControl { public: false, max_age: 60 }); -/// assert_eq!(schema.execute("{ value1 value2 }").await.into_result().unwrap().cache_control, CacheControl { public: false, max_age: 30 }); -/// }); +/// # tokio::runtime::Runtime::new().unwrap().block_on(async { +/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); +/// assert_eq!(schema.execute("{ value1 }").await.into_result().unwrap().cache_control, CacheControl { public: true, max_age: 30 }); +/// assert_eq!(schema.execute("{ value2 }").await.into_result().unwrap().cache_control, CacheControl { public: false, max_age: 60 }); +/// assert_eq!(schema.execute("{ value1 value2 }").await.into_result().unwrap().cache_control, CacheControl { public: false, max_age: 30 }); +/// # }); /// ``` #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct CacheControl { diff --git a/src/resolver_utils/scalar.rs b/src/resolver_utils/scalar.rs index 90de393a..f17b01c4 100644 --- a/src/resolver_utils/scalar.rs +++ b/src/resolver_utils/scalar.rs @@ -80,18 +80,16 @@ pub trait ScalarType: Sized + Send { /// } /// } /// -/// tokio::runtime::Runtime::new().unwrap().block_on(async move { -/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); -/// let res = schema.execute(r#"{ value(input: {a: 10, b: {v1: 1, v2: 2} }) }"#).await.into_result().unwrap().data; -/// assert_eq!(res, value!({ -/// "value": { -/// "a": 10, -/// "b": {"v1": 1, "v2": 2}, -/// } -/// })); -/// }); -/// -/// +/// # tokio::runtime::Runtime::new().unwrap().block_on(async move { +/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); +/// let res = schema.execute(r#"{ value(input: {a: 10, b: {v1: 1, v2: 2} }) }"#).await.into_result().unwrap().data; +/// assert_eq!(res, value!({ +/// "value": { +/// "a": 10, +/// "b": {"v1": 1, "v2": 2}, +/// } +/// })); +/// # }); /// ``` #[macro_export] macro_rules! scalar { diff --git a/src/types/connection/mod.rs b/src/types/connection/mod.rs index 51afd1d5..fc818552 100644 --- a/src/types/connection/mod.rs +++ b/src/types/connection/mod.rs @@ -28,7 +28,7 @@ pub struct EmptyFields; /// use async_graphql::*; /// use async_graphql::connection::*; /// -/// struct QueryRoot; +/// struct Query; /// /// struct Numbers; /// @@ -38,7 +38,7 @@ pub struct EmptyFields; /// } /// /// #[Object] -/// impl QueryRoot { +/// impl Query { /// async fn numbers(&self, /// after: Option, /// before: Option, @@ -68,27 +68,27 @@ pub struct EmptyFields; /// } /// } /// -/// tokio::runtime::Runtime::new().unwrap().block_on(async { -/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); +/// # tokio::runtime::Runtime::new().unwrap().block_on(async { +/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); /// -/// assert_eq!(schema.execute("{ numbers(first: 2) { edges { node diff } } }").await.into_result().unwrap().data, value!({ -/// "numbers": { -/// "edges": [ -/// {"node": 0, "diff": 10000}, -/// {"node": 1, "diff": 9999}, -/// ] -/// }, -/// })); +/// assert_eq!(schema.execute("{ numbers(first: 2) { edges { node diff } } }").await.into_result().unwrap().data, value!({ +/// "numbers": { +/// "edges": [ +/// {"node": 0, "diff": 10000}, +/// {"node": 1, "diff": 9999}, +/// ] +/// }, +/// })); /// -/// assert_eq!(schema.execute("{ numbers(last: 2) { edges { node diff } } }").await.into_result().unwrap().data, value!({ -/// "numbers": { -/// "edges": [ -/// {"node": 9998, "diff": 2}, -/// {"node": 9999, "diff": 1}, -/// ] -/// }, -/// })); -/// }); +/// assert_eq!(schema.execute("{ numbers(last: 2) { edges { node diff } } }").await.into_result().unwrap().data, value!({ +/// "numbers": { +/// "edges": [ +/// {"node": 9998, "diff": 2}, +/// {"node": 9999, "diff": 1}, +/// ] +/// }, +/// })); +/// # }); /// ``` pub async fn query( after: Option, @@ -131,10 +131,10 @@ where /// page_info: PageInfo, /// } /// -/// struct QueryRoot; +/// struct Query; /// /// #[Object] -/// impl QueryRoot { +/// impl Query { /// async fn numbers(&self, /// after: Option, /// before: Option, @@ -174,7 +174,7 @@ where /// /// #[tokio::main] /// async fn main() { -/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); +/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); /// /// assert_eq!(schema.execute("{ numbers(first: 2) { edges { node diff } } }").await.into_result().unwrap().data, value!({ /// "numbers": { diff --git a/src/types/empty_mutation.rs b/src/types/empty_mutation.rs index f2a2c581..7b5f2f3c 100644 --- a/src/types/empty_mutation.rs +++ b/src/types/empty_mutation.rs @@ -16,17 +16,17 @@ use crate::{ /// ```rust /// use async_graphql::*; /// -/// struct QueryRoot; +/// struct Query; /// /// #[Object] -/// impl QueryRoot { +/// impl Query { /// async fn value(&self) -> i32 { /// // A GraphQL Object type must define one or more fields. /// 100 /// } /// } /// -/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); +/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); /// ``` #[derive(Default, Copy, Clone)] pub struct EmptyMutation; diff --git a/src/types/maybe_undefined.rs b/src/types/maybe_undefined.rs index fe99a759..2bc99633 100644 --- a/src/types/maybe_undefined.rs +++ b/src/types/maybe_undefined.rs @@ -29,23 +29,23 @@ use crate::{registry, InputType, InputValueError, InputValueResult, Value}; /// } /// } /// -/// tokio::runtime::Runtime::new().unwrap().block_on(async { -/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); -/// let query = r#" -/// { -/// v1:value1(input: 99) -/// v2:value1(input: null) -/// v3:value1 -/// }"#; -/// assert_eq!( -/// schema.execute(query).await.into_result().unwrap().data, -/// value!({ -/// "v1": 99, -/// "v2": 1, -/// "v3": 2, -/// }) -/// ); -/// }); +/// # tokio::runtime::Runtime::new().unwrap().block_on(async { +/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription); +/// let query = r#" +/// { +/// v1:value1(input: 99) +/// v2:value1(input: null) +/// v3:value1 +/// }"#; +/// assert_eq!( +/// schema.execute(query).await.into_result().unwrap().data, +/// value!({ +/// "v1": 99, +/// "v2": 1, +/// "v3": 2, +/// }) +/// ); +/// # }); /// ``` #[allow(missing_docs)] #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] diff --git a/src/types/upload.rs b/src/types/upload.rs index e6552c29..23df60e3 100644 --- a/src/types/upload.rs +++ b/src/types/upload.rs @@ -68,10 +68,10 @@ impl UploadValue { /// ``` /// use async_graphql::*; /// -/// struct MutationRoot; +/// struct Mutation; /// /// #[Object] -/// impl MutationRoot { +/// impl Mutation { /// async fn upload(&self, ctx: &Context<'_>, file: Upload) -> bool { /// println!("upload: filename={}", file.value(ctx).unwrap().filename); /// true @@ -80,7 +80,8 @@ impl UploadValue { /// /// ``` /// # Example Curl Request -/// Assuming you have defined your MutationRoot like in the example above, +/// +/// Assuming you have defined your Mutation like in the example above, /// you can now upload a file `myFile.txt` with the below curl command: /// /// ```curl diff --git a/src/validation/test_harness.rs b/src/validation/test_harness.rs index c691be7a..5f97f505 100644 --- a/src/validation/test_harness.rs +++ b/src/validation/test_harness.rs @@ -288,10 +288,10 @@ impl ComplicatedArgs { } } -pub struct QueryRoot; +pub struct Query; #[Object(internal)] -impl QueryRoot { +impl Query { async fn human(&self, id: Option) -> Option { unimplemented!() } @@ -337,26 +337,26 @@ impl QueryRoot { } } -pub struct MutationRoot; +pub struct Mutation; #[Object(internal)] -impl MutationRoot { +impl Mutation { async fn test_input(&self, #[graphql(default)] input: TestInput) -> i32 { unimplemented!() } } -pub struct SubscriptionRoot; +pub struct Subscription; #[Subscription(internal)] -impl SubscriptionRoot { +impl Subscription { async fn values(&self) -> impl Stream { futures_util::stream::once(async move { 10 }) } } -static TEST_HARNESS: Lazy> = - Lazy::new(|| Schema::new(QueryRoot, MutationRoot, SubscriptionRoot)); +static TEST_HARNESS: Lazy> = + Lazy::new(|| Schema::new(Query, Mutation, Subscription)); pub(crate) fn validate<'a, V, F>( doc: &'a ExecutableDocument, diff --git a/tests/connection.rs b/tests/connection.rs index 6a2c589c..92b09242 100644 --- a/tests/connection.rs +++ b/tests/connection.rs @@ -3,7 +3,7 @@ use async_graphql::*; #[tokio::test] pub async fn test_connection_additional_fields() { - struct QueryRoot; + struct Query; #[derive(SimpleObject)] struct ConnectionFields { @@ -16,7 +16,7 @@ pub async fn test_connection_additional_fields() { } #[Object] - impl QueryRoot { + impl Query { async fn numbers( &self, after: Option, @@ -59,7 +59,7 @@ pub async fn test_connection_additional_fields() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); assert_eq!( schema diff --git a/tests/directive.rs b/tests/directive.rs index d10fd924..1acce702 100644 --- a/tests/directive.rs +++ b/tests/directive.rs @@ -2,20 +2,20 @@ use async_graphql::*; #[tokio::test] pub async fn test_directive_skip() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { pub async fn value(&self) -> i32 { 10 } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let data = schema .execute( r#" - fragment A on QueryRoot { + fragment A on Query { value5: value @skip(if: true) value6: value @skip(if: false) } @@ -49,16 +49,16 @@ pub async fn test_directive_skip() { #[tokio::test] pub async fn test_directive_include() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { pub async fn value(&self) -> i32 { 10 } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let resp = schema .execute( r#" @@ -105,16 +105,16 @@ pub async fn test_custom_directive() { Concat { prefix, suffix } } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { pub async fn value(&self) -> &'static str { "abc" } } - let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription) + let schema = Schema::build(Query, EmptyMutation, EmptySubscription) .directive(concat) .finish(); assert_eq!( diff --git a/tests/federation.rs b/tests/federation.rs index 711c2fbd..82de3365 100644 --- a/tests/federation.rs +++ b/tests/federation.rs @@ -26,7 +26,7 @@ pub async fn test_nested_key() { Some("{ a b c { v } }") ); - struct QueryRoot; + struct Query; #[derive(SimpleObject)] struct MyObj { @@ -36,7 +36,7 @@ pub async fn test_nested_key() { } #[Object] - impl QueryRoot { + impl Query { #[graphql(entity)] async fn find_obj(&self, input: MyInputA) -> MyObj { MyObj { @@ -47,7 +47,7 @@ pub async fn test_nested_key() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let query = r#"{ _entities(representations: [{__typename: "MyObj", input: {a: 1, b: 2, c: { v: 3 }}}]) { __typename @@ -117,10 +117,10 @@ pub async fn test_federation() { } } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { #[graphql(entity)] async fn find_user_by_id(&self, id: ID) -> User { User { id } @@ -132,7 +132,7 @@ pub async fn test_federation() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let query = r#"{ _entities(representations: [{__typename: "Product", upc: "B00005N5PF"}]) { __typename @@ -183,10 +183,10 @@ pub async fn test_find_entity_with_context() { value: i32, } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { #[graphql(entity)] async fn find_user_by_id(&self, ctx: &Context<'_>, id: ID) -> FieldResult { let loader = ctx.data_unchecked::>(); @@ -198,7 +198,7 @@ pub async fn test_find_entity_with_context() { } } - let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription) + let schema = Schema::build(Query, EmptyMutation, EmptySubscription) .data(DataLoader::new(MyLoader)) .finish(); let query = r#"{ diff --git a/tests/field_features.rs b/tests/field_features.rs index 0329cb47..69ee961d 100644 --- a/tests/field_features.rs +++ b/tests/field_features.rs @@ -14,10 +14,10 @@ pub async fn test_field_features() { value_abc: i32, } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self) -> impl Stream { futures_util::stream::once(async move { 10 }) } @@ -33,10 +33,10 @@ pub async fn test_field_features() { } } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } @@ -62,7 +62,7 @@ pub async fn test_field_features() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let query = "{ value }"; assert_eq!( schema.execute(query).await.data, @@ -83,7 +83,7 @@ pub async fn test_field_features() { assert_eq!( schema.execute(query).await.into_result().unwrap_err(), vec![ServerError { - message: r#"Unknown field "valueAbc" on type "QueryRoot". Did you mean "value"?"# + message: r#"Unknown field "valueAbc" on type "Query". Did you mean "value"?"# .to_owned(), source: None, locations: vec![Pos { column: 3, line: 1 }], @@ -149,7 +149,7 @@ pub async fn test_field_features() { .unwrap() .errors, vec![ServerError { - message: r#"Unknown field "valuesAbc" on type "SubscriptionRoot". Did you mean "values", "valuesBson"?"#.to_owned(), + message: r#"Unknown field "valuesAbc" on type "Subscription". Did you mean "values", "valuesBson"?"#.to_owned(), source: None, locations: vec![Pos { column: 16, diff --git a/tests/list.rs b/tests/list.rs index 90a62a09..8084b6ff 100644 --- a/tests/list.rs +++ b/tests/list.rs @@ -118,10 +118,10 @@ pub async fn test_list_type() { #[tokio::test] pub async fn test_array_type() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn values(&self) -> [i32; 6] { [1, 2, 3, 4, 5, 6] } @@ -132,7 +132,7 @@ pub async fn test_array_type() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); assert_eq!( schema diff --git a/tests/mutation.rs b/tests/mutation.rs index 383d5638..853d0347 100644 --- a/tests/mutation.rs +++ b/tests/mutation.rs @@ -7,19 +7,19 @@ use tokio::sync::Mutex; pub async fn test_root_mutation_execution_order() { type List = Arc>>; - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct MutationRoot; + struct Mutation; #[Object] - impl MutationRoot { + impl Mutation { async fn append1(&self, ctx: &Context<'_>) -> bool { tokio::time::sleep(Duration::from_secs(1)).await; ctx.data_unchecked::().lock().await.push(1); @@ -34,7 +34,7 @@ pub async fn test_root_mutation_execution_order() { } let list = List::default(); - let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription) + let schema = Schema::build(Query, Mutation, EmptySubscription) .data(list.clone()) .finish(); schema.execute("mutation { append1 append2 }").await; @@ -43,25 +43,25 @@ pub async fn test_root_mutation_execution_order() { #[tokio::test] pub async fn test_mutation_fragment() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct MutationRoot; + struct Mutation; #[Object] - impl MutationRoot { + impl Mutation { async fn action(&self) -> bool { true } } - let schema = Schema::new(QueryRoot, MutationRoot, EmptySubscription); + let schema = Schema::new(Query, Mutation, EmptySubscription); let resp = schema .execute( r#" @@ -69,7 +69,7 @@ pub async fn test_mutation_fragment() { ... { actionInUnnamedFragment: action } - ... on MutationRoot { + ... on Mutation { actionInNamedFragment: action } }"#, @@ -105,26 +105,26 @@ pub async fn test_serial_object() { } } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct MutationRoot; + struct Mutation; #[Object] - impl MutationRoot { + impl Mutation { async fn obj(&self) -> MyObj { MyObj } } let list = List::default(); - let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription) + let schema = Schema::build(Query, Mutation, EmptySubscription) .data(list.clone()) .finish(); schema.execute("mutation { obj { append1 append2 } }").await; @@ -156,26 +156,26 @@ pub async fn test_serial_simple_object() { } } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct MutationRoot; + struct Mutation; #[Object] - impl MutationRoot { + impl Mutation { async fn obj(&self) -> MyObj { MyObj { value: 10 } } } let list = List::default(); - let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription) + let schema = Schema::build(Query, Mutation, EmptySubscription) .data(list.clone()) .finish(); schema.execute("mutation { obj { append1 append2 } }").await; @@ -224,26 +224,26 @@ pub async fn test_serial_merged_object() { } } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct MutationRoot; + struct Mutation; #[Object] - impl MutationRoot { + impl Mutation { async fn obj(&self) -> MyObj { MyObj(MyObj1, MyObj2) } } let list = List::default(); - let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription) + let schema = Schema::build(Query, Mutation, EmptySubscription) .data(list.clone()) .finish(); schema diff --git a/tests/raw_ident.rs b/tests/raw_ident.rs index 663a5798..5b0beef2 100644 --- a/tests/raw_ident.rs +++ b/tests/raw_ident.rs @@ -38,16 +38,16 @@ pub async fn test_input_value_custom_error() { } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn r#type(&self) -> impl Stream { futures_util::stream::iter(0..10) } } - let schema = Schema::new(Query, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let query = r#" { type(match: 99) diff --git a/tests/schema.rs b/tests/schema.rs index d18c68d9..304960e0 100644 --- a/tests/schema.rs +++ b/tests/schema.rs @@ -3,16 +3,16 @@ use async_graphql::*; #[tokio::test] pub async fn test_schema_default() { #[derive(Default)] - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - type MySchema = Schema; + type MySchema = Schema; let _schema = MySchema::default(); } @@ -20,10 +20,10 @@ pub async fn test_schema_default() { #[tokio::test] pub async fn test_http_headers() { #[derive(Default)] - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self, ctx: &Context<'_>) -> i32 { ctx.insert_http_header("A", "1"); 10 @@ -35,7 +35,7 @@ pub async fn test_http_headers() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let resp = schema.execute("{ value }").await; assert_eq!(resp.http_headers.get("A").map(|s| &**s), Some("1")); diff --git a/tests/subscription.rs b/tests/subscription.rs index 778b523d..78090d71 100644 --- a/tests/subscription.rs +++ b/tests/subscription.rs @@ -1,10 +1,10 @@ use async_graphql::*; use futures_util::stream::{Stream, StreamExt, TryStreamExt}; -struct QueryRoot; +struct Query; #[Object] -impl QueryRoot { +impl Query { async fn value(&self) -> i32 { 10 } @@ -18,10 +18,10 @@ pub async fn test_subscription() { b: i32, } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self, start: i32, end: i32) -> impl Stream { futures_util::stream::iter(start..end) } @@ -31,7 +31,7 @@ pub async fn test_subscription() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); { let mut stream = schema @@ -59,10 +59,10 @@ pub async fn test_subscription() { #[tokio::test] pub async fn test_subscription_with_ctx_data() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } @@ -77,10 +77,10 @@ pub async fn test_subscription_with_ctx_data() { } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self, ctx: &Context<'_>) -> impl Stream { let value = *ctx.data_unchecked::(); futures_util::stream::once(async move { value }) @@ -91,7 +91,7 @@ pub async fn test_subscription_with_ctx_data() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); { let mut stream = schema @@ -108,21 +108,21 @@ pub async fn test_subscription_with_ctx_data() { #[tokio::test] pub async fn test_subscription_with_token() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; struct Token(String); #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self, ctx: &Context<'_>) -> Result> { if ctx.data_unchecked::().0 != "123456" { return Err("forbidden".into()); @@ -131,7 +131,7 @@ pub async fn test_subscription_with_token() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); { let mut stream = schema @@ -163,25 +163,25 @@ pub async fn test_subscription_inline_fragment() { b: i32, } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn events(&self, start: i32, end: i32) -> impl Stream { futures_util::stream::iter((start..end).map(|n| Event { a: n, b: n * 10 })) } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let mut stream = schema .execute_stream( r#" @@ -219,16 +219,16 @@ pub async fn test_subscription_fragment() { Event(Event), } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn events(&self, start: i32, end: i32) -> impl Stream { futures_util::stream::iter((start..end).map(|n| Event { a: n, b: n * 10 })) } } - let schema = Schema::build(QueryRoot, EmptyMutation, SubscriptionRoot) + let schema = Schema::build(Query, EmptyMutation, Subscription) .register_output_type::() .finish(); let mut stream = schema @@ -268,16 +268,16 @@ pub async fn test_subscription_fragment2() { Event(Event), } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn events(&self, start: i32, end: i32) -> impl Stream { futures_util::stream::iter((start..end).map(|n| Event { a: n, b: n * 10 })) } } - let schema = Schema::build(QueryRoot, EmptyMutation, SubscriptionRoot) + let schema = Schema::build(Query, EmptyMutation, Subscription) .register_output_type::() .finish(); let mut stream = schema @@ -321,16 +321,16 @@ pub async fn test_subscription_error() { } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn events(&self) -> impl Stream { futures_util::stream::iter((0..10).map(|n| Event { value: n })) } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let mut stream = schema .execute_stream("subscription { events { value } }") .map(|resp| resp.into_result()) @@ -363,10 +363,10 @@ pub async fn test_subscription_error() { #[tokio::test] pub async fn test_subscription_fieldresult() { - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self) -> impl Stream> { futures_util::stream::iter(0..5) .map(Result::Ok) @@ -376,7 +376,7 @@ pub async fn test_subscription_fieldresult() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let mut stream = schema.execute_stream("subscription { values }"); for i in 0i32..5 { assert_eq!( diff --git a/tests/subscription_websocket_graphql_ws.rs b/tests/subscription_websocket_graphql_ws.rs index 1b123ee9..503e9bd2 100644 --- a/tests/subscription_websocket_graphql_ws.rs +++ b/tests/subscription_websocket_graphql_ws.rs @@ -10,25 +10,25 @@ use futures_util::SinkExt; #[tokio::test] pub async fn test_subscription_ws_transport() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self) -> impl Stream { futures_util::stream::iter(0..10) } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::GraphQLWS); @@ -88,19 +88,19 @@ pub async fn test_subscription_ws_transport() { pub async fn test_subscription_ws_transport_with_token() { struct Token(String); - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self, ctx: &Context<'_>) -> Result> { if ctx.data_unchecked::().0 != "123456" { return Err("forbidden".into()); @@ -109,7 +109,7 @@ pub async fn test_subscription_ws_transport_with_token() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema.clone(), rx, WebSocketProtocols::GraphQLWS) .on_connection_init(|value| async { @@ -245,25 +245,25 @@ pub async fn test_subscription_ws_transport_error() { } } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn events(&self) -> impl Stream { futures_util::stream::iter((0..10).map(|n| Event { value: n })) } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::GraphQLWS); @@ -326,25 +326,25 @@ pub async fn test_subscription_ws_transport_error() { #[tokio::test] pub async fn test_subscription_init_error() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn events(&self) -> impl Stream { futures_util::stream::once(async move { 10 }) } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::GraphQLWS) .on_connection_init(|_| async move { Err("Error!".into()) }); @@ -366,25 +366,25 @@ pub async fn test_subscription_init_error() { #[tokio::test] pub async fn test_subscription_too_many_initialisation_requests_error() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn events(&self) -> impl Stream { futures_util::stream::once(async move { 10 }) } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::GraphQLWS); @@ -421,16 +421,16 @@ pub async fn test_subscription_too_many_initialisation_requests_error() { #[tokio::test] pub async fn test_query_over_websocket() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 999 } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::GraphQLWS); @@ -483,16 +483,16 @@ pub async fn test_query_over_websocket() { #[tokio::test] pub async fn test_start_before_connection_init() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 999 } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::GraphQLWS); @@ -540,19 +540,19 @@ pub async fn test_stream_drop() { } } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 999 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self, ctx: &Context<'_>) -> impl Stream { TestStream { inner: Box::pin(async_stream::stream! { @@ -567,7 +567,7 @@ pub async fn test_stream_drop() { } let dropped = Dropped::default(); - let schema = Schema::build(QueryRoot, EmptyMutation, SubscriptionRoot) + let schema = Schema::build(Query, EmptyMutation, Subscription) .data(dropped.clone()) .finish(); let (mut tx, rx) = mpsc::unbounded(); @@ -651,25 +651,25 @@ pub async fn test_stream_drop() { #[tokio::test] pub async fn test_ping_pong() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self) -> impl Stream { futures_util::stream::iter(0..10) } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::GraphQLWS); diff --git a/tests/subscription_websocket_subscriptions_transport_ws.rs b/tests/subscription_websocket_subscriptions_transport_ws.rs index dfc924c2..fb170a3c 100644 --- a/tests/subscription_websocket_subscriptions_transport_ws.rs +++ b/tests/subscription_websocket_subscriptions_transport_ws.rs @@ -6,25 +6,25 @@ use futures_util::SinkExt; #[tokio::test] pub async fn test_subscription_ws_transport() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self) -> impl Stream { futures_util::stream::iter(0..10) } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::SubscriptionsTransportWS); @@ -84,19 +84,19 @@ pub async fn test_subscription_ws_transport() { pub async fn test_subscription_ws_transport_with_token() { struct Token(String); - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn values(&self, ctx: &Context<'_>) -> Result> { if ctx.data_unchecked::().0 != "123456" { return Err("forbidden".into()); @@ -105,7 +105,7 @@ pub async fn test_subscription_ws_transport_with_token() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::SubscriptionsTransportWS) .on_connection_init(|value| async { @@ -187,25 +187,25 @@ pub async fn test_subscription_ws_transport_error() { } } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn events(&self) -> impl Stream { futures_util::stream::iter((0..10).map(|n| Event { value: n })) } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::SubscriptionsTransportWS); @@ -268,25 +268,25 @@ pub async fn test_subscription_ws_transport_error() { #[tokio::test] pub async fn test_subscription_too_many_initialisation_requests_error() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 10 } } - struct SubscriptionRoot; + struct Subscription; #[Subscription] - impl SubscriptionRoot { + impl Subscription { async fn events(&self) -> impl Stream { futures_util::stream::once(async move { 10 }) } } - let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); + let schema = Schema::new(Query, EmptyMutation, Subscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::SubscriptionsTransportWS); @@ -328,16 +328,16 @@ pub async fn test_subscription_too_many_initialisation_requests_error() { #[tokio::test] pub async fn test_query_over_websocket() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 999 } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::SubscriptionsTransportWS); @@ -390,16 +390,16 @@ pub async fn test_query_over_websocket() { #[tokio::test] pub async fn test_start_before_connection_init() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn value(&self) -> i32 { 999 } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let (mut tx, rx) = mpsc::unbounded(); let mut stream = http::WebSocket::new(schema, rx, WebSocketProtocols::SubscriptionsTransportWS); diff --git a/tests/variables.rs b/tests/variables.rs index 0239c216..8cd8c376 100644 --- a/tests/variables.rs +++ b/tests/variables.rs @@ -3,10 +3,10 @@ use std::collections::HashMap; #[tokio::test] pub async fn test_variables() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { pub async fn int_val(&self, value: i32) -> i32 { value } @@ -16,7 +16,7 @@ pub async fn test_variables() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let query = Request::new( r#" query QueryWithVariables($intVal: Int!, $intListVal: [Int!]!) { @@ -41,16 +41,16 @@ pub async fn test_variables() { #[tokio::test] pub async fn test_variable_default_value() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { pub async fn int_val(&self, value: i32) -> i32 { value } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); assert_eq!( schema .execute( @@ -70,16 +70,16 @@ pub async fn test_variable_default_value() { #[tokio::test] pub async fn test_variable_no_value() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { pub async fn int_val(&self, value: Option) -> i32 { value.unwrap_or(10) } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let resp = schema .execute(Request::new( r#" @@ -101,16 +101,16 @@ pub async fn test_variable_no_value() { #[tokio::test] pub async fn test_variable_null() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { pub async fn int_val(&self, value: Option) -> i32 { value.unwrap_or(10) } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let query = Request::new( r#" query QueryWithVariables($intVal: Int) { @@ -137,10 +137,10 @@ pub async fn test_variable_in_input_object() { value: i32, } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { async fn test(&self, input: MyInput) -> i32 { input.value } @@ -150,16 +150,16 @@ pub async fn test_variable_in_input_object() { } } - struct MutationRoot; + struct Mutation; #[Object] - impl MutationRoot { + impl Mutation { async fn test(&self, input: MyInput) -> i32 { input.value } } - let schema = Schema::new(QueryRoot, MutationRoot, EmptySubscription); + let schema = Schema::new(Query, Mutation, EmptySubscription); // test query { @@ -228,10 +228,10 @@ pub async fn test_variables_enum() { C, } - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { pub async fn value(&self, value: MyEnum) -> i32 { match value { MyEnum::A => 1, @@ -241,7 +241,7 @@ pub async fn test_variables_enum() { } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let query = Request::new( r#" query QueryWithVariables($value1: MyEnum, $value2: MyEnum, $value3: MyEnum) { @@ -269,16 +269,16 @@ pub async fn test_variables_enum() { #[tokio::test] pub async fn test_variables_json() { - struct QueryRoot; + struct Query; #[Object] - impl QueryRoot { + impl Query { pub async fn value(&self, value: Json>) -> i32 { *value.get("a-b").unwrap() } } - let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); + let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let query = Request::new( r#" query QueryWithVariables($value: JSON) {