Merge pull request from instrumentisto/async-await-resolve-some-todos

Resolve some todos in async-await branch
This commit is contained in:
Christian Legnitto 2019-11-06 18:17:50 -08:00 committed by GitHub
commit b133a0f3c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 373 additions and 184 deletions

127
benches/bench.rs Normal file
View file

@ -0,0 +1,127 @@
#[macro_use] extern crate bencher;
extern crate juniper;
use bencher::Bencher;
use juniper::{execute, RootNode, EmptyMutation, Variables};
use juniper::tests::model::Database;
fn query_type_name(b: &mut Bencher) {
let database = Database::new();
let schema = RootNode::new(&database, EmptyMutation::<Database>::new());
let doc = r#"
query IntrospectionQueryTypeQuery {
__schema {
queryType {
name
}
}
}"#;
b.iter(|| execute(doc, None, &schema, &Variables::new(), &database));
}
fn introspection_query(b: &mut Bencher) {
let database = Database::new();
let schema = RootNode::new(&database, EmptyMutation::<Database>::new());
let doc = r#"
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}
fragment InputValue on __InputValue {
name
description
type { ...TypeRef }
defaultValue
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
"#;
b.iter(|| execute(doc, None, &schema, &Variables::new(), &database));
}
benchmark_group!(queries, query_type_name, introspection_query);
benchmark_main!(queries);

View file

