Skip to content
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

Codegen reimplementation for GraphQL unions #666

Merged
merged 30 commits into from
Jun 4, 2020
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0a6a943
Refactor ground for union macros reimplementation
tyranron May 15, 2020
d7ea1c9
Implement #[derive(GraphQLUnion)] for enums
tyranron May 18, 2020
8f59c4d
Polish #[derive(GraphQLUnion)] for enums
tyranron May 21, 2020
fe24eee
Check duplicate custom resolvers between enum variants and whole enum…
tyranron May 25, 2020
a59964e
Remove From impls generation in favor of derive_more usage
tyranron May 25, 2020
8e88d2c
Support structs
tyranron May 25, 2020
92c4e07
Bootstrap #[graphql_union] for traits
tyranron May 26, 2020
4a23538
Implement #[graphql_union] for traits
tyranron May 27, 2020
9d7e99f
Relax more usages of :Sized trait bound
tyranron May 29, 2020
df39c15
Test trait implementation and pack Send + Sync into trait object
tyranron May 29, 2020
4feb141
Support custom resolver for traits
tyranron May 29, 2020
38464a4
Strip out old code
tyranron Jun 1, 2020
cc07fe2
Polish common code
tyranron Jun 1, 2020
810cc87
Impl PascalCasing for default union names
tyranron Jun 1, 2020
81af517
Verify trait method signature to not be async
tyranron Jun 1, 2020
f8d3e33
Fix type-level union variants duplication check with static assertion…
tyranron Jun 1, 2020
2c25f38
Merge branch 'master' into async-unions
tyranron Jun 1, 2020
a088cdc
Update CHANGELOG
tyranron Jun 1, 2020
23015f6
Bootstrap new book docs for unions
tyranron Jun 1, 2020
25ec5d8
Apply typos corrections provided by @LegNeato
tyranron Jun 2, 2020
2287679
Fix book tests
tyranron Jun 2, 2020
838e826
Add ScalarValue considerations to book
tyranron Jun 2, 2020
ab74bd8
Document macro definitions
tyranron Jun 2, 2020
8d51763
Try infer context type from trait method signature
tyranron Jun 2, 2020
ced1fe7
Fix juniper crate tests
tyranron Jun 2, 2020
6815418
Rework existing codegen failure tests
tyranron Jun 2, 2020
8f40bde
Cover up codegen fail cases
tyranron Jun 3, 2020
ecfe7e1
Cover up positive cases of #[graphql_union] macro
tyranron Jun 3, 2020
13d96ae
Cover up positive cases of #[derive(GraphQLUnion)] macro
tyranron Jun 3, 2020
d9cb3f1
Apply corrections provided by @LegNeato
tyranron Jun 4, 2020
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
397 changes: 306 additions & 91 deletions docs/book/content/types/unions.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docs/book/tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ build = "build.rs"
juniper = { path = "../../../juniper" }
juniper_iron = { path = "../../../juniper_iron" }
juniper_subscriptions = { path = "../../../juniper_subscriptions" }

derive_more = "0.99.7"
futures = "0.3"
tokio = { version = "0.2", features = ["rt-core", "blocking", "stream", "rt-util"] }
iron = "0.5.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
error[E0277]: the trait bound `Test: juniper::types::marker::GraphQLObjectType<juniper::value::scalar::DefaultScalarValue>` is not satisfied
--> $DIR/derive_enum_field.rs:7:10
|
7 | #[derive(juniper::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/derive_enum_field.rs:7:10
|
Expand Down
5 changes: 3 additions & 2 deletions integration_tests/juniper_tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
[package]
name = "juniper_tests"
version = "0.1.0"
publish = false
edition = "2018"
publish = false

[dependencies]
juniper = { path = "../../juniper" }
derive_more = "0.99.7"
futures = "0.3.1"
juniper = { path = "../../juniper" }

[dev-dependencies]
serde_json = { version = "1" }
Expand Down
44 changes: 0 additions & 44 deletions integration_tests/juniper_tests/src/codegen/impl_union.rs

