correct adding implicit interfaces to the schema introspection

If an interface is added manually (as it is not directly referenced by any type) it was not showing up in the introspection.

Now it will be exposed if it is visible and references already accessible types
This commit is contained in:
Edward Rudd 2021-12-04 13:39:36 -05:00
parent 56cf5c1d0c
commit 5c8ff9d3a6
2 changed files with 216 additions and 0 deletions

View File

@ -922,6 +922,19 @@ impl Registry {
traverse_type(ctx, &self.types, &mut visible_types, ty.name());
}
for ty in self.types.values() {
if let MetaType::Interface { possible_types, .. } = ty {
if ty.is_visible(ctx) && !visible_types.contains(ty.name()) {
for type_name in possible_types.iter() {
if visible_types.contains(type_name.as_str()) {
traverse_type(ctx, &self.types, &mut visible_types, ty.name());
break;
}
}
}
}
}
self.types
.values()
.filter_map(|ty| {

View File

@ -0,0 +1,203 @@
use async_graphql::*;
use serde::Deserialize;
#[derive(SimpleObject)]
struct ObjectA {
id: i32,
title: String,
}
#[derive(SimpleObject)]
struct ObjectB {
id: i32,
title: String,
}
#[derive(Interface)]
#[graphql(field(name = "id", type = "&i32"))]
enum ImplicitInterface {
ObjectA(ObjectA),
ObjectB(ObjectB),
}
#[derive(Interface)]
#[graphql(field(name = "title", type = "String"))]
enum ExplicitInterface {
ObjectA(ObjectA),
ObjectB(ObjectB),
}
#[derive(Interface)]
#[graphql(visible = false)]
#[graphql(field(name = "title", type = "String"))]
enum InvisibleInterface {
ObjectA(ObjectA),
ObjectB(ObjectB),
}
#[derive(SimpleObject)]
struct ObjectC {
id: i32,
title: String,
}
#[derive(Interface)]
#[graphql(field(name = "id", type = "&i32"))]
enum UnreferencedInterface {
ObjectC(ObjectC),
}
#[derive(Union)]
enum ObjectUnion {
ObjectA(ObjectA),
ObjectB(ObjectB),
}
struct Query;
#[Object]
impl Query {
async fn implicit(&self) -> ObjectUnion {
ObjectA {
id: 33,
title: "haha".to_string(),
}.into()
}
async fn explicit(&self) -> ExplicitInterface {
ObjectA {
id: 40,
title: "explicit".to_string(),
}.into()
}
}
fn build_schema() -> Schema<Query, EmptyMutation, EmptySubscription> {
Schema::build(Query, EmptyMutation, EmptySubscription)
.register_output_type::<ImplicitInterface>()
.register_output_type::<InvisibleInterface>()
.register_output_type::<UnreferencedInterface>()
.finish()
}
#[tokio::test]
pub async fn test_interface_exports_interfaces_on_object_type() {
#[derive(Deserialize)]
struct QueryResponse {
#[serde(rename = "__type")]
ty: TypeResponse,
}
#[derive(Deserialize)]
struct TypeResponse {
name: String,
kind: String,
interfaces: Vec<InterfaceResponse>,
}
#[derive(Deserialize)]
struct InterfaceResponse {
name: String,
}
let schema = build_schema();
let resp: QueryResponse = from_value(
schema
.execute(r#"{ __type(name: "ObjectA") { name kind interfaces { name }} }"#)
.await
.into_result()
.unwrap()
.data,
)
.unwrap();
assert_eq!(resp.ty.name, "ObjectA");
assert_eq!(resp.ty.kind, "OBJECT");
assert!(resp.ty.interfaces.iter().any(|i| i.name == "ExplicitInterface"));
assert!(resp.ty.interfaces.iter().any(|i| i.name == "ImplicitInterface"));
assert!(!resp.ty.interfaces.iter().any(|i| i.name == "InvisibleInterface"));
}
#[tokio::test]
pub async fn test_interface_exports_explicit_interface_type() {
let schema = build_schema();
let data = schema
.execute(r#"{ __type(name: "ExplicitInterface") { name kind } }"#)
.await
.into_result()
.unwrap()
.data;
assert_eq!(
data,
value!({
"__type": {
"name": "ExplicitInterface",
"kind": "INTERFACE",
}
})
);
}
#[tokio::test]
pub async fn test_interface_exports_implicit_interface_type() {
let schema = build_schema();
let data = schema
.execute(r#"{ __type(name: "ImplicitInterface") { name kind } }"#)
.await
.into_result()
.unwrap()
.data;
assert_eq!(
data,
value!({
"__type": {
"name": "ImplicitInterface",
"kind": "INTERFACE",
}
})
);
}
#[tokio::test]
pub async fn test_interface_no_export_invisible_interface_type() {
let schema = build_schema();
let data = schema
.execute(r#"{ __type(name: "InvisibleInterface") { name } }"#)
.await
.into_result()
.unwrap()
.data;
assert_eq!(
data,
value!({
"__type": null,
})
);
}
#[tokio::test]
pub async fn test_interface_no_export_unreferenced_interface_type() {
let schema = build_schema();
let data = schema
.execute(r#"{ __type(name: "UnreferencedInterface") { name } }"#)
.await
.into_result()
.unwrap()
.data;
assert_eq!(
data,
value!({
"__type": null,
})
);
}