@ -43,7 +43,7 @@ impl User {
} }
} }
struct Query; struct Query;
#[juniper::object(Context = Context)] #[juniper::object(Context = Context)]
impl Query { impl Query {

View file

@ -2,6 +2,11 @@
- No changes yet - No changes yet
# [[0.14.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.14.1)
- Fix panic when an invalid scalar is used by a client [#434](https://github.com/graphql-rust/juniper/pull/434)
- `EmptyMutation` now implements `Send` [#443](https://github.com/graphql-rust/juniper/pull/443)
# [[0.14.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.14.0) # [[0.14.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.14.0)
- Require `url` 2.x if `url` feature is enabled. - Require `url` 2.x if `url` feature is enabled.

View file

@ -1,6 +1,6 @@
[package] [package]
name = "juniper" name = "juniper"
version = "0.14.0" version = "0.14.1"
authors = [ authors = [
"Magnus Hallin <mhallin@fastmail.com>", "Magnus Hallin <mhallin@fastmail.com>",
"Christoph Herzog <chris@theduke.at>", "Christoph Herzog <chris@theduke.at>",
@ -33,20 +33,19 @@ default = [
] ]
[dependencies] [dependencies]
juniper_codegen = { version = "0.14.0", path = "../juniper_codegen" } juniper_codegen = { version = "0.14.1", path = "../juniper_codegen" }
async-trait = "0.1.16"
chrono = { version = "0.4.0", optional = true }
fnv = "1.0.3" fnv = "1.0.3"
futures-preview = { version = "=0.3.0-alpha.19", optional = true }
indexmap = { version = "1.0.0", features = ["serde-1"] } indexmap = { version = "1.0.0", features = ["serde-1"] }
serde = { version = "1.0.8" } serde = { version = "1.0.8" }
serde_derive = { version = "1.0.2" } serde_derive = { version = "1.0.2" }
chrono = { version = "0.4.0", optional = true }
serde_json = { version="1.0.2", optional = true } serde_json = { version="1.0.2", optional = true }
url = { version = "2", optional = true } url = { version = "2", optional = true }
uuid = { version = "0.7", optional = true } uuid = { version = "0.7", optional = true }
futures-preview = { version = "=0.3.0-alpha.19", optional = true }
[dev-dependencies] [dev-dependencies]
bencher = "0.1.2" bencher = "0.1.2"
serde_json = { version = "1.0.2" } serde_json = { version = "1.0.2" }

View file

@ -210,20 +210,6 @@ impl<S> FieldError<S> {
/// The result of resolving the value of a field of type `T` /// The result of resolving the value of a field of type `T`
pub type FieldResult<T, S = DefaultScalarValue> = Result<T, FieldError<S>>; pub type FieldResult<T, S = DefaultScalarValue> = Result<T, FieldError<S>>;
/*
pub enum ResolvedValue<'a, S = DefaultScalarValue> {
Value(Value<S>),
Future(crate::BoxFuture<'a, Value<S>>),
}
impl<'a, S> From<Value<S>> for ResolvedValue<'a, S> {
#[inline]
fn from(value: Value<S>) -> Self {
ResolvedValue::Value(value)
}
}
*/
/// The result of resolving an unspecified field /// The result of resolving an unspecified field
pub type ExecutionResult<S = DefaultScalarValue> = Result<Value<S>, FieldError<S>>; pub type ExecutionResult<S = DefaultScalarValue> = Result<Value<S>, FieldError<S>>;

View file

@ -88,7 +88,7 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected.
[chrono]: https://crates.io/crates/chrono [chrono]: https://crates.io/crates/chrono
*/ */
#![doc(html_root_url = "https://docs.rs/juniper/0.14.0")] #![doc(html_root_url = "https://docs.rs/juniper/0.14.1")]
#![warn(missing_docs)] #![warn(missing_docs)]
#[doc(hidden)] #[doc(hidden)]

View file

@ -96,25 +96,6 @@ macro_rules! __juniper_insert_generic {
}; };
} }
// TODO: remove me.
#[doc(hidden)]
#[macro_export]
macro_rules! __juniper_extract_generic {
(<$name:ident>) => {
$name
};
(
<$generic:tt $(: $bound: tt)*>
) => {
$generic
};
(
$scalar: ty
) => {
$scalar
};
}
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! __juniper_parse_object_header { macro_rules! __juniper_parse_object_header {

View file

@ -423,12 +423,16 @@ macro_rules! graphql_scalar {
) )
{ {
fn resolve_async<'a>( fn resolve_async<'a, 'async_trait>(
&'a self, &'a self,
info: &'a Self::TypeInfo, info: &'a Self::TypeInfo,
selection_set: Option<&'a [$crate::Selection<$crate::__juniper_insert_generic!($($scalar)+)>]>, selection_set: Option<&'a [$crate::Selection<'a, $crate::__juniper_insert_generic!($($scalar)+)>]>,
executor: &'a $crate::Executor<Self::Context, $crate::__juniper_insert_generic!($($scalar)+)>, executor: &'a $crate::Executor<'a, Self::Context, $crate::__juniper_insert_generic!($($scalar)+)>,
) -> futures::future::BoxFuture<'a, $crate::Value<$crate::__juniper_insert_generic!($($scalar)+)>> { ) -> futures::future::BoxFuture<'async_trait, $crate::Value<$crate::__juniper_insert_generic!($($scalar)+)>>
where
'a: 'async_trait,
Self: 'async_trait,
{
use $crate::GraphQLType; use $crate::GraphQLType;
use futures::future; use futures::future;
let v = self.resolve(info, selection_set, executor); let v = self.resolve(info, selection_set, executor);

View file

@ -73,13 +73,17 @@ impl Root {
0 0
} }
// TODO: enable once [RFC 2565](https://github.com/rust-lang/rust/issues/60406) is implemented // TODO: enable once [parameter attributes are supported by proc macros]
// fn attr_arg_descr(#[doc = "The arg"] arg: i32) -> i32 { 0 } // (https://github.com/graphql-rust/juniper/pull/441)
// fn attr_arg_descr_collapse( // fn attr_arg_descr(
// #[doc = "The arg"] // #[graphql(description = "The arg")]
// #[doc = "and more details"] // arg: i32) -> i32
// arg: i32, // { 0 }
// ) -> i32 { 0 } // fn attr_arg_descr_collapse(
// #[graphql(description = "The first arg")]
// #[graphql(description = "and more details")]
// arg: i32,
// ) -> i32 { 0 }
#[graphql(arguments(arg(default = 123,),))] #[graphql(arguments(arg(default = 123,),))]
fn arg_with_default(arg: i32) -> i32 { fn arg_with_default(arg: i32) -> i32 {
@ -559,7 +563,8 @@ fn introspect_field_multi_args_descr_trailing_comma() {
}); });
} }
// TODO: enable once [RFC 2565](https://github.com/rust-lang/rust/issues/60406) is implemented // TODO: enable once [parameter attributes are supported by proc macros]
// (https://github.com/graphql-rust/juniper/pull/441)
// #[test] // #[test]
// fn introspect_field_attr_arg_descr() { // fn introspect_field_attr_arg_descr() {
// run_args_info_query("attrArgDescr", |args| { // run_args_info_query("attrArgDescr", |args| {
@ -593,7 +598,8 @@ fn introspect_field_multi_args_descr_trailing_comma() {
// }); // });
// } // }
// TODO: enable once [RFC 2565](https://github.com/rust-lang/rust/issues/60406) is implemented // TODO: enable once [parameter attributes are supported by proc macros]
// (https://github.com/graphql-rust/juniper/pull/441)
// #[test] // #[test]
// fn introspect_field_attr_arg_descr_collapse() { // fn introspect_field_attr_arg_descr_collapse() {
// run_args_info_query("attrArgDescrCollapse", |args| { // run_args_info_query("attrArgDescrCollapse", |args| {

View file

@ -13,6 +13,9 @@ pub enum ParseError<'a> {
/// An error during tokenization occurred /// An error during tokenization occurred
LexerError(LexerError), LexerError(LexerError),
/// A scalar of unexpected type occurred in the source
ExpectedScalarError(&'static str),
} }
#[doc(hidden)] #[doc(hidden)]
@ -196,6 +199,7 @@ impl<'a> fmt::Display for ParseError<'a> {
ParseError::UnexpectedToken(ref token) => write!(f, "Unexpected \"{}\"", token), ParseError::UnexpectedToken(ref token) => write!(f, "Unexpected \"{}\"", token),
ParseError::UnexpectedEndOfFile => write!(f, "Unexpected end of input"), ParseError::UnexpectedEndOfFile => write!(f, "Unexpected end of input"),
ParseError::LexerError(ref err) => err.fmt(f), ParseError::LexerError(ref err) => err.fmt(f),
ParseError::ExpectedScalarError(err) => err.fmt(f),
} }
} }
} }

View file