This file was deleted.

4 changes: 2 additions & 2 deletions integration_tests/juniper_tests/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ mod derive_enum;
mod derive_input_object;
mod derive_object;
mod derive_object_with_raw_idents;
mod derive_union;
mod impl_object;
mod impl_scalar;
mod impl_union;
mod scalar_value_transparent;
mod union_attr;
mod union_derive;
211 changes: 211 additions & 0 deletions integration_tests/juniper_tests/src/codegen/union_attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
use std::any::Any;

use juniper::{graphql_object, graphql_union, GraphQLObject};

#[cfg(test)]
use juniper::{
self, execute, DefaultScalarValue, EmptyMutation, EmptySubscription, GraphQLType, RootNode,
Value, Variables,
};

#[derive(GraphQLObject)]
struct Human {
id: String,
home_planet: String,
}

#[derive(GraphQLObject)]
struct Droid {
id: String,
primary_function: String,
}

#[derive(GraphQLObject)]
struct Jedi {
id: String,
rank: String,
}

#[graphql_union(name = "Character")]
#[graphql_union(description = "A Collection of things")]
#[graphql_union(on Jedi = resolve_character_jedi)]
trait Character<T> {
fn as_human(&self, _: &()) -> Option<&Human> {
None
}
fn as_droid(&self) -> Option<&Droid> {
None
}
#[graphql_union(ignore)]
fn as_jedi(&self) -> Option<&Jedi> {
None
}
#[graphql_union(ignore)]
fn some(&self) {}
}

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

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

impl<T> Character<T> for Jedi {
fn as_jedi(&self) -> Option<&Jedi> {
Some(&self)
}
}

fn resolve_character_jedi<'a, T>(
jedi: &'a (dyn Character<T> + Send + Sync),
_: &(),
) -> Option<&'a Jedi> {
jedi.as_jedi()
}

enum Query {
Human,
Droid,
Jedi,
}

#[graphql_object]
impl Query {
fn context(&self) -> Box<dyn Character<()> + Send + Sync> {
let ch: Box<dyn Character<()> + Send + Sync> = 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(),
}),
Self::Jedi => Box::new(Jedi {
id: "Obi Wan Kenobi".to_string(),
rank: "Master".to_string(),
}),
};
ch
}
}

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

#[tokio::test]
async fn resolves_human() {
let schema = RootNode::new(
Query::Human,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);

let actual = execute(DOC, None, &schema, &Variables::new(), &()).await;

let expected = Ok((
Value::object(
vec![(
"context",
Value::object(
vec![
("humanId", Value::scalar("human-32".to_string())),
("homePlanet", Value::scalar("earth".to_string())),
]
.into_iter()
.collect(),
),
)]
.into_iter()
.collect(),
),
vec![],
));

assert_eq!(actual, expected);
}

#[tokio::test]
async fn resolves_droid() {
let schema = RootNode::new(
Query::Droid,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);

let actual = execute(DOC, None, &schema, &Variables::new(), &()).await;

let expected = Ok((
Value::object(
vec![(
"context",
Value::object(
vec![
("droidId", Value::scalar("droid-99".to_string())),
("primaryFunction", Value::scalar("run".to_string())),
]
.into_iter()
.collect(),
),
)]
.into_iter()
.collect(),
),
vec![],
));

assert_eq!(actual, expected);
}

#[tokio::test]
async fn resolves_jedi() {
let schema = RootNode::new(
Query::Jedi,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);

let actual = execute(DOC, None, &schema, &Variables::new(), &()).await;

let expected = Ok((
Value::object(
vec![(
"context",
Value::object(
vec![
("jediId", Value::scalar("Obi Wan Kenobi".to_string())),
("rank", Value::scalar("Master".to_string())),
]
.into_iter()
.collect(),
),
)]
.into_iter()
.collect(),
),
vec![],
));

assert_eq!(actual, expected);
}
Loading