Merge pull request #64 from DataCorrupted/master
Translate type system almost done with some TODO.
This commit is contained in:
commit
9902b3c339
|
@ -1 +1,21 @@
|
||||||
# Context
|
# Context
|
||||||
|
|
||||||
|
The main goal of `Context` is to acquire global data attached to Schema. **Note that if the return value of resolve function is borrowed from `Context`, you need to explictly state the lifetime of the argument.**
|
||||||
|
|
||||||
|
The following example shows how to borrow data in `Context`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use async_graphql::*;
|
||||||
|
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Query {
|
||||||
|
async fn borrow_from_context_data<'ctx'>(
|
||||||
|
&self,
|
||||||
|
ctx: &'ctx Context<'_>
|
||||||
|
) -> &'ctx String {
|
||||||
|
ctx.data::<String>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -1 +1,34 @@
|
||||||
# Object
|
# Object
|
||||||
|
|
||||||
|
Different from `SimpleObject`, `Object` must have Resolve defined for each field in `impl`.
|
||||||
|
|
||||||
|
**A Resolve function has to be asynchronous. The first argument has to be `&self`, second being optional `Context` and followed by field arguments.**
|
||||||
|
|
||||||
|
Resolve is used to get the value of the field. You can query a database and return the result. **The return type of the function is the type of the field.** You can also return a `async_graphql::FieldResult` so to return an error if it occrs and error message will be send to query result.
|
||||||
|
|
||||||
|
<!--TODO: 全局类型的数据?-->
|
||||||
|
When querying a database, you may need a global data base connection pool, you can use `Schema::data` to attach a global data when creating Schema, the following `value_from_db` function showed how to retrive a database connection from `Context`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use async_graphql::*;
|
||||||
|
|
||||||
|
struct MyObject {
|
||||||
|
value: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl MyObject {
|
||||||
|
async fn value(&self) -> String {
|
||||||
|
self.value.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn value_from_db(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_'>,
|
||||||
|
#[arg(desc = "Id of object")] id: i64
|
||||||
|
) -> FieldResult<String> {
|
||||||
|
let conn = ctx.data::<DbPool>().take();
|
||||||
|
Ok(conn.query_something(id)?.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -1 +1,22 @@
|
||||||
# Enum
|
# Enum
|
||||||
|
|
||||||
|
It's easy to define an `Enum`, here we have an example:
|
||||||
|
|
||||||
|
**Async-graphql can automatically change the name of each item to GraphQL's CONSTANT_CASE convension, you can also use `name` to rename.**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use async_graphql::*;
|
||||||
|
|
||||||
|
#[Enum(desc = "One of the films in the Star Wars Trilogy")]
|
||||||
|
pub enum Episode {
|
||||||
|
#[item(desc = "Released in 1977.")]
|
||||||
|
NewHope,
|
||||||
|
|
||||||
|
#[item(desc = "Released in 1980.")]
|
||||||
|
Empire,
|
||||||
|
|
||||||
|
// rename to `AAA`
|
||||||
|
#[item(name="AAA", desc = "Released in 1983.")]
|
||||||
|
Jedi,
|
||||||
|
}
|
||||||
|
```
|
|
@ -1 +1,31 @@
|
||||||
# InputObject
|
# InputObject
|
||||||
|
|
||||||
|
<!--Input Object and SimpleObject inconsistant space.-->
|
||||||
|
You can define an `Object` as argument, GraphQL calls it `InputObject`.
|
||||||
|
The definition of `InputObject` is similar to [SimpleObject](define_simple_object.md).
|
||||||
|
However, `SimpleObject` can only be used for output and `InputObject` can only be used as input.
|
||||||
|
|
||||||
|
`InputObject` don't need a `#[field]` for each field, every field is `InputValue`.
|
||||||
|
But you can add optional `#[field]` to add description or rename the field.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use async_graphql::*;
|
||||||
|
|
||||||
|
#[InputObject]
|
||||||
|
struct Coordinate {
|
||||||
|
latitude: f64,
|
||||||
|
|
||||||
|
#[field(desc = "...")]
|
||||||
|
longitude: f64
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Mutation;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Mutation {
|
||||||
|
async fn users_at_location(&self, coordinate: Coordinate, radius: f64) -> Vec<User> {
|
||||||
|
// Writes coordination to database.
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -1 +1,48 @@
|
||||||
# Interface
|
# Interface
|
||||||
|
|
||||||
|
`Interface` is used to abstract `Object`s with common fields.
|
||||||
|
`Async-graphql` implemented it as a wrapper.
|
||||||
|
The wrapper will forward Resolve to the `Object` that implemented this `Interface`.
|
||||||
|
Therefore, the `Object`'s fields' type, arguments must match with the `Interface`'s.
|
||||||
|
|
||||||
|
`Async-graphql` implemented auto conversion from `Object` to `Interface`, you only need to call `Into::into`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use async_graphql::*;
|
||||||
|
|
||||||
|
struct Circle {
|
||||||
|
radius: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Circle {
|
||||||
|
async fn area(&self) -> f32 {
|
||||||
|
std::f32::consts::PI * self.radius * self.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn scale(&self, s: f32) -> Shape {
|
||||||
|
Circle { radius: self.radius * s }.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Square {
|
||||||
|
width: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Square {
|
||||||
|
async fn area(&self) -> f32 {
|
||||||
|
self.width * self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn scale(&self, s: f32) -> Shape {
|
||||||
|
Square { width: self.width * s }.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Interface(
|
||||||
|
field(name = "area", type = "f32"),
|
||||||
|
field(name = "scale", type = "Shape", arg(name = "s", type = "f32"))
|
||||||
|
)]
|
||||||
|
struct Shape(Circle, Square);
|
||||||
|
```
|
|
@ -1 +1,18 @@
|
||||||
# SimpleObject
|
# SimpleObject
|
||||||
|
|
||||||
|
`SimpleObject` directly map all field of a struct to GraphQL object, you cannot define a Resolve function on it.
|
||||||
|
|
||||||
|
The example below defined an object `MyObject`, including field `a` and `b`. `c` will be not mapped to GraphQL as it is labelled as `#[field(skip)]`
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use async_graphql::*;
|
||||||
|
|
||||||
|
#[SimpleObject]
|
||||||
|
struct MyObject {
|
||||||
|
a: i32,
|
||||||
|
b: i32,
|
||||||
|
|
||||||
|
#[field(skip)]
|
||||||
|
c: i32,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -1 +1,44 @@
|
||||||
# Union
|
# Union
|
||||||
|
|
||||||
|
The definition of `Union` is similar to `Interface`'s, **but no field allowed.**.
|
||||||
|
The implemention is quite similar for `Async-graphql`.
|
||||||
|
From `Async-graphql`'s perspective, `Union` is a subset of `Interface`.
|
||||||
|
|
||||||
|
The following example modified the definition of `Interface` a little bit and removed fields.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use async_graphql::*;
|
||||||
|
|
||||||
|
struct Circle {
|
||||||
|
radius: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Circle {
|
||||||
|
async fn area(&self) -> f32 {
|
||||||
|
std::f32::consts::PI * self.radius * self.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn scale(&self, s: f32) -> Shape {
|
||||||
|
Circle { radius: self.radius * s }.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Square {
|
||||||
|
width: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Square {
|
||||||
|
async fn area(&self) -> f32 {
|
||||||
|
self.width * self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn scale(&self, s: f32) -> Shape {
|
||||||
|
Square { width: self.width * s }.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Union]
|
||||||
|
struct Shape(Circle, Square);
|
||||||
|
```
|
|
@ -1 +1,28 @@
|
||||||
# Error handling
|
# Error handling
|
||||||
|
|
||||||
|
Resolve can return a `FieldResult`, following is the definition:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
type FieldResult<T> = std::result::Result<T, FieldError>;
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--TODO: 扩展标准的错误输出? -->
|
||||||
|
Any `Error` can be converted to `FieldError` and you can extend error message.
|
||||||
|
|
||||||
|
Following example shows how to parse an input string to integer. When parsing failed, it would return error and attach error message.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use async_graphql::*;
|
||||||
|
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Query {
|
||||||
|
#[field]
|
||||||
|
async fn parse_with_extensions(&self, input: String) -> FieldResult<i32> {
|
||||||
|
Ok("234a"
|
||||||
|
.parse()
|
||||||
|
.map_err(|err| err.extend_with(|_| json!({"code": 400})))?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -1 +1,3 @@
|
||||||
# Type System
|
# Type System
|
||||||
|
|
||||||
|
`Async-graphql` implemented conversion from GraphQL Object to Rust struct, and it's easy to use.
|
Loading…
Reference in New Issue
Block a user