If a GraphQL name conflict is detected when creating schema, it will cause panic. #499
This commit is contained in:
parent
ff994dc1ec
commit
b359b62976
|
@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## Unreleased
|
||||
|
||||
- Fix the problem that `EmptyMutation` may cause when used in `MergedObject`. [#694](https://github.com/async-graphql/async-graphql/issues/694)
|
||||
- If a GraphQL name conflict is detected when creating schema, it will cause panic. [#499](https://github.com/async-graphql/async-graphql/issues/499)
|
||||
|
||||
## [2.11.1] 2021-11-07
|
||||
|
||||
|
|
|
@ -140,6 +140,7 @@ pub fn generate(enum_args: &args::Enum) -> GeneratorResult<TokenStream> {
|
|||
enum_items
|
||||
},
|
||||
visible: #visible,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -215,6 +215,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
|
|||
fields
|
||||
},
|
||||
visible: #visible,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -259,6 +260,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
|
|||
fields
|
||||
},
|
||||
visible: #visible,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -350,6 +350,7 @@ pub fn generate(interface_args: &args::Interface) -> GeneratorResult<TokenStream
|
|||
extends: #extends,
|
||||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ pub fn generate(object_args: &args::MergedObject) -> GeneratorResult<TokenStream
|
|||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: false,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ pub fn generate(object_args: &args::MergedSubscription) -> GeneratorResult<Token
|
|||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: true,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -564,6 +564,7 @@ pub fn generate(
|
|||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: false,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
});
|
||||
#(#create_entity_types)*
|
||||
#(#add_keys)*
|
||||
|
@ -632,6 +633,7 @@ pub fn generate(
|
|||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: false,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
});
|
||||
#(#create_entity_types)*
|
||||
#(#add_keys)*
|
||||
|
|
|
@ -294,6 +294,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
|||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: false,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -345,6 +346,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
|||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: false,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -457,6 +457,7 @@ pub fn generate(
|
|||
keys: ::std::option::Option::None,
|
||||
visible: ::std::option::Option::None,
|
||||
is_subscription: true,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,6 +193,7 @@ pub fn generate(union_args: &args::Union) -> GeneratorResult<TokenStream> {
|
|||
union_values
|
||||
},
|
||||
visible: #visible,
|
||||
rust_typename: ::std::any::type_name::<Self>(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -200,6 +200,7 @@ pub enum MetaType {
|
|||
keys: Option<Vec<String>>,
|
||||
visible: Option<MetaVisibleFn>,
|
||||
is_subscription: bool,
|
||||
rust_typename: &'static str,
|
||||
},
|
||||
Interface {
|
||||
name: String,
|
||||
|
@ -209,6 +210,7 @@ pub enum MetaType {
|
|||
extends: bool,
|
||||
keys: Option<Vec<String>>,
|
||||
visible: Option<MetaVisibleFn>,
|
||||
rust_typename: &'static str,
|
||||
},
|
||||
Union {
|
||||
name: String,
|
||||
|
@ -216,18 +218,21 @@ pub enum MetaType {
|
|||
union_values: IndexMap<String, MetaUnionValue>,
|
||||
possible_types: IndexSet<String>,
|
||||
visible: Option<MetaVisibleFn>,
|
||||
rust_typename: &'static str,
|
||||
},
|
||||
Enum {
|
||||
name: String,
|
||||
description: Option<&'static str>,
|
||||
enum_values: IndexMap<&'static str, MetaEnumValue>,
|
||||
visible: Option<MetaVisibleFn>,
|
||||
rust_typename: &'static str,
|
||||
},
|
||||
InputObject {
|
||||
name: String,
|
||||
description: Option<&'static str>,
|
||||
input_fields: IndexMap<String, MetaInputValue>,
|
||||
visible: Option<MetaVisibleFn>,
|
||||
rust_typename: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -336,6 +341,17 @@ impl MetaType {
|
|||
(false, false) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rust_typename(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
MetaType::Scalar { .. } => None,
|
||||
MetaType::Object { rust_typename, .. } => Some(rust_typename),
|
||||
MetaType::Interface { rust_typename, .. } => Some(rust_typename),
|
||||
MetaType::Union { rust_typename, .. } => Some(rust_typename),
|
||||
MetaType::Enum { rust_typename, .. } => Some(rust_typename),
|
||||
MetaType::InputObject { rust_typename, .. } => Some(rust_typename),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MetaDirective {
|
||||
|
@ -364,24 +380,40 @@ impl Registry {
|
|||
mut f: F,
|
||||
) -> String {
|
||||
let name = T::type_name();
|
||||
if !self.types.contains_key(name.as_ref()) {
|
||||
// Inserting a fake type before calling the function allows recursive types to exist.
|
||||
self.types.insert(
|
||||
name.clone().into_owned(),
|
||||
MetaType::Object {
|
||||
name: "".to_string(),
|
||||
description: None,
|
||||
fields: Default::default(),
|
||||
cache_control: Default::default(),
|
||||
extends: false,
|
||||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
},
|
||||
);
|
||||
let ty = f(self);
|
||||
*self.types.get_mut(&*name).unwrap() = ty;
|
||||
|
||||
match self.types.get(name.as_ref()) {
|
||||
Some(ty) => {
|
||||
let rust_typename = std::any::type_name::<T>();
|
||||
if let Some(prev_typename) = ty.rust_typename() {
|
||||
if prev_typename != "__fake_type__" && rust_typename != prev_typename {
|
||||
panic!(
|
||||
"`{}` and `{}` have the same GraphQL name `{}`",
|
||||
prev_typename, rust_typename, name,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Inserting a fake type before calling the function allows recursive types to exist.
|
||||
self.types.insert(
|
||||
name.clone().into_owned(),
|
||||
MetaType::Object {
|
||||
name: "".to_string(),
|
||||
description: None,
|
||||
fields: Default::default(),
|
||||
cache_control: Default::default(),
|
||||
extends: false,
|
||||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
rust_typename: "__fake_type__",
|
||||
},
|
||||
);
|
||||
let ty = f(self);
|
||||
*self.types.get_mut(&*name).unwrap() = ty;
|
||||
}
|
||||
}
|
||||
|
||||
T::qualified_type_name()
|
||||
}
|
||||
|
||||
|
@ -474,6 +506,7 @@ impl Registry {
|
|||
union_values: Default::default(),
|
||||
possible_types,
|
||||
visible: None,
|
||||
rust_typename: "async_graphql::federation::Entity",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -511,6 +544,7 @@ impl Registry {
|
|||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
rust_typename: "async_graphql::federation::Service",
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -191,6 +191,7 @@ where
|
|||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
rust_typename: std::any::type_name::<Self>(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ where
|
|||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
rust_typename: std::any::type_name::<Self>(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ impl Type for EmptyMutation {
|
|||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
rust_typename: std::any::type_name::<Self>(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ impl Type for EmptySubscription {
|
|||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: true,
|
||||
rust_typename: std::any::type_name::<Self>(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ impl<A: Type, B: Type> Type for MergedObject<A, B> {
|
|||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
rust_typename: std::any::type_name::<Self>(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,239 @@
|
|||
use async_graphql::*;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn object() {
|
||||
mod t {
|
||||
use async_graphql::*;
|
||||
|
||||
pub struct MyObj;
|
||||
|
||||
#[Object]
|
||||
impl MyObj {
|
||||
async fn a(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MyObj;
|
||||
|
||||
#[Object]
|
||||
impl MyObj {
|
||||
async fn b(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SimpleObject)]
|
||||
struct Query {
|
||||
a: MyObj,
|
||||
b: t::MyObj,
|
||||
}
|
||||
|
||||
Schema::new(
|
||||
Query {
|
||||
a: MyObj,
|
||||
b: t::MyObj,
|
||||
},
|
||||
EmptyMutation,
|
||||
EmptySubscription,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn simple_object() {
|
||||
mod t {
|
||||
use async_graphql::*;
|
||||
|
||||
#[derive(SimpleObject, Default)]
|
||||
pub struct MyObj {
|
||||
a: i32,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SimpleObject, Default)]
|
||||
struct MyObj {
|
||||
a: i32,
|
||||
}
|
||||
|
||||
#[derive(SimpleObject)]
|
||||
struct Query {
|
||||
a: MyObj,
|
||||
b: t::MyObj,
|
||||
}
|
||||
|
||||
Schema::new(
|
||||
Query {
|
||||
a: MyObj::default(),
|
||||
b: t::MyObj::default(),
|
||||
},
|
||||
EmptyMutation,
|
||||
EmptySubscription,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn merged_object() {
|
||||
mod t {
|
||||
use async_graphql::*;
|
||||
|
||||
#[derive(SimpleObject, Default)]
|
||||
pub struct Query {
|
||||
a: i32,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SimpleObject, Default)]
|
||||
struct Query {
|
||||
a: i32,
|
||||
}
|
||||
|
||||
#[derive(MergedObject)]
|
||||
struct QueryRoot(Query, t::Query);
|
||||
|
||||
Schema::new(
|
||||
QueryRoot(Query::default(), t::Query::default()),
|
||||
EmptyMutation,
|
||||
EmptySubscription,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn enum_type() {
|
||||
mod t {
|
||||
use async_graphql::*;
|
||||
|
||||
#[derive(Enum, Eq, PartialEq, Copy, Clone)]
|
||||
pub enum MyEnum {
|
||||
A,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Enum, Eq, PartialEq, Copy, Clone)]
|
||||
enum MyEnum {
|
||||
B,
|
||||
}
|
||||
|
||||
#[derive(SimpleObject)]
|
||||
struct Query {
|
||||
a: MyEnum,
|
||||
b: t::MyEnum,
|
||||
}
|
||||
|
||||
Schema::new(
|
||||
Query {
|
||||
a: MyEnum::B,
|
||||
b: t::MyEnum::A,
|
||||
},
|
||||
EmptyMutation,
|
||||
EmptySubscription,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn union() {
|
||||
mod t {
|
||||
use async_graphql::*;
|
||||
|
||||
#[derive(SimpleObject, Default)]
|
||||
pub struct ObjA {
|
||||
a: i32,
|
||||
}
|
||||
|
||||
#[derive(SimpleObject, Default)]
|
||||
pub struct ObjB {
|
||||
a: i32,
|
||||
}
|
||||
|
||||
#[derive(SimpleObject, Default)]
|
||||
pub struct ObjC {
|
||||
a: i32,
|
||||
}
|
||||
|
||||
#[derive(Union)]
|
||||
pub enum MyUnion {
|
||||
ObjA(ObjA),
|
||||
ObjB(ObjB),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Union)]
|
||||
pub enum MyUnion {
|
||||
ObjA(t::ObjA),
|
||||
ObjB(t::ObjB),
|
||||
ObjC(t::ObjC),
|
||||
}
|
||||
|
||||
#[derive(SimpleObject)]
|
||||
struct Query {
|
||||
a: MyUnion,
|
||||
b: t::MyUnion,
|
||||
}
|
||||
|
||||
Schema::new(
|
||||
Query {
|
||||
a: MyUnion::ObjA(t::ObjA::default()),
|
||||
b: t::MyUnion::ObjB(t::ObjB::default()),
|
||||
},
|
||||
EmptyMutation,
|
||||
EmptySubscription,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn interface() {
|
||||
mod t {
|
||||
use async_graphql::*;
|
||||
|
||||
#[derive(SimpleObject, Default)]
|
||||
pub struct ObjA {
|
||||
pub a: i32,
|
||||
}
|
||||
|
||||
#[derive(SimpleObject, Default)]
|
||||
pub struct ObjB {
|
||||
pub a: i32,
|
||||
}
|
||||
|
||||
#[derive(SimpleObject, Default)]
|
||||
pub struct ObjC {
|
||||
pub a: i32,
|
||||
}
|
||||
|
||||
#[derive(Interface)]
|
||||
#[graphql(field(name = "a", type = "&i32"))]
|
||||
pub enum MyInterface {
|
||||
ObjA(ObjA),
|
||||
ObjB(ObjB),
|
||||
ObjC(ObjC),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Interface)]
|
||||
#[graphql(field(name = "a", type = "&i32"))]
|
||||
enum MyInterface {
|
||||
ObjA(t::ObjA),
|
||||
ObjB(t::ObjB),
|
||||
}
|
||||
|
||||
#[derive(SimpleObject)]
|
||||
struct Query {
|
||||
a: MyInterface,
|
||||
b: t::MyInterface,
|
||||
}
|
||||
|
||||
Schema::new(
|
||||
Query {
|
||||
a: MyInterface::ObjA(t::ObjA::default()),
|
||||
b: t::MyInterface::ObjB(t::ObjB::default()),
|
||||
},
|
||||
EmptyMutation,
|
||||
EmptySubscription,
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue