Skip to content

Commit 61f288b

Browse files
authored
Merge pull request #333 from theduke/graphql-object-proc-macro
Graphql object proc macro
2 parents 794568e + 29025e6 commit 61f288b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2789
-1024
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ rust:
55
- beta
66
- nightly
77

8+
# TODO: re-enable once new versions are released.
89
# Prevent accidentally breaking older Rust versions
9-
- 1.32.0
10-
- 1.31.0
10+
# - 1.33.0
1111

1212
matrix:
1313
include:

_build/azure-pipelines-template.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ jobs:
1010
rustup_toolchain: beta
1111
nightly:
1212
rustup_toolchain: nightly
13-
minimum_supported_version_plus_one:
14-
rustup_toolchain: 1.32.0
15-
minimum_supported_version:
16-
rustup_toolchain: 1.31.0
13+
# TODO: re-enable once new versions are released.
14+
# minimum_supported_version_plus_one:
15+
# rustup_toolchain: 1.32.0
16+
#minimum_supported_version:
17+
# rustup_toolchain: 1.33.0
1718
steps:
1819
- ${{ if ne(parameters.name, 'Windows') }}:
1920
# Linux and macOS.

docs/book/content/advanced/introspection.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@ result can then be converted to JSON for use with tools and libraries such as
3030
[graphql-client](https://github.com/graphql-rust/graphql-client):
3131

3232
```rust
33-
# // Only needed due to 2018 edition because the macro is not accessible.
34-
# extern crate juniper;
35-
# extern crate serde_json;
3633
use juniper::{EmptyMutation, FieldResult, IntrospectionFormat};
3734

3835
// Define our schema.
@@ -47,11 +44,14 @@ impl juniper::Context for Context {}
4744

4845
struct Query;
4946

50-
juniper::graphql_object!(Query: Context |&self| {
51-
field example(&executor, id: String) -> FieldResult<Example> {
47+
#[juniper::object(
48+
Context = Context,
49+
)]
50+
impl Query {
51+
fn example(id: String) -> FieldResult<Example> {
5252
unimplemented!()
5353
}
54-
});
54+
}
5555

5656
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Context>>;
5757

docs/book/content/advanced/non_struct_objects.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,22 @@ enum SignUpResult {
2323
Error(Vec<ValidationError>),
2424
}
2525

26-
juniper::graphql_object!(SignUpResult: () |&self| {
27-
field user() -> Option<&User> {
26+
#[juniper::object]
27+
impl SignUpResult {
28+
fn user(&self) -> Option<&User> {
2829
match *self {
2930
SignUpResult::Ok(ref user) => Some(user),
3031
SignUpResult::Error(_) => None,
3132
}
3233
}
3334

34-
field error() -> Option<&Vec<ValidationError>> {
35+
fn error(&self) -> Option<&Vec<ValidationError>> {
3536
match *self {
3637
SignUpResult::Ok(_) => None,
3738
SignUpResult::Error(ref errors) => Some(errors)
3839
}
3940
}
40-
});
41+
}
4142

4243
# fn main() {}
4344
```

docs/book/content/advanced/objects_and_generics.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,31 @@ struct ValidationError {
2525
# #[allow(dead_code)]
2626
struct MutationResult<T>(Result<T, Vec<ValidationError>>);
2727

28-
juniper::graphql_object!(MutationResult<User>: () as "UserResult" |&self| {
29-
field user() -> Option<&User> {
28+
#[juniper::object(
29+
name = "UserResult",
30+
)]
31+
impl MutationResult<User> {
32+
fn user(&self) -> Option<&User> {
3033
self.0.as_ref().ok()
3134
}
3235

33-
field error() -> Option<&Vec<ValidationError>> {
36+
fn error(&self) -> Option<&Vec<ValidationError>> {
3437
self.0.as_ref().err()
3538
}
36-
});
39+
}
3740

38-
juniper::graphql_object!(MutationResult<ForumPost>: () as "ForumPostResult" |&self| {
39-
field forum_post() -> Option<&ForumPost> {
41+
#[juniper::object(
42+
name = "ForumPostResult",
43+
)]
44+
impl MutationResult<ForumPost> {
45+
fn forum_post(&self) -> Option<&ForumPost> {
4046
self.0.as_ref().ok()
4147
}
4248

43-
field error() -> Option<&Vec<ValidationError>> {
49+
fn error(&self) -> Option<&Vec<ValidationError>> {
4450
self.0.as_ref().err()
4551
}
46-
});
52+
}
4753

4854
# fn main() {}
4955
```

docs/book/content/quickstart.md

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ naturally map to GraphQL features, such as `Option<T>`, `Vec<T>`, `Box<T>`,
2020

2121
For more advanced mappings, Juniper provides multiple macros to map your Rust
2222
types to a GraphQL schema. The most important one is the
23-
[graphql_object!][jp_obj_macro] macro that is used for declaring an object with
23+
[object][jp_object] procedural macro that is used for declaring an object with
2424
resolvers, which you will use for the `Query` and `Mutation` roots.
2525

2626
```rust
@@ -60,7 +60,7 @@ struct NewHuman {
6060
}
6161

6262
// Now, we create our root Query and Mutation types with resolvers by using the
63-
// graphql_object! macro.
63+
// object macro.
6464
// Objects can have contexts that allow accessing shared state like a database
6565
// pool.
6666

@@ -74,17 +74,23 @@ impl juniper::Context for Context {}
7474

7575
struct Query;
7676

77-
juniper::graphql_object!(Query: Context |&self| {
77+
#[juniper::object(
78+
// Here we specify the context type for the object.
79+
// We need to do this in every type that
80+
// needs access to the context.
81+
Context = Context,
82+
)]
83+
impl Query {
7884

79-
field apiVersion() -> &str {
85+
fn apiVersion() -> &str {
8086
"1.0"
8187
}
8288

8389
// Arguments to resolvers can either be simple types or input objects.
84-
// The executor is a special (optional) argument that allows accessing the context.
85-
field human(&executor, id: String) -> FieldResult<Human> {
86-
// Get the context from the executor.
87-
let context = executor.context();
90+
// To gain access to the context, we specify a argument
91+
// that is a reference to the Context type.
92+
// Juniper automatically injects the correct context here.
93+
fn human(context: &Context, id: String) -> FieldResult<Human> {
8894
// Get a db connection.
8995
let connection = context.pool.get_connection()?;
9096
// Execute a db query.
@@ -93,18 +99,23 @@ juniper::graphql_object!(Query: Context |&self| {
9399
// Return the result.
94100
Ok(human)
95101
}
96-
});
102+
}
103+
104+
// Now, we do the same for our Mutation type.
97105

98106
struct Mutation;
99107

100-
juniper::graphql_object!(Mutation: Context |&self| {
108+
#[juniper::object(
109+
Context = Context,
110+
)]
111+
impl Mutation {
101112

102-
field createHuman(&executor, new_human: NewHuman) -> FieldResult<Human> {
113+
fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult<Human> {
103114
let db = executor.context().pool.get_connection()?;
104115
let human: Human = db.insert_human(&new_human)?;
105116
Ok(human)
106117
}
107-
});
118+
}
108119

109120
// A root schema consists of a query and a mutation.
110121
// Request queries can be executed against a RootNode.
@@ -130,24 +141,30 @@ You can invoke `juniper::execute` directly to run a GraphQL query:
130141
# #[macro_use] extern crate juniper;
131142
use juniper::{FieldResult, Variables, EmptyMutation};
132143

144+
133145
#[derive(juniper::GraphQLEnum, Clone, Copy)]
134146
enum Episode {
135147
NewHope,
136148
Empire,
137149
Jedi,
138150
}
139151

152+
// Arbitrary context data.
153+
struct Ctx(Episode);
154+
155+
impl juniper::Context for Ctx {}
156+
140157
struct Query;
141158

142-
juniper::graphql_object!(Query: Ctx |&self| {
143-
field favoriteEpisode(&executor) -> FieldResult<Episode> {
144-
// Use the special &executor argument to fetch our fav episode.
145-
Ok(executor.context().0)
159+
#[juniper::object(
160+
Context = Ctx,
161+
)]
162+
impl Query {
163+
fn favoriteEpisode(context: &Ctx) -> FieldResult<Episode> {
164+
Ok(context.0)
146165
}
147-
});
166+
}
148167

149-
// Arbitrary context data.
150-
struct Ctx(Episode);
151168

152169
// A root schema consists of a query and a mutation.
153170
// Request queries can be executed against a RootNode.
@@ -181,4 +198,4 @@ fn main() {
181198
[rocket]: servers/rocket.md
182199
[iron]: servers/iron.md
183200
[tutorial]: ./tutorial.html
184-
[jp_obj_macro]: https://docs.rs/juniper/latest/juniper/macro.graphql_object.html
201+
[jp_obj_macro]: https://docs.rs/juniper/latest/juniper/macro.object.html

docs/book/content/schema/schemas_and_mutations.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,20 @@ object somewhere but never references it, it will not be exposed in a schema.
2020
## The query root
2121

2222
The query root is just a GraphQL object. You define it like any other GraphQL
23-
object in Juniper, most commonly using the `graphql_object!` macro:
23+
object in Juniper, most commonly using the `object` proc macro:
2424

2525
```rust
2626
# use juniper::FieldResult;
2727
# #[derive(juniper::GraphQLObject)] struct User { name: String }
2828
struct Root;
2929

30-
juniper::graphql_object!(Root: () |&self| {
31-
field userWithUsername(username: String) -> FieldResult<Option<User>> {
30+
#[juniper::object]
31+
impl Root {
32+
fn userWithUsername(username: String) -> FieldResult<Option<User>> {
3233
// Look up user in database...
3334
# unimplemented!()
3435
}
35-
});
36+
}
3637

3738
# fn main() { }
3839
```
@@ -47,12 +48,13 @@ usually performs some mutating side-effect, such as updating a database.
4748
# #[derive(juniper::GraphQLObject)] struct User { name: String }
4849
struct Mutations;
4950

50-
juniper::graphql_object!(Mutations: () |&self| {
51-
field signUpUser(name: String, email: String) -> FieldResult<User> {
51+
#[juniper::object]
52+
impl Mutations {
53+
fn signUpUser(name: String, email: String) -> FieldResult<User> {
5254
// Validate inputs and save user in database...
5355
# unimplemented!()
5456
}
55-
});
57+
}
5658

5759
# fn main() { }
5860
```

docs/book/content/servers/iron.md

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ fn context_factory(_: &mut Request) -> IronResult<()> {
4747
4848
struct Root;
4949
50-
juniper::graphql_object!(Root: () |&self| {
51-
field foo() -> String {
50+
#[juniper::object]
51+
impl Root {
52+
fn foo() -> String {
5253
"Bar".to_owned()
5354
}
54-
});
55+
}
5556
5657
# #[allow(unreachable_code, unused_variables)]
5758
fn main() {
@@ -98,13 +99,14 @@ fn context_factory(req: &mut Request) -> IronResult<Context> {
9899
99100
struct Root;
100101
101-
juniper::graphql_object!(Root: Context |&self| {
102-
field my_addr(&executor) -> String {
103-
let context = executor.context();
104-
102+
#[juniper::object(
103+
Context = Context,
104+
)]
105+
impl Root {
106+
field my_addr(context: &Context) -> String {
105107
format!("Hello, you're coming from {}", context.remote_addr)
106108
}
107-
});
109+
}
108110
109111
# fn main() {
110112
# let _graphql_endpoint = juniper_iron::GraphQLHandler::new(
@@ -115,10 +117,6 @@ juniper::graphql_object!(Root: Context |&self| {
115117
# }
116118
```
117119

118-
## Accessing global data
119-
120-
FIXME: Show how the `persistent` crate works with contexts using e.g. `r2d2`.
121-
122120
[iron]: http://ironframework.io
123121
[graphiql]: https://github.com/graphql/graphiql
124122
[mount]: https://github.com/iron/mount

docs/book/content/types/input_objects.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ struct Coordinate {
1414
struct Root;
1515
# #[derive(juniper::GraphQLObject)] struct User { name: String }
1616

17-
juniper::graphql_object!(Root: () |&self| {
18-
field users_at_location(coordinate: Coordinate, radius: f64) -> Vec<User> {
17+
#[juniper::object]
18+
impl Root {
19+
fn users_at_location(coordinate: Coordinate, radius: f64) -> Vec<User> {
1920
// Send coordinate to database
21+
// ...
2022
# unimplemented!()
2123
}
22-
});
24+
}
2325

2426
# fn main() {}
2527
```
@@ -43,12 +45,14 @@ struct WorldCoordinate {
4345
struct Root;
4446
# #[derive(juniper::GraphQLObject)] struct User { name: String }
4547

46-
juniper::graphql_object!(Root: () |&self| {
47-
field users_at_location(coordinate: WorldCoordinate, radius: f64) -> Vec<User> {
48+
#[juniper::object]
49+
impl Root {
50+
fn users_at_location(coordinate: WorldCoordinate, radius: f64) -> Vec<User> {
4851
// Send coordinate to database
52+
// ...
4953
# unimplemented!()
5054
}
51-
});
55+
}
5256

5357
# fn main() {}
5458
```

docs/book/content/types/interfaces.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl Character for Droid {
4949
fn as_droid(&self) -> Option<&Droid> { Some(&self) }
5050
}
5151

52-
juniper::graphql_interface!(<'a> &'a Character: () as "Character" where Scalar = <S>|&self| {
52+
juniper::graphql_interface!(<'a> &'a Character: () as "Character" where Scalar = <S> |&self| {
5353
field id() -> &str { self.id() }
5454

5555
instance_resolvers: |_| {
@@ -79,14 +79,14 @@ we'll use two hashmaps, but this could be two tables and some SQL calls instead:
7979
```rust
8080
# use std::collections::HashMap;
8181
#[derive(juniper::GraphQLObject)]
82-
#[graphql(Context = "Database")]
82+
#[graphql(Context = Database)]
8383
struct Human {
8484
id: String,
8585
home_planet: String,
8686
}
8787

8888
#[derive(juniper::GraphQLObject)]
89-
#[graphql(Context = "Database")]
89+
#[graphql(Context = Database)]
9090
struct Droid {
9191
id: String,
9292
primary_function: String,

0 commit comments

Comments
 (0)