Allow spreading interface fragments on unions and other interfaces (#965, #798)

This commit is contained in:
Kai Ren 2021-07-19 14:06:47 +03:00 committed by GitHub
parent 88a7571b30
commit 7597523720
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 215 additions and 8 deletions

View file

@ -0,0 +1,195 @@
use juniper::{
graphql_interface, graphql_object, graphql_value, EmptyMutation, EmptySubscription,
GraphQLObject, GraphQLUnion, RootNode, Variables,
};
#[graphql_interface(for = [Human, Droid])]
trait Character {
fn id(&self) -> &str;
}
#[derive(GraphQLObject)]
#[graphql(impl = CharacterValue)]
struct Human {
id: String,
home_planet: String,
}
#[graphql_interface]
impl Character for Human {
fn id(&self) -> &str {
&self.id
}
}
#[derive(GraphQLObject)]
#[graphql(impl = CharacterValue)]
struct Droid {
id: String,
primary_function: String,
}
#[graphql_interface]
impl Character for Droid {
fn id(&self) -> &str {
&self.id
}
}
#[derive(GraphQLUnion)]
enum FieldResult {
Human(Human),
Droid(Droid),
}
#[derive(Clone, Copy)]
enum Query {
Human,
Droid,
}
#[graphql_object]
impl Query {
fn field(&self) -> FieldResult {
match self {
Self::Human => FieldResult::Human(Human {
id: "human-32".to_owned(),
home_planet: "earth".to_owned(),
}),
Self::Droid => FieldResult::Droid(Droid {
id: "droid-99".to_owned(),
primary_function: "run".to_owned(),
}),
}
}
}
type Schema = RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;
#[tokio::test]
async fn test_interface_inline_fragment_on_union() {
let query = r#"
query Query {
field {
__typename
... on Character {
id
}
... on Human {
homePlanet
}
... on Droid {
primaryFunction
}
}
}
"#;
let (res, errors) = juniper::execute(
query,
None,
&Schema::new(Query::Human, EmptyMutation::new(), EmptySubscription::new()),
&Variables::new(),
&(),
)
.await
.unwrap();
assert_eq!(errors.len(), 0);
assert_eq!(
res,
graphql_value!({
"field": {
"__typename": "Human",
"id": "human-32",
"homePlanet": "earth",
},
}),
);
let (res, errors) = juniper::execute_sync(
query,
None,
&Schema::new(Query::Droid, EmptyMutation::new(), EmptySubscription::new()),
&Variables::new(),
&(),
)
.unwrap();
assert_eq!(errors.len(), 0);
assert_eq!(
res,
graphql_value!({
"field": {
"__typename": "Droid",
"id": "droid-99",
"primaryFunction": "run",
},
}),
);
}
#[tokio::test]
async fn test_interface_fragment_on_union() {
let query = r#"
query Query {
field {
__typename
... CharacterFragment
... on Human {
homePlanet
}
... on Droid {
primaryFunction
}
}
}
fragment CharacterFragment on Character {
id
}
"#;
let (res, errors) = juniper::execute(
query,
None,
&Schema::new(Query::Human, EmptyMutation::new(), EmptySubscription::new()),
&Variables::new(),
&(),
)
.await
.unwrap();
assert_eq!(errors.len(), 0);
assert_eq!(
res,
graphql_value!({
"field": {
"__typename": "Human",
"id": "human-32",
"homePlanet": "earth",
},
}),
);
let (res, errors) = juniper::execute_sync(
query,
None,
&Schema::new(Query::Droid, EmptyMutation::new(), EmptySubscription::new()),
&Variables::new(),
&(),
)
.unwrap();
assert_eq!(errors.len(), 0);
assert_eq!(
res,
graphql_value!({
"field": {
"__typename": "Droid",
"id": "droid-99",
"primaryFunction": "run",
},
}),
);
}

View file

@ -19,6 +19,8 @@ mod issue_407;
#[cfg(test)]
mod issue_500;
#[cfg(test)]
mod issue_798;
#[cfg(test)]
mod issue_914;
#[cfg(test)]
mod issue_922;

View file

@ -1,6 +1,6 @@
# master
- No changes yet
- Allow spreading interface fragments on unions and other interfaces ([#965](https://github.com/graphql-rust/juniper/pull/965), [#798](https://github.com/graphql-rust/juniper/issues/798))
# [[0.15.7] 2021-07-08](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.7)

View file

@ -33,7 +33,7 @@ type Schema =
RootNode<'static, MyQuery, EmptyMutation<MyContext>, MySubscription, DefaultScalarValue>;
fn run<O>(f: impl std::future::Future<Output = O>) -> O {
let mut rt = tokio::runtime::Runtime::new().unwrap();
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(f)
}

View file

@ -307,7 +307,9 @@ where
let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
let type_name = instance.type_name(info);
if fragment.type_condition.item == concrete_type_name
if executor
.schema()
.is_named_subtype(&concrete_type_name, &fragment.type_condition.item)
|| Some(fragment.type_condition.item) == type_name
{
let sub_result = instance
@ -351,11 +353,14 @@ where
if let Some(ref type_condition) = fragment.type_condition {
// Check whether the type matches the type condition.
let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
if type_condition.item == concrete_type_name {
if executor
.schema()
.is_named_subtype(&concrete_type_name, &type_condition.item)
{
let sub_result = instance
.resolve_into_type_async(
info,
type_condition.item,
&concrete_type_name,
Some(&fragment.selection_set[..]),
&sub_exec,
)

View file

@ -516,7 +516,9 @@ where
let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
let type_name = instance.type_name(info);
if fragment.type_condition.item == concrete_type_name
if executor
.schema()
.is_named_subtype(&concrete_type_name, &fragment.type_condition.item)
|| Some(fragment.type_condition.item) == type_name
{
let sub_result = instance.resolve_into_type(
@ -552,10 +554,13 @@ where
if let Some(ref type_condition) = fragment.type_condition {
// Check whether the type matches the type condition.
let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
if type_condition.item == concrete_type_name {
if executor
.schema()
.is_named_subtype(&concrete_type_name, &type_condition.item)
{
let sub_result = instance.resolve_into_type(
info,
type_condition.item,
&concrete_type_name,
Some(&fragment.selection_set[..]),
&sub_exec,
);