translate section 5 (extensions)
This commit is contained in:
parent
ab9cefb349
commit
774cde91aa
|
@ -1 +1,15 @@
|
|||
# Apollo Tracing
|
||||
|
||||
Apollo Tracing provides performance analysis results for each step of query. This is an extension to `Schema`, and the performance analysis results are stored in `QueryResponse`.
|
||||
|
||||
To enable the Apollo Tracing extension, add the extension when a `Schema` is created.
|
||||
|
||||
```rust
|
||||
use async_graphql::*;
|
||||
use async_graphql::extensions::ApolloTracing;
|
||||
|
||||
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
|
||||
.extension(|| ApolloTracing::default()) // 启用ApolloTracing扩展
|
||||
.finish();
|
||||
|
||||
```
|
|
@ -1 +1,49 @@
|
|||
# Cache control
|
||||
|
||||
Production environments often rely on caching to improve performance.
|
||||
|
||||
A GraphQL query will call multiple resolver functions and each resolver can have a different cache definition. Some may cache for a few seconds, some may cache for a few hours, some may be the same for all users, and some may be different for each session.
|
||||
|
||||
`Async-Graphql` provides a mechanism that allows you to define the cache time and scope for each resolver.
|
||||
|
||||
You can define cache parameters on the object or on its fields. The following example shows two uses of cache control parameters.
|
||||
|
||||
You can use `max_age` parameters to control the age of the cache (in seconds), and you can also use `public` and `private` to control the scope of the cache. When you do not specify it, the scope will default to `public`.
|
||||
|
||||
lWhen querying multiple resolvers, the results of all cache control parameters will be combined and the `max_age` minimum value will be taken. If the scope of any object or field is `private`, the result will be `private`.
|
||||
|
||||
We can use `QueryResponse` to get a merged cache control result from a query result, and call `CacheControl::value` to get the corresponding HTTP header.
|
||||
|
||||
```rust
|
||||
#[Object(cache_control(max_age = 60))]
|
||||
impl Query {
|
||||
#[field(cache_control(max_age = 30))]
|
||||
async fn value1(&self) -> i32 {
|
||||
}
|
||||
|
||||
#[field(cache_control(private))]
|
||||
async fn value2(&self) -> i32 {
|
||||
}
|
||||
|
||||
async fn value3(&self) -> i32 {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The following are different queries corresponding to different cache control results:
|
||||
|
||||
```graphql
|
||||
# max_age=30
|
||||
{ value1 }
|
||||
```
|
||||
|
||||
```graphql
|
||||
# max_age=30, private
|
||||
{ value1 value2 }
|
||||
```
|
||||
|
||||
```graphql
|
||||
# max_age=60
|
||||
{ value3 }
|
||||
```
|
||||
|
||||
|
|
|
@ -1 +1,82 @@
|
|||
# Cursor
|
||||
# Cursor connections
|
||||
|
||||
Relay's cursor connection specification is defined to provide a consistent method for query paging. For more details on the specification see the [GraphQL Cursor Connections Specification](https://facebook.github.io/relay/graphql/connections.htm)。
|
||||
|
||||
It is simple to define a cursor connection in `Async-GraphQL`
|
||||
|
||||
1. Implement `async_graphql::DataSource` and write the `query_operation` function.
|
||||
2. Call `DataSource::query` in the field's resolve function and return the result.
|
||||
|
||||
Here is a simple data source that returns continuous integers:
|
||||
|
||||
```rust
|
||||
use async_graphql::*;
|
||||
|
||||
struct Integers;
|
||||
|
||||
#[DataSource]
|
||||
impl DataSource for Integers {
|
||||
// Type for response
|
||||
type Element = i32;
|
||||
|
||||
// We don't need to extend the edge fields, so this can be empty
|
||||
type EdgeFieldsObj = EmptyEdgeFields;
|
||||
|
||||
async fn query_operation(&self, _ctx: &Context<'_>, operation: &QueryOperation<'_>) -> FieldResult<Connection<Self::Element, Self::EdgeFieldsObj>> {
|
||||
let (start, end) = match operation {
|
||||
// Look from beginning up to limit
|
||||
QueryOperation::First {limit} => {
|
||||
let start = 0;
|
||||
let end = start + *limit as i32;
|
||||
(start, end)
|
||||
}
|
||||
QueryOperation::FirstAfter {after, limit} => {
|
||||
// Look after number up to limit
|
||||
let start = after.parse::<i32>()
|
||||
.ok()
|
||||
.map(|after| after + 1)
|
||||
.unwrap_or(0);
|
||||
(start, end + start + *limit)
|
||||
}
|
||||
// Look backward from last element up to limit
|
||||
QueryOperation::Last {limit} => {
|
||||
let end = 0;
|
||||
let start = end - *limit as i32;
|
||||
(start, end)
|
||||
}
|
||||
QueryOperation::LastBefore {before, limit} => {
|
||||
// Look before number up to limit
|
||||
let end = before.parse::<i32>()
|
||||
.ok()
|
||||
.unwrap_or(0);
|
||||
(end - *limit, end)
|
||||
}
|
||||
// TODO: Need to handle all conditions
|
||||
_ => (0, 10)
|
||||
};
|
||||
|
||||
// Create nodes. Each node is a tuple containing three values: the cursor, extended edge object, and node value
|
||||
let nodes = (start..end).into_iter().map(|n| (n.to_string(), EmptyEdgeFields, n)).collect();
|
||||
|
||||
Ok(Connection::new(None, true, true, nodes))
|
||||
}
|
||||
}
|
||||
|
||||
struct Query;
|
||||
|
||||
#[Object]
|
||||
impl Query {
|
||||
#[field]
|
||||
async fn numbers(&self,
|
||||
ctx: &Context<'_>,
|
||||
after: Option<String>,
|
||||
before: Option<String>,
|
||||
first: Option<i32>,
|
||||
last: Option<i32>,
|
||||
) -> FieldResult<Connection<i32, EmptyEdgeFields>> {
|
||||
// Make the query
|
||||
Integers.query(ctx, after, before, first, last).await
|
||||
}
|
||||
}
|
||||
|
||||
```
|
|
@ -1 +1,73 @@
|
|||
# Input value validators
|
||||
|
||||
|
||||
Arguments to a query ([InputObject](define_input_object.md)) are called `Input Objects` in GraphQL. If the provided input type does not match for a query, the query will return a type mismatch error. But sometimes we want to provide more restrictions on specific types of values. For example, we might want to require that an argument is a valid email address. Async-graphql provides an input validators to solve this problem.
|
||||
|
||||
An input validator can be combined via `and` and `or` operators.
|
||||
|
||||
|
||||
The following is an input validator which checks that a `String` is a valid Email or MAC address:
|
||||
|
||||
|
||||
```rust
|
||||
use async_graphql::*;
|
||||
use async_graphql::validators::{Email, MAC};
|
||||
|
||||
struct Query;
|
||||
|
||||
#[Object]
|
||||
impl Query {
|
||||
async fn input(#[arg(validator(or(Email, MAC(colon = "false"))))] a: String) {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
The following example verifies that the `i32` parameter `a` is greater than 10 and less than 100, or else equal to 0:
|
||||
|
||||
```rust
|
||||
use async_graphql:*;
|
||||
use async_graphql::validators::{IntGreaterThan, IntLessThan, IntEqual};
|
||||
|
||||
struct Query;
|
||||
|
||||
#[Object]
|
||||
impl Query {
|
||||
async fn input(#[validator(
|
||||
or(
|
||||
and(IntGreaterThan(value = "10"), IntLessThan(value = "100")),
|
||||
IntEqual(value = "0")
|
||||
))] a: String) {
|
||||
} {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Custom validator
|
||||
|
||||
Here is an example of a custom validator:
|
||||
|
||||
```rust
|
||||
struct MustBeZero {}
|
||||
|
||||
impl InputValueValidator for InputValueValidator {
|
||||
fn is_valid(&self, value: &Value) -> Option<String> {
|
||||
if let Value::Int(n) = value {
|
||||
if n.as_i64().unwrap() != 0 {
|
||||
// Validation failed
|
||||
Some(format!(
|
||||
"the value is {}, but must be zero",
|
||||
n.as_i64().unwrap(),
|
||||
))
|
||||
} else {
|
||||
// Validation succeeded
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// If the type does not match we can return None and built-in validations
|
||||
// will pick up on the error
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue
Block a user