Add entity lookup support for MergedObject.
Add some GraphQL specification constraints for all derived macros. Use `Registry::create_dummy_type` to create a merged type.
This commit is contained in:
parent
b5e602342d
commit
e3d693da28
|
@ -83,6 +83,8 @@ pub struct SimpleObject {
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
pub internal: bool,
|
pub internal: bool,
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
|
pub dummy: bool,
|
||||||
|
#[darling(default)]
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
pub rename_fields: Option<RenameRule>,
|
pub rename_fields: Option<RenameRule>,
|
||||||
|
|
|
@ -111,6 +111,14 @@ pub fn generate(enum_args: &args::Enum) -> GeneratorResult<TokenStream> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if schema_enum_items.is_empty() {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&ident,
|
||||||
|
"An GraphQL Enum type must define one or more unique enum values.",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#[allow(clippy::all, clippy::pedantic)]
|
#[allow(clippy::all, clippy::pedantic)]
|
||||||
impl #crate_name::resolver_utils::EnumType for #ident {
|
impl #crate_name::resolver_utils::EnumType for #ident {
|
||||||
|
|
|
@ -67,9 +67,9 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
|
||||||
schema_fields.push(quote! {
|
schema_fields.push(quote! {
|
||||||
#crate_name::static_assertions::assert_impl_one!(#ty: #crate_name::InputObjectType);
|
#crate_name::static_assertions::assert_impl_one!(#ty: #crate_name::InputObjectType);
|
||||||
#ty::create_type_info(registry);
|
#ty::create_type_info(registry);
|
||||||
if let Some(#crate_name::registry::MetaType::InputObject { input_fields, .. }) =
|
if let #crate_name::registry::MetaType::InputObject { input_fields, .. } =
|
||||||
registry.types.get(&*<#ty as #crate_name::Type>::type_name()) {
|
registry.create_dummy_type::<#ty>() {
|
||||||
fields.extend(::std::clone::Clone::clone(input_fields));
|
fields.extend(input_fields);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -151,6 +151,14 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if get_fields.is_empty() {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&ident,
|
||||||
|
"An GraphQL Input Object type must define one or more input fields.",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#[allow(clippy::all, clippy::pedantic)]
|
#[allow(clippy::all, clippy::pedantic)]
|
||||||
impl #crate_name::Type for #ident {
|
impl #crate_name::Type for #ident {
|
||||||
|
|
|
@ -121,6 +121,14 @@ pub fn generate(interface_args: &args::Interface) -> GeneratorResult<TokenStream
|
||||||
let mut schema_fields = Vec::new();
|
let mut schema_fields = Vec::new();
|
||||||
let mut resolvers = Vec::new();
|
let mut resolvers = Vec::new();
|
||||||
|
|
||||||
|
if interface_args.fields.is_empty() {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&ident,
|
||||||
|
"An GraphQL Interface type must define one or more fields.",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
for InterfaceField {
|
for InterfaceField {
|
||||||
name,
|
name,
|
||||||
method,
|
method,
|
||||||
|
|
|
@ -62,18 +62,16 @@ pub fn generate(object_args: &args::MergedObject) -> GeneratorResult<TokenStream
|
||||||
|
|
||||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
|
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
|
||||||
registry.create_type::<Self, _>(|registry| {
|
registry.create_type::<Self, _>(|registry| {
|
||||||
#merged_type::create_type_info(registry);
|
|
||||||
|
|
||||||
let mut fields = ::std::default::Default::default();
|
let mut fields = ::std::default::Default::default();
|
||||||
let mut cache_control = ::std::default::Default::default();
|
let mut cache_control = ::std::default::Default::default();
|
||||||
|
|
||||||
if let ::std::option::Option::Some(#crate_name::registry::MetaType::Object {
|
if let #crate_name::registry::MetaType::Object {
|
||||||
fields: obj_fields,
|
fields: obj_fields,
|
||||||
cache_control: obj_cache_control,
|
cache_control: obj_cache_control,
|
||||||
..
|
..
|
||||||
}) = registry.types.get(&*#merged_type::type_name()) {
|
} = registry.create_dummy_type::<#merged_type>() {
|
||||||
fields = ::std::clone::Clone::clone(obj_fields);
|
fields = obj_fields;
|
||||||
cache_control = *obj_cache_control;
|
cache_control = obj_cache_control;
|
||||||
}
|
}
|
||||||
|
|
||||||
#crate_name::registry::MetaType::Object {
|
#crate_name::registry::MetaType::Object {
|
||||||
|
@ -94,6 +92,10 @@ pub fn generate(object_args: &args::MergedObject) -> GeneratorResult<TokenStream
|
||||||
async fn resolve_field(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::ServerResult<::std::option::Option<#crate_name::Value>> {
|
async fn resolve_field(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::ServerResult<::std::option::Option<#crate_name::Value>> {
|
||||||
#create_merged_obj.resolve_field(ctx).await
|
#create_merged_obj.resolve_field(ctx).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn find_entity(&self, ctx: &#crate_name::Context<'_>, params: &#crate_name::Value) -> #crate_name::ServerResult<::std::option::Option<#crate_name::Value>> {
|
||||||
|
#create_merged_obj.find_entity(ctx, params).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::all, clippy::pedantic)]
|
#[allow(clippy::all, clippy::pedantic)]
|
||||||
|
|
|
@ -53,15 +53,13 @@ pub fn generate(object_args: &args::MergedSubscription) -> GeneratorResult<Token
|
||||||
|
|
||||||
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
|
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
|
||||||
registry.create_type::<Self, _>(|registry| {
|
registry.create_type::<Self, _>(|registry| {
|
||||||
#merged_type::create_type_info(registry);
|
|
||||||
|
|
||||||
let mut fields = ::std::default::Default::default();
|
let mut fields = ::std::default::Default::default();
|
||||||
|
|
||||||
if let ::std::option::Option::Some(#crate_name::registry::MetaType::Object {
|
if let #crate_name::registry::MetaType::Object {
|
||||||
fields: obj_fields,
|
fields: obj_fields,
|
||||||
..
|
..
|
||||||
}) = registry.types.get(&*#merged_type::type_name()) {
|
} = registry.create_dummy_type::<#merged_type>() {
|
||||||
fields = ::std::clone::Clone::clone(obj_fields);
|
fields = obj_fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
#crate_name::registry::MetaType::Object {
|
#crate_name::registry::MetaType::Object {
|
||||||
|
|
|
@ -471,6 +471,14 @@ pub fn generate(
|
||||||
find_entities.sort_by(|(a, _), (b, _)| b.cmp(a));
|
find_entities.sort_by(|(a, _), (b, _)| b.cmp(a));
|
||||||
let find_entities_iter = find_entities.iter().map(|(_, code)| code);
|
let find_entities_iter = find_entities.iter().map(|(_, code)| code);
|
||||||
|
|
||||||
|
if resolvers.is_empty() && create_entity_types.is_empty() {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&self_ty,
|
||||||
|
"An GraphQL Object type must define one or more fields.",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#item_impl
|
#item_impl
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,14 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !object_args.dummy && resolvers.is_empty() {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&ident,
|
||||||
|
"An GraphQL Object type must define one or more fields.",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
let cache_control = {
|
let cache_control = {
|
||||||
let public = object_args.cache_control.is_public();
|
let public = object_args.cache_control.is_public();
|
||||||
let max_age = object_args.cache_control.max_age;
|
let max_age = object_args.cache_control.max_age;
|
||||||
|
@ -181,7 +189,6 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
||||||
#[allow(clippy::all, clippy::pedantic)]
|
#[allow(clippy::all, clippy::pedantic)]
|
||||||
#[#crate_name::async_trait::async_trait]
|
#[#crate_name::async_trait::async_trait]
|
||||||
impl #generics #crate_name::OutputValueType for #ident #generics #where_clause {
|
impl #generics #crate_name::OutputValueType for #ident #generics #where_clause {
|
||||||
|
|
||||||
async fn resolve(&self, ctx: &#crate_name::ContextSelectionSet<'_>, _field: &#crate_name::Positioned<#crate_name::parser::types::Field>) -> #crate_name::ServerResult<#crate_name::Value> {
|
async fn resolve(&self, ctx: &#crate_name::ContextSelectionSet<'_>, _field: &#crate_name::Positioned<#crate_name::parser::types::Field>) -> #crate_name::ServerResult<#crate_name::Value> {
|
||||||
#crate_name::resolver_utils::resolve_container(ctx, self).await
|
#crate_name::resolver_utils::resolve_container(ctx, self).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -364,6 +364,14 @@ pub fn generate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if create_stream.is_empty() {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&self_ty,
|
||||||
|
"An GraphQL Object type must define one or more fields.",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#item_impl
|
#item_impl
|
||||||
|
|
||||||
|
|
|
@ -106,19 +106,18 @@ pub fn generate(union_args: &args::Union) -> GeneratorResult<TokenStream> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !variant.flatten {
|
||||||
registry_types.push(quote! {
|
registry_types.push(quote! {
|
||||||
<#p as #crate_name::Type>::create_type_info(registry);
|
<#p as #crate_name::Type>::create_type_info(registry);
|
||||||
});
|
});
|
||||||
|
|
||||||
if !variant.flatten {
|
|
||||||
possible_types.push(quote! {
|
possible_types.push(quote! {
|
||||||
possible_types.insert(<#p as #crate_name::Type>::type_name().into_owned());
|
possible_types.insert(<#p as #crate_name::Type>::type_name().into_owned());
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
possible_types.push(quote! {
|
possible_types.push(quote! {
|
||||||
if let Some(#crate_name::registry::MetaType::Union { possible_types: possible_types2, .. }) =
|
if let #crate_name::registry::MetaType::Union { possible_types: possible_types2, .. } =
|
||||||
registry.types.get(&*<#p as #crate_name::Type>::type_name()) {
|
registry.create_dummy_type::<#p>() {
|
||||||
possible_types.extend(::std::clone::Clone::clone(possible_types2));
|
possible_types.extend(possible_types2);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -141,6 +140,14 @@ pub fn generate(union_args: &args::Union) -> GeneratorResult<TokenStream> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if possible_types.is_empty() {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&ident,
|
||||||
|
"A GraphQL Union type must include one or more unique member types.",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#(#type_into_impls)*
|
#(#type_into_impls)*
|
||||||
|
|
||||||
|
|
|
@ -142,8 +142,13 @@ async fn upload() -> Result<()> {
|
||||||
|
|
||||||
async_std::task::spawn(async move {
|
async_std::task::spawn(async move {
|
||||||
struct QueryRoot;
|
struct QueryRoot;
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl QueryRoot {}
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, SimpleObject)]
|
#[derive(Clone, SimpleObject)]
|
||||||
pub struct FileInfo {
|
pub struct FileInfo {
|
||||||
|
|
|
@ -17,7 +17,12 @@ use warp::{Filter, Rejection, Reply};
|
||||||
/// struct QueryRoot;
|
/// struct QueryRoot;
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {}
|
/// impl QueryRoot {
|
||||||
|
/// async fn value(&self) -> i32 {
|
||||||
|
/// // An GraphQL Object type must define one or more fields.
|
||||||
|
/// 100
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
///
|
///
|
||||||
/// struct SubscriptionRoot;
|
/// struct SubscriptionRoot;
|
||||||
///
|
///
|
||||||
|
|
|
@ -254,6 +254,7 @@ pub struct MetaDirective {
|
||||||
pub args: IndexMap<&'static str, MetaInputValue>,
|
pub args: IndexMap<&'static str, MetaInputValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct Registry {
|
pub struct Registry {
|
||||||
pub types: IndexMap<String, MetaType>,
|
pub types: IndexMap<String, MetaType>,
|
||||||
pub directives: HashMap<String, MetaDirective>,
|
pub directives: HashMap<String, MetaDirective>,
|
||||||
|
@ -288,6 +289,18 @@ impl Registry {
|
||||||
T::qualified_type_name()
|
T::qualified_type_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_dummy_type<T: crate::Type>(&mut self) -> MetaType {
|
||||||
|
let mut dummy_registry = Registry::default();
|
||||||
|
T::create_type_info(&mut dummy_registry);
|
||||||
|
if let Some(ty) = dummy_registry.types.remove(&*T::type_name()) {
|
||||||
|
self.types.extend(dummy_registry.types);
|
||||||
|
self.implements.extend(dummy_registry.implements);
|
||||||
|
ty
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_directive(&mut self, directive: MetaDirective) {
|
pub fn add_directive(&mut self, directive: MetaDirective) {
|
||||||
self.directives
|
self.directives
|
||||||
.insert(directive.name.to_string(), directive);
|
.insert(directive.name.to_string(), directive);
|
||||||
|
|
|
@ -55,6 +55,10 @@ impl<T: ContainerType + Send + Sync> ContainerType for &T {
|
||||||
async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
|
async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
|
||||||
T::resolve_field(*self, ctx).await
|
T::resolve_field(*self, ctx).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
|
||||||
|
T::find_entity(*self, ctx, params).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve an container by executing each of the fields concurrently.
|
/// Resolve an container by executing each of the fields concurrently.
|
||||||
|
|
|
@ -70,9 +70,15 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use async_graphql::*;
|
/// use async_graphql::*;
|
||||||
///
|
///
|
||||||
/// #[derive(SimpleObject)]
|
|
||||||
/// struct Query;
|
/// struct Query;
|
||||||
///
|
///
|
||||||
|
/// #[Object]
|
||||||
|
/// impl Query {
|
||||||
|
/// async fn value(&self) -> i32 {
|
||||||
|
/// 100
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
/// let schema = Schema::build(Query, EmptyMutation,EmptySubscription)
|
/// let schema = Schema::build(Query, EmptyMutation,EmptySubscription)
|
||||||
/// .extension(extensions::Logger)
|
/// .extension(extensions::Logger)
|
||||||
/// .finish();
|
/// .finish();
|
||||||
|
|
|
@ -52,11 +52,10 @@ where
|
||||||
|
|
||||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||||
registry.create_type::<Self, _>(|registry| {
|
registry.create_type::<Self, _>(|registry| {
|
||||||
E::create_type_info(registry);
|
let additional_fields = if let registry::MetaType::Object { fields, .. } =
|
||||||
let additional_fields = if let Some(registry::MetaType::Object { fields, .. }) =
|
registry.create_dummy_type::<E>()
|
||||||
registry.types.get(E::type_name().as_ref())
|
|
||||||
{
|
{
|
||||||
fields.clone()
|
fields
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub use page_info::PageInfo;
|
||||||
|
|
||||||
/// Empty additional fields
|
/// Empty additional fields
|
||||||
#[derive(SimpleObject)]
|
#[derive(SimpleObject)]
|
||||||
#[graphql(internal)]
|
#[graphql(internal, dummy)]
|
||||||
pub struct EmptyFields;
|
pub struct EmptyFields;
|
||||||
|
|
||||||
/// Parses the parameters and executes the query.
|
/// Parses the parameters and executes the query.
|
||||||
|
|
|
@ -19,7 +19,12 @@ use crate::{
|
||||||
/// struct QueryRoot;
|
/// struct QueryRoot;
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {}
|
/// impl QueryRoot {
|
||||||
|
/// async fn value(&self) -> i32 {
|
||||||
|
/// // An GraphQL Object type must define one or more fields.
|
||||||
|
/// 100
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
///
|
///
|
||||||
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::{
|
||||||
CacheControl, ContainerType, Context, ContextSelectionSet, ObjectType, OutputValueType,
|
CacheControl, ContainerType, Context, ContextSelectionSet, ObjectType, OutputValueType,
|
||||||
Positioned, ServerResult, SimpleObject, Type, Value,
|
Positioned, ServerResult, SimpleObject, Type, Value,
|
||||||
};
|
};
|
||||||
|
use async_graphql_value::ConstValue;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct MergedObject<A, B>(pub A, pub B);
|
pub struct MergedObject<A, B>(pub A, pub B);
|
||||||
|
@ -23,25 +24,23 @@ impl<A: Type, B: Type> Type for MergedObject<A, B> {
|
||||||
let mut fields = IndexMap::new();
|
let mut fields = IndexMap::new();
|
||||||
let mut cc = CacheControl::default();
|
let mut cc = CacheControl::default();
|
||||||
|
|
||||||
A::create_type_info(registry);
|
if let MetaType::Object {
|
||||||
if let Some(MetaType::Object {
|
|
||||||
fields: a_fields,
|
fields: a_fields,
|
||||||
cache_control: a_cc,
|
cache_control: a_cc,
|
||||||
..
|
..
|
||||||
}) = registry.types.get(&*A::type_name())
|
} = registry.create_dummy_type::<A>()
|
||||||
{
|
{
|
||||||
fields.extend(a_fields.clone());
|
fields.extend(a_fields);
|
||||||
cc = cc.merge(&a_cc);
|
cc = cc.merge(&a_cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
B::create_type_info(registry);
|
if let MetaType::Object {
|
||||||
if let Some(MetaType::Object {
|
|
||||||
fields: b_fields,
|
fields: b_fields,
|
||||||
cache_control: b_cc,
|
cache_control: b_cc,
|
||||||
..
|
..
|
||||||
}) = registry.types.get(&*B::type_name())
|
} = registry.create_dummy_type::<B>()
|
||||||
{
|
{
|
||||||
fields.extend(b_fields.clone());
|
fields.extend(b_fields);
|
||||||
cc = cc.merge(&b_cc);
|
cc = cc.merge(&b_cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +69,18 @@ where
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn find_entity(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
params: &ConstValue,
|
||||||
|
) -> ServerResult<Option<ConstValue>> {
|
||||||
|
match self.0.find_entity(ctx, params).await {
|
||||||
|
Ok(Some(value)) => Ok(Some(value)),
|
||||||
|
Ok(None) => self.1.find_entity(ctx, params).await,
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
@ -96,5 +107,5 @@ where
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[derive(SimpleObject, Default)]
|
#[derive(SimpleObject, Default)]
|
||||||
#[graphql(internal)]
|
#[graphql(internal, dummy)]
|
||||||
pub struct MergedObjectTail;
|
pub struct MergedObjectTail;
|
||||||
|
|
|
@ -185,7 +185,11 @@ pub async fn test_merged_subscription() {
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl Query {}
|
impl Query {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let schema = Schema::new(Query, EmptyMutation, Subscription::default());
|
let schema = Schema::new(Query, EmptyMutation, Subscription::default());
|
||||||
|
|
||||||
|
@ -221,3 +225,68 @@ pub async fn test_merged_subscription() {
|
||||||
assert!(stream.next().await.is_none());
|
assert!(stream.next().await.is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
pub async fn test_merged_entity() {
|
||||||
|
#[derive(SimpleObject)]
|
||||||
|
struct Fruit {
|
||||||
|
id: ID,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SimpleObject)]
|
||||||
|
struct Vegetable {
|
||||||
|
id: ID,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct FruitQuery;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl FruitQuery {
|
||||||
|
#[graphql(entity)]
|
||||||
|
async fn get_fruit(&self, id: ID) -> Fruit {
|
||||||
|
Fruit {
|
||||||
|
id,
|
||||||
|
name: "Apple".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct VegetableQuery;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl VegetableQuery {
|
||||||
|
#[graphql(entity)]
|
||||||
|
async fn get_vegetable(&self, id: ID) -> Vegetable {
|
||||||
|
Vegetable {
|
||||||
|
id,
|
||||||
|
name: "Carrot".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(MergedObject, Default)]
|
||||||
|
struct Query(FruitQuery, VegetableQuery);
|
||||||
|
|
||||||
|
let schema = Schema::new(Query::default(), EmptyMutation, EmptySubscription);
|
||||||
|
let query = r#"{
|
||||||
|
_entities(representations: [{__typename: "Fruit", id: "1"}]) {
|
||||||
|
__typename
|
||||||
|
... on Fruit {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(query).await.into_result().unwrap().data,
|
||||||
|
value!({
|
||||||
|
"_entities": [
|
||||||
|
{"__typename": "Fruit", "id": "1", "name": "Apple"},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -7,9 +7,15 @@ use std::time::Duration;
|
||||||
pub async fn test_mutation_execution_order() {
|
pub async fn test_mutation_execution_order() {
|
||||||
type List = Arc<Mutex<Vec<i32>>>;
|
type List = Arc<Mutex<Vec<i32>>>;
|
||||||
|
|
||||||
#[derive(SimpleObject)]
|
|
||||||
struct QueryRoot;
|
struct QueryRoot;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct MutationRoot;
|
struct MutationRoot;
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
|
@ -38,9 +44,15 @@ pub async fn test_mutation_execution_order() {
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_mutation_fragment() {
|
pub async fn test_mutation_fragment() {
|
||||||
#[derive(SimpleObject)]
|
|
||||||
struct QueryRoot;
|
struct QueryRoot;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct MutationRoot;
|
struct MutationRoot;
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
|
|
|
@ -105,9 +105,15 @@ pub async fn test_input_object() {
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_subscription() {
|
pub async fn test_subscription() {
|
||||||
#[derive(SimpleObject)]
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Query {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Subscription;
|
struct Subscription;
|
||||||
|
|
||||||
#[Subscription(rename_fields = "SCREAMING_SNAKE_CASE", rename_args = "lowercase")]
|
#[Subscription(rename_fields = "SCREAMING_SNAKE_CASE", rename_args = "lowercase")]
|
||||||
|
|
|
@ -2,10 +2,17 @@ use async_graphql::*;
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_schema_default() {
|
pub async fn test_schema_default() {
|
||||||
#[derive(SimpleObject, Default)]
|
#[derive(Default)]
|
||||||
struct Query;
|
struct QueryRoot;
|
||||||
|
|
||||||
type MySchema = Schema<Query, EmptyMutation, EmptySubscription>;
|
#[Object]
|
||||||
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MySchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;
|
||||||
|
|
||||||
let _schema = MySchema::default();
|
let _schema = MySchema::default();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
use async_graphql::*;
|
use async_graphql::*;
|
||||||
use futures_util::stream::{Stream, StreamExt, TryStreamExt};
|
use futures_util::stream::{Stream, StreamExt, TryStreamExt};
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
pub async fn test_subscription() {
|
|
||||||
struct QueryRoot;
|
struct QueryRoot;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
pub async fn test_subscription() {
|
||||||
#[derive(SimpleObject)]
|
#[derive(SimpleObject)]
|
||||||
struct Event {
|
struct Event {
|
||||||
a: i32,
|
a: i32,
|
||||||
b: i32,
|
b: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Object]
|
|
||||||
impl QueryRoot {}
|
|
||||||
|
|
||||||
struct SubscriptionRoot;
|
struct SubscriptionRoot;
|
||||||
|
|
||||||
#[Subscription]
|
#[Subscription]
|
||||||
|
@ -60,7 +64,11 @@ pub async fn test_subscription_with_ctx_data() {
|
||||||
struct QueryRoot;
|
struct QueryRoot;
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl QueryRoot {}
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct MyObject;
|
struct MyObject;
|
||||||
|
|
||||||
|
@ -106,7 +114,11 @@ pub async fn test_subscription_with_token() {
|
||||||
struct QueryRoot;
|
struct QueryRoot;
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl QueryRoot {}
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SubscriptionRoot;
|
struct SubscriptionRoot;
|
||||||
|
|
||||||
|
@ -150,16 +162,20 @@ pub async fn test_subscription_with_token() {
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_subscription_inline_fragment() {
|
pub async fn test_subscription_inline_fragment() {
|
||||||
struct QueryRoot;
|
|
||||||
|
|
||||||
#[derive(SimpleObject)]
|
#[derive(SimpleObject)]
|
||||||
struct Event {
|
struct Event {
|
||||||
a: i32,
|
a: i32,
|
||||||
b: i32,
|
b: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct QueryRoot;
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl QueryRoot {}
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SubscriptionRoot;
|
struct SubscriptionRoot;
|
||||||
|
|
||||||
|
@ -197,8 +213,6 @@ pub async fn test_subscription_inline_fragment() {
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_subscription_fragment() {
|
pub async fn test_subscription_fragment() {
|
||||||
struct QueryRoot;
|
|
||||||
|
|
||||||
#[derive(SimpleObject)]
|
#[derive(SimpleObject)]
|
||||||
struct Event {
|
struct Event {
|
||||||
a: i32,
|
a: i32,
|
||||||
|
@ -211,9 +225,6 @@ pub async fn test_subscription_fragment() {
|
||||||
Event(Event),
|
Event(Event),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Object]
|
|
||||||
impl QueryRoot {}
|
|
||||||
|
|
||||||
struct SubscriptionRoot;
|
struct SubscriptionRoot;
|
||||||
|
|
||||||
#[Subscription]
|
#[Subscription]
|
||||||
|
@ -252,8 +263,6 @@ pub async fn test_subscription_fragment() {
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_subscription_fragment2() {
|
pub async fn test_subscription_fragment2() {
|
||||||
struct QueryRoot;
|
|
||||||
|
|
||||||
#[derive(SimpleObject)]
|
#[derive(SimpleObject)]
|
||||||
struct Event {
|
struct Event {
|
||||||
a: i32,
|
a: i32,
|
||||||
|
@ -266,9 +275,6 @@ pub async fn test_subscription_fragment2() {
|
||||||
Event(Event),
|
Event(Event),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Object]
|
|
||||||
impl QueryRoot {}
|
|
||||||
|
|
||||||
struct SubscriptionRoot;
|
struct SubscriptionRoot;
|
||||||
|
|
||||||
#[Subscription]
|
#[Subscription]
|
||||||
|
@ -308,8 +314,6 @@ pub async fn test_subscription_fragment2() {
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_subscription_error() {
|
pub async fn test_subscription_error() {
|
||||||
struct QueryRoot;
|
|
||||||
|
|
||||||
struct Event {
|
struct Event {
|
||||||
value: i32,
|
value: i32,
|
||||||
}
|
}
|
||||||
|
@ -325,9 +329,6 @@ pub async fn test_subscription_error() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Object]
|
|
||||||
impl QueryRoot {}
|
|
||||||
|
|
||||||
struct SubscriptionRoot;
|
struct SubscriptionRoot;
|
||||||
|
|
||||||
#[Subscription]
|
#[Subscription]
|
||||||
|
@ -370,11 +371,6 @@ pub async fn test_subscription_error() {
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_subscription_fieldresult() {
|
pub async fn test_subscription_fieldresult() {
|
||||||
struct QueryRoot;
|
|
||||||
|
|
||||||
#[Object]
|
|
||||||
impl QueryRoot {}
|
|
||||||
|
|
||||||
struct SubscriptionRoot;
|
struct SubscriptionRoot;
|
||||||
|
|
||||||
#[Subscription]
|
#[Subscription]
|
||||||
|
|
|
@ -6,7 +6,11 @@ pub async fn test_subscription_ws_transport() {
|
||||||
struct QueryRoot;
|
struct QueryRoot;
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl QueryRoot {}
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SubscriptionRoot;
|
struct SubscriptionRoot;
|
||||||
|
|
||||||
|
@ -77,7 +81,11 @@ pub async fn test_subscription_ws_transport_with_token() {
|
||||||
struct QueryRoot;
|
struct QueryRoot;
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl QueryRoot {}
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SubscriptionRoot;
|
struct SubscriptionRoot;
|
||||||
|
|
||||||
|
@ -161,8 +169,6 @@ pub async fn test_subscription_ws_transport_with_token() {
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
pub async fn test_subscription_ws_transport_error() {
|
pub async fn test_subscription_ws_transport_error() {
|
||||||
struct QueryRoot;
|
|
||||||
|
|
||||||
struct Event {
|
struct Event {
|
||||||
value: i32,
|
value: i32,
|
||||||
}
|
}
|
||||||
|
@ -178,8 +184,14 @@ pub async fn test_subscription_ws_transport_error() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct QueryRoot;
|
||||||
|
|
||||||
#[Object]
|
#[Object]
|
||||||
impl QueryRoot {}
|
impl QueryRoot {
|
||||||
|
async fn value(&self) -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SubscriptionRoot;
|
struct SubscriptionRoot;
|
||||||
|
|
||||||
|
|
|
@ -327,6 +327,10 @@ pub async fn test_union_flatten() {
|
||||||
async fn value2(&self) -> MyUnion {
|
async fn value2(&self) -> MyUnion {
|
||||||
InnerUnion2::B(MyObj2 { value2: 88 }).into()
|
InnerUnion2::B(MyObj2 { value2: 88 }).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn value3(&self) -> InnerUnion1 {
|
||||||
|
InnerUnion1::A(MyObj1 { value1: 77 })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||||
|
@ -342,6 +346,11 @@ pub async fn test_union_flatten() {
|
||||||
value2
|
value2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
value3 {
|
||||||
|
... on MyObj1 {
|
||||||
|
value1
|
||||||
|
}
|
||||||
|
}
|
||||||
}"#;
|
}"#;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
schema.execute(query).await.into_result().unwrap().data,
|
schema.execute(query).await.into_result().unwrap().data,
|
||||||
|
@ -351,6 +360,9 @@ pub async fn test_union_flatten() {
|
||||||
},
|
},
|
||||||
"value2": {
|
"value2": {
|
||||||
"value2": 88,
|
"value2": 88,
|
||||||
|
},
|
||||||
|
"value3": {
|
||||||
|
"value1": 77,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user