Update docs

This commit is contained in:
Sunli 2021-11-20 11:16:48 +08:00
parent 296eba3847
commit db83b9440c
39 changed files with 1425 additions and 1427 deletions

View File

@ -31,14 +31,14 @@ use crate::{graphql_batch_opts, GraphQLBadRequest, GraphQLBatchResponse};
///
/// type MySchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;
///
/// 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<Query, Mutation, Subscription>(
schema: Schema<Query, Mutation, Subscription>,

View File

@ -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<Query, Mutation, Subscription>(
schema: Schema<Query, Mutation, Subscription>,
@ -136,27 +136,27 @@ fn default_on_connection_init(_: serde_json::Value) -> Ready<async_graphql::Resu
/// }
/// }
///
/// tokio::runtime::Runtime::new().unwrap().block_on(async {
/// let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot);
/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
/// let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot);
///
/// let filter = warp::ws()
/// .and(graphql_protocol())
/// .map(move |ws: ws::Ws, protocol| {
/// let schema = schema.clone();
/// let filter = warp::ws()
/// .and(graphql_protocol())
/// .map(move |ws: ws::Ws, protocol| {
/// let schema = schema.clone();
///
/// let reply = ws.on_upgrade(move |socket| {
/// GraphQLWebSocket::new(socket, schema, protocol).serve()
/// });
///
/// warp::reply::with_header(
/// reply,
/// "Sec-WebSocket-Protocol",
/// protocol.sec_websocket_protocol(),
/// )
/// let reply = ws.on_upgrade(move |socket| {
/// GraphQLWebSocket::new(socket, schema, protocol).serve()
/// });
///
/// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await;
/// });
/// warp::reply::with_header(
/// reply,
/// "Sec-WebSocket-Protocol",
/// protocol.sec_websocket_protocol(),
/// )
/// });
///
/// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await;
/// # });
/// ```
pub struct GraphQLWebSocket<Sink, Stream, Query, Mutation, Subscription, OnInit> {
sink: Sink,

View File

@ -618,12 +618,12 @@ impl<'a> ContextBase<'a, &'a Positioned<Field>> {
/// }
/// }
///
/// 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 {

View File

@ -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;

View File

@ -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,
},
}));
# });
```

40
src/docs/description.md Normal file
View File

@ -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" }
})
);
# });
```

74
src/docs/directive.md Normal file
View File

@ -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<Option<Value>> {
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",
}));
# });
```

59
src/docs/enum.md Normal file
View File

@ -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" }));
# });
```

60
src/docs/input_object.md Normal file
View File

@ -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 }));
# });
```

140
src/docs/interface.md Normal file
View File

@ -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::<String>()?.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
}
}));
# });
```

40
src/docs/merged_object.md Normal file
View File

@ -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 });
```

View File

@ -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<Item = i32> {
futures_util::stream::iter(0..10)
}
}
#[derive(Default)]
struct Subscription2;
#[Subscription]
impl Subscription2 {
async fn events2(&self) -> impl Stream<Item = i32> {
futures_util::stream::iter(10..20)
}
}
#[derive(MergedSubscription, Default)]
struct Subscription(Subscription1, Subscription2);
```

132
src/docs/newtype.md Normal file
View File

@ -0,0 +1,132 @@
Define a NewType Scalar
It also implements `From<InnerType>` and `Into<InnerType>`.
# 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"
}
}));
# });
```

189
src/docs/object.md Normal file
View File

@ -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<T>`, such as `Vec<i32>`
- Slices, such as `&[i32]`
- `Option<T>`, such as `Option<i32>`
- `BTree<T>`, `HashMap<T>`, `HashSet<T>`, `BTreeSet<T>`, `LinkedList<T>`, `VecDeque<T>`
- GraphQL objects.
- GraphQL enums.
- References to any of the above types, such as `&i32` or `&Option<String>`.
- `Result<T, E>`, such as `Result<i32, E>`
# 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<i32> {
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<Box<dyn MyTrait>> {
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" },
]
}));
# });
```

8
src/docs/scalar.md Normal file
View File

@ -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 |

65
src/docs/simple_object.md Normal file
View File

@ -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,
}));
# });
```

64
src/docs/subscription.md Normal file
View File

@ -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<Item = i32> {
// Returns the number from 0 to `condition`.
futures_util::stream::iter(0..condition)
}
}
```

71
src/docs/union.md Normal file
View File

@ -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<MyUnion> {
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 },
]
}));
# });
```

View File

@ -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;

1126
src/lib.rs

File diff suppressed because it is too large Load Diff

View File

@ -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 {

View File

@ -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 {

View File

@ -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<String>,
/// before: Option<String>,
@ -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<Cursor, Node, ConnectionFields, EdgeFields, F, R, E>(
after: Option<String>,
@ -131,10 +131,10 @@ where
/// page_info: PageInfo,
/// }
///
/// struct QueryRoot;
/// struct Query;
///
/// #[Object]
/// impl QueryRoot {
/// impl Query {
/// async fn numbers(&self,
/// after: Option<String>,
/// before: Option<String>,
@ -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": {

View File

@ -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;

View File

@ -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)]

View File

@ -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

View File

@ -288,10 +288,10 @@ impl ComplicatedArgs {
}
}
pub struct QueryRoot;
pub struct Query;
#[Object(internal)]
impl QueryRoot {
impl Query {
async fn human(&self, id: Option<ID>) -> Option<Human> {
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<Item = i32> {
futures_util::stream::once(async move { 10 })
}
}
static TEST_HARNESS: Lazy<Schema<QueryRoot, MutationRoot, SubscriptionRoot>> =
Lazy::new(|| Schema::new(QueryRoot, MutationRoot, SubscriptionRoot));
static TEST_HARNESS: Lazy<Schema<Query, Mutation, Subscription>> =
Lazy::new(|| Schema::new(Query, Mutation, Subscription));
pub(crate) fn validate<'a, V, F>(
doc: &'a ExecutableDocument,

View File

@ -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<String>,
@ -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

View File

@ -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!(

View File

@ -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<MyObj> {
let loader = ctx.data_unchecked::<DataLoader<MyLoader>>();
@ -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#"{

View File

@ -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<Item = i32> {
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,

View File

@ -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

View File

@ -7,19 +7,19 @@ use tokio::sync::Mutex;
pub async fn test_root_mutation_execution_order() {
type List = Arc<Mutex<Vec<i32>>>;
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::<List>().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

View File

@ -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<Item = i32> {
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)

View File

@ -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<QueryRoot, EmptyMutation, EmptySubscription>;
type MySchema = Schema<Query, EmptyMutation, EmptySubscription>;
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"));

View File

@ -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<Item = i32> {
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<Item = i32> {
let value = *ctx.data_unchecked::<i32>();
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<impl Stream<Item = i32>> {
if ctx.data_unchecked::<Token>().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<Item = Event> {
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<Item = Event> {
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::<MyInterface>()
.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<Item = Event> {
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::<MyInterface>()
.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<Item = Event> {
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<Item = Result<i32>> {
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!(

View File

@ -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<Item = i32> {
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<impl Stream<Item = i32>> {
if ctx.data_unchecked::<Token>().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<Item = Event> {
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<Item = i32> {
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<Item = i32> {
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<Item = i32> {
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<Item = i32> {
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);

View File

@ -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<Item = i32> {
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<impl Stream<Item = i32>> {
if ctx.data_unchecked::<Token>().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<Item = Event> {
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<Item = i32> {
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);

View File

@ -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>) -> 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>) -> 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<HashMap<String, i32>>) -> 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) {