#![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 { 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 { 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 for MyLoader { type Value = MyObj; type Error = Infallible; async fn load(&self, keys: &[ID]) -> Result, 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 { let loader = ctx.data_unchecked::>(); 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, }] ); }