@ -4,6 +4,7 @@ use crate::{
}, },
parser::{document::parse_document_source, ParseError, SourcePosition, Spanning, Token}, parser::{document::parse_document_source, ParseError, SourcePosition, Spanning, Token},
schema::model::SchemaType, schema::model::SchemaType,
types::scalars::EmptyMutation,
validation::test_harness::{MutationRoot, QueryRoot}, validation::test_harness::{MutationRoot, QueryRoot},
value::{DefaultScalarValue, ScalarRefValue, ScalarValue}, value::{DefaultScalarValue, ScalarRefValue, ScalarValue},
}; };
@ -145,3 +146,23 @@ fn errors() {
) )
); );
} }
#[test]
fn issue_427_panic_is_not_expected() {
struct QueryWithoutFloat;
#[crate::object_internal]
impl QueryWithoutFloat {
fn echo(value: String) -> String {
value
}
}
let schema = SchemaType::new::<QueryWithoutFloat, EmptyMutation<()>>(&(), &());
let parse_result = parse_document_source(r##"{ echo(value: 123.0) }"##, &schema);
assert_eq!(
parse_result.unwrap_err().item,
ParseError::ExpectedScalarError("There needs to be a Float type")
);
}

View file

@ -210,33 +210,36 @@ fn parse_scalar_literal_by_infered_type<'a, 'b, S>(
where where
S: ScalarValue, S: ScalarValue,
{ {
match token { let result = match token {
ScalarToken::String(_) => { ScalarToken::String(_) => {
if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("String") { if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("String") {
(s.parse_fn)(token) (s.parse_fn)(token).map(InputValue::Scalar)
.map(|s| Spanning::start_end(start, end, InputValue::Scalar(s)))
.map_err(|e| Spanning::start_end(start, end, e))
} else { } else {
panic!("There needs to be a String type") Err(ParseError::ExpectedScalarError(
"There needs to be a String type",
))
} }
} }
ScalarToken::Int(_) => { ScalarToken::Int(_) => {
if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("Int") { if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("Int") {
(s.parse_fn)(token) (s.parse_fn)(token).map(InputValue::Scalar)
.map(|s| Spanning::start_end(start, end, InputValue::Scalar(s)))
.map_err(|e| Spanning::start_end(start, end, e))
} else { } else {
panic!("There needs to be a Int type") Err(ParseError::ExpectedScalarError(
"There needs to be an Int type",
))
} }
} }
ScalarToken::Float(_) => { ScalarToken::Float(_) => {
if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("Float") { if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("Float") {
(s.parse_fn)(token) (s.parse_fn)(token).map(InputValue::Scalar)
.map(|s| Spanning::start_end(start, end, InputValue::Scalar(s)))
.map_err(|e| Spanning::start_end(start, end, e))
} else { } else {
panic!("There needs to be a Float type") Err(ParseError::ExpectedScalarError(
"There needs to be a Float type",
))
} }
} }
} };
result
.map(|s| Spanning::start_end(start, end, s))
.map_err(|e| Spanning::start_end(start, end, e))
} }

View file

@ -77,6 +77,7 @@ where
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[async_trait::async_trait]
impl<'a, CtxT, S, QueryT, MutationT> crate::GraphQLTypeAsync<S> impl<'a, CtxT, S, QueryT, MutationT> crate::GraphQLTypeAsync<S>
for RootNode<'a, QueryT, MutationT, S> for RootNode<'a, QueryT, MutationT, S>
where where
@ -85,16 +86,16 @@ where
QueryT::TypeInfo: Send + Sync, QueryT::TypeInfo: Send + Sync,
MutationT: crate::GraphQLTypeAsync<S, Context = CtxT>, MutationT: crate::GraphQLTypeAsync<S, Context = CtxT>,
MutationT::TypeInfo: Send + Sync, MutationT::TypeInfo: Send + Sync,
CtxT: Send + Sync, CtxT: Send + Sync + 'a,
for<'b> &'b S: ScalarRefValue<'b>, for<'c> &'c S: ScalarRefValue<'c>,
{ {
fn resolve_field_async<'b>( async fn resolve_field_async<'b>(
&'b self, &'b self,
info: &'b Self::TypeInfo, info: &'b <Self as crate::GraphQLType<S>>::TypeInfo,
field_name: &'b str, field_name: &'b str,
arguments: &'b Arguments<S>, arguments: &'b Arguments<'b, S>,
executor: &'b Executor<Self::Context, S>, executor: &'b Executor<'b, <Self as crate::GraphQLType<S>>::Context, S>,
) -> crate::BoxFuture<'b, ExecutionResult<S>> { ) -> ExecutionResult<S> {
use futures::future::{ready, FutureExt}; use futures::future::{ready, FutureExt};
match field_name { match field_name {
"__schema" | "__type" => { "__schema" | "__type" => {

View file

@ -12,6 +12,7 @@ use crate::BoxFuture;
use super::base::{is_excluded, merge_key_into, Arguments, GraphQLType}; use super::base::{is_excluded, merge_key_into, Arguments, GraphQLType};
#[async_trait::async_trait]
pub trait GraphQLTypeAsync<S>: GraphQLType<S> + Send + Sync pub trait GraphQLTypeAsync<S>: GraphQLType<S> + Send + Sync
where where
Self::Context: Send + Sync, Self::Context: Send + Sync,
@ -19,28 +20,42 @@ where
S: ScalarValue + Send + Sync, S: ScalarValue + Send + Sync,
for<'b> &'b S: ScalarRefValue<'b>, for<'b> &'b S: ScalarRefValue<'b>,
{ {
fn resolve_field_async<'a>( async fn resolve_field_async<'a>(
&'a self, &'a self,
info: &'a Self::TypeInfo, info: &'a Self::TypeInfo,
field_name: &'a str, field_name: &'a str,
arguments: &'a Arguments<S>, arguments: &'a Arguments<'a, S>,
executor: &'a Executor<Self::Context, S>, executor: &'a Executor<'a, Self::Context, S>,
) -> BoxFuture<'a, ExecutionResult<S>> { ) -> ExecutionResult<S> {
panic!("resolve_field must be implemented by object types"); panic!("resolve_field must be implemented by object types");
} }
fn resolve_async<'a>( async fn resolve_async<'a>(
&'a self, &'a self,
info: &'a Self::TypeInfo, info: &'a Self::TypeInfo,
selection_set: Option<&'a [Selection<S>]>, selection_set: Option<&'a [Selection<'a, S>]>,
executor: &'a Executor<Self::Context, S>, executor: &'a Executor<'a, Self::Context, S>,
) -> BoxFuture<'a, Value<S>> { ) -> Value<S> {
if let Some(selection_set) = selection_set { if let Some(selection_set) = selection_set {
resolve_selection_set_into_async(self, info, selection_set, executor) resolve_selection_set_into_async(self, info, selection_set, executor).await
} else { } else {
panic!("resolve() must be implemented by non-object output types"); panic!("resolve() must be implemented by non-object output types");
} }
} }
async fn resolve_into_type_async<'a>(
&'a self,
info: &'a Self::TypeInfo,
type_name: &str,
selection_set: Option<&'a [Selection<'a, S>]>,
executor: &'a Executor<'a, Self::Context, S>,
) -> ExecutionResult<S> {
if Self::name(info).unwrap() == type_name {
Ok(self.resolve_async(info, selection_set, executor).await)
} else {
panic!("resolve_into_type_async must be implemented by unions and interfaces");
}
}
} }
// Wrapper function around resolve_selection_set_into_async_recursive. // Wrapper function around resolve_selection_set_into_async_recursive.
@ -160,7 +175,7 @@ where
let response_name = response_name.to_string(); let response_name = response_name.to_string();
let field_future = async move { let field_future = async move {
// TODO: implement custom future type instead of // TODO: implement custom future type instead of
// two-level boxing. // two-level boxing.
let res = instance let res = instance
.resolve_field_async(info, f.name.item, &args, &sub_exec) .resolve_field_async(info, f.name.item, &args, &sub_exec)
.await; .await;
@ -223,14 +238,14 @@ where
); );
if let Some(ref type_condition) = fragment.type_condition { if let Some(ref type_condition) = fragment.type_condition {
// FIXME: implement async version. let sub_result = instance
.resolve_into_type_async(
let sub_result = instance.resolve_into_type( info,
info, type_condition.item,
type_condition.item, Some(&fragment.selection_set[..]),
Some(&fragment.selection_set[..]), &sub_exec,
&sub_exec, )
); .await;
if let Ok(Value::Object(obj)) = sub_result { if let Ok(Value::Object(obj)) = sub_result {
for (k, v) in obj { for (k, v) in obj {

View file

@ -343,7 +343,7 @@ where
} }
} }
pub fn resolve_selection_set_into<T, CtxT, S>( pub(crate) fn resolve_selection_set_into<T, CtxT, S>(
instance: &T, instance: &T,
info: &T::TypeInfo, info: &T::TypeInfo,
selection_set: &[Selection<S>], selection_set: &[Selection<S>],

View file

@ -257,6 +257,7 @@ where
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[async_trait::async_trait]
impl<S, T, CtxT> crate::GraphQLTypeAsync<S> for Vec<T> impl<S, T, CtxT> crate::GraphQLTypeAsync<S> for Vec<T>
where where
T: crate::GraphQLTypeAsync<S, Context = CtxT>, T: crate::GraphQLTypeAsync<S, Context = CtxT>,
@ -265,18 +266,18 @@ where
CtxT: Send + Sync, CtxT: Send + Sync,
for<'b> &'b S: ScalarRefValue<'b>, for<'b> &'b S: ScalarRefValue<'b>,
{ {
fn resolve_async<'a>( async fn resolve_async<'a>(
&'a self, &'a self,
info: &'a Self::TypeInfo, info: &'a <Self as crate::GraphQLType<S>>::TypeInfo,
selection_set: Option<&'a [Selection<S>]>, selection_set: Option<&'a [Selection<'a, S>]>,
executor: &'a Executor<Self::Context, S>, executor: &'a Executor<'a, <Self as crate::GraphQLType<S>>::Context, S>,
) -> crate::BoxFuture<'a, Value<S>> { ) -> Value<S> {
let f = resolve_into_list_async(executor, info, self.iter()); resolve_into_list_async(executor, info, self.iter()).await
Box::pin(f)
} }
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[async_trait::async_trait]
impl<S, T, CtxT> crate::GraphQLTypeAsync<S> for &[T] impl<S, T, CtxT> crate::GraphQLTypeAsync<S> for &[T]
where where
T: crate::GraphQLTypeAsync<S, Context = CtxT>, T: crate::GraphQLTypeAsync<S, Context = CtxT>,
@ -285,18 +286,18 @@ where
CtxT: Send + Sync, CtxT: Send + Sync,
for<'b> &'b S: ScalarRefValue<'b>, for<'b> &'b S: ScalarRefValue<'b>,
{ {
fn resolve_async<'a>( async fn resolve_async<'a>(
&'a self, &'a self,
info: &'a Self::TypeInfo, info: &'a <Self as crate::GraphQLType<S>>::TypeInfo,
selection_set: Option<&'a [Selection<S>]>, selection_set: Option<&'a [Selection<'a, S>]>,
executor: &'a Executor<Self::Context, S>, executor: &'a Executor<'a, <Self as crate::GraphQLType<S>>::Context, S>,
) -> crate::BoxFuture<'a, Value<S>> { ) -> Value<S> {
let f = resolve_into_list_async(executor, info, self.iter()); resolve_into_list_async(executor, info, self.iter()).await
Box::pin(f)
} }
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[async_trait::async_trait]
impl<S, T, CtxT> crate::GraphQLTypeAsync<S> for Option<T> impl<S, T, CtxT> crate::GraphQLTypeAsync<S> for Option<T>
where where
T: crate::GraphQLTypeAsync<S, Context = CtxT>, T: crate::GraphQLTypeAsync<S, Context = CtxT>,
@ -305,18 +306,15 @@ where
CtxT: Send + Sync, CtxT: Send + Sync,
for<'b> &'b S: ScalarRefValue<'b>, for<'b> &'b S: ScalarRefValue<'b>,
{ {
fn resolve_async<'a>( async fn resolve_async<'a>(
&'a self, &'a self,
info: &'a Self::TypeInfo, info: &'a <Self as crate::GraphQLType<S>>::TypeInfo,
selection_set: Option<&'a [Selection<S>]>, selection_set: Option<&'a [Selection<'a, S>]>,
executor: &'a Executor<Self::Context, S>, executor: &'a Executor<'a, <Self as crate::GraphQLType<S>>::Context, S>,
) -> crate::BoxFuture<'a, Value<S>> { ) -> Value<S> {
let f = async move { match *self {
match *self { Some(ref obj) => executor.resolve_into_value_async(info, obj).await,
Some(ref obj) => executor.resolve_into_value_async(info, obj).await, None => Value::null(),
None => Value::null(), }
}
};
Box::pin(f)
} }
} }

View file

@ -137,31 +137,33 @@ where
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[async_trait::async_trait]
impl<'e, S, T> crate::GraphQLTypeAsync<S> for &'e T impl<'e, S, T> crate::GraphQLTypeAsync<S> for &'e T
where where
S: ScalarValue + Send + Sync, S: ScalarValue + Send + Sync,
T: crate::GraphQLTypeAsync<S>, T: crate::GraphQLTypeAsync<S>,
T::TypeInfo: Send + Sync, T::TypeInfo: Send + Sync,
T::Context: Send + Sync, T::Context: Send + Sync,
for<'b> &'b S: ScalarRefValue<'b>, for<'c> &'c S: ScalarRefValue<'c>,
{ {
fn resolve_field_async<'b>( async fn resolve_field_async<'b>(
&'b self, &'b self,
info: &'b Self::TypeInfo, info: &'b <Self as crate::GraphQLType<S>>::TypeInfo,
field_name: &'b str, field_name: &'b str,
arguments: &'b Arguments<S>, arguments: &'b Arguments<'b, S>,
executor: &'b Executor<Self::Context, S>, executor: &'b Executor<'b, <Self as crate::GraphQLType<S>>::Context, S>,
) -> crate::BoxFuture<'b, ExecutionResult<S>> { ) -> ExecutionResult<S> {
crate::GraphQLTypeAsync::resolve_field_async(&**self, info, field_name, arguments, executor) crate::GraphQLTypeAsync::resolve_field_async(&**self, info, field_name, arguments, executor)
.await
} }
fn resolve_async<'a>( async fn resolve_async<'a>(
&'a self, &'a self,
info: &'a Self::TypeInfo, info: &'a <Self as crate::GraphQLType<S>>::TypeInfo,
selection_set: Option<&'a [Selection<S>]>, selection_set: Option<&'a [Selection<'a, S>]>,
executor: &'a Executor<Self::Context, S>, executor: &'a Executor<'a, <Self as crate::GraphQLType<S>>::Context, S>,
) -> crate::BoxFuture<'a, Value<S>> { ) -> Value<S> {
crate::GraphQLTypeAsync::resolve_async(&**self, info, selection_set, executor) crate::GraphQLTypeAsync::resolve_async(&**self, info, selection_set, executor).await
} }
} }

View file

@ -197,19 +197,19 @@ where
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[async_trait::async_trait]
impl<'e, S> crate::GraphQLTypeAsync<S> for &'e str impl<'e, S> crate::GraphQLTypeAsync<S> for &'e str
where where
S: ScalarValue + Send + Sync, S: ScalarValue + Send + Sync,
for<'b> &'b S: ScalarRefValue<'b>, for<'b> &'b S: ScalarRefValue<'b>,
{ {
fn resolve_async<'a>( async fn resolve_async<'a>(
&'a self, &'a self,
info: &'a Self::TypeInfo, info: &'a <Self as crate::GraphQLType<S>>::TypeInfo,
selection_set: Option<&'a [Selection<S>]>, selection_set: Option<&'a [Selection<'a, S>]>,
executor: &'a Executor<Self::Context, S>, executor: &'a Executor<'a, <Self as crate::GraphQLType<S>>::Context, S>,
) -> crate::BoxFuture<'a, crate::Value<S>> { ) -> crate::Value<S> {
use futures::future; self.resolve(info, selection_set, executor)
future::FutureExt::boxed(future::ready(self.resolve(info, selection_set, executor)))
} }
} }
@ -308,6 +308,9 @@ impl<T> EmptyMutation<T> {
} }
} }
// This is safe due to never using `T`.
unsafe impl<T> Send for EmptyMutation<T> {}
impl<S, T> GraphQLType<S> for EmptyMutation<T> impl<S, T> GraphQLType<S> for EmptyMutation<T>
where where
S: ScalarValue, S: ScalarValue,
@ -343,7 +346,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::ID; use super::{EmptyMutation, ID};
use crate::{ use crate::{
parser::ScalarToken, parser::ScalarToken,
value::{DefaultScalarValue, ParseScalarValue}, value::{DefaultScalarValue, ParseScalarValue},
@ -390,4 +393,10 @@ mod tests {
"unicode \u{1234}\u{5678}\u{90ab}\u{cdef}", "unicode \u{1234}\u{5678}\u{90ab}\u{cdef}",
); );
} }
#[test]
fn empty_mutation_is_send() {
fn check_if_send<T: Send>() {}
check_if_send::<EmptyMutation<()>>();
}
} }

