Update docs
This commit is contained in:
parent
296eba3847
commit
db83b9440c
|
@ -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>,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}));
|
||||
# });
|
||||
```
|
|
@ -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" }
|
||||
})
|
||||
);
|
||||
# });
|
||||
```
|
|
@ -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",
|
||||
}));
|
||||
# });
|
||||
```
|
|
@ -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" }));
|
||||
# });
|
||||
```
|
|
@ -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 }));
|
||||
# });
|
||||
```
|
|
@ -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
|
||||
}
|
||||
}));
|
||||
# });
|
||||
```
|
|
@ -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 });
|
||||
```
|
|
@ -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);
|
||||
```
|
|
@ -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"
|
||||
}
|
||||
}));
|
||||
# });
|
||||
```
|
|
@ -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" },
|
||||
]
|
||||
}));
|
||||
# });
|
||||
```
|
|
@ -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 |
|
|
@ -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,
|
||||
}));
|
||||
# });
|
||||
```
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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 },
|
||||
]
|
||||
}));
|
||||
# });
|
||||
```
|
|
@ -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
1126
src/lib.rs
File diff suppressed because it is too large
Load Diff
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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#"{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"));
|
||||
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue