translate section 5 (extensions)

This commit is contained in:
Ethan Fast 2020-05-09 14:39:04 -07:00
parent ab9cefb349
commit 774cde91aa
4 changed files with 216 additions and 1 deletions

View File

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

View File

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

View File

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

View File

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