From a4871887bb6b30029bd4671af716c0649a0cc60c Mon Sep 17 00:00:00 2001 From: Kai Ren Date: Sat, 7 Nov 2020 03:15:18 +0100 Subject: [PATCH] Default to generic ScalarValue in #[graphql_object] macro (#779) * Change codegen ScalarValue defaults for #[graphql_object] macro * Fix integration tests * Fix codegen failure tests * Fix 'juniper' crate tests * Fix integration crates tests * Fix 'juniper_benchmarks' crate * Fix examples * Fix Book * Fix * Add CHANGELOG entry * Some Book corrections * Fix * Bootstrap coercion machinery * Reimpl coercion * Correct tests, vol.1 * Correct tests, vol.2 * Correct tests, vol.3 * Correct tests, vol.4 * Correct tests, vol.5 * Fix coercion for subscriptions * README fixes Co-authored-by: Christian Legnitto Co-authored-by: Christian Legnitto --- docs/book/content/advanced/introspection.md | 11 +- .../content/advanced/non_struct_objects.md | 9 +- docs/book/content/advanced/subscriptions.md | 29 ++- docs/book/content/quickstart.md | 56 +++-- .../content/schema/schemas_and_mutations.md | 26 +- .../content/types/objects/complex_fields.md | 40 ++-- .../content/types/objects/defining_objects.md | 59 +++-- .../content/types/objects/error_handling.md | 73 +++--- .../content/types/objects/using_contexts.md | 16 +- examples/actix_subscriptions/src/main.rs | 5 +- examples/basic_subscriptions/src/main.rs | 8 +- examples/warp_async/src/main.rs | 4 +- examples/warp_subscriptions/src/main.rs | 13 +- integration_tests/async_await/src/main.rs | 12 +- .../object/impl_argument_no_object.stderr | 21 +- .../juniper_tests/src/codegen/derive_enum.rs | 26 +- .../src/codegen/derive_input_object.rs | 13 +- .../src/codegen/derive_object.rs | 224 +++++++++--------- .../codegen/derive_object_with_raw_idents.rs | 8 +- .../juniper_tests/src/codegen/impl_object.rs | 17 +- .../juniper_tests/src/codegen/impl_scalar.rs | 4 +- .../src/codegen/interface_attr.rs | 155 ++++++------ .../src/codegen/scalar_value_transparent.rs | 21 +- .../juniper_tests/src/codegen/union_attr.rs | 27 ++- .../juniper_tests/src/codegen/union_derive.rs | 31 ++- .../juniper_tests/src/custom_scalar.rs | 69 +++--- .../juniper_tests/src/issue_371.rs | 19 +- .../juniper_tests/src/issue_398.rs | 11 +- juniper/CHANGELOG.md | 12 +- juniper/src/executor/mod.rs | 42 +++- juniper/src/executor_tests/async_await/mod.rs | 10 +- juniper/src/executor_tests/executor.rs | 14 +- .../src/executor_tests/introspection/mod.rs | 18 +- juniper/src/executor_tests/variables.rs | 23 +- juniper/src/macros/helper/subscription.rs | 6 +- juniper/src/macros/tests/field.rs | 6 +- juniper/src/macros/tests/interface.rs | 18 +- juniper/src/macros/tests/union.rs | 15 +- juniper/src/parser/tests/document.rs | 10 +- juniper/src/schema/model.rs | 46 ++-- juniper/src/tests/fixtures/starwars/schema.rs | 25 +- .../fixtures/starwars/schema_language.rs | 3 +- juniper/src/validation/test_harness.rs | 2 +- juniper/src/value/mod.rs | 72 ++++-- juniper/src/value/scalar.rs | 73 ++++-- juniper_actix/src/lib.rs | 5 +- juniper_benchmarks/src/lib.rs | 12 +- juniper_codegen/src/impl_object.rs | 2 +- juniper_codegen/src/util/mod.rs | 18 +- juniper_iron/examples/iron_server.rs | 4 +- juniper_iron/src/lib.rs | 10 +- juniper_warp/src/lib.rs | 10 +- 52 files changed, 817 insertions(+), 646 deletions(-) diff --git a/docs/book/content/advanced/introspection.md b/docs/book/content/advanced/introspection.md index 9bf7e895..3fabcb73 100644 --- a/docs/book/content/advanced/introspection.md +++ b/docs/book/content/advanced/introspection.md @@ -33,11 +33,14 @@ result can then be converted to JSON for use with tools and libraries such as # #![allow(unused_variables)] # extern crate juniper; # extern crate serde_json; -use juniper::{EmptyMutation, EmptySubscription, FieldResult, IntrospectionFormat}; +use juniper::{ + graphql_object, EmptyMutation, EmptySubscription, FieldResult, + GraphQLObject, IntrospectionFormat, +}; // Define our schema. -#[derive(juniper::GraphQLObject)] +#[derive(GraphQLObject)] struct Example { id: String, } @@ -47,9 +50,7 @@ impl juniper::Context for Context {} struct Query; -#[juniper::graphql_object( - Context = Context, -)] +#[graphql_object(context = Context)] impl Query { fn example(id: String) -> FieldResult { unimplemented!() diff --git a/docs/book/content/advanced/non_struct_objects.md b/docs/book/content/advanced/non_struct_objects.md index 19b2626b..75169350 100644 --- a/docs/book/content/advanced/non_struct_objects.md +++ b/docs/book/content/advanced/non_struct_objects.md @@ -10,9 +10,10 @@ errors from a mutation: ```rust # extern crate juniper; +# use juniper::{graphql_object, GraphQLObject}; # #[derive(juniper::GraphQLObject)] struct User { name: String } - -#[derive(juniper::GraphQLObject)] +# +#[derive(GraphQLObject)] struct ValidationError { field: String, message: String, @@ -24,7 +25,7 @@ enum SignUpResult { Error(Vec), } -#[juniper::graphql_object] +#[graphql_object] impl SignUpResult { fn user(&self) -> Option<&User> { match *self { @@ -40,7 +41,7 @@ impl SignUpResult { } } } - +# # fn main() {} ``` diff --git a/docs/book/content/advanced/subscriptions.md b/docs/book/content/advanced/subscriptions.md index 59f14516..c1052b37 100644 --- a/docs/book/content/advanced/subscriptions.md +++ b/docs/book/content/advanced/subscriptions.md @@ -29,7 +29,7 @@ sequentially: # extern crate juniper; # extern crate juniper_subscriptions; # extern crate tokio; -# use juniper::FieldError; +# use juniper::{graphql_object, graphql_subscription, FieldError}; # use futures::Stream; # use std::pin::Pin; # @@ -38,7 +38,7 @@ sequentially: # impl juniper::Context for Database {} # pub struct Query; -# #[juniper::graphql_object(Context = Database)] +# #[graphql_object(context = Database)] # impl Query { # fn hello_world() -> &str { # "Hello World!" @@ -48,7 +48,7 @@ pub struct Subscription; type StringStream = Pin> + Send>>; -#[juniper::graphql_subscription(Context = Database)] +#[graphql_subscription(context = Database)] impl Subscription { async fn hello_world() -> StringStream { let stream = tokio::stream::iter(vec![ @@ -58,8 +58,9 @@ impl Subscription { Box::pin(stream) } } +# # fn main () {} -``` +``` @@ -84,8 +85,12 @@ where [`Connection`][Connection] is a `Stream` of values returned by the operati # extern crate juniper_subscriptions; # extern crate serde_json; # extern crate tokio; -# use juniper::http::GraphQLRequest; -# use juniper::{DefaultScalarValue, EmptyMutation, FieldError, RootNode, SubscriptionCoordinator}; +# use juniper::{ +# http::GraphQLRequest, +# graphql_object, graphql_subscription, +# DefaultScalarValue, EmptyMutation, FieldError, +# RootNode, SubscriptionCoordinator, +# }; # use juniper_subscriptions::Coordinator; # use futures::{Stream, StreamExt}; # use std::pin::Pin; @@ -103,7 +108,7 @@ where [`Connection`][Connection] is a `Stream` of values returned by the operati # # pub struct Query; # -# #[juniper::graphql_object(Context = Database)] +# #[graphql_object(context = Database)] # impl Query { # fn hello_world() -> &str { # "Hello World!" @@ -114,7 +119,7 @@ where [`Connection`][Connection] is a `Stream` of values returned by the operati # # type StringStream = Pin> + Send>>; # -# #[juniper::graphql_subscription(Context = Database)] +# #[graphql_subscription(context = Database)] # impl Subscription { # async fn hello_world() -> StringStream { # let stream = @@ -132,11 +137,9 @@ async fn run_subscription() { let schema = schema(); let coordinator = Coordinator::new(schema); let req: GraphQLRequest = serde_json::from_str( - r#" - { + r#"{ "query": "subscription { helloWorld }" - } - "#, + }"#, ) .unwrap(); let ctx = Database::new(); @@ -145,7 +148,7 @@ async fn run_subscription() { println!("{}", serde_json::to_string(&result).unwrap()); } } - +# # fn main() { } ``` diff --git a/docs/book/content/quickstart.md b/docs/book/content/quickstart.md index b3b58015..aa9661ae 100644 --- a/docs/book/content/quickstart.md +++ b/docs/book/content/quickstart.md @@ -28,8 +28,12 @@ resolvers, which you will use for the `Query` and `Mutation` roots. ```rust # #![allow(unused_variables)] # extern crate juniper; -use juniper::{FieldResult, EmptySubscription}; - +# use std::fmt::Display; +use juniper::{ + graphql_object, EmptySubscription, FieldResult, GraphQLEnum, + GraphQLInputObject, GraphQLObject, ScalarValue, +}; +# # struct DatabasePool; # impl DatabasePool { # fn get_connection(&self) -> FieldResult { Ok(DatabasePool) } @@ -37,15 +41,15 @@ use juniper::{FieldResult, EmptySubscription}; # fn insert_human(&self, _human: &NewHuman) -> FieldResult { Err("")? } # } -#[derive(juniper::GraphQLEnum)] +#[derive(GraphQLEnum)] enum Episode { NewHope, Empire, Jedi, } -#[derive(juniper::GraphQLObject)] -#[graphql(description="A humanoid creature in the Star Wars universe")] +#[derive(GraphQLObject)] +#[graphql(description = "A humanoid creature in the Star Wars universe")] struct Human { id: String, name: String, @@ -55,8 +59,8 @@ struct Human { // There is also a custom derive for mapping GraphQL input objects. -#[derive(juniper::GraphQLInputObject)] -#[graphql(description="A humanoid creature in the Star Wars universe")] +#[derive(GraphQLInputObject)] +#[graphql(description = "A humanoid creature in the Star Wars universe")] struct NewHuman { name: String, appears_in: Vec, @@ -78,14 +82,13 @@ impl juniper::Context for Context {} struct Query; -#[juniper::graphql_object( +#[graphql_object( // Here we specify the context type for the object. // We need to do this in every type that // needs access to the context. - Context = Context, + context = Context, )] impl Query { - fn apiVersion() -> &str { "1.0" } @@ -109,14 +112,18 @@ impl Query { struct Mutation; -#[juniper::graphql_object( - Context = Context, -)] -impl Mutation { +#[graphql_object( + context = Context, - fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult { - let db = context.pool.get_connection()?; - let human: Human = db.insert_human(&new_human)?; + // If we need to use `ScalarValue` parametrization explicitly somewhere + // in the object definition (like here in `FieldResult`), we should + // declare an explicit type parameter for that, and specify it. + scalar = S, +)] +impl Mutation { + fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult { + let db = context.pool.get_connection().map_err(|e| e.map_scalar_value())?; + let human: Human = db.insert_human(&new_human).map_err(|e| e.map_scalar_value())?; Ok(human) } } @@ -124,7 +131,7 @@ impl Mutation { // A root schema consists of a query, a mutation, and a subscription. // Request queries can be executed against a RootNode. type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription>; - +# # fn main() { # let _ = Schema::new(Query, Mutation{}, EmptySubscription::new()); # } @@ -143,10 +150,12 @@ You can invoke `juniper::execute` directly to run a GraphQL query: ```rust # // Only needed due to 2018 edition because the macro is not accessible. # #[macro_use] extern crate juniper; -use juniper::{FieldResult, Variables, EmptyMutation, EmptySubscription}; +use juniper::{ + graphql_object, EmptyMutation, EmptySubscription, FieldResult, + GraphQLEnum, Variables, +}; - -#[derive(juniper::GraphQLEnum, Clone, Copy)] +#[derive(GraphQLEnum, Clone, Copy)] enum Episode { NewHope, Empire, @@ -160,16 +169,13 @@ impl juniper::Context for Ctx {} struct Query; -#[juniper::graphql_object( - Context = Ctx, -)] +#[graphql_object(context = Ctx)] impl Query { fn favoriteEpisode(context: &Ctx) -> FieldResult { Ok(context.0) } } - // A root schema consists of a query, a mutation, and a subscription. // Request queries can be executed against a RootNode. type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>; diff --git a/docs/book/content/schema/schemas_and_mutations.md b/docs/book/content/schema/schemas_and_mutations.md index 77cb4557..c7ecc7d3 100644 --- a/docs/book/content/schema/schemas_and_mutations.md +++ b/docs/book/content/schema/schemas_and_mutations.md @@ -24,18 +24,18 @@ object in Juniper, most commonly using the `graphql_object` proc macro: ```rust # #![allow(unused_variables)] # extern crate juniper; -# use juniper::FieldResult; -# #[derive(juniper::GraphQLObject)] struct User { name: String } +# use juniper::{graphql_object, FieldResult, GraphQLObject}; +# #[derive(GraphQLObject)] struct User { name: String } struct Root; -#[juniper::graphql_object] +#[graphql_object] impl Root { fn userWithUsername(username: String) -> FieldResult> { // Look up user in database... -# unimplemented!() +# unimplemented!() } } - +# # fn main() { } ``` @@ -47,18 +47,18 @@ that performs some mutating side-effect such as updating a database. ```rust # #![allow(unused_variables)] # extern crate juniper; -# use juniper::FieldResult; -# #[derive(juniper::GraphQLObject)] struct User { name: String } +# use juniper::{graphql_object, FieldResult, GraphQLObject}; +# #[derive(GraphQLObject)] struct User { name: String } struct Mutations; -#[juniper::graphql_object] +#[graphql_object] impl Mutations { fn signUpUser(name: String, email: String) -> FieldResult { // Validate inputs and save user in database... -# unimplemented!() +# unimplemented!() } } - +# # fn main() { } ``` @@ -68,11 +68,13 @@ Many tools in the GraphQL ecosystem require the schema to be defined in the [Gra ```rust # extern crate juniper; -use juniper::{FieldResult, EmptyMutation, EmptySubscription, RootNode}; +use juniper::{ + graphql_object, EmptyMutation, EmptySubscription, FieldResult, RootNode, +}; struct Query; -#[juniper::graphql_object] +#[graphql_object] impl Query { fn hello(&self) -> FieldResult<&str> { Ok("hello world") diff --git a/docs/book/content/types/objects/complex_fields.md b/docs/book/content/types/objects/complex_fields.md index 6abc6a04..75867184 100644 --- a/docs/book/content/types/objects/complex_fields.md +++ b/docs/book/content/types/objects/complex_fields.md @@ -2,7 +2,7 @@ If you've got a struct that can't be mapped directly to GraphQL, that contains computed fields or circular structures, you have to use a more powerful tool: -the `object` procedural macro. This macro lets you define GraphQL object +the `#[graphql_object]` procedural macro. This macro lets you define GraphQL object fields in a Rust `impl` block for a type. Note that only GraphQL fields can be specified in this `impl` block. If you want to define normal methods on the struct, you have to do so in a separate, normal `impl` block. Continuing with the @@ -12,13 +12,14 @@ macro: ```rust # #![allow(dead_code)] # extern crate juniper; +# use juniper::graphql_object; # struct Person { name: String, age: i32, } -#[juniper::graphql_object] +#[graphql_object] impl Person { fn name(&self) -> &str { self.name.as_str() @@ -36,7 +37,7 @@ impl Person { // [...] } } - +# # fn main() { } ``` @@ -46,7 +47,9 @@ field resolver. With this syntax, fields can also take arguments: ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] +# use juniper::{graphql_object, GraphQLObject}; +# +#[derive(GraphQLObject)] struct Person { name: String, age: i32, @@ -56,14 +59,14 @@ struct House { inhabitants: Vec, } -#[juniper::graphql_object] +#[graphql_object] impl House { // Creates the field inhabitantWithName(name), returning a nullable person fn inhabitant_with_name(&self, name: String) -> Option<&Person> { self.inhabitants.iter().find(|p| p.name == name) } } - +# # fn main() {} ``` @@ -79,20 +82,20 @@ the field. Also, the type name can be changed with an alias: ```rust # extern crate juniper; - -struct Person { -} +# use juniper::graphql_object; +# +struct Person; /// Doc comments are used as descriptions for GraphQL. -#[juniper::graphql_object( +#[graphql_object( // With this attribute you can change the public GraphQL name of the type. name = "PersonObject", + // You can also specify a description here, which will overwrite // a doc comment description. description = "...", )] impl Person { - /// A doc comment on the field will also be used for GraphQL. #[graphql( // Or provide a description here. @@ -103,9 +106,7 @@ impl Person { } // Fields can also be renamed if required. - #[graphql( - name = "myCustomFieldName", - )] + #[graphql(name = "myCustomFieldName")] fn renamed_field() -> bool { true } @@ -122,7 +123,7 @@ impl Person { true } } - +# # fn main() { } ``` @@ -137,10 +138,11 @@ This will become better once the [Rust RFC 2565](https://github.com/rust-lang/ru ```rust # extern crate juniper; - +# use juniper::graphql_object; +# struct Person {} -#[juniper::graphql_object] +#[graphql_object] impl Person { #[graphql( arguments( @@ -160,7 +162,7 @@ impl Person { format!("{} {}", arg1, arg2) } } - +# # fn main() { } ``` @@ -172,5 +174,5 @@ GraphQL fields expose more features than Rust's standard method syntax gives us: * Per-argument default values * Per-argument descriptions -These, and more features, are described more thorougly in [the reference +These, and more features, are described more thoroughly in [the reference documentation](https://docs.rs/juniper/latest/juniper/macro.object.html). diff --git a/docs/book/content/types/objects/defining_objects.md b/docs/book/content/types/objects/defining_objects.md index b0c95dd2..1a4dcb7d 100644 --- a/docs/book/content/types/objects/defining_objects.md +++ b/docs/book/content/types/objects/defining_objects.md @@ -10,12 +10,13 @@ chapter. ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] +# use juniper::GraphQLObject; +#[derive(GraphQLObject)] struct Person { name: String, age: i32, } - +# # fn main() {} ``` @@ -33,7 +34,8 @@ descriptions: ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] +# use juniper::GraphQLObject; +#[derive(GraphQLObject)] /// Information about a person struct Person { /// The person's full name, including both first and last names @@ -41,7 +43,7 @@ struct Person { /// The person's age in years, rounded down age: i32, } - +# # fn main() {} ``` @@ -52,15 +54,16 @@ via the `graphql` attribute. The following example is equivalent to the above: ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] -#[graphql(description="Information about a person")] +# use juniper::GraphQLObject; +#[derive(GraphQLObject)] +#[graphql(description = "Information about a person")] struct Person { - #[graphql(description="The person's full name, including both first and last names")] + #[graphql(description = "The person's full name, including both first and last names")] name: String, - #[graphql(description="The person's age in years, rounded down")] + #[graphql(description = "The person's age in years, rounded down")] age: i32, } - +# # fn main() {} ``` @@ -70,17 +73,18 @@ documentation to differ: ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] -#[graphql(description="This description shows up in GraphQL")] +# use juniper::GraphQLObject; +#[derive(GraphQLObject)] +#[graphql(description = "This description shows up in GraphQL")] /// This description shows up in RustDoc struct Person { - #[graphql(description="This description shows up in GraphQL")] + #[graphql(description = "This description shows up in GraphQL")] /// This description shows up in RustDoc name: String, /// This description shows up in both RustDoc and GraphQL age: i32, } - +# # fn main() {} ``` @@ -100,18 +104,19 @@ Let's see what that means for building relationships between objects: ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] +# use juniper::GraphQLObject; +#[derive(GraphQLObject)] struct Person { name: String, age: i32, } -#[derive(juniper::GraphQLObject)] +#[derive(GraphQLObject)] struct House { address: Option, // Converted into String (nullable) inhabitants: Vec, // Converted into [Person!]! } - +# # fn main() {} ``` @@ -126,12 +131,13 @@ convention into GraphQL's `camelCase` convention: ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] +# use juniper::GraphQLObject; +#[derive(GraphQLObject)] struct Person { first_name: String, // Would be exposed as firstName in the GraphQL schema last_name: String, // Exposed as lastName } - +# # fn main() {} ``` @@ -140,14 +146,15 @@ fields: ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] +# use juniper::GraphQLObject; +#[derive(GraphQLObject)] struct Person { name: String, age: i32, - #[graphql(name="websiteURL")] + #[graphql(name = "websiteURL")] website_url: Option, // Now exposed as websiteURL in the schema } - +# # fn main() {} ``` @@ -158,14 +165,15 @@ attribute: ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] +# use juniper::GraphQLObject; +#[derive(GraphQLObject)] struct Person { name: String, age: i32, #[graphql(deprecated = "Please use the name field instead")] first_name: String, } - +# # fn main() {} ``` @@ -179,7 +187,8 @@ By default all fields in a `GraphQLObject` are included in the generated GraphQL ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] +# use juniper::GraphQLObject; +#[derive(GraphQLObject)] struct Person { name: String, age: i32, @@ -187,6 +196,6 @@ struct Person { # #[allow(dead_code)] password_hash: String, // This cannot be queried or modified from GraphQL } - +# # fn main() {} ``` diff --git a/docs/book/content/types/objects/error_handling.md b/docs/book/content/types/objects/error_handling.md index 247b07e3..ea64e764 100644 --- a/docs/book/content/types/objects/error_handling.md +++ b/docs/book/content/types/objects/error_handling.md @@ -28,13 +28,13 @@ use std::{ fs::{File}, io::{Read}, }; -use juniper::FieldResult; +use juniper::{graphql_object, FieldResult}; struct Example { filename: PathBuf, } -#[juniper::graphql_object] +#[graphql_object] impl Example { fn contents() -> FieldResult { let mut file = File::open(&self.filename)?; @@ -53,7 +53,7 @@ impl Example { } } } - +# # fn main() {} ``` @@ -137,14 +137,16 @@ to clients. This can be accomplished by implementing [`IntoFieldError`](https:// ```rust # #[macro_use] extern crate juniper; +# use juniper::{graphql_object, FieldError, IntoFieldError, ScalarValue}; +# enum CustomError { WhateverNotSet, } -impl juniper::IntoFieldError for CustomError { - fn into_field_error(self) -> juniper::FieldError { +impl IntoFieldError for CustomError { + fn into_field_error(self) -> FieldError { match self { - CustomError::WhateverNotSet => juniper::FieldError::new( + CustomError::WhateverNotSet => FieldError::new( "Whatever does not exist", graphql_value!({ "type": "NO_WHATEVER" @@ -158,7 +160,7 @@ struct Example { whatever: Option, } -#[juniper::graphql_object] +#[graphql_object] impl Example { fn whatever() -> Result { if let Some(value) = self.whatever { @@ -167,21 +169,21 @@ impl Example { Err(CustomError::WhateverNotSet) } } - +# # fn main() {} ``` The specified structured error information is included in the [`extensions`](https://facebook.github.io/graphql/June2018/#sec-Errors) key: -```js +```json { - "errors": [ + "errors": [{ "message": "Whatever does not exist", - "locations": [{ "line": 2, "column": 4 }]), + "locations": [{"line": 2, "column": 4}], "extensions": { "type": "NO_WHATEVER" } - ] + }] } ``` @@ -211,24 +213,26 @@ possible to return a unique string identifier and have the client present a loca ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] +# use juniper::{graphql_object, GraphQLObject, GraphQLUnion}; +# +#[derive(GraphQLObject)] pub struct Item { name: String, quantity: i32, } -#[derive(juniper::GraphQLObject)] +#[derive(GraphQLObject)] pub struct ValidationError { field: String, message: String, } -#[derive(juniper::GraphQLObject)] +#[derive(GraphQLObject)] pub struct ValidationErrors { errors: Vec, } -#[derive(juniper::GraphQLUnion)] +#[derive(GraphQLUnion)] pub enum GraphQLResult { Ok(Item), Err(ValidationErrors), @@ -236,7 +240,7 @@ pub enum GraphQLResult { pub struct Mutation; -#[juniper::graphql_object] +#[graphql_object] impl Mutation { fn addItem(&self, name: String, quantity: i32) -> GraphQLResult { let mut errors = Vec::new(); @@ -262,7 +266,7 @@ impl Mutation { } } } - +# # fn main() {} ``` @@ -308,19 +312,21 @@ contains only fields provided by the function. ```rust # extern crate juniper; -#[derive(juniper::GraphQLObject)] +# use juniper::{graphql_object, GraphQLObject, GraphQLUnion}; +# +#[derive(GraphQLObject)] pub struct Item { name: String, quantity: i32, } -#[derive(juniper::GraphQLObject)] +#[derive(GraphQLObject)] pub struct ValidationError { name: Option, quantity: Option, } -#[derive(juniper::GraphQLUnion)] +#[derive(GraphQLUnion)] pub enum GraphQLResult { Ok(Item), Err(ValidationError), @@ -328,7 +334,7 @@ pub enum GraphQLResult { pub struct Mutation; -#[juniper::graphql_object] +#[graphql_object] impl Mutation { fn addItem(&self, name: String, quantity: i32) -> GraphQLResult { let mut error = ValidationError { @@ -351,7 +357,7 @@ impl Mutation { } } } - +# # fn main() {} ``` @@ -385,22 +391,23 @@ and would generate errors. Since it is not common for the database to fail, the corresponding error is returned as a critical error: ```rust -# // Only needed due to 2018 edition because the macro is not accessible. -# #[macro_use] extern crate juniper; +# extern crate juniper; +# +use juniper::{graphql_object, graphql_value, FieldError, GraphQLObject, GraphQLUnion, ScalarValue}; -#[derive(juniper::GraphQLObject)] +#[derive(GraphQLObject)] pub struct Item { name: String, quantity: i32, } -#[derive(juniper::GraphQLObject)] +#[derive(GraphQLObject)] pub struct ValidationErrorItem { name: Option, quantity: Option, } -#[derive(juniper::GraphQLUnion)] +#[derive(GraphQLUnion)] pub enum GraphQLResult { Ok(Item), Err(ValidationErrorItem), @@ -410,10 +417,10 @@ pub enum ApiError { Database, } -impl juniper::IntoFieldError for ApiError { - fn into_field_error(self) -> juniper::FieldError { +impl juniper::IntoFieldError for ApiError { + fn into_field_error(self) -> FieldError { match self { - ApiError::Database => juniper::FieldError::new( + ApiError::Database => FieldError::new( "Internal database error", graphql_value!({ "type": "DATABASE" @@ -425,7 +432,7 @@ impl juniper::IntoFieldError for ApiError { pub struct Mutation; -#[juniper::graphql_object] +#[graphql_object] impl Mutation { fn addItem(&self, name: String, quantity: i32) -> Result { let mut error = ValidationErrorItem { @@ -448,7 +455,7 @@ impl Mutation { } } } - +# # fn main() {} ``` diff --git a/docs/book/content/types/objects/using_contexts.md b/docs/book/content/types/objects/using_contexts.md index 1233ec5b..0576a664 100644 --- a/docs/book/content/types/objects/using_contexts.md +++ b/docs/book/content/types/objects/using_contexts.md @@ -13,7 +13,7 @@ resolvers. Let's say that we have a simple user database in a `HashMap`: ```rust # #![allow(dead_code)] # use std::collections::HashMap; - +# struct Database { users: HashMap, } @@ -23,7 +23,7 @@ struct User { name: String, friend_ids: Vec, } - +# # fn main() { } ``` @@ -38,9 +38,10 @@ type as the specified `Context` for the type: ```rust +# extern crate juniper; # use std::collections::HashMap; -extern crate juniper; - +# use juniper::graphql_object; +# // This struct represents our context. struct Database { users: HashMap, @@ -55,11 +56,8 @@ struct User { friend_ids: Vec, } - // Assign Database as the context type for User -#[juniper::graphql_object( - Context = Database, -)] +#[graphql_object(context = Database)] impl User { // 3. Inject the context by specifying an argument // with the context type. @@ -82,7 +80,7 @@ impl User { self.id } } - +# # fn main() { } ``` diff --git a/examples/actix_subscriptions/src/main.rs b/examples/actix_subscriptions/src/main.rs index 08a78651..0061c023 100644 --- a/examples/actix_subscriptions/src/main.rs +++ b/examples/actix_subscriptions/src/main.rs @@ -4,6 +4,7 @@ use actix_cors::Cors; use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; use juniper::{ + graphql_object, graphql_subscription, tests::fixtures::starwars::schema::{Character as _, Database, Query}, DefaultScalarValue, EmptyMutation, FieldError, RootNode, }; @@ -37,7 +38,7 @@ struct RandomHuman { } // TODO: remove this when async interfaces are merged -#[juniper::graphql_object(Context = Database)] +#[graphql_object(context = Database)] impl RandomHuman { fn id(&self) -> &str { &self.id @@ -51,7 +52,7 @@ impl RandomHuman { type RandomHumanStream = Pin> + Send>>; -#[juniper::graphql_subscription(Context = Database)] +#[graphql_subscription(context = Database)] impl Subscription { #[graphql( description = "A random humanoid creature in the Star Wars universe every 3 seconds. Second result will be an error." diff --git a/examples/basic_subscriptions/src/main.rs b/examples/basic_subscriptions/src/main.rs index a5d463e1..819fe535 100644 --- a/examples/basic_subscriptions/src/main.rs +++ b/examples/basic_subscriptions/src/main.rs @@ -4,8 +4,8 @@ use std::pin::Pin; use futures::{Stream, StreamExt}; use juniper::{ - http::GraphQLRequest, DefaultScalarValue, EmptyMutation, FieldError, RootNode, - SubscriptionCoordinator, + graphql_object, graphql_subscription, http::GraphQLRequest, DefaultScalarValue, EmptyMutation, + FieldError, RootNode, SubscriptionCoordinator, }; use juniper_subscriptions::Coordinator; @@ -22,7 +22,7 @@ impl Database { pub struct Query; -#[juniper::graphql_object(Context = Database)] +#[graphql_object(context = Database)] impl Query { fn hello_world() -> &str { "Hello World!" @@ -33,7 +33,7 @@ pub struct Subscription; type StringStream = Pin> + Send>>; -#[juniper::graphql_subscription(Context = Database)] +#[graphql_subscription(context = Database)] impl Subscription { async fn hello_world() -> StringStream { let stream = diff --git a/examples/warp_async/src/main.rs b/examples/warp_async/src/main.rs index 95a1671a..7f1b902f 100644 --- a/examples/warp_async/src/main.rs +++ b/examples/warp_async/src/main.rs @@ -23,7 +23,7 @@ struct User { name: String, } -#[graphql_object(Context = Context)] +#[graphql_object(context = Context)] impl User { fn id(&self) -> i32 { self.id @@ -45,7 +45,7 @@ impl User { #[derive(Clone, Copy, Debug)] struct Query; -#[graphql_object(Context = Context)] +#[graphql_object(context = Context)] impl Query { async fn users() -> Vec { vec![User { diff --git a/examples/warp_subscriptions/src/main.rs b/examples/warp_subscriptions/src/main.rs index 0d4f31a6..369a14b9 100644 --- a/examples/warp_subscriptions/src/main.rs +++ b/examples/warp_subscriptions/src/main.rs @@ -3,7 +3,10 @@ use std::{env, pin::Pin, sync::Arc, time::Duration}; use futures::{FutureExt as _, Stream}; -use juniper::{DefaultScalarValue, EmptyMutation, FieldError, RootNode}; +use juniper::{ + graphql_object, graphql_subscription, DefaultScalarValue, EmptyMutation, FieldError, + GraphQLEnum, RootNode, +}; use juniper_graphql_ws::ConnectionConfig; use juniper_warp::{playground_filter, subscriptions::serve_graphql_ws}; use warp::{http::Response, Filter}; @@ -13,7 +16,7 @@ struct Context {} impl juniper::Context for Context {} -#[derive(Clone, Copy, juniper::GraphQLEnum)] +#[derive(Clone, Copy, GraphQLEnum)] enum UserKind { Admin, User, @@ -27,7 +30,7 @@ struct User { } // Field resolvers implementation -#[juniper::graphql_object(Context = Context)] +#[graphql_object(context = Context)] impl User { fn id(&self) -> i32 { self.id @@ -87,7 +90,7 @@ impl User { struct Query; -#[juniper::graphql_object(Context = Context)] +#[graphql_object(context = Context)] impl Query { async fn users(id: i32) -> Vec { vec![User { @@ -102,7 +105,7 @@ type UsersStream = Pin> + Send>>; struct Subscription; -#[juniper::graphql_subscription(Context = Context)] +#[graphql_subscription(context = Context)] impl Subscription { async fn users() -> UsersStream { let mut counter = 0; diff --git a/integration_tests/async_await/src/main.rs b/integration_tests/async_await/src/main.rs index d4004aa3..6d9d3d41 100644 --- a/integration_tests/async_await/src/main.rs +++ b/integration_tests/async_await/src/main.rs @@ -1,7 +1,9 @@ -#[cfg(test)] -use juniper::{graphql_value, EmptyMutation, EmptySubscription, GraphQLError, RootNode, Value}; +use juniper::{ + graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLEnum, GraphQLError, + RootNode, Value, +}; -#[derive(juniper::GraphQLEnum)] +#[derive(GraphQLEnum)] enum UserKind { Admin, User, @@ -15,7 +17,7 @@ struct User { kind: UserKind, } -#[juniper::graphql_object] +#[graphql_object] impl User { async fn id(&self) -> i32 { self.id @@ -47,7 +49,7 @@ impl User { struct Query; -#[juniper::graphql_object] +#[graphql_object] impl Query { fn field_sync(&self) -> &'static str { "field_sync" diff --git a/integration_tests/codegen_fail/fail/object/impl_argument_no_object.stderr b/integration_tests/codegen_fail/fail/object/impl_argument_no_object.stderr index c1017787..87040d36 100644 --- a/integration_tests/codegen_fail/fail/object/impl_argument_no_object.stderr +++ b/integration_tests/codegen_fail/fail/object/impl_argument_no_object.stderr @@ -1,16 +1,29 @@ -error[E0277]: the trait bound `Obj: IsInputType` is not satisfied +error[E0277]: the trait bound `Obj: IsInputType<__S>` is not satisfied --> $DIR/impl_argument_no_object.rs:8:1 | 8 | #[juniper::graphql_object] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType` is not implemented for `Obj` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `Obj` | = note: required by `juniper::marker::IsInputType::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 `Obj: FromInputValue` is not satisfied +error[E0277]: the trait bound `Obj: FromInputValue<__S>` is not satisfied --> $DIR/impl_argument_no_object.rs:8:1 | 8 | #[juniper::graphql_object] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue` is not implemented for `Obj` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `Obj` | = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Obj: FromInputValue` is not satisfied + --> $DIR/impl_argument_no_object.rs:8:1 + | +8 | #[juniper::graphql_object] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue` is not implemented for `Obj` + | + ::: $WORKSPACE/juniper/src/ast.rs + | + | pub trait FromInputValue: Sized { + | ------------------------------------------------------- required by this bound in `FromInputValue` + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/integration_tests/juniper_tests/src/codegen/derive_enum.rs b/integration_tests/juniper_tests/src/codegen/derive_enum.rs index f4adbc69..47329b9b 100644 --- a/integration_tests/juniper_tests/src/codegen/derive_enum.rs +++ b/integration_tests/juniper_tests/src/codegen/derive_enum.rs @@ -1,14 +1,14 @@ -#[cfg(test)] use fnv::FnvHashMap; - -#[cfg(test)] -use juniper::{self, DefaultScalarValue, FromInputValue, GraphQLType, InputValue, ToInputValue}; +use juniper::{ + DefaultScalarValue, FromInputValue, GraphQLEnum, GraphQLType, InputValue, Registry, + ToInputValue, +}; pub struct CustomContext {} impl juniper::Context for CustomContext {} -#[derive(juniper::GraphQLEnum, Debug, PartialEq)] +#[derive(GraphQLEnum, Debug, PartialEq)] #[graphql(name = "Some", description = "enum descr")] enum SomeEnum { Regular, @@ -24,7 +24,7 @@ enum NoRenameEnum { } /// Enum doc. -#[derive(juniper::GraphQLEnum)] +#[derive(GraphQLEnum)] enum DocEnum { /// Variant doc. Foo, @@ -34,7 +34,7 @@ enum DocEnum { /// Doc 2. /// /// Doc 4. -#[derive(juniper::GraphQLEnum, Debug, PartialEq)] +#[derive(GraphQLEnum, Debug, PartialEq)] enum MultiDocEnum { /// Variant 1. /// Variant 2. @@ -42,7 +42,7 @@ enum MultiDocEnum { } /// This is not used as the description. -#[derive(juniper::GraphQLEnum, Debug, PartialEq)] +#[derive(GraphQLEnum, Debug, PartialEq)] #[graphql(description = "enum override")] enum OverrideDocEnum { /// This is not used as the description. @@ -50,7 +50,7 @@ enum OverrideDocEnum { Foo, } -#[derive(juniper::GraphQLEnum)] +#[derive(GraphQLEnum)] #[graphql(context = CustomContext, noasync)] enum ContextEnum { A, @@ -65,7 +65,7 @@ fn test_derived_enum() { ); // Ensure validity of meta info. - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = SomeEnum::meta(&(), &mut registry); assert_eq!(meta.name(), Some("Some")); @@ -100,14 +100,14 @@ fn test_derived_enum() { #[test] fn test_doc_comment() { - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = DocEnum::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"Enum doc.".to_string())); } #[test] fn test_multi_doc_comment() { - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = MultiDocEnum::meta(&(), &mut registry); assert_eq!( meta.description(), @@ -117,7 +117,7 @@ fn test_multi_doc_comment() { #[test] fn test_doc_comment_override() { - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = OverrideDocEnum::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"enum override".to_string())); } diff --git a/integration_tests/juniper_tests/src/codegen/derive_input_object.rs b/integration_tests/juniper_tests/src/codegen/derive_input_object.rs index d6fa4ca5..cc1595f5 100644 --- a/integration_tests/juniper_tests/src/codegen/derive_input_object.rs +++ b/integration_tests/juniper_tests/src/codegen/derive_input_object.rs @@ -1,8 +1,7 @@ use fnv::FnvHashMap; - use juniper::{ marker, DefaultScalarValue, FromInputValue, GraphQLInputObject, GraphQLType, GraphQLValue, - InputValue, ToInputValue, + InputValue, Registry, ToInputValue, }; #[derive(GraphQLInputObject, Debug, PartialEq)] @@ -74,7 +73,7 @@ impl<'a> GraphQLType for &'a Fake { fn name(_: &()) -> Option<&'static str> { None } - fn meta<'r>(_: &(), registry: &mut juniper::Registry<'r>) -> juniper::meta::MetaType<'r> + fn meta<'r>(_: &(), registry: &mut Registry<'r>) -> juniper::meta::MetaType<'r> where DefaultScalarValue: 'r, { @@ -113,7 +112,7 @@ fn test_derived_input_object() { ); // Validate meta info. - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = Input::meta(&(), &mut registry); assert_eq!(meta.name(), Some("MyInput")); assert_eq!(meta.description(), Some(&"input descr".to_string())); @@ -172,14 +171,14 @@ fn test_derived_input_object() { #[test] fn test_doc_comment() { - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = DocComment::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"Object comment.".to_string())); } #[test] fn test_multi_doc_comment() { - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = MultiDocComment::meta(&(), &mut registry); assert_eq!( meta.description(), @@ -189,7 +188,7 @@ fn test_multi_doc_comment() { #[test] fn test_doc_comment_override() { - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = OverrideDocComment::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"obj override".to_string())); } diff --git a/integration_tests/juniper_tests/src/codegen/derive_object.rs b/integration_tests/juniper_tests/src/codegen/derive_object.rs index 8a387b80..2a5336ab 100644 --- a/integration_tests/juniper_tests/src/codegen/derive_object.rs +++ b/integration_tests/juniper_tests/src/codegen/derive_object.rs @@ -1,20 +1,11 @@ -#[cfg(test)] use fnv::FnvHashMap; -#[cfg(test)] -use juniper::Object; -use juniper::{DefaultScalarValue, GraphQLObject}; - -#[cfg(test)] use juniper::{ - self, execute, EmptyMutation, EmptySubscription, GraphQLType, RootNode, Value, Variables, + execute, graphql_object, DefaultScalarValue, EmptyMutation, EmptySubscription, GraphQLObject, + GraphQLType, Object, Registry, RootNode, Value, Variables, }; #[derive(GraphQLObject, Debug, PartialEq)] -#[graphql( - name = "MyObj", - description = "obj descr", - scalar = DefaultScalarValue -)] +#[graphql(name = "MyObj", description = "obj descr")] struct Obj { regular_field: bool, #[graphql( @@ -26,14 +17,10 @@ struct Obj { } #[derive(GraphQLObject, Debug, PartialEq)] -#[graphql(scalar = DefaultScalarValue)] struct Nested { obj: Obj, } -struct Query; -struct NoRenameQuery; - /// Object comment. #[derive(GraphQLObject, Debug, PartialEq)] struct DocComment { @@ -79,16 +66,19 @@ struct NoRenameObj { one_field: bool, another_field: i32, } + struct Context; impl juniper::Context for Context {} #[derive(GraphQLObject, Debug)] -#[graphql(Context = Context)] +#[graphql(context = Context)] struct WithCustomContext { a: bool, } -#[juniper::graphql_object] +struct Query; + +#[graphql_object] impl Query { fn obj() -> Obj { Obj { @@ -139,7 +129,9 @@ impl Query { } } -#[juniper::graphql_object(rename = "none")] +struct NoRenameQuery; + +#[graphql_object(rename = "none")] impl NoRenameQuery { fn obj() -> Obj { Obj { @@ -158,7 +150,7 @@ impl NoRenameQuery { #[tokio::test] async fn test_doc_comment_simple() { - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = DocComment::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"Object comment.".to_string())); @@ -173,7 +165,7 @@ async fn test_doc_comment_simple() { #[tokio::test] async fn test_multi_doc_comment() { - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = MultiDocComment::meta(&(), &mut registry); assert_eq!( meta.description(), @@ -191,7 +183,7 @@ async fn test_multi_doc_comment() { #[tokio::test] async fn test_doc_comment_override() { - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = OverrideDocComment::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"obj override".to_string())); @@ -204,98 +196,6 @@ async fn test_doc_comment_override() { .await; } -#[tokio::test] -async fn test_no_rename_root() { - let doc = r#" - { - no_rename_obj { - one_field - another_field - } - - obj { - regularField - } - }"#; - - let schema = RootNode::new( - NoRenameQuery, - EmptyMutation::<()>::new(), - EmptySubscription::<()>::new(), - ); - - assert_eq!( - execute(doc, None, &schema, &Variables::new(), &()).await, - Ok(( - Value::object( - vec![ - ( - "no_rename_obj", - Value::object( - vec![ - ("one_field", Value::scalar(true)), - ("another_field", Value::scalar(146)), - ] - .into_iter() - .collect(), - ), - ), - ( - "obj", - Value::object( - vec![("regularField", Value::scalar(false)),] - .into_iter() - .collect(), - ), - ) - ] - .into_iter() - .collect() - ), - vec![] - )) - ); -} - -#[tokio::test] -async fn test_no_rename_obj() { - let doc = r#" - { - noRenameObj { - one_field - another_field - } - }"#; - - let schema = RootNode::new( - Query, - EmptyMutation::<()>::new(), - EmptySubscription::<()>::new(), - ); - - assert_eq!( - execute(doc, None, &schema, &Variables::new(), &()).await, - Ok(( - Value::object( - vec![( - "noRenameObj", - Value::object( - vec![ - ("one_field", Value::scalar(true)), - ("another_field", Value::scalar(146)), - ] - .into_iter() - .collect(), - ), - )] - .into_iter() - .collect() - ), - vec![] - )) - ); -} - #[tokio::test] async fn test_derived_object() { assert_eq!( @@ -304,7 +204,7 @@ async fn test_derived_object() { ); // Verify meta info. - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = Obj::meta(&(), &mut registry); assert_eq!(meta.name(), Some("MyObj")); @@ -432,7 +332,98 @@ async fn test_derived_object_nested() { ); } -#[cfg(test)] +#[tokio::test] +async fn test_no_rename_root() { + let doc = r#" + { + no_rename_obj { + one_field + another_field + } + + obj { + regularField + } + }"#; + + let schema = RootNode::new( + NoRenameQuery, + EmptyMutation::<()>::new(), + EmptySubscription::<()>::new(), + ); + + assert_eq!( + execute(doc, None, &schema, &Variables::new(), &()).await, + Ok(( + Value::object( + vec![ + ( + "no_rename_obj", + Value::object( + vec![ + ("one_field", Value::scalar(true)), + ("another_field", Value::scalar(146)), + ] + .into_iter() + .collect(), + ), + ), + ( + "obj", + Value::object( + vec![("regularField", Value::scalar(false)),] + .into_iter() + .collect(), + ), + ) + ] + .into_iter() + .collect() + ), + vec![] + )) + ); +} + +#[tokio::test] +async fn test_no_rename_obj() { + let doc = r#" + { + noRenameObj { + one_field + another_field + } + }"#; + + let schema = RootNode::new( + Query, + EmptyMutation::<()>::new(), + EmptySubscription::<()>::new(), + ); + + assert_eq!( + execute(doc, None, &schema, &Variables::new(), &()).await, + Ok(( + Value::object( + vec![( + "noRenameObj", + Value::object( + vec![ + ("one_field", Value::scalar(true)), + ("another_field", Value::scalar(146)), + ] + .into_iter() + .collect(), + ), + )] + .into_iter() + .collect() + ), + vec![] + )) + ); +} + async fn check_descriptions( object_name: &str, object_description: &Value, @@ -475,7 +466,6 @@ async fn check_descriptions( .await; } -#[cfg(test)] async fn run_type_info_query(doc: &str, f: F) where F: Fn((&Object, &Vec)) -> (), diff --git a/integration_tests/juniper_tests/src/codegen/derive_object_with_raw_idents.rs b/integration_tests/juniper_tests/src/codegen/derive_object_with_raw_idents.rs index b043425a..241a2ab8 100644 --- a/integration_tests/juniper_tests/src/codegen/derive_object_with_raw_idents.rs +++ b/integration_tests/juniper_tests/src/codegen/derive_object_with_raw_idents.rs @@ -1,12 +1,11 @@ -#[cfg(test)] use juniper::{ - self, execute, graphql_value, EmptyMutation, EmptySubscription, GraphQLInputObject, RootNode, - Value, Variables, + execute, graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLInputObject, + RootNode, Value, Variables, }; pub struct Query; -#[juniper::graphql_object] +#[graphql_object] impl Query { fn r#type(r#fn: MyInputType) -> Vec { let _ = r#fn; @@ -87,7 +86,6 @@ async fn supports_raw_idents_in_fields_of_input_types() { ); } -#[cfg(test)] async fn run_type_info_query(doc: &str) -> Value { let schema = RootNode::new( Query, diff --git a/integration_tests/juniper_tests/src/codegen/impl_object.rs b/integration_tests/juniper_tests/src/codegen/impl_object.rs index 463be9b7..6349f7aa 100644 --- a/integration_tests/juniper_tests/src/codegen/impl_object.rs +++ b/integration_tests/juniper_tests/src/codegen/impl_object.rs @@ -1,13 +1,11 @@ -use juniper::DefaultScalarValue; -#[cfg(test)] -use juniper::Object; - -#[cfg(test)] -use juniper::{execute, EmptyMutation, EmptySubscription, FieldError, RootNode, Value, Variables}; +use juniper::{ + execute, graphql_object, DefaultScalarValue, EmptyMutation, EmptySubscription, Object, + RootNode, Value, Variables, +}; pub struct MyObject; -#[juniper::graphql_object] +#[graphql_object] impl MyObject { #[graphql(arguments(arg(name = "test")))] fn test(&self, arg: String) -> String { @@ -49,7 +47,6 @@ async fn check_argument_rename() { .await; } -#[cfg(test)] async fn run_type_info_query(doc: &str, f: F) where F: Fn((&Object, &Vec)) -> (), @@ -86,11 +83,11 @@ where } mod fallible { - use super::*; + use juniper::{graphql_object, FieldError}; struct Obj; - #[juniper::graphql_object] + #[graphql_object] impl Obj { fn test(&self, arg: String) -> Result { Ok(arg) diff --git a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs index 640937fa..721aba8f 100644 --- a/integration_tests/juniper_tests/src/codegen/impl_scalar.rs +++ b/integration_tests/juniper_tests/src/codegen/impl_scalar.rs @@ -86,7 +86,7 @@ impl GraphQLScalar for ScalarDescription { } } -#[graphql_object] +#[graphql_object(scalar = DefaultScalarValue)] impl Root { fn default_name() -> DefaultName { DefaultName(0) @@ -278,7 +278,7 @@ async fn scalar_description_introspection() { async fn resolves_with_custom_scalar_value() { const DOC: &str = r#"{ withCustomScalarValue }"#; - let schema = RootNode::<_, _, _, MyScalarValue>::new( + let schema = RootNode::<_, _, _, MyScalarValue>::new_with_scalar_value( RootWithCustomScalarValue, EmptyMutation::<()>::new(), EmptySubscription::<()>::new(), diff --git a/integration_tests/juniper_tests/src/codegen/interface_attr.rs b/integration_tests/juniper_tests/src/codegen/interface_attr.rs index 3cf4f198..3cffc9a4 100644 --- a/integration_tests/juniper_tests/src/codegen/interface_attr.rs +++ b/integration_tests/juniper_tests/src/codegen/interface_attr.rs @@ -6,12 +6,25 @@ use juniper::{ IntoFieldError, RootNode, ScalarValue, Variables, }; -fn schema<'q, C, S, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation, EmptySubscription, S> +fn schema<'q, C, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation, EmptySubscription> +where + Q: GraphQLType + 'q, +{ + RootNode::new( + query_root, + EmptyMutation::::new(), + EmptySubscription::::new(), + ) +} + +fn schema_with_scalar<'q, S, C, Q>( + query_root: Q, +) -> RootNode<'q, Q, EmptyMutation, EmptySubscription, S> where Q: GraphQLType + 'q, S: ScalarValue + 'q, { - RootNode::new( + RootNode::new_with_scalar_value( query_root, EmptyMutation::::new(), EmptySubscription::::new(), @@ -39,7 +52,7 @@ mod no_implers { unimplemented!() } - fn hero(&self) -> Box> { + fn hero(&self) -> Box> { unimplemented!() } } @@ -170,8 +183,8 @@ mod trivial { Droid, } - #[graphql_object] - impl QueryRoot { + #[graphql_object(scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -187,8 +200,8 @@ mod trivial { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -692,8 +705,8 @@ mod trivial_async { Droid, } - #[graphql_object] - impl QueryRoot { + #[graphql_object(scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -709,8 +722,8 @@ mod trivial_async { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -1055,8 +1068,8 @@ mod explicit_async { Droid, } - #[graphql_object] - impl QueryRoot { + #[graphql_object(scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -1072,8 +1085,8 @@ mod explicit_async { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -1305,8 +1318,8 @@ mod fallible_field { Droid, } - #[graphql_object] - impl QueryRoot { + #[graphql_object(scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -1322,8 +1335,8 @@ mod fallible_field { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -1571,8 +1584,8 @@ mod generic { Droid, } - #[graphql_object] - impl QueryRoot { + #[graphql_object(scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -1588,8 +1601,8 @@ mod generic { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -1815,8 +1828,8 @@ mod generic_async { Droid, } - #[graphql_object] - impl QueryRoot { + #[graphql_object(scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -1832,8 +1845,8 @@ mod generic_async { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -2059,8 +2072,8 @@ mod generic_lifetime_async { Droid, } - #[graphql_object] - impl QueryRoot { + #[graphql_object(scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue<'_, ()> { match self { Self::Human => Human { @@ -2076,8 +2089,8 @@ mod generic_lifetime_async { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -2286,8 +2299,8 @@ mod argument { struct QueryRoot; - #[graphql_object] - impl QueryRoot { + #[graphql_object(scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { Human { id: "human-32".to_string(), @@ -2296,7 +2309,7 @@ mod argument { .into() } - fn hero(&self) -> Box> { + fn hero(&self) -> Box> { Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -3011,7 +3024,7 @@ mod explicit_scalar { } }"#; - let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Human); + let schema = schema(QueryRoot::Human); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -3033,7 +3046,7 @@ mod explicit_scalar { } }"#; - let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Droid); + let schema = schema(QueryRoot::Droid); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -3055,7 +3068,7 @@ mod explicit_scalar { } }"#; - let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Human); + let schema = schema(QueryRoot::Human); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -3077,7 +3090,7 @@ mod explicit_scalar { } }"#; - let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Droid); + let schema = schema(QueryRoot::Droid); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -3100,7 +3113,7 @@ mod explicit_scalar { (QueryRoot::Human, "human-32"), (QueryRoot::Droid, "droid-99"), ] { - let schema = schema::<_, DefaultScalarValue, _>(*root); + let schema = schema(*root); let expected_id: &str = *expected_id; assert_eq!( @@ -3119,7 +3132,7 @@ mod explicit_scalar { }"#; for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] { - let schema = schema::<_, DefaultScalarValue, _>(*root); + let schema = schema(*root); let expected_info: &str = *expected_info; assert_eq!( @@ -3237,7 +3250,7 @@ mod custom_scalar { } }"#; - let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human); + let schema = schema_with_scalar::(QueryRoot::Human); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -3259,7 +3272,7 @@ mod custom_scalar { } }"#; - let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid); + let schema = schema_with_scalar::(QueryRoot::Droid); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -3281,7 +3294,7 @@ mod custom_scalar { } }"#; - let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human); + let schema = schema_with_scalar::(QueryRoot::Human); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -3303,7 +3316,7 @@ mod custom_scalar { } }"#; - let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid); + let schema = schema_with_scalar::(QueryRoot::Droid); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -3326,7 +3339,7 @@ mod custom_scalar { (QueryRoot::Human, "human-32"), (QueryRoot::Droid, "droid-99"), ] { - let schema = schema::<_, MyScalarValue, _>(*root); + let schema = schema_with_scalar::(*root); let expected_id: &str = *expected_id; assert_eq!( @@ -3345,7 +3358,7 @@ mod custom_scalar { }"#; for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] { - let schema = schema::<_, MyScalarValue, _>(*root); + let schema = schema_with_scalar::(*root); let expected_info: &str = *expected_info; assert_eq!( @@ -3417,9 +3430,9 @@ mod explicit_generic_scalar { Droid, } - #[graphql_object(scalar = DefaultScalarValue)] - impl QueryRoot { - fn character(&self) -> CharacterValue { + #[graphql_object(scalar = S)] + impl QueryRoot { + fn character(&self) -> CharacterValue { match self { Self::Human => Human { id: "human-32".to_string(), @@ -3434,8 +3447,8 @@ mod explicit_generic_scalar { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -3685,8 +3698,8 @@ mod explicit_custom_context { Droid, } - #[graphql_object(context = CustomContext)] - impl QueryRoot { + #[graphql_object(context = CustomContext, scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -3702,8 +3715,8 @@ mod explicit_custom_context { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -3931,8 +3944,8 @@ mod inferred_custom_context_from_field { Droid, } - #[graphql_object(context = CustomContext)] - impl QueryRoot { + #[graphql_object(context = CustomContext, scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -3948,8 +3961,8 @@ mod inferred_custom_context_from_field { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -4183,8 +4196,8 @@ mod inferred_custom_context_from_downcast { Droid, } - #[graphql_object(context = Database)] - impl QueryRoot { + #[graphql_object(context = Database, scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -4200,8 +4213,8 @@ mod inferred_custom_context_from_downcast { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -4793,8 +4806,8 @@ mod downcast_method { Droid, } - #[graphql_object] - impl QueryRoot { + #[graphql_object(scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -4810,8 +4823,8 @@ mod downcast_method { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), @@ -5058,8 +5071,8 @@ mod external_downcast { Droid, } - #[graphql_object(context = Database)] - impl QueryRoot { + #[graphql_object(context = Database, scalar = S)] + impl QueryRoot { fn character(&self) -> CharacterValue { match self { Self::Human => Human { @@ -5075,8 +5088,8 @@ mod external_downcast { } } - fn hero(&self) -> Box> { - let ch: Box> = match self { + fn hero(&self) -> Box> { + let ch: Box> = match self { Self::Human => Box::new(Human { id: "human-32".to_string(), home_planet: "earth".to_string(), diff --git a/integration_tests/juniper_tests/src/codegen/scalar_value_transparent.rs b/integration_tests/juniper_tests/src/codegen/scalar_value_transparent.rs index 6f52e5b6..087bc91c 100644 --- a/integration_tests/juniper_tests/src/codegen/scalar_value_transparent.rs +++ b/integration_tests/juniper_tests/src/codegen/scalar_value_transparent.rs @@ -1,20 +1,23 @@ use fnv::FnvHashMap; -use juniper::{DefaultScalarValue, FromInputValue, GraphQLType, InputValue, ToInputValue}; +use juniper::{ + graphql_object, DefaultScalarValue, FromInputValue, GraphQLObject, GraphQLScalarValue, + GraphQLType, InputValue, Registry, ToInputValue, +}; -#[derive(juniper::GraphQLScalarValue, PartialEq, Eq, Debug)] +#[derive(GraphQLScalarValue, Debug, Eq, PartialEq)] #[graphql(transparent)] struct UserId(String); -#[derive(juniper::GraphQLScalarValue, PartialEq, Eq, Debug)] +#[derive(GraphQLScalarValue, Debug, Eq, PartialEq)] #[graphql(transparent, name = "MyUserId", description = "custom description...")] struct CustomUserId(String); /// The doc comment... -#[derive(juniper::GraphQLScalarValue, PartialEq, Eq, Debug)] +#[derive(GraphQLScalarValue, Debug, Eq, PartialEq)] #[graphql(transparent)] struct IdWithDocComment(i32); -#[derive(juniper::GraphQLObject)] +#[derive(GraphQLObject)] struct User { id: UserId, id_custom: CustomUserId, @@ -22,7 +25,7 @@ struct User { struct User2; -#[juniper::graphql_object] +#[graphql_object] impl User2 { fn id(&self) -> UserId { UserId("id".to_string()) @@ -36,7 +39,7 @@ fn test_scalar_value_simple() { Some("UserId") ); - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = UserId::meta(&(), &mut registry); assert_eq!(meta.name(), Some("UserId")); assert_eq!(meta.description(), None); @@ -57,7 +60,7 @@ fn test_scalar_value_custom() { Some("MyUserId") ); - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = CustomUserId::meta(&(), &mut registry); assert_eq!(meta.name(), Some("MyUserId")); assert_eq!( @@ -76,7 +79,7 @@ fn test_scalar_value_custom() { #[test] fn test_scalar_value_doc_comment() { - let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: Registry = Registry::new(FnvHashMap::default()); let meta = IdWithDocComment::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"The doc comment...".to_string())); } diff --git a/integration_tests/juniper_tests/src/codegen/union_attr.rs b/integration_tests/juniper_tests/src/codegen/union_attr.rs index ebad8221..8d414630 100644 --- a/integration_tests/juniper_tests/src/codegen/union_attr.rs +++ b/integration_tests/juniper_tests/src/codegen/union_attr.rs @@ -51,12 +51,25 @@ struct EwokCustomContext { funny: bool, } -fn schema<'q, C, S, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation, EmptySubscription, S> +fn schema<'q, C, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation, EmptySubscription> +where + Q: GraphQLType + 'q, +{ + RootNode::new( + query_root, + EmptyMutation::::new(), + EmptySubscription::::new(), + ) +} + +fn schema_with_scalar<'q, S, C, Q>( + query_root: Q, +) -> RootNode<'q, Q, EmptyMutation, EmptySubscription, S> where Q: GraphQLType + 'q, S: ScalarValue + 'q, { - RootNode::new( + RootNode::new_with_scalar_value( query_root, EmptyMutation::::new(), EmptySubscription::::new(), @@ -531,7 +544,7 @@ mod explicit_scalar { #[tokio::test] async fn resolves_human() { - let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Human); + let schema = schema(QueryRoot::Human); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -544,7 +557,7 @@ mod explicit_scalar { #[tokio::test] async fn resolves_droid() { - let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Droid); + let schema = schema(QueryRoot::Droid); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -622,7 +635,7 @@ mod custom_scalar { #[tokio::test] async fn resolves_human() { - let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human); + let schema = schema_with_scalar::(QueryRoot::Human); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -635,7 +648,7 @@ mod custom_scalar { #[tokio::test] async fn resolves_droid() { - let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid); + let schema = schema_with_scalar::(QueryRoot::Droid); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -1054,7 +1067,7 @@ mod full_featured { struct QueryRoot; - #[graphql_object(context = CustomContext)] + #[graphql_object(context = CustomContext, scalar = DefaultScalarValue)] impl QueryRoot { fn character(&self, ctx: &CustomContext) -> Box> { let ch: Box> = match ctx { diff --git a/integration_tests/juniper_tests/src/codegen/union_derive.rs b/integration_tests/juniper_tests/src/codegen/union_derive.rs index 655ca995..8db96512 100644 --- a/integration_tests/juniper_tests/src/codegen/union_derive.rs +++ b/integration_tests/juniper_tests/src/codegen/union_derive.rs @@ -53,12 +53,25 @@ struct EwokCustomContext { funny: bool, } -fn schema<'q, C, S, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation, EmptySubscription, S> +fn schema<'q, C, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation, EmptySubscription> +where + Q: GraphQLType + 'q, +{ + RootNode::new( + query_root, + EmptyMutation::::new(), + EmptySubscription::::new(), + ) +} + +fn schema_with_scalar<'q, S, C, Q>( + query_root: Q, +) -> RootNode<'q, Q, EmptyMutation, EmptySubscription, S> where Q: GraphQLType + 'q, S: ScalarValue + 'q, { - RootNode::new( + RootNode::new_with_scalar_value( query_root, EmptyMutation::::new(), EmptySubscription::::new(), @@ -429,7 +442,7 @@ mod explicit_scalar { Droid, } - #[graphql_object] + #[graphql_object(scalar = DefaultScalarValue)] impl QueryRoot { fn character(&self) -> Character { match self { @@ -460,7 +473,7 @@ mod explicit_scalar { #[tokio::test] async fn resolves_human() { - let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Human); + let schema = schema(QueryRoot::Human); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -473,7 +486,7 @@ mod explicit_scalar { #[tokio::test] async fn resolves_droid() { - let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Droid); + let schema = schema(QueryRoot::Droid); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -533,7 +546,7 @@ mod custom_scalar { #[tokio::test] async fn resolves_human() { - let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human); + let schema = schema_with_scalar::(QueryRoot::Human); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -546,7 +559,7 @@ mod custom_scalar { #[tokio::test] async fn resolves_droid() { - let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid); + let schema = schema_with_scalar::(QueryRoot::Droid); assert_eq!( execute(DOC, None, &schema, &Variables::new(), &()).await, @@ -993,7 +1006,7 @@ mod full_featured_enum { struct QueryRoot; - #[graphql_object(context = CustomContext)] + #[graphql_object(context = CustomContext, scalar = DefaultScalarValue)] impl QueryRoot { fn character(&self, ctx: &CustomContext) -> Character<()> { match ctx { @@ -1349,7 +1362,7 @@ mod full_featured_struct { Droid, } - #[graphql_object(context = Database)] + #[graphql_object(context = Database, scalar = DefaultScalarValue)] impl QueryRoot { fn character(&self) -> Character<()> { Character { diff --git a/integration_tests/juniper_tests/src/custom_scalar.rs b/integration_tests/juniper_tests/src/custom_scalar.rs index f76df3e1..fcd9b5bd 100644 --- a/integration_tests/juniper_tests/src/custom_scalar.rs +++ b/integration_tests/juniper_tests/src/custom_scalar.rs @@ -1,14 +1,15 @@ -use std::fmt; +use std::{fmt, pin::Pin}; +use futures::{stream, Stream}; use juniper::{ - execute, + execute, graphql_object, graphql_scalar, graphql_subscription, parser::{ParseError, ScalarToken, Spanning, Token}, serde::de, - EmptyMutation, FieldResult, InputValue, Object, ParseScalarResult, RootNode, ScalarValue, - Value, Variables, + EmptyMutation, FieldResult, GraphQLScalarValue, InputValue, Object, ParseScalarResult, + RootNode, ScalarValue, Value, Variables, }; -#[derive(Debug, Clone, PartialEq, juniper::GraphQLScalarValue)] +#[derive(GraphQLScalarValue, Clone, Debug, PartialEq)] pub(crate) enum MyScalarValue { Int(i32), Long(i64), @@ -22,42 +23,49 @@ impl ScalarValue for MyScalarValue { fn as_int(&self) -> Option { match *self { - MyScalarValue::Int(ref i) => Some(*i), + Self::Int(ref i) => Some(*i), _ => None, } } fn as_string(&self) -> Option { match *self { - MyScalarValue::String(ref s) => Some(s.clone()), + Self::String(ref s) => Some(s.clone()), + _ => None, + } + } + + fn into_string(self) -> Option { + match self { + Self::String(s) => Some(s), _ => None, } } fn as_str(&self) -> Option<&str> { match *self { - MyScalarValue::String(ref s) => Some(s.as_str()), + Self::String(ref s) => Some(s.as_str()), _ => None, } } fn as_float(&self) -> Option { match *self { - MyScalarValue::Int(ref i) => Some(*i as f64), - MyScalarValue::Float(ref f) => Some(*f), + Self::Int(ref i) => Some(f64::from(*i)), + Self::Float(ref f) => Some(*f), _ => None, } } fn as_boolean(&self) -> Option { match *self { - MyScalarValue::Boolean(ref b) => Some(*b), + Self::Boolean(ref b) => Some(*b), _ => None, } } } -#[derive(Default, Debug)] +#[derive(Debug, Default)] pub(crate) struct MyScalarValueVisitor; impl<'de> de::Visitor<'de> for MyScalarValueVisitor { @@ -82,7 +90,7 @@ impl<'de> de::Visitor<'de> for MyScalarValueVisitor { where E: de::Error, { - if value <= i32::max_value() as i64 { + if value <= i64::from(i32::max_value()) { self.visit_i32(value as i32) } else { Ok(MyScalarValue::Long(value)) @@ -132,7 +140,7 @@ impl<'de> de::Visitor<'de> for MyScalarValueVisitor { } } -#[juniper::graphql_scalar(name = "Long")] +#[graphql_scalar(name = "Long")] impl GraphQLScalar for i64 { fn resolve(&self) -> Value { Value::scalar(*self) @@ -158,12 +166,10 @@ impl GraphQLScalar for i64 { struct TestType; -#[juniper::graphql_object( - Scalar = MyScalarValue -)] +#[graphql_object(scalar = MyScalarValue)] impl TestType { fn long_field() -> i64 { - (::std::i32::MAX as i64) + 1 + i64::from(i32::max_value()) + 1 } fn long_with_arg(long_arg: i64) -> i64 { @@ -173,14 +179,10 @@ impl TestType { struct TestSubscriptionType; -#[juniper::graphql_subscription( - Scalar = MyScalarValue -)] +#[graphql_subscription(scalar = MyScalarValue)] impl TestSubscriptionType { - async fn foo( - ) -> std::pin::Pin> + Send>> - { - Box::pin(futures::stream::empty()) + async fn foo() -> Pin> + Send>> { + Box::pin(stream::empty()) } } @@ -188,7 +190,8 @@ async fn run_variable_query(query: &str, vars: Variables, f: F where F: Fn(&Object) -> (), { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new(), TestSubscriptionType); + let schema = + RootNode::new_with_scalar_value(TestType, EmptyMutation::<()>::new(), TestSubscriptionType); let (result, errs) = execute(query, None, &schema, &vars, &()) .await @@ -215,7 +218,7 @@ async fn querying_long() { run_query("{ longField }", |result| { assert_eq!( result.get_field_value("longField"), - Some(&Value::scalar((::std::i32::MAX as i64) + 1)) + Some(&Value::scalar(i64::from(i32::max_value()) + 1)) ); }) .await; @@ -226,12 +229,12 @@ async fn querying_long_arg() { run_query( &format!( "{{ longWithArg(longArg: {}) }}", - (::std::i32::MAX as i64) + 3 + i64::from(i32::max_value()) + 3 ), |result| { assert_eq!( result.get_field_value("longWithArg"), - Some(&Value::scalar((::std::i32::MAX as i64) + 3)) + Some(&Value::scalar(i64::from(i32::max_value()) + 3)) ); }, ) @@ -244,14 +247,14 @@ async fn querying_long_variable() { "query q($test: Long!){ longWithArg(longArg: $test) }", vec![( "test".to_owned(), - InputValue::Scalar(MyScalarValue::Long((::std::i32::MAX as i64) + 42)), + InputValue::Scalar(MyScalarValue::Long(i64::from(i32::max_value()) + 42)), )] .into_iter() .collect(), |result| { assert_eq!( result.get_field_value("longWithArg"), - Some(&Value::scalar((::std::i32::MAX as i64) + 42)) + Some(&Value::scalar(i64::from(i32::max_value()) + 42)) ); }, ) @@ -260,7 +263,7 @@ async fn querying_long_variable() { #[test] fn deserialize_variable() { - let json = format!("{{\"field\": {}}}", (::std::i32::MAX as i64) + 42); + let json = format!("{{\"field\": {}}}", i64::from(i32::max_value()) + 42); let input_value: InputValue = serde_json::from_str(&json).unwrap(); assert_eq!( @@ -268,7 +271,7 @@ fn deserialize_variable() { InputValue::Object(vec![( Spanning::unlocated("field".into()), Spanning::unlocated(InputValue::Scalar(MyScalarValue::Long( - (::std::i32::MAX as i64) + 42 + i64::from(i32::max_value()) + 42 ))) )]) ); diff --git a/integration_tests/juniper_tests/src/issue_371.rs b/integration_tests/juniper_tests/src/issue_371.rs index 77184380..d4085e60 100644 --- a/integration_tests/juniper_tests/src/issue_371.rs +++ b/integration_tests/juniper_tests/src/issue_371.rs @@ -1,5 +1,8 @@ // Original author of this test is . -use juniper::*; + +use juniper::{ + graphql_object, EmptyMutation, EmptySubscription, LookAheadMethods as _, RootNode, Variables, +}; pub struct Context; @@ -7,9 +10,7 @@ impl juniper::Context for Context {} pub struct Query; -#[graphql_object( - Context = Context -)] +#[graphql_object(context = Context)] impl Query { fn users(exec: &Executor) -> Vec { let lh = exec.look_ahead(); @@ -27,9 +28,7 @@ impl Query { #[derive(Clone)] pub struct User; -#[graphql_object( - Context = Context -)] +#[graphql_object(context = Context)] impl User { fn id() -> i32 { 1 @@ -46,7 +45,7 @@ impl Country { } } -type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>; +type Schema = RootNode<'static, Query, EmptyMutation, EmptySubscription>; #[tokio::test] async fn users() { @@ -109,7 +108,7 @@ async fn both() { EmptyMutation::::new(), EmptySubscription::::new(), ), - &juniper::Variables::new(), + &Variables::new(), &ctx, ) .await @@ -137,7 +136,7 @@ async fn both_in_different_order() { EmptyMutation::::new(), EmptySubscription::::new(), ), - &juniper::Variables::new(), + &Variables::new(), &ctx, ) .await diff --git a/integration_tests/juniper_tests/src/issue_398.rs b/integration_tests/juniper_tests/src/issue_398.rs index 124e1bef..5a42ac23 100644 --- a/integration_tests/juniper_tests/src/issue_398.rs +++ b/integration_tests/juniper_tests/src/issue_398.rs @@ -1,9 +1,10 @@ // Original author of this test is . -use juniper::*; + +use juniper::{graphql_object, EmptyMutation, EmptySubscription, RootNode, Variables}; struct Query; -#[juniper::graphql_object] +#[graphql_object] impl Query { fn users(executor: &Executor) -> Vec { // This doesn't cause a panic @@ -19,7 +20,7 @@ struct User { country: Country, } -#[juniper::graphql_object] +#[graphql_object] impl User { fn country(&self, executor: &Executor) -> &Country { // This panics! @@ -33,14 +34,14 @@ struct Country { id: i32, } -#[juniper::graphql_object] +#[graphql_object] impl Country { fn id(&self) -> i32 { self.id } } -type Schema = juniper::RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>; +type Schema = RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>; #[tokio::test] async fn test_lookahead_from_fragment_with_nested_type() { diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 25cf215b..df66705e 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -32,11 +32,10 @@ - `LexerError` - `ParseError` - `RuleError` - + - Support `chrono-tz::Tz` scalar behind a `chrono-tz` feature flag. ([#519](https://github.com/graphql-rust/juniper/pull/519)) - Added support for distinguishing between between implicit and explicit null. ([#795](https://github.com/graphql-rust/juniper/pull/795)) - - Implement `IntoFieldError` for `std::convert::Infallible`. ([#796](https://github.com/graphql-rust/juniper/pull/796)) @@ -76,7 +75,7 @@ ```rust #[graphql(arguments(argA(name = "test")))] ``` - + - `SchemaType` is now public. - This is helpful when using `context.getSchema()` inside of your field resolvers. @@ -94,9 +93,10 @@ - `juniper::graphiql` has moved to `juniper::http::graphiql`. - `juniper::http::graphiql::graphiql_source()` now requires a second parameter for subscriptions. - + - Renamed the `object` proc macro to `graphql_object`. - Removed the `graphql_object!` macro. Use the `#[graphql_object]` proc macro instead. +- Made `#[graphql_object]` macro to generate code generic over `ScalarValue` by default. ([#779](https://github.com/graphql-rust/juniper/pull/779)) - Renamed the `scalar` proc macro to `graphql_scalar`. - Removed the `graphql_scalar!` macro. Use the `#[graphql_scalar]` proc macro instead. @@ -117,7 +117,7 @@ - The return type of `GraphQLType::resolve()` has been changed to `ExecutionResult`. - This was done to unify the return type of all resolver methods. The previous `Value` return type was just an internal artifact of error handling. - + - Subscription-related: - Add subscription type to `RootNode`. - Add subscription endpoint to `playground_source()`. @@ -130,6 +130,8 @@ - Renamed `http::tests::HTTPIntegration` as `http::tests::HttpIntegration`. - Added support for `application/graphql` POST request. +- `RootNode::new()` now returns `RootNode` parametrized with `DefaultScalarValue`. For custom `ScalarValue` use `RootNode::new_with_scalar_value()` instead. ([#779](https://github.com/graphql-rust/juniper/pull/779)) + - When using `LookAheadMethods` to access child selections, children are always found using their alias if it exists rather than their name. ([#662](https://github.com/graphql-rust/juniper/pull/662)) - These methods are also deprecated in favor of the new `LookAheadMethods::children()` method. diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 9a4588fa..527e5540 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -216,6 +216,18 @@ impl FieldError { pub fn extensions(&self) -> &Value { &self.extensions } + + /// Maps the [`ScalarValue`] type of this [`FieldError`] into the specified one. + pub fn map_scalar_value(self) -> FieldError + where + S: ScalarValue, + Into: ScalarValue, + { + FieldError { + message: self.message, + extensions: self.extensions.map_scalar_value(), + } + } } /// The result of resolving the value of a field of type `T` @@ -240,9 +252,10 @@ pub trait IntoFieldError { fn into_field_error(self) -> FieldError; } -impl IntoFieldError for FieldError { - fn into_field_error(self) -> FieldError { - self +impl IntoFieldError for FieldError { + #[inline] + fn into_field_error(self) -> FieldError { + self.map_scalar_value() } } @@ -315,28 +328,31 @@ where } } -impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for FieldResult<(&'a T::Context, T), S> +impl<'a, S1, S2, T, C> IntoResolvable<'a, S2, T, C> for FieldResult<(&'a T::Context, T), S1> where - S: ScalarValue, - T: GraphQLValue, + S1: ScalarValue, + S2: ScalarValue, + T: GraphQLValue, { type Type = T; - fn into(self, _: &'a C) -> FieldResult, S> { - self.map(Some) + fn into(self, _: &'a C) -> FieldResult, S2> { + self.map(Some).map_err(FieldError::map_scalar_value) } } -impl<'a, S, T, C> IntoResolvable<'a, S, Option, C> - for FieldResult, S> +impl<'a, S1, S2, T, C> IntoResolvable<'a, S2, Option, C> + for FieldResult, S1> where - S: ScalarValue, - T: GraphQLValue, + S1: ScalarValue, + S2: ScalarValue, + T: GraphQLValue, { type Type = T; - fn into(self, _: &'a C) -> FieldResult)>, S> { + fn into(self, _: &'a C) -> FieldResult)>, S2> { self.map(|o| o.map(|(ctx, v)| (ctx, Some(v)))) + .map_err(FieldError::map_scalar_value) } } diff --git a/juniper/src/executor_tests/async_await/mod.rs b/juniper/src/executor_tests/async_await/mod.rs index f1717c76..064b4067 100644 --- a/juniper/src/executor_tests/async_await/mod.rs +++ b/juniper/src/executor_tests/async_await/mod.rs @@ -1,6 +1,6 @@ -use crate::{EmptyMutation, RootNode, Value}; +use crate::{graphql_object, EmptyMutation, EmptySubscription, GraphQLEnum, RootNode, Value}; -#[derive(crate::GraphQLEnum)] +#[derive(GraphQLEnum)] enum UserKind { Admin, User, @@ -14,7 +14,7 @@ struct User { kind: UserKind, } -#[crate::graphql_object] +#[graphql_object] impl User { async fn id(&self) -> i32 { self.id @@ -46,7 +46,7 @@ impl User { struct Query; -#[crate::graphql_object] +#[graphql_object] impl Query { fn field_sync(&self) -> &'static str { "field_sync" @@ -72,7 +72,7 @@ impl Query { #[tokio::test] async fn async_simple() { - let schema = RootNode::new(Query, EmptyMutation::new(), crate::EmptySubscription::new()); + let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new()); let doc = r#" query { fieldSync diff --git a/juniper/src/executor_tests/executor.rs b/juniper/src/executor_tests/executor.rs index 1b1f2537..fb45ba17 100644 --- a/juniper/src/executor_tests/executor.rs +++ b/juniper/src/executor_tests/executor.rs @@ -58,7 +58,7 @@ mod field_execution { #[tokio::test] async fn test() { - let schema = RootNode::<_, _, _, crate::DefaultScalarValue>::new( + let schema = RootNode::new( DataType, EmptyMutation::<()>::new(), EmptySubscription::<()>::new(), @@ -470,6 +470,7 @@ mod dynamic_context_switching { use crate::{ executor::{Context, ExecutionError, FieldError, FieldResult}, + graphql_object, parser::SourcePosition, schema::model::RootNode, types::scalars::{EmptyMutation, EmptySubscription}, @@ -491,7 +492,7 @@ mod dynamic_context_switching { struct ItemRef; - #[crate::graphql_object(Context = OuterContext)] + #[graphql_object(context = OuterContext)] impl Schema { fn item_opt(_context: &OuterContext, key: i32) -> Option<(&InnerContext, ItemRef)> { executor.context().items.get(&key).map(|c| (c, ItemRef)) @@ -521,7 +522,7 @@ mod dynamic_context_switching { } } - #[crate::graphql_object(Context = InnerContext)] + #[graphql_object(context = InnerContext)] impl ItemRef { fn value(context: &InnerContext) -> String { context.value.clone() @@ -828,6 +829,7 @@ mod dynamic_context_switching { mod propagates_errors_to_nullable_fields { use crate::{ executor::{ExecutionError, FieldError, FieldResult, IntoFieldError}, + graphql_object, parser::SourcePosition, schema::model::RootNode, types::scalars::{EmptyMutation, EmptySubscription}, @@ -857,7 +859,7 @@ mod propagates_errors_to_nullable_fields { } } - #[crate::graphql_object] + #[graphql_object] impl Schema { fn inner() -> Inner { Inner @@ -870,7 +872,7 @@ mod propagates_errors_to_nullable_fields { } } - #[crate::graphql_object] + #[graphql_object] impl Inner { fn nullable_field() -> Option { Some(Inner) @@ -1176,7 +1178,7 @@ mod named_operations { #[tokio::test] async fn uses_inline_operation_if_no_name_provided() { - let schema = RootNode::<_, _, _, crate::DefaultScalarValue>::new( + let schema = RootNode::new( Schema, EmptyMutation::<()>::new(), EmptySubscription::<()>::new(), diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs index eeb4e144..74403ff8 100644 --- a/juniper/src/executor_tests/introspection/mod.rs +++ b/juniper/src/executor_tests/introspection/mod.rs @@ -7,10 +7,10 @@ use self::input_object::{NamedPublic, NamedPublicWithDescription}; use crate::{ executor::Variables, - graphql_interface, graphql_object, + graphql_interface, graphql_object, graphql_scalar, schema::model::RootNode, types::scalars::{EmptyMutation, EmptySubscription}, - value::{DefaultScalarValue, ParseScalarResult, ParseScalarValue, Value}, + value::{ParseScalarResult, ParseScalarValue, ScalarValue, Value}, GraphQLEnum, }; @@ -23,23 +23,23 @@ enum Sample { struct Scalar(i32); -#[crate::graphql_scalar(name = "SampleScalar")] -impl GraphQLScalar for Scalar { +#[graphql_scalar(name = "SampleScalar")] +impl GraphQLScalar for Scalar { fn resolve(&self) -> Value { Value::scalar(self.0) } fn from_input_value(v: &InputValue) -> Option { - v.as_scalar_value().map(|i: &i32| Scalar(*i)) + v.as_scalar().and_then(ScalarValue::as_int).map(Scalar) } - fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { - ::from_str(value) + fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { + >::from_str(value) } } /// A sample interface -#[graphql_interface(name = "SampleInterface", for = Root, scalar = DefaultScalarValue)] +#[graphql_interface(name = "SampleInterface", for = Root)] trait Interface { /// A sample field in the interface fn sample_enum(&self) -> Sample { @@ -50,7 +50,7 @@ trait Interface { struct Root; /// The root query object in the schema -#[graphql_object(interfaces = InterfaceValue)] +#[graphql_object(impl = InterfaceValue)] impl Root { fn sample_enum() -> Sample { Sample::One diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index 4875c134..76f73e01 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -1,11 +1,12 @@ use crate::{ ast::InputValue, executor::Variables, + graphql_object, graphql_scalar, parser::SourcePosition, schema::model::RootNode, types::scalars::{EmptyMutation, EmptySubscription}, validation::RuleError, - value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, Value}, + value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue, Value}, GraphQLError::ValidationError, GraphQLInputObject, }; @@ -13,17 +14,15 @@ use crate::{ #[derive(Debug)] struct TestComplexScalar; -struct TestType; - -#[crate::graphql_scalar] -impl GraphQLScalar for TestComplexScalar { +#[graphql_scalar] +impl GraphQLScalar for TestComplexScalar { fn resolve(&self) -> Value { Value::scalar(String::from("SerializedValue")) } fn from_input_value(v: &InputValue) -> Option { - if let Some(s) = v.as_scalar_value::() { - if *s == "SerializedValue" { + if let Some(s) = v.as_scalar().and_then(ScalarValue::as_str) { + if s == "SerializedValue" { return Some(TestComplexScalar); } } @@ -31,13 +30,12 @@ impl GraphQLScalar for TestComplexScalar { None } - fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { - ::from_str(value) + fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { + >::from_str(value) } } #[derive(GraphQLInputObject, Debug)] -#[graphql(scalar = DefaultScalarValue)] struct TestInputObject { a: Option, b: Option>>, @@ -46,7 +44,6 @@ struct TestInputObject { } #[derive(GraphQLInputObject, Debug)] -#[graphql(scalar = DefaultScalarValue)] struct TestNestedInputObject { na: TestInputObject, nb: String, @@ -64,7 +61,9 @@ struct InputWithDefaults { a: i32, } -#[crate::graphql_object] +struct TestType; + +#[graphql_object] impl TestType { fn field_with_object_input(input: Option) -> String { format!("{:?}", input) diff --git a/juniper/src/macros/helper/subscription.rs b/juniper/src/macros/helper/subscription.rs index 9d533185..88d1cb40 100644 --- a/juniper/src/macros/helper/subscription.rs +++ b/juniper/src/macros/helper/subscription.rs @@ -5,7 +5,7 @@ use futures::Stream; -use crate::{FieldError, GraphQLValue, ScalarValue}; +use crate::{FieldError, GraphQLValue, IntoFieldError, ScalarValue}; /// Trait for wrapping [`Stream`] into [`Ok`] if it's not [`Result`]. /// @@ -22,12 +22,12 @@ pub trait IntoFieldResult { impl IntoFieldResult for Result where T: IntoFieldResult, - E: Into>, + E: IntoFieldError, { type Item = T::Item; fn into_result(self) -> Result> { - self.map_err(|e| e.into()) + self.map_err(E::into_field_error) } } diff --git a/juniper/src/macros/tests/field.rs b/juniper/src/macros/tests/field.rs index 1104454f..3e884493 100644 --- a/juniper/src/macros/tests/field.rs +++ b/juniper/src/macros/tests/field.rs @@ -24,7 +24,7 @@ use crate::{ #[derive(Debug)] struct Root; -#[graphql_object(interfaces = [InterfaceValue])] +#[graphql_object(impl = InterfaceValue)] impl Root { fn simple() -> i32 { 0 @@ -104,7 +104,7 @@ impl Root { } } -#[graphql_interface(scalar = DefaultScalarValue)] +#[graphql_interface] impl Interface for Root { fn simple(&self) -> i32 { 0 @@ -147,7 +147,7 @@ impl Interface for Root { } } -#[graphql_interface(for = Root, scalar = DefaultScalarValue)] +#[graphql_interface(for = Root)] trait Interface { fn simple(&self) -> i32; diff --git a/juniper/src/macros/tests/interface.rs b/juniper/src/macros/tests/interface.rs index fffeded6..0f2eb3a5 100644 --- a/juniper/src/macros/tests/interface.rs +++ b/juniper/src/macros/tests/interface.rs @@ -29,44 +29,44 @@ impl Concrete { } } -#[graphql_interface(for = Concrete, name = "ACustomNamedInterface", scalar = DefaultScalarValue)] +#[graphql_interface(for = Concrete, name = "ACustomNamedInterface")] trait CustomName { fn simple(&self) -> i32; } -#[graphql_interface(scalar = DefaultScalarValue)] +#[graphql_interface] impl CustomName for Concrete { fn simple(&self) -> i32 { 0 } } -#[graphql_interface(for = Concrete, scalar = DefaultScalarValue)] +#[graphql_interface(for = Concrete)] trait WithLifetime<'a> { fn simple(&self) -> i32; } -#[graphql_interface(scalar = DefaultScalarValue)] +#[graphql_interface] impl<'a> WithLifetime<'a> for Concrete { fn simple(&self) -> i32 { 0 } } -#[graphql_interface(for = Concrete, scalar = DefaultScalarValue)] +#[graphql_interface(for = Concrete)] trait WithGenerics { fn simple(&self) -> i32; } -#[graphql_interface(scalar = DefaultScalarValue)] +#[graphql_interface] impl WithGenerics for Concrete { fn simple(&self) -> i32 { 0 } } -#[graphql_interface(for = Concrete, desc = "A description", scalar = DefaultScalarValue)] +#[graphql_interface(for = Concrete, desc = "A description")] trait Description { fn simple(&self) -> i32; } -#[graphql_interface(scalar = DefaultScalarValue)] +#[graphql_interface] impl Description for Concrete { fn simple(&self) -> i32 { 0 @@ -75,7 +75,7 @@ impl Description for Concrete { struct Root; -#[crate::graphql_object] +#[graphql_object] impl Root { fn custom_name() -> CustomNameValue { Concrete.into() diff --git a/juniper/src/macros/tests/union.rs b/juniper/src/macros/tests/union.rs index 01e3ff7d..f36010b6 100644 --- a/juniper/src/macros/tests/union.rs +++ b/juniper/src/macros/tests/union.rs @@ -31,13 +31,13 @@ impl Concrete { } #[derive(GraphQLUnion)] -#[graphql(name = "ACustomNamedUnion", scalar = DefaultScalarValue)] +#[graphql(name = "ACustomNamedUnion")] enum CustomName { Concrete(Concrete), } #[derive(GraphQLUnion)] -#[graphql(on Concrete = WithLifetime::resolve, scalar = DefaultScalarValue)] +#[graphql(on Concrete = WithLifetime::resolve)] enum WithLifetime<'a> { #[graphql(ignore)] Int(PhantomData<&'a i32>), @@ -54,7 +54,7 @@ impl<'a> WithLifetime<'a> { } #[derive(GraphQLUnion)] -#[graphql(on Concrete = WithGenerics::resolve, scalar = DefaultScalarValue)] +#[graphql(on Concrete = WithGenerics::resolve)] enum WithGenerics { #[graphql(ignore)] Generic(T), @@ -71,20 +71,19 @@ impl WithGenerics { } #[derive(GraphQLUnion)] -#[graphql(description = "A description", scalar = DefaultScalarValue)] +#[graphql(description = "A description")] enum DescriptionFirst { Concrete(Concrete), } struct Root; -// FIXME: make async work -#[crate::graphql_object(noasync)] -impl<'a> Root { +#[graphql_object] +impl Root { fn custom_name() -> CustomName { CustomName::Concrete(Concrete) } - fn with_lifetime() -> WithLifetime<'a> { + fn with_lifetime() -> WithLifetime<'_> { WithLifetime::Int(PhantomData) } fn with_generics() -> WithGenerics { diff --git a/juniper/src/parser/tests/document.rs b/juniper/src/parser/tests/document.rs index 993a4edf..5958ec7d 100644 --- a/juniper/src/parser/tests/document.rs +++ b/juniper/src/parser/tests/document.rs @@ -162,11 +162,11 @@ fn issue_427_panic_is_not_expected() { } } - let schema = SchemaType::new::, EmptySubscription<()>>( - &(), - &(), - &(), - ); + let schema = >::new::< + QueryWithoutFloat, + EmptyMutation<()>, + EmptySubscription<()>, + >(&(), &(), &()); let parse_result = parse_document_source(r##"{ echo(value: 123.0) }"##, &schema); assert_eq!( diff --git a/juniper/src/schema/model.rs b/juniper/src/schema/model.rs index 2d6bf691..7be0c616 100644 --- a/juniper/src/schema/model.rs +++ b/juniper/src/schema/model.rs @@ -88,6 +88,20 @@ pub enum DirectiveLocation { InlineFragment, } +impl<'a, QueryT, MutationT, SubscriptionT> + RootNode<'a, QueryT, MutationT, SubscriptionT, DefaultScalarValue> +where + QueryT: GraphQLType, + MutationT: GraphQLType, + SubscriptionT: GraphQLType, +{ + /// Constructs a new [`RootNode`] from `query`, `mutation` and `subscription` nodes, + /// parametrizing it with a [`DefaultScalarValue`]. + pub fn new(query: QueryT, mutation: MutationT, subscription: SubscriptionT) -> Self { + Self::new_with_info(query, mutation, subscription, (), (), ()) + } +} + impl<'a, QueryT, MutationT, SubscriptionT, S> RootNode<'a, QueryT, MutationT, SubscriptionT, S> where S: ScalarValue + 'a, @@ -95,16 +109,14 @@ where MutationT: GraphQLType, SubscriptionT: GraphQLType, { - /// Construct a new root node from query, mutation, and subscription nodes - /// - /// If the schema should not support mutations, use the - /// `new` constructor instead. - pub fn new( - query_obj: QueryT, - mutation_obj: MutationT, - subscription_obj: SubscriptionT, + /// Constructs a new [`RootNode`] from `query`, `mutation` and `subscription` nodes, + /// parametrizing it with the provided [`ScalarValue`]. + pub fn new_with_scalar_value( + query: QueryT, + mutation: MutationT, + subscription: SubscriptionT, ) -> Self { - RootNode::new_with_info(query_obj, mutation_obj, subscription_obj, (), (), ()) + RootNode::new_with_info(query, mutation, subscription, (), (), ()) } #[cfg(feature = "schema-language")] @@ -560,19 +572,18 @@ mod test { #[cfg(feature = "graphql-parser-integration")] mod graphql_parser_integration { - use crate as juniper; - use crate::{EmptyMutation, EmptySubscription}; + use crate::{graphql_object, EmptyMutation, EmptySubscription, RootNode}; #[test] fn graphql_parser_doc() { struct Query; - #[juniper::graphql_object] + #[graphql_object] impl Query { fn blah() -> bool { true } }; - let schema = crate::RootNode::new( + let schema = RootNode::new( Query, EmptyMutation::<()>::new(), EmptySubscription::<()>::new(), @@ -598,10 +609,9 @@ mod test { #[cfg(feature = "schema-language")] mod schema_language { - use crate as juniper; use crate::{ - EmptyMutation, EmptySubscription, GraphQLEnum, GraphQLInputObject, GraphQLObject, - GraphQLUnion, + graphql_object, EmptyMutation, EmptySubscription, GraphQLEnum, GraphQLInputObject, + GraphQLObject, GraphQLUnion, RootNode, }; #[test] @@ -630,7 +640,7 @@ mod test { longitude: f64, } struct Query; - #[juniper::graphql_object] + #[graphql_object] impl Query { fn blah() -> bool { true @@ -666,7 +676,7 @@ mod test { } }; - let schema = crate::RootNode::new( + let schema = RootNode::new( Query, EmptyMutation::<()>::new(), EmptySubscription::<()>::new(), diff --git a/juniper/src/tests/fixtures/starwars/schema.rs b/juniper/src/tests/fixtures/starwars/schema.rs index 8693f4b2..95e5be1d 100644 --- a/juniper/src/tests/fixtures/starwars/schema.rs +++ b/juniper/src/tests/fixtures/starwars/schema.rs @@ -2,14 +2,11 @@ use std::{collections::HashMap, pin::Pin}; -use crate::{ - graphql_interface, graphql_object, graphql_subscription, Context, DefaultScalarValue, - GraphQLEnum, -}; +use crate::{graphql_interface, graphql_object, graphql_subscription, Context, GraphQLEnum}; pub struct Query; -#[graphql_object(context = Database, scalar = DefaultScalarValue)] +#[graphql_object(context = Database)] /// The root query object of the schema impl Query { #[graphql(arguments(id(description = "id of the human")))] @@ -51,7 +48,7 @@ pub enum Episode { Jedi, } -#[graphql_interface(for = [Human, Droid], context = Database, scalar = DefaultScalarValue)] +#[graphql_interface(for = [Human, Droid], context = Database)] /// A character in the Star Wars Trilogy pub trait Character { /// The id of the character @@ -105,11 +102,7 @@ impl Human { } /// A humanoid creature in the Star Wars universe. -#[graphql_object( - context = Database, - scalar = DefaultScalarValue, - interfaces = CharacterValue, -)] +#[graphql_object(context = Database, impl = CharacterValue)] impl Human { /// The id of the human fn id(&self) -> &str { @@ -137,7 +130,7 @@ impl Human { } } -#[graphql_interface(scalar = DefaultScalarValue)] +#[graphql_interface] impl Character for Human { fn id(&self) -> &str { &self.id @@ -195,11 +188,7 @@ impl Droid { } /// A mechanical creature in the Star Wars universe. -#[graphql_object( - context = Database, - scalar = DefaultScalarValue, - interfaces = CharacterValue, -)] +#[graphql_object(context = Database, impl = CharacterValue)] impl Droid { /// The id of the droid fn id(&self) -> &str { @@ -227,7 +216,7 @@ impl Droid { } } -#[graphql_interface(scalar = DefaultScalarValue)] +#[graphql_interface] impl Character for Droid { fn id(&self) -> &str { &self.id diff --git a/juniper/src/tests/fixtures/starwars/schema_language.rs b/juniper/src/tests/fixtures/starwars/schema_language.rs index 7a71c8d7..044362f6 100644 --- a/juniper/src/tests/fixtures/starwars/schema_language.rs +++ b/juniper/src/tests/fixtures/starwars/schema_language.rs @@ -5,6 +5,8 @@ pub const STATIC_GRAPHQL_SCHEMA_DEFINITION: &str = include_str!("starwars.graphq #[cfg(test)] mod tests { + use pretty_assertions::assert_eq; + use crate::{ schema::model::RootNode, tests::fixtures::starwars::{ @@ -13,7 +15,6 @@ mod tests { }, types::scalars::{EmptyMutation, EmptySubscription}, }; - use pretty_assertions::assert_eq; #[test] fn dynamic_schema_language_matches_static() { diff --git a/juniper/src/validation/test_harness.rs b/juniper/src/validation/test_harness.rs index ce1c7fce..8d3acd19 100644 --- a/juniper/src/validation/test_harness.rs +++ b/juniper/src/validation/test_harness.rs @@ -827,7 +827,7 @@ where V: Visitor<'a, S> + 'a, F: Fn() -> V, { - let mut root = RootNode::new(r, m, s); + let mut root = RootNode::new_with_scalar_value(r, m, s); root.schema.add_directive(DirectiveType::new( "onQuery", diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs index 7f729a83..eb6b6259 100644 --- a/juniper/src/value/mod.rs +++ b/juniper/src/value/mod.rs @@ -1,14 +1,21 @@ +mod object; +mod scalar; + +use std::{ + any::TypeId, + fmt::{self, Display, Formatter}, + mem, +}; + use crate::{ ast::{InputValue, ToInputValue}, parser::Spanning, }; -use std::fmt::{self, Display, Formatter}; -mod object; -mod scalar; -pub use self::object::Object; - -pub use self::scalar::{DefaultScalarValue, ParseScalarResult, ParseScalarValue, ScalarValue}; +pub use self::{ + object::Object, + scalar::{DefaultScalarValue, ParseScalarResult, ParseScalarValue, ScalarValue}, +}; /// Serializable value returned from query and field execution. /// @@ -28,15 +35,12 @@ pub enum Value { Object(Object), } -impl Value -where - S: ScalarValue, -{ +impl Value { // CONSTRUCTORS /// Construct a null value. pub fn null() -> Self { - Value::Null + Self::Null } /// Construct an integer value. @@ -65,12 +69,12 @@ where /// Construct a list value. pub fn list(l: Vec) -> Self { - Value::List(l) + Self::List(l) } /// Construct an object value. pub fn object(o: Object) -> Self { - Value::Object(o) + Self::Object(o) } /// Construct a scalar value @@ -78,7 +82,7 @@ where where T: Into, { - Value::Scalar(s.into()) + Self::Scalar(s.into()) } // DISCRIMINATORS @@ -86,7 +90,7 @@ where /// Does this value represent null? pub fn is_null(&self) -> bool { match *self { - Value::Null => true, + Self::Null => true, _ => false, } } @@ -97,16 +101,15 @@ where &'a S: Into>, { match *self { - Value::Scalar(ref s) => s.into(), + Self::Scalar(ref s) => s.into(), _ => None, } } /// View the underlying float value, if present. - pub fn as_float_value(&self) -> Option -where { + pub fn as_float_value(&self) -> Option { match self { - Value::Scalar(ref s) => s.as_float(), + Self::Scalar(ref s) => s.as_float(), _ => None, } } @@ -114,7 +117,7 @@ where { /// View the underlying object value, if present. pub fn as_object_value(&self) -> Option<&Object> { match *self { - Value::Object(ref o) => Some(o), + Self::Object(ref o) => Some(o), _ => None, } } @@ -124,7 +127,7 @@ where { /// Returns None if value is not an Object. pub fn into_object(self) -> Option> { match self { - Value::Object(o) => Some(o), + Self::Object(o) => Some(o), _ => None, } } @@ -132,7 +135,7 @@ where { /// Mutable view into the underlying object value, if present. pub fn as_mut_object_value(&mut self) -> Option<&mut Object> { match *self { - Value::Object(ref mut o) => Some(o), + Self::Object(ref mut o) => Some(o), _ => None, } } @@ -140,7 +143,7 @@ where { /// View the underlying list value, if present. pub fn as_list_value(&self) -> Option<&Vec> { match *self { - Value::List(ref l) => Some(l), + Self::List(ref l) => Some(l), _ => None, } } @@ -148,7 +151,7 @@ where { /// View the underlying scalar value, if present pub fn as_scalar(&self) -> Option<&S> { match *self { - Value::Scalar(ref s) => Some(s), + Self::Scalar(ref s) => Some(s), _ => None, } } @@ -160,6 +163,27 @@ where { { self.as_scalar_value::().map(|s| s as &str) } + + /// Maps the [`ScalarValue`] type of this [`Value`] into the specified one. + pub fn map_scalar_value(self) -> Value { + if TypeId::of::() == TypeId::of::() { + // This is totally safe, because we're transmuting the value into itself, + // so no invariants may change and we're just satisfying the type checker. + let val = mem::ManuallyDrop::new(self); + unsafe { mem::transmute_copy(&*val) } + } else { + match self { + Self::Null => Value::Null, + Self::Scalar(s) => Value::Scalar(s.into_another()), + Self::List(l) => Value::List(l.into_iter().map(Value::map_scalar_value).collect()), + Self::Object(o) => Value::Object( + o.into_iter() + .map(|(k, v)| (k, v.map_scalar_value())) + .collect(), + ), + } + } + } } impl ToInputValue for Value { diff --git a/juniper/src/value/scalar.rs b/juniper/src/value/scalar.rs index 84643e07..397860e1 100644 --- a/juniper/src/value/scalar.rs +++ b/juniper/src/value/scalar.rs @@ -53,36 +53,43 @@ pub trait ParseScalarValue { /// /// fn as_int(&self) -> Option { /// match *self { -/// MyScalarValue::Int(ref i) => Some(*i), +/// Self::Int(ref i) => Some(*i), /// _ => None, /// } /// } /// /// fn as_string(&self) -> Option { /// match *self { -/// MyScalarValue::String(ref s) => Some(s.clone()), +/// Self::String(ref s) => Some(s.clone()), +/// _ => None, +/// } +/// } +/// +/// fn into_string(self) -> Option { +/// match self { +/// Self::String(s) => Some(s), /// _ => None, /// } /// } /// /// fn as_str(&self) -> Option<&str> { /// match *self { -/// MyScalarValue::String(ref s) => Some(s.as_str()), +/// Self::String(ref s) => Some(s.as_str()), /// _ => None, /// } /// } /// /// fn as_float(&self) -> Option { /// match *self { -/// MyScalarValue::Int(ref i) => Some(*i as f64), -/// MyScalarValue::Float(ref f) => Some(*f), +/// Self::Int(ref i) => Some(*i as f64), +/// Self::Float(ref f) => Some(*f), /// _ => None, /// } /// } /// /// fn as_boolean(&self) -> Option { /// match *self { -/// MyScalarValue::Boolean(ref b) => Some(*b), +/// Self::Boolean(ref b) => Some(*b), /// _ => None, /// } /// } @@ -175,6 +182,7 @@ pub trait ScalarValue: + From + From + From + + 'static { /// Serde visitor used to deserialize this scalar value type Visitor: for<'de> de::Visitor<'de, Value = Self> + Default; @@ -205,12 +213,18 @@ pub trait ScalarValue: /// types with 32 bit or less to an integer if requested. fn as_int(&self) -> Option; - /// Convert the given scalar value into a string value + /// Represents this [`ScalarValue`] a [`String`] value. /// /// This function is used for implementing `GraphQLValue` for `String` for all /// scalar values fn as_string(&self) -> Option; + /// Converts this [`ScalarValue`] into a [`String`] value. + /// + /// Same as [`ScalarValue::as_string`], but takes ownership, so allows to omit redundant + /// cloning. + fn into_string(self) -> Option; + /// Convert the given scalar value into a string value /// /// This function is used for implementing `GraphQLValue` for `String` for all @@ -230,6 +244,21 @@ pub trait ScalarValue: /// This function is used for implementing `GraphQLValue` for `bool` for all /// scalar values. fn as_boolean(&self) -> Option; + + /// Converts this [`ScalarValue`] into another one. + fn into_another(self) -> S { + if let Some(i) = self.as_int() { + S::from(i) + } else if let Some(f) = self.as_float() { + S::from(f) + } else if let Some(b) = self.as_boolean() { + S::from(b) + } else if let Some(s) = self.into_string() { + S::from(s) + } else { + unreachable!("`ScalarValue` must represent at least one of the GraphQL spec types") + } + } } /// The default scalar value representation in juniper @@ -249,44 +278,60 @@ impl ScalarValue for DefaultScalarValue { fn as_int(&self) -> Option { match *self { - DefaultScalarValue::Int(ref i) => Some(*i), + Self::Int(ref i) => Some(*i), _ => None, } } fn as_float(&self) -> Option { match *self { - DefaultScalarValue::Int(ref i) => Some(*i as f64), - DefaultScalarValue::Float(ref f) => Some(*f), + Self::Int(ref i) => Some(*i as f64), + Self::Float(ref f) => Some(*f), _ => None, } } fn as_str(&self) -> Option<&str> { match *self { - DefaultScalarValue::String(ref s) => Some(s.as_str()), + Self::String(ref s) => Some(s.as_str()), _ => None, } } fn as_string(&self) -> Option { match *self { - DefaultScalarValue::String(ref s) => Some(s.clone()), + Self::String(ref s) => Some(s.clone()), + _ => None, + } + } + + fn into_string(self) -> Option { + match self { + Self::String(s) => Some(s), _ => None, } } fn as_boolean(&self) -> Option { match *self { - DefaultScalarValue::Boolean(ref b) => Some(*b), + Self::Boolean(ref b) => Some(*b), _ => None, } } + + fn into_another(self) -> S { + match self { + Self::Int(i) => S::from(i), + Self::Float(f) => S::from(f), + Self::String(s) => S::from(s), + Self::Boolean(b) => S::from(b), + } + } } impl<'a> From<&'a str> for DefaultScalarValue { fn from(s: &'a str) -> Self { - DefaultScalarValue::String(s.into()) + Self::String(s.into()) } } diff --git a/juniper_actix/src/lib.rs b/juniper_actix/src/lib.rs index 1821f2e2..e62ded6d 100644 --- a/juniper_actix/src/lib.rs +++ b/juniper_actix/src/lib.rs @@ -481,7 +481,6 @@ pub mod subscriptions { #[cfg(test)] mod tests { - use super::*; use actix_web::{dev::ServiceResponse, http, http::header::CONTENT_TYPE, test, App}; use juniper::{ futures::stream::StreamExt, @@ -490,6 +489,8 @@ mod tests { EmptyMutation, EmptySubscription, RootNode, }; + use super::*; + type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>; @@ -713,7 +714,7 @@ mod tests { impl TestActixWebIntegration { fn make_request(&self, req: test::TestRequest) -> TestResponse { actix_web::rt::System::new("request").block_on(async move { - let schema = RootNode::new( + let schema = Schema::new( Query, EmptyMutation::::new(), EmptySubscription::::new(), diff --git a/juniper_benchmarks/src/lib.rs b/juniper_benchmarks/src/lib.rs index f06bbf68..526d0043 100644 --- a/juniper_benchmarks/src/lib.rs +++ b/juniper_benchmarks/src/lib.rs @@ -1,6 +1,6 @@ use juniper::{ graphql_object, DefaultScalarValue, EmptyMutation, EmptySubscription, ExecutionError, - FieldError, GraphQLEnum, Value, Variables, + FieldError, GraphQLEnum, GraphQLObject, RootNode, Value, Variables, }; pub type QueryResult = Result< @@ -37,7 +37,7 @@ pub enum UserKind { Guest, } -#[derive(juniper::GraphQLObject)] +#[derive(GraphQLObject)] pub struct User { pub id: i32, pub kind: UserKind, @@ -60,7 +60,7 @@ impl User { pub struct Query; -#[graphql_object(Context = Context)] +#[graphql_object(context = Context)] impl Query { fn user_sync_instant(id: i32) -> Result { Ok(User::new(id)) @@ -89,9 +89,9 @@ impl Query { } } -pub fn new_schema( -) -> juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription> { - juniper::RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new()) +pub fn new_schema() -> RootNode<'static, Query, EmptyMutation, EmptySubscription> +{ + RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new()) } pub fn execute_sync(query: &str, vars: Variables) -> QueryResult { diff --git a/juniper_codegen/src/impl_object.rs b/juniper_codegen/src/impl_object.rs index ed90f932..9de0a755 100644 --- a/juniper_codegen/src/impl_object.rs +++ b/juniper_codegen/src/impl_object.rs @@ -223,7 +223,7 @@ fn create( .map(SpanContainer::into_inner) .collect(), include_type_generics: false, - generic_scalar: false, + generic_scalar: true, no_async: _impl.attrs.no_async.is_some(), }; diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs index f5459280..062b52b1 100644 --- a/juniper_codegen/src/util/mod.rs +++ b/juniper_codegen/src/util/mod.rs @@ -1232,11 +1232,10 @@ impl GraphQLTypeDefiniton { // No custom scalar specified, but always generic specified. // Therefore we inject the generic scalar. - generics.params.push(parse_quote!(__S)); - - let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); // Insert ScalarValue constraint. - where_clause + generics.params.push(parse_quote!(__S)); + generics + .make_where_clause() .predicates .push(parse_quote!(__S: ::juniper::ScalarValue)); } @@ -1248,6 +1247,15 @@ impl GraphQLTypeDefiniton { }; let (impl_generics, _, where_clause) = generics.split_for_impl(); + let mut generics_with_send_sync = generics.clone(); + if self.scalar.is_none() && self.generic_scalar { + generics_with_send_sync + .make_where_clause() + .predicates + .push(parse_quote!(__S: Send + Sync)); + } + let (_, _, where_clause_with_send_sync) = generics_with_send_sync.split_for_impl(); + let resolve_matches_async = self.fields.iter().filter(|field| field.is_async).map( |field| { let name = &field.name; @@ -1378,7 +1386,7 @@ impl GraphQLTypeDefiniton { let subscription_implementation = quote!( impl#impl_generics ::juniper::GraphQLSubscriptionValue<#scalar> for #ty #type_generics_tokens - #where_clause + #where_clause_with_send_sync { #[allow(unused_variables)] fn resolve_field_into_stream< diff --git a/juniper_iron/examples/iron_server.rs b/juniper_iron/examples/iron_server.rs index 6df29668..d243b221 100644 --- a/juniper_iron/examples/iron_server.rs +++ b/juniper_iron/examples/iron_server.rs @@ -3,7 +3,7 @@ use std::env; use iron::prelude::*; use juniper::{ tests::fixtures::starwars::schema::{Database, Query}, - EmptyMutation, EmptySubscription, + DefaultScalarValue, EmptyMutation, EmptySubscription, }; use juniper_iron::{GraphQLHandler, GraphiQLHandler}; use logger::Logger; @@ -16,7 +16,7 @@ fn context_factory(_: &mut Request) -> IronResult { fn main() { let mut mount = Mount::new(); - let graphql_endpoint = GraphQLHandler::new( + let graphql_endpoint = >::new( context_factory, Query, EmptyMutation::::new(), diff --git a/juniper_iron/src/lib.rs b/juniper_iron/src/lib.rs index 1f40bae5..91c60041 100644 --- a/juniper_iron/src/lib.rs +++ b/juniper_iron/src/lib.rs @@ -34,7 +34,7 @@ use juniper::{Context, EmptyMutation, EmptySubscription}; # struct QueryRoot; # struct Database { users: HashMap } # -# #[juniper::graphql_object( Context = Database )] +# #[juniper::graphql_object(context = Database)] # impl User { # fn id(&self) -> FieldResult<&String> { # Ok(&self.id) @@ -51,7 +51,7 @@ use juniper::{Context, EmptyMutation, EmptySubscription}; # } # } # -# #[juniper::graphql_object( Context = Database )] +# #[juniper::graphql_object(context = Database, scalar = juniper::DefaultScalarValue)] # impl QueryRoot { # fn user(context: &Database, id: String) -> FieldResult> { # Ok(executor.context().users.get(&id)) @@ -220,7 +220,7 @@ where ) -> Self { GraphQLHandler { context_factory, - root_node: RootNode::new(query, mutation, subscription), + root_node: RootNode::new_with_scalar_value(query, mutation, subscription), } } @@ -412,7 +412,7 @@ mod tests { use juniper::{ http::tests as http_tests, tests::fixtures::starwars::schema::{Database, Query}, - EmptyMutation, EmptySubscription, + DefaultScalarValue, EmptyMutation, EmptySubscription, }; use super::GraphQLHandler; @@ -511,7 +511,7 @@ mod tests { } fn make_handler() -> Box { - Box::new(GraphQLHandler::new( + Box::new(>::new( context_factory, Query, EmptyMutation::::new(), diff --git a/juniper_warp/src/lib.rs b/juniper_warp/src/lib.rs index a8bbf0d5..7bb88cb5 100644 --- a/juniper_warp/src/lib.rs +++ b/juniper_warp/src/lib.rs @@ -64,7 +64,7 @@ use warp::{body, filters::BoxedFilter, http, query, Filter}; /// ``` /// # use std::sync::Arc; /// # use warp::Filter; -/// # use juniper::{EmptyMutation, EmptySubscription, RootNode}; +/// # use juniper::{graphql_object, EmptyMutation, EmptySubscription, RootNode}; /// # use juniper_warp::make_graphql_filter; /// # /// type UserId = String; @@ -74,9 +74,7 @@ use warp::{body, filters::BoxedFilter, http, query, Filter}; /// /// struct QueryRoot; /// -/// #[juniper::graphql_object( -/// Context = ExampleContext -/// )] +/// #[graphql_object(context = ExampleContext)] /// impl QueryRoot { /// fn say_hello(context: &ExampleContext) -> String { /// format!( @@ -683,18 +681,18 @@ mod tests { #[cfg(test)] mod tests_http_harness { - use super::*; use juniper::{ http::tests::{run_http_test_suite, HttpIntegration, TestResponse}, tests::fixtures::starwars::schema::{Database, Query}, EmptyMutation, EmptySubscription, RootNode, }; use warp::{ - self, filters::{path, BoxedFilter}, Filter, }; + use super::*; + struct TestWarpIntegration { filter: BoxedFilter<(http::Response>,)>, }