Check type before resolving in inline fragments to fix panic when using inline fragments with interfaces (#816, #815)

This commit is contained in:
Lukas Kalbertodt 2020-12-09 19:26:59 +01:00 committed by GitHub
parent 4ffd276a5b
commit 2c15ea798c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 29 deletions

View file

@ -51,6 +51,10 @@ where
QueryT::name(info)
}
fn concrete_type_name(&self, context: &Self::Context, info: &Self::TypeInfo) -> String {
self.query_type.concrete_type_name(context, info)
}
fn resolve_field(
&self,
info: &Self::TypeInfo,

View file

@ -1,6 +1,7 @@
use crate::{
ast::InputValue,
executor::Variables,
graphql_value,
schema::model::RootNode,
tests::fixtures::starwars::schema::{Database, Query},
types::scalars::{EmptyMutation, EmptySubscription},
@ -731,3 +732,36 @@ async fn test_object_typename() {
))
);
}
#[tokio::test]
async fn interface_inline_fragment_friends() {
let doc = r#"{
human(id: "1002") {
friends {
name
... on Human { homePlanet }
... on Droid { primaryFunction }
}
}
}"#;
let database = Database::new();
let schema = RootNode::new(
Query,
EmptyMutation::<Database>::new(),
EmptySubscription::<Database>::new(),
);
assert_eq!(
crate::execute(doc, None, &schema, &Variables::new(), &database).await,
Ok((
graphql_value!({"human": {
"friends": [
{"name": "Luke Skywalker", "homePlanet": "Tatooine"},
{"name": "Leia Organa", "homePlanet": "Alderaan"},
{"name": "R2-D2", "primaryFunction": "Astromech"},
],
}}),
vec![],
))
);
}

View file

@ -323,26 +323,30 @@ where
);
if let Some(ref type_condition) = fragment.type_condition {
let sub_result = instance
.resolve_into_type_async(
info,
type_condition.item,
Some(&fragment.selection_set[..]),
&sub_exec,
)
.await;
// 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 {
let sub_result = instance
.resolve_into_type_async(
info,
type_condition.item,
Some(&fragment.selection_set[..]),
&sub_exec,
)
.await;
if let Ok(Value::Object(obj)) = sub_result {
for (k, v) in obj {
async_values.push(AsyncValueFuture::InlineFragment1(async move {
AsyncValue::Field(AsyncField {
name: k,
value: Some(v),
})
}));
if let Ok(Value::Object(obj)) = sub_result {
for (k, v) in obj {
async_values.push(AsyncValueFuture::InlineFragment1(async move {
AsyncValue::Field(AsyncField {
name: k,
value: Some(v),
})
}));
}
} else if let Err(e) = sub_result {
sub_exec.push_error_at(e, *start_pos);
}
} else if let Err(e) = sub_result {
sub_exec.push_error_at(e, *start_pos);
}
} else {
async_values.push(AsyncValueFuture::InlineFragment2(async move {

View file

@ -532,19 +532,23 @@ where
);
if let Some(ref type_condition) = fragment.type_condition {
let sub_result = instance.resolve_into_type(
info,
type_condition.item,
Some(&fragment.selection_set[..]),
&sub_exec,
);
// 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 {
let sub_result = instance.resolve_into_type(
info,
type_condition.item,
Some(&fragment.selection_set[..]),
&sub_exec,
);
if let Ok(Value::Object(object)) = sub_result {
for (k, v) in object {
merge_key_into(result, &k, v);
if let Ok(Value::Object(object)) = sub_result {
for (k, v) in object {
merge_key_into(result, &k, v);
}
} else if let Err(e) = sub_result {
sub_exec.push_error_at(e, *start_pos);
}
} else if let Err(e) = sub_result {
sub_exec.push_error_at(e, *start_pos);
}
} else if !resolve_selection_set_into(
instance,