Skip to content

fix unions with custom scalar values #681

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 14, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not satisfied
--> $DIR/enum_non_object_variant.rs:9:10
|
9 | #[derive(GraphQLUnion)]
| ^^^^^^^^^^^^ the trait `juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not implemented for `Test`
|
= note: required by `juniper::types::marker::GraphQLObjectType::mark`
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<__S>` is not satisfied
--> $DIR/enum_non_object_variant.rs:9:10
|
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0119]: conflicting implementations of trait `<Character as juniper::types::marker::GraphQLUnion>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
error[E0119]: conflicting implementations of trait `<Character as juniper::types::marker::GraphQLUnion<__S>>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
--> $DIR/enum_same_type_ugly.rs:3:10
|
3 | #[derive(GraphQLUnion)]
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not satisfied
--> $DIR/struct_non_object_variant.rs:9:10
|
9 | #[derive(GraphQLUnion)]
| ^^^^^^^^^^^^ the trait `juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not implemented for `Test`
|
= note: required by `juniper::types::marker::GraphQLObjectType::mark`
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<__S>` is not satisfied
--> $DIR/struct_non_object_variant.rs:9:10
|
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0119]: conflicting implementations of trait `<Character as juniper::types::marker::GraphQLUnion>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
error[E0119]: conflicting implementations of trait `<Character as juniper::types::marker::GraphQLUnion<__S>>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
--> $DIR/struct_same_type_ugly.rs:3:10
|
3 | #[derive(GraphQLUnion)]
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not satisfied
--> $DIR/trait_non_object_variant.rs:9:1
|
9 | #[graphql_union]
| ^^^^^^^^^^^^^^^^ the trait `juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not implemented for `Test`
|
= note: required by `juniper::types::marker::GraphQLObjectType::mark`
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<__S>` is not satisfied
--> $DIR/trait_non_object_variant.rs:9:1
|
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0119]: conflicting implementations of trait `<(dyn Character + std::marker::Send + std::marker::Sync + '__obj) as juniper::types::marker::GraphQLUnion>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
error[E0119]: conflicting implementations of trait `<(dyn Character + std::marker::Send + std::marker::Sync + '__obj) as juniper::types::marker::GraphQLUnion<__S>>::mark::_::{{closure}}#0::MutuallyExclusive` for type `std::string::String`:
--> $DIR/trait_same_type_ugly.rs:3:1
|
3 | #[graphql_union]
91 changes: 91 additions & 0 deletions integration_tests/juniper_tests/src/codegen/union_attr.rs
Original file line number Diff line number Diff line change
@@ -556,6 +556,97 @@ mod explicit_scalar {
}
}

mod custom_scalar {
use crate::custom_scalar::MyScalarValue;

use super::*;

#[graphql_union(scalar = MyScalarValue)]
trait Character {
fn as_human(&self) -> Option<&Human> {
None
}
fn as_droid(&self) -> Option<&Droid> {
None
}
}

impl Character for Human {
fn as_human(&self) -> Option<&Human> {
Some(&self)
}
}

impl Character for Droid {
fn as_droid(&self) -> Option<&Droid> {
Some(&self)
}
}

type DynCharacter<'a> = dyn Character + Send + Sync + 'a;

enum QueryRoot {
Human,
Droid,
}

#[graphql_object(scalar = MyScalarValue)]
impl QueryRoot {
fn character(&self) -> Box<DynCharacter<'_>> {
let ch: Box<DynCharacter<'_>> = match self {
Self::Human => Box::new(Human {
id: "human-32".to_string(),
home_planet: "earth".to_string(),
}),
Self::Droid => Box::new(Droid {
id: "droid-99".to_string(),
primary_function: "run".to_string(),
}),
};
ch
}
}

const DOC: &str = r#"{
character {
... on Human {
humanId: id
homePlanet
}
... on Droid {
droidId: id
primaryFunction
}
}
}"#;

#[tokio::test]
async fn resolves_human() {
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human);

assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await,
Ok((
graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
vec![],
)),
);
}

#[tokio::test]
async fn resolves_droid() {
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid);

assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await,
Ok((
graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
vec![],
)),
);
}
}

mod inferred_custom_context {
use super::*;

73 changes: 73 additions & 0 deletions integration_tests/juniper_tests/src/codegen/union_derive.rs
Original file line number Diff line number Diff line change
@@ -485,6 +485,79 @@ mod explicit_scalar {
}
}

