254 lines
6.0 KiB
Rust
254 lines
6.0 KiB
Rust
#![allow(unreachable_code)]
|
|
|
|
use std::collections::HashMap;
|
|
use std::convert::Infallible;
|
|
|
|
use async_graphql::dataloader::{DataLoader, Loader};
|
|
use async_graphql::*;
|
|
|
|
#[tokio::test]
|
|
pub async fn test_nested_key() {
|
|
#[derive(InputObject)]
|
|
struct MyInputA {
|
|
a: i32,
|
|
b: i32,
|
|
c: MyInputB,
|
|
}
|
|
|
|
#[derive(InputObject)]
|
|
struct MyInputB {
|
|
v: i32,
|
|
}
|
|
|
|
assert_eq!(MyInputB::federation_fields().as_deref(), Some("{ v }"));
|
|
assert_eq!(
|
|
MyInputA::federation_fields().as_deref(),
|
|
Some("{ a b c { v } }")
|
|
);
|
|
|
|
struct QueryRoot;
|
|
|
|
#[derive(SimpleObject)]
|
|
struct MyObj {
|
|
a: i32,
|
|
b: i32,
|
|
c: i32,
|
|
}
|
|
|
|
#[Object]
|
|
impl QueryRoot {
|
|
#[graphql(entity)]
|
|
async fn find_obj(&self, input: MyInputA) -> MyObj {
|
|
MyObj {
|
|
a: input.a,
|
|
b: input.b,
|
|
c: input.c.v,
|
|
}
|
|
}
|
|
}
|
|
|
|
let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
|
let query = r#"{
|
|
_entities(representations: [{__typename: "MyObj", input: {a: 1, b: 2, c: { v: 3 }}}]) {
|
|
__typename
|
|
... on MyObj {
|
|
a b c
|
|
}
|
|
}
|
|
}"#;
|
|
assert_eq!(
|
|
schema.execute(query).await.into_result().unwrap().data,
|
|
value!({
|
|
"_entities": [
|
|
{"__typename": "MyObj", "a": 1, "b": 2, "c": 3},
|
|
]
|
|
})
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
pub async fn test_federation() {
|
|
struct User {
|
|
id: ID,
|
|
}
|
|
|
|
#[Object(extends)]
|
|
impl User {
|
|
#[graphql(external)]
|
|
async fn id(&self) -> &ID {
|
|
&self.id
|
|
}
|
|
|
|
async fn reviews(&self) -> Vec<Review> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
struct Review;
|
|
|
|
#[Object]
|
|
impl Review {
|
|
async fn body(&self) -> String {
|
|
todo!()
|
|
}
|
|
|
|
async fn author(&self) -> User {
|
|
todo!()
|
|
}
|
|
|
|
async fn product(&self) -> Product {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
struct Product {
|
|
upc: String,
|
|
}
|
|
|
|
#[Object(extends)]
|
|
impl Product {
|
|
#[graphql(external)]
|
|
async fn upc(&self) -> &str {
|
|
&self.upc
|
|
}
|
|
|
|
async fn reviews(&self) -> Vec<Review> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
struct QueryRoot;
|
|
|
|
#[Object]
|
|
impl QueryRoot {
|
|
#[graphql(entity)]
|
|
async fn find_user_by_id(&self, id: ID) -> User {
|
|
User { id }
|
|
}
|
|
|
|
#[graphql(entity)]
|
|
async fn find_product_by_upc(&self, upc: String) -> Product {
|
|
Product { upc }
|
|
}
|
|
}
|
|
|
|
let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
|
let query = r#"{
|
|
_entities(representations: [{__typename: "Product", upc: "B00005N5PF"}]) {
|
|
__typename
|
|
... on Product {
|
|
upc
|
|
}
|
|
}
|
|
}"#;
|
|
assert_eq!(
|
|
schema.execute(query).await.into_result().unwrap().data,
|
|
value!({
|
|
"_entities": [
|
|
{"__typename": "Product", "upc": "B00005N5PF"},
|
|
]
|
|
})
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
pub async fn test_find_entity_with_context() {
|
|
struct MyLoader;
|
|
|
|
#[async_trait::async_trait]
|
|
impl Loader<ID> for MyLoader {
|
|
type Value = MyObj;
|
|
type Error = Infallible;
|
|
|
|
async fn load(&self, keys: &[ID]) -> Result<HashMap<ID, Self::Value>, Self::Error> {
|
|
Ok(keys
|
|
.iter()
|
|
.filter(|id| id.as_str() != "999")
|
|
.map(|id| {
|
|
(
|
|
id.clone(),
|
|
MyObj {
|
|
id: id.clone(),
|
|
value: 999,
|
|
},
|
|
)
|
|
})
|
|
.collect())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, SimpleObject)]
|
|
struct MyObj {
|
|
id: ID,
|
|
value: i32,
|
|
}
|
|
|
|
struct QueryRoot;
|
|
|
|
#[Object]
|
|
impl QueryRoot {
|
|
#[graphql(entity)]
|
|
async fn find_user_by_id(&self, ctx: &Context<'_>, id: ID) -> FieldResult<MyObj> {
|
|
let loader = ctx.data_unchecked::<DataLoader<MyLoader>>();
|
|
loader
|
|
.load_one(id)
|
|
.await
|
|
.unwrap()
|
|
.ok_or_else(|| "Not found".into())
|
|
}
|
|
}
|
|
|
|
let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription)
|
|
.data(DataLoader::new(MyLoader))
|
|
.finish();
|
|
let query = r#"{
|
|
_entities(representations: [
|
|
{__typename: "MyObj", id: "1"},
|
|
{__typename: "MyObj", id: "2"},
|
|
{__typename: "MyObj", id: "3"},
|
|
{__typename: "MyObj", id: "4"}
|
|
]) {
|
|
__typename
|
|
... on MyObj {
|
|
id
|
|
value
|
|
}
|
|
}
|
|
}"#;
|
|
assert_eq!(
|
|
schema.execute(query).await.into_result().unwrap().data,
|
|
value!({
|
|
"_entities": [
|
|
{"__typename": "MyObj", "id": "1", "value": 999 },
|
|
{"__typename": "MyObj", "id": "2", "value": 999 },
|
|
{"__typename": "MyObj", "id": "3", "value": 999 },
|
|
{"__typename": "MyObj", "id": "4", "value": 999 },
|
|
]
|
|
})
|
|
);
|
|
|
|
let query = r#"{
|
|
_entities(representations: [
|
|
{__typename: "MyObj", id: "999"}
|
|
]) {
|
|
__typename
|
|
... on MyObj {
|
|
id
|
|
value
|
|
}
|
|
}
|
|
}"#;
|
|
assert_eq!(
|
|
schema.execute(query).await.into_result().unwrap_err(),
|
|
vec![ServerError {
|
|
message: "Not found".to_string(),
|
|
locations: vec![Pos {
|
|
line: 2,
|
|
column: 13
|
|
}],
|
|
path: vec![PathSegment::Field("_entities".to_owned())],
|
|
extensions: None,
|
|
}]
|
|
);
|
|
}
|