View file

@ -260,8 +260,6 @@ pub enum DefaultScalarValue {
Boolean(bool), Boolean(bool),
} }
trait S: Send + Sync {}
impl ScalarValue for DefaultScalarValue { impl ScalarValue for DefaultScalarValue {
type Visitor = DefaultScalarValueVisitor; type Visitor = DefaultScalarValueVisitor;

View file

@ -1,6 +1,6 @@
[package] [package]
name = "juniper_codegen" name = "juniper_codegen"
version = "0.14.0" version = "0.14.1"
authors = [ authors = [
"Magnus Hallin <mhallin@fastmail.com>", "Magnus Hallin <mhallin@fastmail.com>",
"Christoph Herzog <chris@theduke.at>", "Christoph Herzog <chris@theduke.at>",
@ -24,7 +24,7 @@ quote = "1.0.2"
proc-macro-error = "0.3.4" proc-macro-error = "0.3.4"
[dev-dependencies] [dev-dependencies]
juniper = { version = "0.14.0", path = "../juniper" } juniper = { version = "0.14.1", path = "../juniper" }
[badges] [badges]
travis-ci = { repository = "graphql-rust/juniper" } travis-ci = { repository = "graphql-rust/juniper" }

View file

@ -213,12 +213,16 @@ pub fn impl_enum(ast: &syn::DeriveInput, is_internal: bool) -> TokenStream {
__S: #juniper_path::ScalarValue + Send + Sync, __S: #juniper_path::ScalarValue + Send + Sync,
for<'__b> &'__b __S: #juniper_path::ScalarRefValue<'__b> for<'__b> &'__b __S: #juniper_path::ScalarRefValue<'__b>
{ {
fn resolve_async<'a>( fn resolve_async<'a, 'async_trait>(
&'a self, &'a self,
info: &'a Self::TypeInfo, info: &'a Self::TypeInfo,
selection_set: Option<&'a [#juniper_path::Selection<__S>]>, selection_set: Option<&'a [#juniper_path::Selection<__S>]>,
executor: &'a #juniper_path::Executor<Self::Context, __S>, executor: &'a #juniper_path::Executor<Self::Context, __S>,
) -> futures::future::BoxFuture<'a, #juniper_path::Value<__S>> { ) -> futures::future::BoxFuture<'async_trait, #juniper_path::Value<__S>>
where
'a: 'async_trait,
Self: 'async_trait
{
use #juniper_path::GraphQLType; use #juniper_path::GraphQLType;
use futures::future; use futures::future;
let v = self.resolve(info, selection_set, executor); let v = self.resolve(info, selection_set, executor);

View file

@ -42,8 +42,7 @@ pub fn build_object(args: TokenStream, body: TokenStream, is_internal: bool) ->
} }
let name = let name = if let Some(name) = impl_attrs.name.as_ref(){
if let Some(name) = impl_attrs.name.as_ref(){
name.to_string() name.to_string()
} }
else { else {

View file

@ -1,7 +1,7 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro_error::MacroError; use proc_macro_error::MacroError;
use quote::{quote}; use quote::quote;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use crate::util; use crate::util;
@ -39,7 +39,7 @@ impl syn::parse::Parse for ResolveBody {
body.parse::<syn::token::Match>()?; body.parse::<syn::token::Match>()?;
body.parse::<syn::token::SelfValue>()?; body.parse::<syn::token::SelfValue>()?;
let match_body; let match_body;
syn::braced!( match_body in body ); syn::braced!( match_body in body );
let mut variants = Vec::new(); let mut variants = Vec::new();
@ -154,7 +154,7 @@ pub fn impl_union(
let context = attrs.context.map(|c| quote!{ #c } ).unwrap_or_else(|| quote!{ () }); let context = attrs.context.map(|c| quote!{ #c } ).unwrap_or_else(|| quote!{ () });
let output = quote! { let output = quote! {
impl #impl_generics #juniper::GraphQLType<#scalar> for #ty #where_clause impl #impl_generics #juniper::GraphQLType<#scalar> for #ty #where_clause
{ {
type Context = #context; type Context = #context;
type TypeInfo = (); type TypeInfo = ();

View file

@ -731,15 +731,31 @@ impl GraphQLTypeDefiniton {
} }
}); });
let scalar = self
.scalar
.as_ref()
.map(|s| quote!( #s ))
.unwrap_or_else(|| {
if self.generic_scalar {
// If generic_scalar is true, we always insert a generic scalar.
// See more comments below.
quote!(__S)
} else {
quote!(#juniper_crate_name::DefaultScalarValue)
}
});
let resolve_matches = self.fields.iter().map(|field| { let resolve_matches = self.fields.iter().map(|field| {
let name = &field.name; let name = &field.name;
let code = &field.resolver_code; let code = &field.resolver_code;
if field.is_async { if field.is_async {
// TODO: better error message with field/type name.
quote!( quote!(
#name => { #name => {
panic!("Tried to resolve async field with a sync resolver"); panic!("Tried to resolve async field {} on type {:?} with a sync resolver",
#name,
<Self as #juniper_crate_name::GraphQLType<#scalar>>::name(_info)
);
}, },
) )
} else { } else {
@ -780,20 +796,6 @@ impl GraphQLTypeDefiniton {
) )
}); });
let scalar = self
.scalar
.as_ref()
.map(|s| quote!( #s ))
.unwrap_or_else(|| {
if self.generic_scalar {
// If generic_scalar is true, we always insert a generic scalar.
// See more comments below.
quote!(__S)
} else {
quote!(#juniper_crate_name::DefaultScalarValue)
}
});
// Preserve the original type_generics before modification, // Preserve the original type_generics before modification,
// since alteration makes them invalid if self.generic_scalar // since alteration makes them invalid if self.generic_scalar
// is specified. // is specified.
@ -925,21 +927,27 @@ impl GraphQLTypeDefiniton {
impl#impl_generics #juniper_crate_name::GraphQLTypeAsync<#scalar> for #ty #type_generics_tokens impl#impl_generics #juniper_crate_name::GraphQLTypeAsync<#scalar> for #ty #type_generics_tokens
#where_async #where_async
{ {
fn resolve_field_async<'b>( fn resolve_field_async<'b, 'async_trait>(
&'b self, &'b self,
info: &'b Self::TypeInfo, info: &'b Self::TypeInfo,
field: &'b str, field: &'b str,
args: &'b #juniper_crate_name::Arguments<#scalar>, args: &'b #juniper_crate_name::Arguments<#scalar>,
executor: &'b #juniper_crate_name::Executor<Self::Context, #scalar>, executor: &'b #juniper_crate_name::Executor<Self::Context, #scalar>,
) -> futures::future::BoxFuture<'b, #juniper_crate_name::ExecutionResult<#scalar>> ) -> futures::future::BoxFuture<'async_trait, #juniper_crate_name::ExecutionResult<#scalar>>
where #scalar: Send + Sync, where
#scalar: Send + Sync,
'b: 'async_trait,
Self: 'async_trait,
{ {
use futures::future; use futures::future;
use #juniper_crate_name::GraphQLType; use #juniper_crate_name::GraphQLType;
match field { match field {
#( #resolve_matches_async )* #( #resolve_matches_async )*
_ => { _ => {
panic!("Field {} not found on type {}", field, "Mutation"); panic!("Field {} not found on type {:?}",
field,
<Self as #juniper_crate_name::GraphQLType<#scalar>>::name(info)
);
} }
} }
} }
@ -989,7 +997,10 @@ impl GraphQLTypeDefiniton {
match field { match field {
#( #resolve_matches )* #( #resolve_matches )*
_ => { _ => {
panic!("Field {} not found on type {}", field, "Mutation"); panic!("Field {} not found on type {:?}",
field,
<Self as #juniper_crate_name::GraphQLType<#scalar>>::name(_info)
);
} }
} }
} }

View file

@ -2,6 +2,10 @@
- Compatibility with the latest `juniper`. - Compatibility with the latest `juniper`.
# [[0.5.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper_hyper-0.5.1)
- Compatibility with the latest `juniper`.
# [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_hyper-0.5.0) # [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_hyper-0.5.0)
- Compatibility with the latest `juniper`. - Compatibility with the latest `juniper`.

View file

@ -1,6 +1,6 @@
[package] [package]
name = "juniper_hyper" name = "juniper_hyper"
version = "0.5.0" version = "0.5.1"
authors = ["Damir Vandic <info@dvic.io>"] authors = ["Damir Vandic <info@dvic.io>"]
description = "Juniper GraphQL integration with Hyper" description = "Juniper GraphQL integration with Hyper"
license = "BSD-2-Clause" license = "BSD-2-Clause"
@ -13,7 +13,7 @@ serde = "1.0"
serde_json = "1.0" serde_json = "1.0"
serde_derive = "1.0" serde_derive = "1.0"
url = "2" url = "2"
juniper = { version = "0.14.0", default-features = false, path = "../juniper"} juniper = { version = "0.14.1", default-features = false, path = "../juniper"}
futures = "0.1" futures = "0.1"
tokio = "0.1.8" tokio = "0.1.8"
@ -25,6 +25,6 @@ pretty_env_logger = "0.2"
reqwest = "0.9" reqwest = "0.9"
[dev-dependencies.juniper] [dev-dependencies.juniper]
version = "0.14.0" version = "0.14.1"
features = ["expose-test-schema", "serde_json"] features = ["expose-test-schema", "serde_json"]
path = "../juniper" path = "../juniper"

View file

@ -2,6 +2,10 @@
- Compatibility with the latest `juniper`. - Compatibility with the latest `juniper`.
# [[0.6.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper_iron-0.6.1)
- Compatibility with the latest `juniper`.
# [[0.6.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_iron-0.6.0) # [[0.6.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_iron-0.6.0)
- Compatibility with the latest `juniper`. - Compatibility with the latest `juniper`.

View file

@ -1,6 +1,6 @@
[package] [package]
name = "juniper_iron" name = "juniper_iron"
version = "0.6.0" version = "0.6.1"
authors = [ authors = [
"Magnus Hallin <mhallin@fastmail.com>", "Magnus Hallin <mhallin@fastmail.com>",
"Christoph Herzog <chris@theduke.at>", "Christoph Herzog <chris@theduke.at>",
@ -15,7 +15,7 @@ edition = "2018"
serde = { version = "1.0.2" } serde = { version = "1.0.2" }
serde_json = { version = "1.0.2" } serde_json = { version = "1.0.2" }
serde_derive = { version = "1.0.2" } serde_derive = { version = "1.0.2" }
juniper = { version = "0.14.0", path = "../juniper" } juniper = { version = "0.14.1", path = "../juniper" }
urlencoded = { version = ">= 0.5, < 0.7" } urlencoded = { version = ">= 0.5, < 0.7" }
iron = ">= 0.5, < 0.7" iron = ">= 0.5, < 0.7"
@ -29,6 +29,6 @@ url = "2"
percent-encoding = "2" percent-encoding = "2"
[dev-dependencies.juniper] [dev-dependencies.juniper]
version = "0.14.0" version = "0.14.1"
features = ["expose-test-schema", "serde_json"] features = ["expose-test-schema", "serde_json"]
path = "../juniper" path = "../juniper"

View file

@ -2,6 +2,10 @@
- Compatibility with the latest `juniper`. - Compatibility with the latest `juniper`.
# [[0.5.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper_rocket-0.5.1)
- Compatibility with the latest `juniper`.
# [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_rocket-0.5.0) # [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_rocket-0.5.0)
- Compatibility with the latest `juniper`. - Compatibility with the latest `juniper`.

View file

@ -1,6 +1,6 @@
[package] [package]
name = "juniper_rocket" name = "juniper_rocket"
version = "0.5.0" version = "0.5.1"
authors = [ authors = [
"Magnus Hallin <mhallin@fastmail.com>", "Magnus Hallin <mhallin@fastmail.com>",
"Christoph Herzog <chris@theduke.at>", "Christoph Herzog <chris@theduke.at>",
@ -18,13 +18,13 @@ async = [ "juniper/async" ]
serde = { version = "1.0.2" } serde = { version = "1.0.2" }
serde_json = { version = "1.0.2" } serde_json = { version = "1.0.2" }
serde_derive = { version = "1.0.2" } serde_derive = { version = "1.0.2" }
juniper = { version = "0.14.0", default-features = false, path = "../juniper"} juniper = { version = "0.14.1", default-features = false, path = "../juniper"}
futures03 = { version = "=0.3.0-alpha.19", package = "futures-preview", features = ["compat"] } futures03 = { version = "=0.3.0-alpha.19", package = "futures-preview", features = ["compat"] }
rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "async" } rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "async" }
tokio = "=0.2.0-alpha.6" tokio = "=0.2.0-alpha.6"
[dev-dependencies.juniper] [dev-dependencies.juniper]
version = "0.14.0" version = "0.14.1"
features = ["expose-test-schema", "serde_json"] features = ["expose-test-schema", "serde_json"]
path = "../juniper" path = "../juniper"

View file

@ -2,6 +2,10 @@
- Compatibility with the latest `juniper`. - Compatibility with the latest `juniper`.
# [[0.5.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper_warp-0.5.1)
- Compatibility with the latest `juniper`.
# [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_warp-0.5.0) # [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_warp-0.5.0)
- Compatibility with the latest `juniper`. - Compatibility with the latest `juniper`.

View file

@ -1,6 +1,6 @@
[package] [package]
name = "juniper_warp" name = "juniper_warp"
version = "0.5.0" version = "0.5.1"
authors = ["Tom Houlé <tom@tomhoule.com>"] authors = ["Tom Houlé <tom@tomhoule.com>"]
description = "Juniper GraphQL integration with Warp" description = "Juniper GraphQL integration with Warp"
license = "BSD-2-Clause" license = "BSD-2-Clause"
@ -13,7 +13,7 @@ async = [ "juniper/async", "futures03" ]
[dependencies] [dependencies]
warp = "0.1.8" warp = "0.1.8"
juniper = { version = "0.14.0", path = "../juniper", default-features = false } juniper = { version = "0.14.1", path = "../juniper", default-features = false }
serde_json = "1.0.24" serde_json = "1.0.24"
serde_derive = "1.0.75" serde_derive = "1.0.75"
failure = "0.1.2" failure = "0.1.2"
@ -24,7 +24,7 @@ tokio-threadpool = "0.1.7"
futures03 = { version = "=0.3.0-alpha.19", optional = true, package = "futures-preview", features = ["compat"] } futures03 = { version = "=0.3.0-alpha.19", optional = true, package = "futures-preview", features = ["compat"] }
[dev-dependencies] [dev-dependencies]
juniper = { version = "0.14.0", path = "../juniper", features = ["expose-test-schema", "serde_json"] } juniper = { version = "0.14.1", path = "../juniper", features = ["expose-test-schema", "serde_json"] }
env_logger = "0.5.11" env_logger = "0.5.11"
log = "0.4.3" log = "0.4.3"
percent-encoding = "1.0" percent-encoding = "1.0"