mod custom_scalar {
use crate::custom_scalar::MyScalarValue;

use super::*;

#[derive(GraphQLUnion)]
#[graphql(scalar = MyScalarValue)]
enum Character {
A(Human),
B(Droid),
}

enum QueryRoot {
Human,
Droid,
}

#[graphql_object(scalar = MyScalarValue)]
impl QueryRoot {
fn character(&self) -> Character {
match self {
Self::Human => Character::A(Human {
id: "human-32".to_string(),
home_planet: "earth".to_string(),
}),
Self::Droid => Character::B(Droid {
id: "droid-99".to_string(),
primary_function: "run".to_string(),
}),
}
}
}

const DOC: &str = r#"{
character {
... on Human {
humanId: id
homePlanet
}
... on Droid {
droidId: id
primaryFunction
}
}
}"#;

#[tokio::test]
async fn resolves_human() {
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human);

assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await,
Ok((
graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
vec![],
)),
);
}

#[tokio::test]
async fn resolves_droid() {
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid);

assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await,
Ok((
graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
vec![],
)),
);
}
}

mod custom_context {
use super::*;

4 changes: 2 additions & 2 deletions integration_tests/juniper_tests/src/custom_scalar.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ use juniper::{
use std::fmt;

#[derive(Debug, Clone, PartialEq, juniper::GraphQLScalarValue)]
enum MyScalarValue {
pub(crate) enum MyScalarValue {
Int(i32),
Long(i64),
Float(f64),
@@ -59,7 +59,7 @@ impl ScalarValue for MyScalarValue {
}

#[derive(Default, Debug)]
struct MyScalarValueVisitor;
pub(crate) struct MyScalarValueVisitor;

impl<'de> de::Visitor<'de> for MyScalarValueVisitor {
type Value = MyScalarValue;
2 changes: 1 addition & 1 deletion juniper/src/types/marker.rs
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ pub trait GraphQLObjectType<S: ScalarValue>: GraphQLType<S> {
/// [4]: https://spec.graphql.org/June2018/#sec-Objects
/// [5]: https://spec.graphql.org/June2018/#sec-Input-Objects
/// [6]: https://spec.graphql.org/June2018/#sec-Interfaces
pub trait GraphQLUnion: GraphQLType {
pub trait GraphQLUnion<S: ScalarValue>: GraphQLType<S> {
/// An arbitrary function without meaning.
///
/// May contain compile timed check logic which ensures that types are used correctly according
20 changes: 6 additions & 14 deletions juniper_codegen/src/graphql_union/mod.rs
Original file line number Diff line number Diff line change
@@ -414,11 +414,6 @@ impl ToTokens for UnionDefinition {
.as_ref()
.map(|scl| quote! { #scl })
.unwrap_or_else(|| quote! { __S });
let default_scalar = self
.scalar
.as_ref()
.map(|scl| quote! { #scl })
.unwrap_or_else(|| quote! { #crate_path::DefaultScalarValue });

let description = self
.description
@@ -493,13 +488,10 @@ impl ToTokens for UnionDefinition {

let (_, ty_generics, _) = self.generics.split_for_impl();

let mut base_generics = self.generics.clone();
let mut ext_generics = self.generics.clone();
if self.is_trait_object {
base_generics.params.push(parse_quote! { '__obj });
ext_generics.params.push(parse_quote! { '__obj });
}
let (impl_generics, _, _) = base_generics.split_for_impl();

let mut ext_generics = base_generics.clone();
if self.scalar.is_none() {
ext_generics.params.push(parse_quote! { #scalar });
ext_generics
@@ -618,13 +610,13 @@ impl ToTokens for UnionDefinition {

let union_impl = quote! {
#[automatically_derived]
impl#impl_generics #crate_path::marker::GraphQLUnion for #ty_full {
impl#ext_impl_generics #crate_path::marker::GraphQLUnion<#scalar> for #ty_full
#where_clause
{
fn mark() {
#all_variants_unique

#( <#var_types as #crate_path::marker::GraphQLObjectType<
#default_scalar,
>>::mark(); )*
#( <#var_types as #crate_path::marker::GraphQLObjectType<#scalar>>::mark(); )*
}
}
};