From c675b929d30ecbc4c46f92eef483e6089425dc6b Mon Sep 17 00:00:00 2001 From: D1plo1d Date: Sun, 30 Aug 2020 16:32:14 -0400 Subject: [PATCH 01/13] Added Default to tracing extension --- src/extensions/tracing.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extensions/tracing.rs b/src/extensions/tracing.rs index 9bf59059..7b9bf366 100644 --- a/src/extensions/tracing.rs +++ b/src/extensions/tracing.rs @@ -8,6 +8,7 @@ use tracing::{span, Id, Level}; /// # References /// /// https://crates.io/crates/tracing +#[derive(Default)] pub struct Tracing { root_id: Option, fields: BTreeMap, From 5a6c77ba9db07e4aaab8070a5f1d7173abbebadf Mon Sep 17 00:00:00 2001 From: D1plo1d Date: Sun, 30 Aug 2020 21:01:53 -0400 Subject: [PATCH 02/13] Refactored tracing extension to allow better span and event filtering and improve signal to noise --- src/extensions/tracing.rs | 51 +++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/extensions/tracing.rs b/src/extensions/tracing.rs index 7b9bf366..74e06748 100644 --- a/src/extensions/tracing.rs +++ b/src/extensions/tracing.rs @@ -1,7 +1,8 @@ use crate::extensions::{Extension, ResolveInfo}; -use crate::{QueryPathSegment, Variables}; +use crate::{Variables}; use std::collections::BTreeMap; -use tracing::{span, Id, Level}; +use tracing::{span, event, Id, Level}; +use uuid::Uuid; /// Tracing extension /// @@ -15,12 +16,26 @@ pub struct Tracing { } impl Extension for Tracing { - fn parse_start(&mut self, query_source: &str, _variables: &Variables) { - let root_span = span!(target: "async-graphql", parent:None, Level::INFO, "query", source = query_source); + fn parse_start(&mut self, query_source: &str, variables: &Variables) { + let root_span: tracing::Span = span!( + target: "async_graphql", + parent:None, + Level::INFO, + "graphql", + id = %Uuid::new_v4().to_string(), + ); + if let Some(id) = root_span.id() { tracing::dispatcher::get_default(|d| d.enter(&id)); self.root_id.replace(id); } + + event!( + target: "async_graphql::query", + Level::DEBUG, + %variables, + query = %query_source + ); } fn execution_end(&mut self) { @@ -34,27 +49,15 @@ impl Extension for Tracing { .resolve_id .parent .and_then(|id| self.fields.get(&id)) + .or_else(|| self.root_id.as_ref()) .cloned(); - let span = match &info.path_node.segment { - QueryPathSegment::Index(idx) => span!( - target: "async-graphql", - parent: parent_span, - Level::INFO, - "field", - index = *idx, - parent_type = info.parent_type, - return_type = info.return_type - ), - QueryPathSegment::Name(name) => span!( - target: "async-graphql", - parent: parent_span, - Level::INFO, - "field", - name = name, - parent_type = info.parent_type, - return_type = info.return_type - ), - }; + let span = span!( + target: "async_graphql::field", + parent: parent_span, + Level::INFO, + "field", + path = %info.path_node, + ); if let Some(id) = span.id() { tracing::dispatcher::get_default(|d| d.enter(&id)); self.fields.insert(info.resolve_id.current, id); From 7f7397650a9f6f05472b083b8d4c47c06832d8f3 Mon Sep 17 00:00:00 2001 From: D1plo1d Date: Sun, 30 Aug 2020 21:11:43 -0400 Subject: [PATCH 03/13] Added async_graphql::graphql tracing namespace --- src/extensions/tracing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/tracing.rs b/src/extensions/tracing.rs index 74e06748..fbf61be3 100644 --- a/src/extensions/tracing.rs +++ b/src/extensions/tracing.rs @@ -18,7 +18,7 @@ pub struct Tracing { impl Extension for Tracing { fn parse_start(&mut self, query_source: &str, variables: &Variables) { let root_span: tracing::Span = span!( - target: "async_graphql", + target: "async_graphql::graphql", parent:None, Level::INFO, "graphql", From 78d3885a67c91ae2c582590fb4b881b38fcb2281 Mon Sep 17 00:00:00 2001 From: Sunli Date: Mon, 31 Aug 2020 15:35:53 +0800 Subject: [PATCH 04/13] Cargo fmt --all --- src/extensions/tracing.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extensions/tracing.rs b/src/extensions/tracing.rs index fbf61be3..cd486ffa 100644 --- a/src/extensions/tracing.rs +++ b/src/extensions/tracing.rs @@ -1,7 +1,7 @@ use crate::extensions::{Extension, ResolveInfo}; -use crate::{Variables}; +use crate::Variables; use std::collections::BTreeMap; -use tracing::{span, event, Id, Level}; +use tracing::{event, span, Id, Level}; use uuid::Uuid; /// Tracing extension From 0c4f8e5c16406ef5a978acd6145b800c1a93a696 Mon Sep 17 00:00:00 2001 From: Sunli Date: Mon, 31 Aug 2020 15:40:44 +0800 Subject: [PATCH 05/13] Clippy clean --- src/extensions/tracing.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extensions/tracing.rs b/src/extensions/tracing.rs index cd486ffa..5e438319 100644 --- a/src/extensions/tracing.rs +++ b/src/extensions/tracing.rs @@ -16,6 +16,7 @@ pub struct Tracing { } impl Extension for Tracing { + #[allow(clippy::deref_addrof)] fn parse_start(&mut self, query_source: &str, variables: &Variables) { let root_span: tracing::Span = span!( target: "async_graphql::graphql", From 1856b57f7b2a53c2f219534f78188cd88587c452 Mon Sep 17 00:00:00 2001 From: Sunli Date: Mon, 31 Aug 2020 15:41:19 +0800 Subject: [PATCH 06/13] Release 1.17.13 async-graphql@1.17.13 Generated by cargo-workspaces --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 312899fa..5b87c2fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-graphql" -version = "1.17.12" +version = "1.17.13" authors = ["sunli "] edition = "2018" description = "A GraphQL server library implemented in Rust" From 40d19f2f580d4a924e6794480da0e3764c97feda Mon Sep 17 00:00:00 2001 From: Sunli Date: Mon, 31 Aug 2020 16:12:12 +0800 Subject: [PATCH 07/13] Add DateTime scalar. Fixed compile problem with `v1.48-nightly`. --- src/scalars/datetime.rs | 25 ++- src/scalars/floats.rs | 73 +++++---- src/scalars/integers.rs | 333 +++++++++++++++++++++++++++++++--------- src/scalars/mod.rs | 15 +- 4 files changed, 336 insertions(+), 110 deletions(-) diff --git a/src/scalars/datetime.rs b/src/scalars/datetime.rs index 31de70f7..855970c8 100644 --- a/src/scalars/datetime.rs +++ b/src/scalars/datetime.rs @@ -1,15 +1,32 @@ use crate::{InputValueError, InputValueResult, ScalarType, Value}; use async_graphql_derive::Scalar; -use chrono::{DateTime, FixedOffset, Utc}; +use chrono::{DateTime, FixedOffset, Local, Utc}; /// Implement the DateTime scalar /// /// The input/output is a string in RFC3339 format. -#[Scalar(internal, name = "DateTimeFixedOffset")] +#[Scalar(internal, name = "DateTime")] impl ScalarType for DateTime { fn parse(value: Value) -> InputValueResult { match &value { - Value::String(s) => Ok(DateTime::parse_from_rfc3339(s)?), + Value::String(s) => Ok(s.parse::>()?), + _ => Err(InputValueError::ExpectedType(value)), + } + } + + fn to_value(&self) -> Value { + Value::String(self.to_rfc3339()) + } +} + +/// Implement the DateTime scalar +/// +/// The input/output is a string in RFC3339 format. +#[Scalar(internal, name = "DateTime")] +impl ScalarType for DateTime { + fn parse(value: Value) -> InputValueResult { + match &value { + Value::String(s) => Ok(s.parse::>()?), _ => Err(InputValueError::ExpectedType(value)), } } @@ -22,7 +39,7 @@ impl ScalarType for DateTime { /// Implement the DateTime scalar /// /// The input/output is a string in RFC3339 format. -#[Scalar(internal, name = "DateTimeUtc")] +#[Scalar(internal, name = "DateTime")] impl ScalarType for DateTime { fn parse(value: Value) -> InputValueResult { match &value { diff --git a/src/scalars/floats.rs b/src/scalars/floats.rs index 04adacc1..1aae5207 100644 --- a/src/scalars/floats.rs +++ b/src/scalars/floats.rs @@ -1,35 +1,52 @@ use crate::{InputValueError, InputValueResult, ScalarType, Value}; use async_graphql_derive::Scalar; -macro_rules! float_scalar { - ($($ty:ty),*) => { - $( - /// The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point). - #[Scalar(internal, name = "Float")] - impl ScalarType for $ty { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Number(n) => Ok(n - .as_f64() - .ok_or_else(|| InputValueError::from("Invalid number"))? - as Self), - _ => Err(InputValueError::ExpectedType(value)), - } - } - - fn is_valid(value: &Value) -> bool { - match value { - Value::Number(_) => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::Number(serde_json::Number::from_f64(*self as f64).unwrap()) - } +/// The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point). +#[Scalar(internal, name = "Float")] +impl ScalarType for f32 { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => Ok(n + .as_f64() + .ok_or_else(|| InputValueError::from("Invalid number"))? + as Self), + _ => Err(InputValueError::ExpectedType(value)), } - )* + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(_) => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from_f64(*self as f64).unwrap()) } } -float_scalar!(f32, f64); +/// The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point). +#[Scalar(internal, name = "Float")] +impl ScalarType for f64 { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => Ok(n + .as_f64() + .ok_or_else(|| InputValueError::from("Invalid number"))? + as Self), + _ => Err(InputValueError::ExpectedType(value)), + } + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(_) => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from_f64(*self as f64).unwrap()) + } +} diff --git a/src/scalars/integers.rs b/src/scalars/integers.rs index 13d2dc61..e7a32c93 100644 --- a/src/scalars/integers.rs +++ b/src/scalars/integers.rs @@ -1,85 +1,274 @@ use crate::{InputValueError, InputValueResult, ScalarType, Value}; use async_graphql_derive::Scalar; -macro_rules! int_scalar { - ($($ty:ty),*) => { - $( - /// The `Int` scalar type represents non-fractional whole numeric values. - #[Scalar(internal, name = "Int")] - impl ScalarType for $ty { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Number(n) => { - let n = n - .as_i64() - .ok_or_else(|| InputValueError::from("Invalid number"))?; - if n < Self::MIN as i64 || n > Self::MAX as i64 { - return Err(InputValueError::from(format!( - "Only integers from {} to {} are accepted.", - Self::MIN, - Self::MAX - ))); - } - Ok(n as Self) - } - _ => Err(InputValueError::ExpectedType(value)), +/// The `Int` scalar type represents non-fractional whole numeric values. +#[Scalar(internal, name = "Int")] +impl ScalarType for i8 { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => { + let n = n + .as_i64() + .ok_or_else(|| InputValueError::from("Invalid number"))?; + if n < Self::MIN as i64 || n > Self::MAX as i64 { + return Err(InputValueError::from(format!( + "Only integers from {} to {} are accepted.", + Self::MIN, + Self::MAX + ))); } + Ok(n as Self) } - - fn is_valid(value: &Value) -> bool { - match value { - Value::Number(n) if n.is_i64() => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::Number(serde_json::Number::from(*self as i64)) - } + _ => Err(InputValueError::ExpectedType(value)), } - )* - }; + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(n) if n.is_i64() => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from(*self as i64)) + } } -macro_rules! uint_scalar { - ($($ty:ty),*) => { - $( - /// The `Int` scalar type represents non-fractional whole numeric values. - #[Scalar(internal, name = "Int")] - impl ScalarType for $ty { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Number(n) => { - let n = n - .as_u64() - .ok_or_else(|| InputValueError::from("Invalid number"))?; - if n > Self::MAX as u64 { - return Err(InputValueError::from(format!( - "Only integers from {} to {} are accepted.", - 0, - Self::MAX - ))); - } - Ok(n as Self) - } - _ => Err(InputValueError::ExpectedType(value)), +/// The `Int` scalar type represents non-fractional whole numeric values. +#[Scalar(internal, name = "Int")] +impl ScalarType for i16 { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => { + let n = n + .as_i64() + .ok_or_else(|| InputValueError::from("Invalid number"))?; + if n < Self::MIN as i64 || n > Self::MAX as i64 { + return Err(InputValueError::from(format!( + "Only integers from {} to {} are accepted.", + Self::MIN, + Self::MAX + ))); } + Ok(n as Self) } - - fn is_valid(value: &Value) -> bool { - match value { - Value::Number(n) if n.is_u64() => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::Number(serde_json::Number::from(*self as u64)) - } + _ => Err(InputValueError::ExpectedType(value)), } - )* - }; + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(n) if n.is_i64() => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from(*self as i64)) + } } -int_scalar!(i8, i16, i32, i64); -uint_scalar!(u8, u16, u32, u64); +/// The `Int` scalar type represents non-fractional whole numeric values. +#[Scalar(internal, name = "Int")] +impl ScalarType for i32 { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => { + let n = n + .as_i64() + .ok_or_else(|| InputValueError::from("Invalid number"))?; + if n < Self::MIN as i64 || n > Self::MAX as i64 { + return Err(InputValueError::from(format!( + "Only integers from {} to {} are accepted.", + Self::MIN, + Self::MAX + ))); + } + Ok(n as Self) + } + _ => Err(InputValueError::ExpectedType(value)), + } + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(n) if n.is_i64() => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from(*self as i64)) + } +} + +/// The `Int` scalar type represents non-fractional whole numeric values. +#[Scalar(internal, name = "Int")] +impl ScalarType for i64 { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => { + let n = n + .as_i64() + .ok_or_else(|| InputValueError::from("Invalid number"))?; + if n < Self::MIN as i64 || n > Self::MAX as i64 { + return Err(InputValueError::from(format!( + "Only integers from {} to {} are accepted.", + Self::MIN, + Self::MAX + ))); + } + Ok(n as Self) + } + _ => Err(InputValueError::ExpectedType(value)), + } + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(n) if n.is_i64() => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from(*self as i64)) + } +} + +/// The `Int` scalar type represents non-fractional whole numeric values. +#[Scalar(internal, name = "Int")] +impl ScalarType for u8 { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => { + let n = n + .as_u64() + .ok_or_else(|| InputValueError::from("Invalid number"))?; + if n > Self::MAX as u64 { + return Err(InputValueError::from(format!( + "Only integers from {} to {} are accepted.", + 0, + Self::MAX + ))); + } + Ok(n as Self) + } + _ => Err(InputValueError::ExpectedType(value)), + } + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(n) if n.is_u64() => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from(*self as u64)) + } +} + +/// The `Int` scalar type represents non-fractional whole numeric values. +#[Scalar(internal, name = "Int")] +impl ScalarType for u16 { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => { + let n = n + .as_u64() + .ok_or_else(|| InputValueError::from("Invalid number"))?; + if n > Self::MAX as u64 { + return Err(InputValueError::from(format!( + "Only integers from {} to {} are accepted.", + 0, + Self::MAX + ))); + } + Ok(n as Self) + } + _ => Err(InputValueError::ExpectedType(value)), + } + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(n) if n.is_u64() => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from(*self as u64)) + } +} + +/// The `Int` scalar type represents non-fractional whole numeric values. +#[Scalar(internal, name = "Int")] +impl ScalarType for u32 { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => { + let n = n + .as_u64() + .ok_or_else(|| InputValueError::from("Invalid number"))?; + if n > Self::MAX as u64 { + return Err(InputValueError::from(format!( + "Only integers from {} to {} are accepted.", + 0, + Self::MAX + ))); + } + Ok(n as Self) + } + _ => Err(InputValueError::ExpectedType(value)), + } + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(n) if n.is_u64() => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from(*self as u64)) + } +} + +/// The `Int` scalar type represents non-fractional whole numeric values. +#[Scalar(internal, name = "Int")] +impl ScalarType for u64 { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => { + let n = n + .as_u64() + .ok_or_else(|| InputValueError::from("Invalid number"))?; + if n > Self::MAX as u64 { + return Err(InputValueError::from(format!( + "Only integers from {} to {} are accepted.", + 0, + Self::MAX + ))); + } + Ok(n as Self) + } + _ => Err(InputValueError::ExpectedType(value)), + } + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(n) if n.is_u64() => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from(*self as u64)) + } +} diff --git a/src/scalars/mod.rs b/src/scalars/mod.rs index 2cf2e585..d6081810 100644 --- a/src/scalars/mod.rs +++ b/src/scalars/mod.rs @@ -25,7 +25,7 @@ mod tests { use super::ID; use crate::Type; use bson::oid::ObjectId; - use chrono::{DateTime, FixedOffset, NaiveDate, NaiveTime, Utc}; + use chrono::{DateTime, FixedOffset, Local, NaiveDate, NaiveTime, Utc}; use uuid::Uuid; #[test] @@ -54,19 +54,22 @@ mod tests { assert_eq!(::type_name(), "NaiveTime"); assert_eq!(::qualified_type_name(), "NaiveTime!"); - assert_eq!( as Type>::type_name(), "DateTimeUtc"); + assert_eq!( as Type>::type_name(), "DateTime"); assert_eq!( as Type>::qualified_type_name(), - "DateTimeUtc!" + "DateTime!" ); + assert_eq!( as Type>::type_name(), "DateTime"); assert_eq!( - as Type>::type_name(), - "DateTimeFixedOffset" + as Type>::qualified_type_name(), + "DateTime!" ); + + assert_eq!( as Type>::type_name(), "DateTime"); assert_eq!( as Type>::qualified_type_name(), - "DateTimeFixedOffset!" + "DateTime!" ); assert_eq!(::type_name(), "UUID"); From e8383550ba3af191940349973b5428be222bfbf7 Mon Sep 17 00:00:00 2001 From: Sunli Date: Mon, 31 Aug 2020 16:13:40 +0800 Subject: [PATCH 08/13] Release 1.17.14 async-graphql@1.17.14 Generated by cargo-workspaces --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5b87c2fb..5580cae3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-graphql" -version = "1.17.13" +version = "1.17.14" authors = ["sunli "] edition = "2018" description = "A GraphQL server library implemented in Rust" From 02f7a5fbbc7532731c257b9df1fc12acfbcc8ce5 Mon Sep 17 00:00:00 2001 From: Koxiaet <38139193+Koxiaet@users.noreply.github.com> Date: Mon, 31 Aug 2020 19:18:02 +0100 Subject: [PATCH 09/13] Remove dependency on serde_derive, satisfy Clippy, improve docs --- Cargo.toml | 2 - src/context.rs | 2 +- src/error.rs | 89 +++++++++------ src/extensions/logger.rs | 1 + src/guard.rs | 18 +-- src/http/mod.rs | 4 +- src/http/playground_source.rs | 1 + src/lib.rs | 105 ++++++++++++++---- src/scalars/json.rs | 3 +- src/subscription/ws_transport.rs | 1 + .../rules/default_values_of_correct_type.rs | 2 +- .../rules/fields_on_correct_type.rs | 2 +- .../rules/fragments_on_composite_types.rs | 2 +- src/validation/test_harness.rs | 2 +- tests/connection.rs | 2 +- tests/enum.rs | 7 +- tests/input_object.rs | 7 +- tests/input_validators.rs | 23 ++-- tests/introspection.rs | 28 ++--- tests/optional.rs | 11 +- tests/subscription_websocket.rs | 2 +- 21 files changed, 200 insertions(+), 114 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5580cae3..a7792d91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ anyhow = "1.0.26" thiserror = "1.0.11" async-trait = "0.1.30" serde = "1.0.104" -serde_derive = "1.0.104" serde_json = "1.0.48" bytes = "0.5.4" Inflector = "0.11.4" @@ -55,7 +54,6 @@ chrono-tz = { version = "0.5.1", optional = true } [dev-dependencies] async-std = { version = "1.5.0", features = ["attributes"] } serde = "1.0.104" -serde_derive = "1.0.104" [workspace] members = [ diff --git a/src/context.rs b/src/context.rs index f7151b9b..fcce3e7f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -8,7 +8,7 @@ use async_graphql_parser::query::Document; use async_graphql_parser::UploadValue; use fnv::FnvHashMap; use serde::ser::SerializeSeq; -use serde::Serializer; +use serde::{Serialize, Serializer}; use std::any::{Any, TypeId}; use std::collections::BTreeMap; use std::fmt::{Display, Formatter}; diff --git a/src/error.rs b/src/error.rs index ff49c9c1..3f286728 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,13 +1,14 @@ use crate::{Pos, QueryPathNode, Value}; use std::fmt::{Debug, Display}; +use thiserror::Error; -/// Input Value Error +/// An error in the format of an input value. #[derive(Debug)] pub enum InputValueError { /// Custom input value parsing error. Custom(String), - /// The type of input value does not match the expectation. + /// The type of input value does not match the expectation. Contains the value that was found. ExpectedType(Value), } @@ -18,7 +19,7 @@ impl From for InputValueError { } impl InputValueError { - #[allow(missing_docs)] + /// Convert this error to a regular `Error` type. pub fn into_error(self, pos: Pos, expected_type: String) -> Error { match self { InputValueError::Custom(reason) => Error::Query { @@ -38,10 +39,10 @@ impl InputValueError { } } -/// InputValueResult type +/// An alias for `Result`. pub type InputValueResult = std::result::Result; -/// FieldError type +/// An error in a field resolver. #[derive(Clone, Debug)] pub struct FieldError(pub String, pub Option); @@ -71,23 +72,17 @@ impl FieldError { } } -/// FieldResult type +/// An alias for `Result`. pub type FieldResult = std::result::Result; -impl From for FieldError -where - E: std::fmt::Display + Send + Sync + 'static, -{ +impl From for FieldError { fn from(err: E) -> Self { FieldError(format!("{}", err), None) } } #[allow(missing_docs)] -pub trait ErrorExtensions -where - Self: Sized, -{ +pub trait ErrorExtensions: Sized { fn extend(&self) -> FieldError; fn extend_with(self, cb: C) -> FieldError where @@ -125,15 +120,14 @@ impl ErrorExtensions for &E { } } -#[allow(missing_docs)] -pub trait ResultExt -where - Self: Sized, -{ - fn extend_err(self, cb: CB) -> FieldResult +/// Extend a `Result`'s error value with [`ErrorExtensions`](trait.ErrorExtensions.html). +pub trait ResultExt: Sized { + /// Extend the error value of the result with the callback. + fn extend_err(self, cb: C) -> FieldResult where - CB: FnOnce(&E) -> serde_json::Value; + C: FnOnce(&E) -> serde_json::Value; + /// Extend the result to a `FieldResult`. fn extend(self) -> FieldResult; } @@ -161,25 +155,28 @@ where } } -/// Error for query +/// An error processing a GraphQL query. #[derive(Debug, Error, PartialEq)] -#[allow(missing_docs)] pub enum QueryError { + /// The feature is not supported. #[error("Not supported.")] NotSupported, + /// The actual input type did not match the expected input type. #[error("Expected input type \"{expect}\", found {actual}.")] ExpectedInputType { - /// Expect input type + /// The expected input type. expect: String, - /// Actual input type + /// The actual input type. actual: Value, }, + /// Parsing of an input value failed. #[error("Failed to parse input value: {reason}")] ParseInputValue { reason: String }, + /// A field was not found on an object type. #[error("Cannot query field \"{field_name}\" on type \"{object}\".")] FieldNotFound { /// Field name @@ -189,27 +186,33 @@ pub enum QueryError { object: String, }, + /// An operation was missing from the query. #[error("Missing operation")] MissingOperation, + /// The operation name was unknown. #[error("Unknown operation named \"{name}\"")] UnknownOperationNamed { - /// Operation name for query + /// Operation name for query. name: String, }, + /// The user attempted to query an object without selecting any subfields. #[error("Type \"{object}\" must have a selection of subfields.")] MustHaveSubFields { /// Object name object: String, }, + /// The schema does not have mutations. #[error("Schema is not configured for mutations.")] NotConfiguredMutations, + /// The schema does not have subscriptions. #[error("Schema is not configured for subscriptions.")] NotConfiguredSubscriptions, + /// The value does not exist in the enum. #[error("Invalid value for enum \"{ty}\".")] InvalidEnumValue { /// Enum type name @@ -219,21 +222,24 @@ pub enum QueryError { value: String, }, + /// A required field in an input object was not present. #[error("Required field \"{field_name}\" for InputObject \"{object}\" does not exist.")] RequiredField { - /// field name + /// Field name field_name: String, - /// object name + /// Object name object: &'static str, }, + /// A variable is used but not defined. #[error("Variable \"${var_name}\" is not defined")] VarNotDefined { /// Variable name var_name: String, }, + /// A directive was required but not provided. #[error( "Directive \"{directive}\" argument \"{arg_name}\" of type \"{arg_type}\" is required, but it was not provided." )] @@ -248,27 +254,36 @@ pub enum QueryError { arg_type: &'static str, }, + /// An unknown directive name was encountered. #[error("Unknown directive \"{name}\".")] UnknownDirective { /// Directive name name: String, }, + /// An unknown fragment was encountered. #[error("Unknown fragment \"{name}\".")] UnknownFragment { - // Fragment name + /// Fragment name name: String, }, + /// The query was too complex. + // TODO: Expand on this #[error("Too complex")] TooComplex, + /// The query was nested too deep. #[error("Too deep")] TooDeep, + /// A field handler errored. #[error("Failed to resolve field: {err}")] FieldError { + /// The error description. err: String, + /// Extensions to the error provided through the [`ErrorExtensions`](trait.ErrorExtensions) + /// or [`ResultExt`](trait.ResultExt) traits. extended_error: Option, }, @@ -280,7 +295,7 @@ pub enum QueryError { } impl QueryError { - #[doc(hidden)] + /// Convert this error to a regular `Error` type. pub fn into_error(self, pos: Pos) -> Error { Error::Query { pos, @@ -290,18 +305,22 @@ impl QueryError { } } -#[allow(missing_docs)] +/// An error parsing the request. #[derive(Debug, Error)] pub enum ParseRequestError { + /// An IO error occurred. #[error("{0}")] Io(#[from] std::io::Error), + /// The request's syntax was invalid. #[error("Invalid request: {0}")] InvalidRequest(serde_json::Error), + /// The request's files map was invalid. #[error("Invalid files map: {0}")] InvalidFilesMap(serde_json::Error), + /// The request's multipart data was invalid. #[error("Invalid multipart data")] InvalidMultipart(multer::Error), @@ -314,9 +333,11 @@ pub enum ParseRequestError { #[error("It's not an upload operation")] NotUpload, + /// Files were missing the request. #[error("Missing files")] MissingFiles, + /// The request's payload is too large, and this server rejected it. #[error("Payload too large")] PayloadTooLarge, } @@ -328,16 +349,20 @@ pub struct RuleError { pub message: String, } -#[allow(missing_docs)] +/// An error serving a GraphQL query. #[derive(Debug, Error, PartialEq)] pub enum Error { + /// Parsing the query failed. #[error("Parse error: {0}")] Parse(#[from] crate::parser::Error), + /// Processing the query failed. #[error("Query error: {err}")] Query { + /// The position at which the processing failed. pos: Pos, path: Option, + /// The query error. err: QueryError, }, diff --git a/src/extensions/logger.rs b/src/extensions/logger.rs index dcc33cd4..edb2f2b8 100644 --- a/src/extensions/logger.rs +++ b/src/extensions/logger.rs @@ -2,6 +2,7 @@ use crate::extensions::{Extension, ResolveInfo}; use crate::{Error, Variables}; use async_graphql_parser::query::{Definition, Document, OperationDefinition, Selection}; use itertools::Itertools; +use log::{error, info, trace}; use std::borrow::Cow; use uuid::Uuid; diff --git a/src/guard.rs b/src/guard.rs index b53eedb5..04d052c9 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -5,14 +5,16 @@ use serde::export::PhantomData; /// Field guard /// -/// Guard is a pre-condition for a field that is resolved if `Ok(()` is returned, otherwise an error is returned. +/// Guard is a pre-condition for a field that is resolved if `Ok(())` is returned, otherwise an error is returned. +/// +/// This trait is defined through the [`async-trait`](https://crates.io/crates/async-trait) macro. #[async_trait::async_trait] pub trait Guard { - #[allow(missing_docs)] + /// Check whether the guard will allow access to the field. async fn check(&self, ctx: &Context<'_>) -> FieldResult<()>; } -/// An extension trait for `Guard` +/// An extension trait for `Guard`. pub trait GuardExt: Guard + Sized { /// Merge the two guards. fn and(self, other: R) -> And { @@ -22,7 +24,7 @@ pub trait GuardExt: Guard + Sized { impl GuardExt for T {} -/// Guard for `GuardExt::and` +/// Guard for [`GuardExt::and`](trait.GuardExt.html#method.and). pub struct And(A, B); #[async_trait::async_trait] @@ -35,10 +37,12 @@ impl Guard for And { /// Field post guard /// -/// Guard is a post-condition for a field that is resolved if `Ok(()` is returned, otherwise an error is returned. +/// This is a post-condition for a field that is resolved if `Ok(()` is returned, otherwise an error is returned. +/// +/// This trait is defined through the [`async-trait`](https://crates.io/crates/async-trait) macro. #[async_trait::async_trait] pub trait PostGuard { - #[allow(missing_docs)] + /// Check whether to allow the result of the field through. async fn check(&self, ctx: &Context<'_>, result: &T) -> FieldResult<()>; } @@ -52,7 +56,7 @@ pub trait PostGuardExt: PostGuard + Sized { impl, R: Send + Sync> PostGuardExt for T {} -/// PostGuard for `PostGuardExt::and` +/// PostGuard for [`PostGuardExt::and`](trait.PostGuardExt.html#method.and). pub struct PostAnd, B: PostGuard>(A, B, PhantomData); #[async_trait::async_trait] diff --git a/src/http/mod.rs b/src/http/mod.rs index c3bce133..6e521e23 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -18,7 +18,7 @@ use crate::{ Error, ParseRequestError, Pos, QueryBuilder, QueryError, QueryResponse, Result, Variables, }; use serde::ser::{SerializeMap, SerializeSeq}; -use serde::{Serialize, Serializer}; +use serde::{Deserialize, Serialize, Serializer}; /// Deserializable GraphQL Request object #[derive(Deserialize, Clone, PartialEq, Debug)] @@ -241,7 +241,7 @@ mod tests { }, }; - let resp = GQLResponse(Err(err.into())); + let resp = GQLResponse(Err(err)); assert_eq!( serde_json::to_value(resp).unwrap(), diff --git a/src/http/playground_source.rs b/src/http/playground_source.rs index e6eb052a..79f46396 100644 --- a/src/http/playground_source.rs +++ b/src/http/playground_source.rs @@ -1,3 +1,4 @@ +use serde::Serialize; use std::collections::HashMap; /// Generate the page for GraphQL Playground diff --git a/src/lib.rs b/src/lib.rs index 555d6e76..8957fa7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,7 +40,7 @@ //! * Custom scalars //! * Minimal overhead //! * Easy integration (hyper, actix_web, tide ...) -//! * Upload files (Multipart request) +//! * File upload (Multipart request) //! * Subscriptions (WebSocket transport) //! * Custom extensions //! * Apollo Tracing extension @@ -89,7 +89,7 @@ //! cargo bench //! ``` //! -//! Now HTML report is available at `benchmark/target/criterion/report` +//! Now a HTML report is available at `benchmark/target/criterion/report`. //! #![warn(missing_docs)] @@ -98,13 +98,6 @@ #![recursion_limit = "256"] #![forbid(unsafe_code)] -#[macro_use] -extern crate thiserror; -#[macro_use] -extern crate serde_derive; -#[macro_use] -extern crate log; - mod base; mod context; mod error; @@ -182,7 +175,11 @@ pub use subscription::SubscriptionType; #[doc(hidden)] pub use types::{EnumItem, EnumType}; -/// Define a GraphQL object +/// Define a GraphQL object with methods +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_complex_object.html).* +/// +/// All methods are converted to camelCase. /// /// # Macro parameters /// @@ -218,16 +215,17 @@ pub use types::{EnumItem, EnumType}; /// | default_with | Expression to generate default value | code string | Y | /// | validator | Input value validator | [`InputValueValidator`](validators/trait.InputValueValidator.html) | Y | /// -/// # The field returns the value type +/// # Valid field return types /// -/// - A scalar value, such as `i32`, `bool` -/// - Borrowing of scalar values, such as `&i32`, `&bool` -/// - Vec, such as `Vec` -/// - Slice, such as `&[i32]` -/// - Option, such as `Option` -/// - Object and &Object -/// - Enum -/// - FieldResult, such as `FieldResult` +/// - Scalar values, such as `i32` and `bool`. `usize`, `isize`, `u128` and `i128` are not +/// supported +/// - `Vec`, such as `Vec` +/// - Slices, such as `&[i32]` +/// - `Option`, such as `Option` +/// - GraphQL objects. +/// - GraphQL enums. +/// - References to any of the above types, such as `&i32` or `&Option`. +/// - `FieldResult`, such as `FieldResult` /// /// # Context /// @@ -272,7 +270,7 @@ pub use types::{EnumItem, EnumType}; /// } /// /// async_std::task::block_on(async move { -/// let schema = Schema::new(QueryRoot{ value: 10 }, EmptyMutation, EmptySubscription); +/// let schema = Schema::new(QueryRoot { value: 10 }, EmptyMutation, EmptySubscription); /// let res = schema.execute(r#"{ /// value /// valueRef @@ -291,9 +289,13 @@ pub use types::{EnumItem, EnumType}; /// ``` pub use async_graphql_derive::Object; -/// Define a GraphQL object +/// Define a GraphQL object with fields /// -/// Similar to `Object`, but defined on a structure that automatically generates getters for all fields. +/// You can also [derive this](derive.GQLSimpleObject.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_simple_object.html).* +/// +/// Similar to `Object`, but defined on a structure that automatically generates getters for all fields. For a list of valid field types, see [`Object`](attr.Object.html). All fields are converted to camelCase. /// /// # Macro parameters /// @@ -340,6 +342,12 @@ pub use async_graphql_derive::SimpleObject; /// Derive a GraphQL enum /// +/// You can also [use an attribute](attr.Enum.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_enum.html).* +/// +/// All variants are converted to SCREAMING_SNAKE_CASE. +/// /// # Examples /// /// ```rust @@ -356,6 +364,10 @@ pub use async_graphql_derive::GQLEnum; /// Derive a GraphQL input object /// +/// You can also [use an attribute](attr.InputObject.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_input_object.html).* +/// /// # Examples /// /// ```rust @@ -368,7 +380,11 @@ pub use async_graphql_derive::GQLEnum; /// ``` pub use async_graphql_derive::GQLInputObject; -/// Derive a GraphQL simple object +/// Derive a GraphQL object with fields +/// +/// You can also [use an attribute](attr.SimpleObject.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_simple_object.html).* /// /// # Examples /// @@ -384,6 +400,10 @@ pub use async_graphql_derive::GQLSimpleObject; /// Define a GraphQL enum /// +/// You can also [derive this](derive.GQLEnum.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_enum.html).* +/// /// # Macro parameters /// /// | Attribute | description | Type | Optional | @@ -438,6 +458,10 @@ pub use async_graphql_derive::Enum; /// Define a GraphQL input object /// +/// You can also [derive this](derive.GQLInputObject.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_input_object.html).* +/// /// # Macro parameters /// /// | Attribute | description | Type | Optional | @@ -492,6 +516,10 @@ pub use async_graphql_derive::InputObject; /// Define a GraphQL interface /// +/// You can also [derive this](derive.GQLInterface.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_interface.html).* +/// /// # Macro parameters /// /// | Attribute | description | Type | Optional | @@ -616,10 +644,17 @@ pub use async_graphql_derive::InputObject; pub use async_graphql_derive::Interface; /// Derive a GraphQL interface +/// +/// You can also [use an attribute](attr.Interface.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_interface.html).* pub use async_graphql_derive::GQLInterface; /// Define a GraphQL union /// +/// You can also [derive this](derive.GQLUnion.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_union.html).* /// /// # Macro parameters /// @@ -684,10 +719,16 @@ pub use async_graphql_derive::GQLInterface; pub use async_graphql_derive::Union; /// Derive a GraphQL union +/// +/// You can also [use an attribute](attr.Union.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_union.html).* pub use async_graphql_derive::GQLUnion; /// Define a GraphQL subscription /// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/subscription.html).* +/// /// The field function is a synchronization function that performs filtering. When true is returned, the message is pushed to the client. /// The second parameter is the type of the field. /// Starting with the third parameter is one or more filtering conditions, The filter condition is the parameter of the field. @@ -756,6 +797,10 @@ pub use async_graphql_derive::Scalar; /// Define a merged object with multiple object types. /// +/// You can also [derive this](derive.GQLMergedObject.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).* +/// /// # Macro parameters /// /// | Attribute | description | Type | Optional | @@ -793,10 +838,18 @@ pub use async_graphql_derive::Scalar; pub use async_graphql_derive::MergedObject; /// Derive a GraphQL Merged object +/// +/// You can also [use an attribute](attr.MergedObject.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).* pub use async_graphql_derive::GQLMergedObject; /// Define a merged subscription with multiple subscription types. /// +/// You can also [derive this](derive.GQLMergedSubscription.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).* +/// /// # Macro parameters /// /// | Attribute | description | Type | Optional | @@ -835,5 +888,9 @@ pub use async_graphql_derive::GQLMergedObject; /// ``` pub use async_graphql_derive::MergedSubscription; -/// Derive a GraphQL Merged object +/// Derive a GraphQL merged subscription with multiple subscription types. +/// +/// You can also [use an attribute](attr.MergedSubscription.html). +/// +/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).* pub use async_graphql_derive::GQLMergedSubscription; diff --git a/src/scalars/json.rs b/src/scalars/json.rs index d9aa37e6..e5f50dfc 100644 --- a/src/scalars/json.rs +++ b/src/scalars/json.rs @@ -6,7 +6,7 @@ use crate::{ use async_graphql_derive::Scalar; use async_graphql_parser::query::Field; use serde::de::DeserializeOwned; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::ops::{Deref, DerefMut}; @@ -100,6 +100,7 @@ impl OutputValueType for OutputJson { #[cfg(test)] mod test { use crate::*; + use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[async_std::test] diff --git a/src/subscription/ws_transport.rs b/src/subscription/ws_transport.rs index f497e2c6..a8a90978 100644 --- a/src/subscription/ws_transport.rs +++ b/src/subscription/ws_transport.rs @@ -5,6 +5,7 @@ use crate::{ QueryResponse, Result, Schema, SubscriptionStreams, SubscriptionType, Variables, }; use bytes::Bytes; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; diff --git a/src/validation/rules/default_values_of_correct_type.rs b/src/validation/rules/default_values_of_correct_type.rs index 4ca5613e..7bd8df38 100644 --- a/src/validation/rules/default_values_of_correct_type.rs +++ b/src/validation/rules/default_values_of_correct_type.rs @@ -42,7 +42,7 @@ mod tests { use super::*; use crate::{expect_fails_rule, expect_passes_rule}; - pub fn factory<'a>() -> DefaultValuesOfCorrectType { + pub fn factory() -> DefaultValuesOfCorrectType { DefaultValuesOfCorrectType } diff --git a/src/validation/rules/fields_on_correct_type.rs b/src/validation/rules/fields_on_correct_type.rs index 3a98808a..8ad841b7 100644 --- a/src/validation/rules/fields_on_correct_type.rs +++ b/src/validation/rules/fields_on_correct_type.rs @@ -55,7 +55,7 @@ mod tests { use super::*; use crate::{expect_fails_rule, expect_passes_rule}; - pub fn factory<'a>() -> FieldsOnCorrectType { + pub fn factory() -> FieldsOnCorrectType { FieldsOnCorrectType } diff --git a/src/validation/rules/fragments_on_composite_types.rs b/src/validation/rules/fragments_on_composite_types.rs index 27b96cbb..40159d1a 100644 --- a/src/validation/rules/fragments_on_composite_types.rs +++ b/src/validation/rules/fragments_on_composite_types.rs @@ -49,7 +49,7 @@ mod tests { use super::*; use crate::{expect_fails_rule, expect_passes_rule}; - pub fn factory<'a>() -> FragmentsOnCompositeTypes { + fn factory() -> FragmentsOnCompositeTypes { FragmentsOnCompositeTypes } diff --git a/src/validation/test_harness.rs b/src/validation/test_harness.rs index 960fcd2f..6c56915f 100644 --- a/src/validation/test_harness.rs +++ b/src/validation/test_harness.rs @@ -381,7 +381,7 @@ where V: Visitor<'a> + 'a, F: Fn() -> V, { - if let Ok(_) = validate(doc, factory) { + if validate(doc, factory).is_ok() { panic!("Expected rule to fail, but no errors were found"); } } diff --git a/tests/connection.rs b/tests/connection.rs index 64d2789a..43fb213c 100644 --- a/tests/connection.rs +++ b/tests/connection.rs @@ -43,7 +43,7 @@ pub async fn test_connection_additional_fields() { end < 10000, ConnectionFields { total_count: 10000 }, ); - connection.append((start..end).into_iter().map(|n| { + connection.append((start..end).map(|n| { Edge::with_additional_fields( n, n as i32, diff --git a/tests/enum.rs b/tests/enum.rs index b9f049b0..33e361ea 100644 --- a/tests/enum.rs +++ b/tests/enum.rs @@ -33,13 +33,12 @@ pub async fn test_enum_type() { } let schema = Schema::new(Root { value: MyEnum::A }, EmptyMutation, EmptySubscription); - let query = format!( - r#"{{ + let query = r#"{{ value testArg(input: A) testInput(input: {{value: B}}) }} "# - ); + .to_owned(); assert_eq!( schema.execute(&query).await.unwrap().data, serde_json::json!({ @@ -52,7 +51,7 @@ pub async fn test_enum_type() { #[async_std::test] pub async fn test_enum_derive_and_item_attributes() { - use serde_derive::Deserialize; + use serde::Deserialize; #[async_graphql::Enum] #[derive(Deserialize, Debug)] diff --git a/tests/input_object.rs b/tests/input_object.rs index ed725d75..c7ac9edc 100644 --- a/tests/input_object.rs +++ b/tests/input_object.rs @@ -67,13 +67,12 @@ pub async fn test_input_object_default_value() { } let schema = Schema::new(Root, EmptyMutation, EmptySubscription); - let query = format!( - r#"{{ + let query = r#"{{ a(input:{{e:777}}) {{ a b c d e }} }}"# - ); + .to_owned(); assert_eq!( schema.execute(&query).await.unwrap().data, serde_json::json!({ @@ -90,7 +89,7 @@ pub async fn test_input_object_default_value() { #[async_std::test] pub async fn test_inputobject_derive_and_item_attributes() { - use serde_derive::Deserialize; + use serde::Deserialize; #[async_graphql::InputObject] #[derive(Deserialize, PartialEq, Debug)] diff --git a/tests/input_validators.rs b/tests/input_validators.rs index 5f0e3780..1aeec9fc 100644 --- a/tests/input_validators.rs +++ b/tests/input_validators.rs @@ -292,9 +292,9 @@ pub async fn test_input_validator_string_email() { case ); let field_error_msg = - format!("Invalid value for argument \"email\", invalid email format"); + "Invalid value for argument \"email\", invalid email format".to_owned(); let object_error_msg = - format!("Invalid value for argument \"input.email\", invalid email format"); + "Invalid value for argument \"input.email\", invalid email format".to_owned(); // Testing FieldValidator assert_eq!( @@ -437,9 +437,9 @@ pub async fn test_input_validator_string_mac() { "MAC validation case {} should have failed, but did not", mac ); - let field_error_msg = format!("Invalid value for argument \"mac\", invalid MAC format"); + let field_error_msg = "Invalid value for argument \"mac\", invalid MAC format".to_owned(); let object_error_msg = - format!("Invalid value for argument \"input.mac\", invalid MAC format"); + "Invalid value for argument \"input.mac\", invalid MAC format".to_owned(); assert_eq!( schema_without_colon @@ -511,14 +511,14 @@ pub async fn test_input_validator_string_mac() { for mac in valid_macs { let field_query = format!("{{fieldParameter(mac: \"{}\")}}", mac); let object_query = format!("{{inputObject(input: {{mac: \"{}\"}})}}", mac); - let contains_colon = mac.contains(":"); + let contains_colon = mac.contains(':'); let should_fail_msg = format!( "MAC validation case {} should have failed, but did not", mac ); - let field_error_msg = format!("Invalid value for argument \"mac\", invalid MAC format"); + let field_error_msg = "Invalid value for argument \"mac\", invalid MAC format".to_owned(); let object_error_msg = - format!("Invalid value for argument \"input.mac\", invalid MAC format"); + "Invalid value for argument \"input.mac\", invalid MAC format".to_owned(); let error_msg = format!("Schema returned error with test_string = {}", mac); if contains_colon { @@ -1413,9 +1413,10 @@ pub async fn test_input_validator_operator_or() { case ); - let field_error_msg = format!("Invalid value for argument \"id\", invalid MAC format"); + let field_error_msg = + "Invalid value for argument \"id\", invalid MAC format".to_owned(); let object_error_msg = - format!("Invalid value for argument \"input.id\", invalid MAC format"); + "Invalid value for argument \"input.id\", invalid MAC format".to_owned(); assert_eq!( schema .execute(&field_query) @@ -1518,13 +1519,13 @@ pub async fn test_input_validator_operator_and() { ); let field_error_msg = if *should_be_invalid_email { - format!("Invalid value for argument \"email\", invalid email format") + "Invalid value for argument \"email\", invalid email format".to_owned() } else { format!("Invalid value for argument \"email\", the value length is {}, must be greater than or equal to {}", case_length, min_length) }; let object_error_msg = if *should_be_invalid_email { - format!("Invalid value for argument \"input.email\", invalid email format") + "Invalid value for argument \"input.email\", invalid email format".to_owned() } else { format!("Invalid value for argument \"input.email\", the value length is {}, must be greater than or equal to {}", case_length, min_length) }; diff --git a/tests/introspection.rs b/tests/introspection.rs index d0900da7..3c24240f 100644 --- a/tests/introspection.rs +++ b/tests/introspection.rs @@ -52,7 +52,7 @@ enum TestEnum { #[item(desc = "Kind 1")] Kind1, - #[item(desc = "Kind 2", deprecation = "Kind 2 depracted")] + #[item(desc = "Kind 2", deprecation = "Kind 2 deprecated")] Kind2, } @@ -269,10 +269,10 @@ impl Subscription { // } #[async_std::test] -pub async fn test_introspection_depraction() { +pub async fn test_introspection_deprecation() { let schema = Schema::new(Query, EmptyMutation, EmptySubscription); - let get_object_query = |obj, is_depracted| { + let get_object_query = |obj, is_deprecated| { format!( r#" {{ @@ -285,11 +285,11 @@ pub async fn test_introspection_depraction() { }} }} "#, - obj, is_depracted + obj, is_deprecated ) }; - // SimpleObject with depracted inclusive + // SimpleObject with deprecated inclusive let mut query = get_object_query("SimpleObject", "true"); let mut res_json = serde_json::json!({ @@ -353,7 +353,7 @@ pub async fn test_introspection_depraction() { assert_eq!(res, res_json); - // SimpleObject with depracted fields exclusive + // SimpleObject with deprecated fields exclusive query = get_object_query("SimpleObject", "false"); res_json = serde_json::json!({ @@ -412,7 +412,7 @@ pub async fn test_introspection_depraction() { assert_eq!(res, res_json); - // Object with only one depracted field inclusive + // Object with only one deprecated field inclusive query = get_object_query("Square", "true"); res_json = serde_json::json!({ @@ -431,7 +431,7 @@ pub async fn test_introspection_depraction() { assert_eq!(res, res_json); - // Object with only one depracted field exclusive + // Object with only one deprecated field exclusive query = get_object_query("Square", "false"); res_json = serde_json::json!({ @@ -444,7 +444,7 @@ pub async fn test_introspection_depraction() { assert_eq!(res, res_json); - let get_enum_query = |obj, is_depracted| { + let get_enum_query = |obj, is_deprecated| { format!( r#" {{ @@ -457,11 +457,11 @@ pub async fn test_introspection_depraction() { }} }} "#, - obj, is_depracted + obj, is_deprecated ) }; - // Enum with depracted value inclusive + // Enum with deprecated value inclusive query = get_enum_query("TestEnum", "true"); res_json = serde_json::json!({ @@ -475,7 +475,7 @@ pub async fn test_introspection_depraction() { { "name": "KIND_2", "isDeprecated": true, - "deprecationReason": "Kind 2 depracted" + "deprecationReason": "Kind 2 deprecated" } ] } @@ -485,7 +485,7 @@ pub async fn test_introspection_depraction() { assert_eq!(res, res_json); - // Enum with depracted value exclusive + // Enum with deprecated value exclusive query = get_enum_query("TestEnum", "false"); res_json = serde_json::json!({ @@ -841,7 +841,7 @@ pub async fn test_introspection_enum() { "name": "KIND_2", "description": "Kind 2", "isDeprecated": true, - "deprecationReason": "Kind 2 depracted" + "deprecationReason": "Kind 2 deprecated" } ] } diff --git a/tests/optional.rs b/tests/optional.rs index 41741d80..468efb1f 100644 --- a/tests/optional.rs +++ b/tests/optional.rs @@ -15,7 +15,7 @@ pub async fn test_optional_type() { #[Object] impl Root { async fn value1(&self) -> Option { - self.value1.clone() + self.value1 } async fn value1_ref(&self) -> &Option { @@ -23,7 +23,7 @@ pub async fn test_optional_type() { } async fn value2(&self) -> Option { - self.value2.clone() + self.value2 } async fn value2_ref(&self) -> &Option { @@ -35,7 +35,7 @@ pub async fn test_optional_type() { } async fn test_input<'a>(&self, input: MyInput) -> Option { - input.value.clone() + input.value } } @@ -47,8 +47,7 @@ pub async fn test_optional_type() { EmptyMutation, EmptySubscription, ); - let query = format!( - r#"{{ + let query = r#"{{ value1 value1Ref value2 @@ -58,7 +57,7 @@ pub async fn test_optional_type() { testInput1: testInput(input: {{value: 10}}) testInput2: testInput(input: {{}}) }}"# - ); + .to_owned(); assert_eq!( schema.execute(&query).await.unwrap().data, serde_json::json!({ diff --git a/tests/subscription_websocket.rs b/tests/subscription_websocket.rs index 8c0838b7..c3c30b0f 100644 --- a/tests/subscription_websocket.rs +++ b/tests/subscription_websocket.rs @@ -88,7 +88,7 @@ pub async fn test_subscription_ws_transport_with_token() { let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot); let (mut sink, mut stream) = schema.subscription_connection(WebSocketTransport::new(|value| { - #[derive(serde_derive::Deserialize)] + #[derive(serde::Deserialize)] struct Payload { token: String, } From 769bffcbf239cbe36ceb0a28f57bed53bdb7d978 Mon Sep 17 00:00:00 2001 From: Sunli Date: Tue, 1 Sep 2020 09:10:12 +0800 Subject: [PATCH 10/13] Fix the test code and add docs. --- Cargo.toml | 1 - src/error.rs | 24 +++++++++++++++++++++--- tests/enum.rs | 6 +++--- tests/input_object.rs | 8 ++++---- tests/optional.rs | 8 ++++---- 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a7792d91..cec892f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,6 @@ chrono-tz = { version = "0.5.1", optional = true } [dev-dependencies] async-std = { version = "1.5.0", features = ["attributes"] } -serde = "1.0.104" [workspace] members = [ diff --git a/src/error.rs b/src/error.rs index 3f286728..5b4035b4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -174,7 +174,10 @@ pub enum QueryError { /// Parsing of an input value failed. #[error("Failed to parse input value: {reason}")] - ParseInputValue { reason: String }, + ParseInputValue { + /// The reason for the failure to resolve. + reason: String, + }, /// A field was not found on an object type. #[error("Cannot query field \"{field_name}\" on type \"{object}\".")] @@ -287,9 +290,11 @@ pub enum QueryError { extended_error: Option, }, + /// Entity not found. #[error("Entity not found")] EntityNotFound, + /// "__typename" must be an existing string. #[error("\"__typename\" must be an existing string")] TypeNameNotExists, } @@ -324,12 +329,15 @@ pub enum ParseRequestError { #[error("Invalid multipart data")] InvalidMultipart(multer::Error), + /// Missing "operators" part for multipart request. #[error("Missing \"operators\" part")] MissingOperatorsPart, + /// Missing "map" part for multipart request. #[error("Missing \"map\" part")] MissingMapPart, + /// It's not an upload operation #[error("It's not an upload operation")] NotUpload, @@ -342,10 +350,13 @@ pub enum ParseRequestError { PayloadTooLarge, } -#[allow(missing_docs)] +/// Verification error. #[derive(Debug, PartialEq)] pub struct RuleError { + /// Location of this error in query string. pub locations: Vec, + + /// A description of this error. pub message: String, } @@ -361,11 +372,18 @@ pub enum Error { Query { /// The position at which the processing failed. pos: Pos, + + /// Node path. path: Option, + /// The query error. err: QueryError, }, + /// The query statement verification failed. #[error("Rule error")] - Rule { errors: Vec }, + Rule { + /// List of errors. + errors: Vec, + }, } diff --git a/tests/enum.rs b/tests/enum.rs index 33e361ea..81d619d7 100644 --- a/tests/enum.rs +++ b/tests/enum.rs @@ -33,11 +33,11 @@ pub async fn test_enum_type() { } let schema = Schema::new(Root { value: MyEnum::A }, EmptyMutation, EmptySubscription); - let query = r#"{{ + let query = r#"{ value testArg(input: A) - testInput(input: {{value: B}}) }} - "# + testInput(input: {value: B}) + }"# .to_owned(); assert_eq!( schema.execute(&query).await.unwrap().data, diff --git a/tests/input_object.rs b/tests/input_object.rs index c7ac9edc..784eb102 100644 --- a/tests/input_object.rs +++ b/tests/input_object.rs @@ -67,11 +67,11 @@ pub async fn test_input_object_default_value() { } let schema = Schema::new(Root, EmptyMutation, EmptySubscription); - let query = r#"{{ - a(input:{{e:777}}) {{ + let query = r#"{ + a(input:{e:777}) { a b c d e - }} - }}"# + } + }"# .to_owned(); assert_eq!( schema.execute(&query).await.unwrap().data, diff --git a/tests/optional.rs b/tests/optional.rs index 468efb1f..7d6254af 100644 --- a/tests/optional.rs +++ b/tests/optional.rs @@ -47,16 +47,16 @@ pub async fn test_optional_type() { EmptyMutation, EmptySubscription, ); - let query = r#"{{ + let query = r#"{ value1 value1Ref value2 value2Ref testArg1: testArg(input: 10) testArg2: testArg - testInput1: testInput(input: {{value: 10}}) - testInput2: testInput(input: {{}}) - }}"# + testInput1: testInput(input: {value: 10}) + testInput2: testInput(input: {}) + }"# .to_owned(); assert_eq!( schema.execute(&query).await.unwrap().data, From 8cc4ed274147d96ecf0bb2001e9cae73fc51ce88 Mon Sep 17 00:00:00 2001 From: Sunli Date: Tue, 1 Sep 2020 09:26:53 +0800 Subject: [PATCH 11/13] Release 1.17.15 async-graphql@1.17.15 Generated by cargo-workspaces --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cec892f3..1be21aad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-graphql" -version = "1.17.14" +version = "1.17.15" authors = ["sunli "] edition = "2018" description = "A GraphQL server library implemented in Rust" From b2a087179814f538107eabcffefdb8ae7b485804 Mon Sep 17 00:00:00 2001 From: Sunli Date: Tue, 1 Sep 2020 09:35:47 +0800 Subject: [PATCH 12/13] Although the `serde_derive` dependency is now removed, it is actually necessary to add a `serde_derive` feature to serde. The reason why it can be compiled now is because other dependent packages have this dependency, so it is safer to add this feature here. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1be21aad..1ba6ac94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ async-graphql-derive = { path = "async-graphql-derive", version = "1.17.11" } anyhow = "1.0.26" thiserror = "1.0.11" async-trait = "0.1.30" -serde = "1.0.104" +serde = { version = "1.0.104", features = ["serde_derive"] } serde_json = "1.0.48" bytes = "0.5.4" Inflector = "0.11.4" @@ -61,6 +61,6 @@ members = [ "async-graphql-actix-web", "async-graphql-warp", "async-graphql-tide", -# "async-graphql-lambda", + # "async-graphql-lambda", "benchmark", ] From 1ef34f2c396d069f08168f1e6604eabc169775ac Mon Sep 17 00:00:00 2001 From: Koxiaet <38139193+Koxiaet@users.noreply.github.com> Date: Tue, 1 Sep 2020 06:47:22 +0100 Subject: [PATCH 13/13] Improve book and fix serde dependency --- Cargo.toml | 2 +- docs/en/book.toml | 2 +- docs/en/src/apollo_federation.md | 18 +++++++++--------- docs/en/src/apollo_tracing.md | 7 +++---- docs/en/src/cache_control.md | 4 ++-- docs/en/src/context.md | 2 +- docs/en/src/cursor_connections.md | 6 +++--- docs/en/src/custom_scalars.md | 6 ++---- docs/en/src/default_value.md | 4 ++-- docs/en/src/define_complex_object.md | 12 ++++++------ docs/en/src/define_enum.md | 4 ++-- docs/en/src/define_input_object.md | 12 ++++++------ docs/en/src/define_interface.md | 14 +++++++------- docs/en/src/define_schema.md | 4 ++-- docs/en/src/define_simple_object.md | 4 ++-- docs/en/src/define_union.md | 5 ++--- docs/en/src/error_extensions.md | 22 ++++++++-------------- docs/en/src/error_handling.md | 8 ++++---- docs/en/src/input_value_validators.md | 4 +--- docs/en/src/integrations.md | 4 ++-- docs/en/src/integrations_to_actix_web.md | 4 ++-- docs/en/src/introduction.md | 2 +- docs/en/src/merging_objects.md | 6 +++--- docs/en/src/procedural_macros.md | 10 +++++----- docs/en/src/query_and_mutation.md | 3 +-- docs/en/src/quickstart.md | 15 +++++++-------- docs/en/src/subscription.md | 4 ++-- docs/en/src/typesystem.md | 2 +- docs/index.html | 15 ++++++--------- 29 files changed, 94 insertions(+), 111 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ba6ac94..e1a9fed3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ async-graphql-derive = { path = "async-graphql-derive", version = "1.17.11" } anyhow = "1.0.26" thiserror = "1.0.11" async-trait = "0.1.30" -serde = { version = "1.0.104", features = ["serde_derive"] } +serde = { version = "1.0.104", features = ["derive"] } serde_json = "1.0.48" bytes = "0.5.4" Inflector = "0.11.4" diff --git a/docs/en/book.toml b/docs/en/book.toml index b137538f..0eac281a 100644 --- a/docs/en/book.toml +++ b/docs/en/book.toml @@ -2,5 +2,5 @@ authors = ["sunli"] description = "Async-graphql Book" src = "src" -language = "zh-CN" +language = "en" title = "Async-graphql Book" diff --git a/docs/en/src/apollo_federation.md b/docs/en/src/apollo_federation.md index f53d7612..8a012629 100644 --- a/docs/en/src/apollo_federation.md +++ b/docs/en/src/apollo_federation.md @@ -1,8 +1,8 @@ # Apollo Federation -`Apollo Federation` is a `GraphQL` API gateway which can combine multiple GraphQL services, allowing each service to implement the subset of the API it is responsible for. You can read more in the [official documentation](https://www.apollographql.com/docs/apollo-server/federation/introduction)。 +`Apollo Federation` is a `GraphQL` API gateway which can combine multiple GraphQL services, allowing each service to implement the subset of the API it is responsible for. You can read more in the [official documentation](https://www.apollographql.com/docs/apollo-server/federation/introduction). -`Async-GraphQL` supports all the functionality of `Apollo Federation`, but some modifications to your `Schema` are required. +`Async-graphql` supports all the functionality of `Apollo Federation`, but some modifications to your `Schema` are required. - You can use the `extends` property declaration on `async_graphql::Object` and `async_graphql::Interface` to extend a type offered by another implementing service. @@ -36,16 +36,16 @@ impl Query { **Notice the difference between these three lookup functions, which are all looking for the `User` object.** -- find_user_by_id +- `find_user_by_id` - Use `id` to find an `User` object, the key for `User` is `id`。 + Use `id` to find an `User` object, the key for `User` is `id`. -- find_user_by_id_with_username +- `find_user_by_id_with_username` - Use `id` to find an `User` object, the key for `User` is `id`, and the `username` field value of the `User` object is requested。 + Use `id` to find an `User` object, the key for `User` is `id`, and the `username` field value of the `User` object is requested. -- find_user_by_id_and_username +- `find_user_by_id_and_username` - Use `id` and `username` to find an `User` object, the keys for `User` are `id` and `username`。 + Use `id` and `username` to find an `User` object, the keys for `User` are `id` and `username`. -For a complete example, refer to: https://github.com/async-graphql/examples/tree/master/federation +For a complete example, refer to: . diff --git a/docs/en/src/apollo_tracing.md b/docs/en/src/apollo_tracing.md index 916816b8..3618564a 100644 --- a/docs/en/src/apollo_tracing.md +++ b/docs/en/src/apollo_tracing.md @@ -2,14 +2,13 @@ Apollo Tracing provides performance analysis results for each step of query. This is an extension to `Schema`, and the performance analysis results are stored in `QueryResponse`. -To enable the Apollo Tracing extension, add the extension when a `Schema` is created. +To enable the Apollo Tracing extension, add the extension when the `Schema` is created. ```rust use async_graphql::*; use async_graphql::extensions::ApolloTracing; let schema = Schema::build(Query, EmptyMutation, EmptySubscription) - .extension(|| ApolloTracing::default()) // Enable ApolloTracing extension + .extension(ApolloTracing::default) // Enable ApolloTracing extension .finish(); - -``` \ No newline at end of file +``` diff --git a/docs/en/src/cache_control.md b/docs/en/src/cache_control.md index d5c8177b..342bbe16 100644 --- a/docs/en/src/cache_control.md +++ b/docs/en/src/cache_control.md @@ -4,13 +4,13 @@ Production environments often rely on caching to improve performance. A GraphQL query will call multiple resolver functions and each resolver can have a different cache definition. Some may cache for a few seconds, some may cache for a few hours, some may be the same for all users, and some may be different for each session. -`Async-Graphql` provides a mechanism that allows you to define the cache time and scope for each resolver. +`Async-graphql` provides a mechanism that allows you to define the cache time and scope for each resolver. You can define cache parameters on the object or on its fields. The following example shows two uses of cache control parameters. You can use `max_age` parameters to control the age of the cache (in seconds), and you can also use `public` and `private` to control the scope of the cache. When you do not specify it, the scope will default to `public`. -lWhen querying multiple resolvers, the results of all cache control parameters will be combined and the `max_age` minimum value will be taken. If the scope of any object or field is `private`, the result will be `private`. +when querying multiple resolvers, the results of all cache control parameters will be combined and the `max_age` minimum value will be taken. If the scope of any object or field is `private`, the result will be `private`. We can use `QueryResponse` to get a merged cache control result from a query result, and call `CacheControl::value` to get the corresponding HTTP header. diff --git a/docs/en/src/context.md b/docs/en/src/context.md index b06ca2fe..18a87181 100644 --- a/docs/en/src/context.md +++ b/docs/en/src/context.md @@ -1,6 +1,6 @@ # Context -The main goal of `Context` is to acquire global data attached to Schema. **Note that if the return value of resolver function is borrowed from `Context`, you need to explicitly state the lifetime of the argument.** +The main goal of `Context` is to acquire global data attached to Schema. **Note that if the return value of resolver function is borrowed from `Context`, you will need to explicitly state the lifetime of the argument.** The following example shows how to borrow data in `Context`. diff --git a/docs/en/src/cursor_connections.md b/docs/en/src/cursor_connections.md index 4cf3f760..9842eb6e 100644 --- a/docs/en/src/cursor_connections.md +++ b/docs/en/src/cursor_connections.md @@ -1,8 +1,8 @@ # Cursor connections -Relay's cursor connection specification is defined to provide a consistent method for query paging. For more details on the specification see the [GraphQL Cursor Connections Specification](https://facebook.github.io/relay/graphql/connections.htm)。 +Relay's cursor connection specification is designed to provide a consistent method for query paging. For more details on the specification see the [GraphQL Cursor Connections Specification](https://facebook.github.io/relay/graphql/connections.htm)。 -Define a cursor connection in `async-graphql` is very simple, you just call the `connection::query` function and query data in closure. +Defining a cursor connection in `async-graphql` is very simple, you just call the `connection::query` function and query data in the closure. ```rust use async_graphql::*; @@ -42,4 +42,4 @@ impl Query { } } -``` \ No newline at end of file +``` diff --git a/docs/en/src/custom_scalars.md b/docs/en/src/custom_scalars.md index f5fe85fe..7565269f 100644 --- a/docs/en/src/custom_scalars.md +++ b/docs/en/src/custom_scalars.md @@ -1,15 +1,14 @@ # Custom scalars -In `Async-GraphQL` most common scalar types are built in, but you can also create your own scalar types. +In `Async-graphql` most common scalar types are built in, but you can also create your own scalar types. Using `async-graphql::Scalar`, you can add support for a scalar when you implement it. You only need to implement parsing and output functions. -The following example defines a 64-bit integer scalar where its input and output are strings. (Note: `Async-graphQL` already supports 64-bit integers and uses strings as input and output.) +The following example defines a 64-bit integer scalar where its input and output are strings. (Note: `Async-graphql` already supports 64-bit integers and uses strings as input and output.) ```rust use async_graphql::*; - struct StringNumber(i64); #[Scalar] @@ -28,5 +27,4 @@ impl ScalarType for StringNumber { Value::String(self.0.to_string()) } } - ``` diff --git a/docs/en/src/default_value.md b/docs/en/src/default_value.md index d545d0a8..7c1d83c0 100644 --- a/docs/en/src/default_value.md +++ b/docs/en/src/default_value.md @@ -1,7 +1,7 @@ # Default value You can define default values for input value types. -The following shows how to define default values for different types. +Below are some examples. ## Object field @@ -57,4 +57,4 @@ struct MyInputObject { #[field(default = "my_default()")] value3: i32, } -``` \ No newline at end of file +``` diff --git a/docs/en/src/define_complex_object.md b/docs/en/src/define_complex_object.md index 7893c128..e1ea0b28 100644 --- a/docs/en/src/define_complex_object.md +++ b/docs/en/src/define_complex_object.md @@ -1,14 +1,14 @@ # Object -Different from `SimpleObject`, `Object` must have Resolve defined for each field in `impl`. +Different from `SimpleObject`, `Object` must have a resolver defined for each field in its `impl`. -**A resolver function has to be asynchronous. The first argument has to be `&self`, second being optional `Context` and followed by field arguments.** +**A resolver function has to be asynchronous. The first argument has to be `&self`, the second is an optional `Context` and it is followed by field arguments.** -Resolve is used to get the value of the field. You can query a database and return the result. **The return type of the function is the type of the field.** You can also return a `async_graphql::FieldResult` so to return an error if it occurs an error message will be sent to query result. +The resolvers is used to get the value of the field. For example, you can query a database and return the result. **The return type of the function is the type of the field.** You can also return a `async_graphql::FieldResult` to return an error if it occurs. The error message will then be sent to query result. -When querying a database, you may need a global data base connection pool. -When creating `Schema`, you can use `SchemaBuilder::data` to setup `Schema` data, and `Context::data` to setup `Context`data. -The following `value_from_db` function showed how to retrieve a database connection from `Context`. +You may need access to global data in your query, for example a database connection pool. +When creating your `Schema`, you can use `SchemaBuilder::data` to configure the global data, and `Context::data` to configure `Context` data. +The following `value_from_db` function shows how to retrieve a database connection from `Context`. ```rust use async_graphql::*; diff --git a/docs/en/src/define_enum.md b/docs/en/src/define_enum.md index 56497955..addd736a 100644 --- a/docs/en/src/define_enum.md +++ b/docs/en/src/define_enum.md @@ -2,7 +2,7 @@ It's easy to define an `Enum`, here we have an example: -**Async-graphql can automatically change the name of each item to GraphQL's CONSTANT_CASE convension, you can also use `name` to rename.** +**Async-graphql will automatically change the name of each item to GraphQL's CONSTANT_CASE convention. You can use `name` to rename.** ```rust use async_graphql::*; @@ -19,4 +19,4 @@ pub enum Episode { #[item(name="AAA", desc = "Released in 1983.")] Jedi, } -``` \ No newline at end of file +``` diff --git a/docs/en/src/define_input_object.md b/docs/en/src/define_input_object.md index 2fdeb14a..c6f31251 100644 --- a/docs/en/src/define_input_object.md +++ b/docs/en/src/define_input_object.md @@ -1,12 +1,12 @@ # InputObject -You can define an `Object` as argument, GraphQL calls it `InputObject`. -The definition of `InputObject` is similar to [SimpleObject](define_simple_object.md). -However, `SimpleObject` can only be used for output and `InputObject` can only be used as input. +You can use an `Object` as an argument, and GraphQL calls it an `InputObject`. +The definition of `InputObject` is similar to [SimpleObject](define_simple_object.md), but +`SimpleObject` can only be used as output and `InputObject` can only be used as input. -`InputObject` don't need a `#[field]` for each field, every field is `InputValue`. -But you can add optional `#[field]` to add description or rename the field. +`InputObject` doesn't need a `#[field]` for each field, every field is an `InputValue`. +You can add optional `#[field]` attributes to add descriptions or rename the field. ```rust use async_graphql::*; @@ -28,4 +28,4 @@ impl Mutation { // ... } } -``` \ No newline at end of file +``` diff --git a/docs/en/src/define_interface.md b/docs/en/src/define_interface.md index ca516114..3edadd11 100644 --- a/docs/en/src/define_interface.md +++ b/docs/en/src/define_interface.md @@ -1,16 +1,16 @@ # Interface `Interface` is used to abstract `Object`s with common fields. -`Async-graphql` implemented it as a wrapper. -The wrapper will forward Resolve to the `Object` that implemented this `Interface`. -Therefore, the `Object`'s fields' type, arguments must match with the `Interface`'s. +`Async-graphql` implements it as a wrapper. +The wrapper will forward field resolution to the `Object` that implements this `Interface`. +Therefore, the `Object`'s fields' type and arguments must match with the `Interface`'s. -`Async-graphql` implemented auto conversion from `Object` to `Interface`, you only need to call `Into::into`. +`Async-graphql` implements auto conversion from `Object` to `Interface`, you only need to call `Into::into`. -Interface fields names transforms to camelCase in schema definition. -If you need e.g. snake_cased GraphQL field name, you can use both the `name` and `method` attribute. +Interface field names are transformed to camelCase for the schema definition. +If you need e.g. a snake_cased GraphQL field name, you can use both the `name` and `method` attributes. -- When the `name` and `method` exist together, the `name` is the GraphQL field name and the `method` is the resolver function name. +- When `name` and `method` exist together, `name` is the GraphQL field name and the `method` is the resolver function name. - When only `name` exists, `name.to_camel_case()` is the GraphQL field name and the `name` is the resolver function name. ```rust diff --git a/docs/en/src/define_schema.md b/docs/en/src/define_schema.md index a38c4a94..c7e767a2 100644 --- a/docs/en/src/define_schema.md +++ b/docs/en/src/define_schema.md @@ -1,6 +1,6 @@ # Schema -After defining the basic types, you need to define a schema to combine them. The schema consists of three types: a query object, mutation object, and subscription object, where the mutation object and subscription object are optional. +After defining the basic types, you need to define a schema to combine them. The schema consists of three types: a query object, a mutation object, and a subscription object, where the mutation object and subscription object are optional. -When the schema is created, `Async-Graphql` will traverse all object graphs and register all types. This means that if a GraphQL object is defined but never referenced, then this object will not be exposed in the schema. +When the schema is created, `Async-graphql` will traverse all object graphs and register all types. This means that if a GraphQL object is defined but never referenced, this object will not be exposed in the schema. diff --git a/docs/en/src/define_simple_object.md b/docs/en/src/define_simple_object.md index 2299ef3e..10393492 100644 --- a/docs/en/src/define_simple_object.md +++ b/docs/en/src/define_simple_object.md @@ -1,8 +1,8 @@ # SimpleObject -`SimpleObject` directly map all field of a struct to GraphQL object, you cannot define a resolver function on it. +`SimpleObject` directly maps all the fields of a struct to GraphQL object. You cannot define a resolver function on it - for that, see [Object](define_complex_object.html). -The example below defined an object `MyObject`, including field `a` and `b`. `c` will be not mapped to GraphQL as it is labelled as `#[field(skip)]` +The example below defines an object `MyObject` which includes the fields `a` and `b`. `c` will be not mapped to GraphQL as it is labelled as `#[field(skip)]` ```rust use async_graphql::*; diff --git a/docs/en/src/define_union.md b/docs/en/src/define_union.md index 7b58423a..6244627c 100644 --- a/docs/en/src/define_union.md +++ b/docs/en/src/define_union.md @@ -1,8 +1,7 @@ # Union -The definition of `Union` is similar to `Interface`'s, **but no field allowed.**. -The implemention is quite similar for `Async-graphql`. -From `Async-graphql`'s perspective, `Union` is a subset of `Interface`. +The definition of a `Union` is similar to an `Interface`, **but with no fields allowed.**. +The implementation is quite similar for `Async-graphql`; from `Async-graphql`'s perspective, `Union` is a subset of `Interface`. The following example modified the definition of `Interface` a little bit and removed fields. diff --git a/docs/en/src/error_extensions.md b/docs/en/src/error_extensions.md index 5089af40..31800470 100644 --- a/docs/en/src/error_extensions.md +++ b/docs/en/src/error_extensions.md @@ -1,7 +1,7 @@ # Error extensions To quote the [graphql-spec](https://spec.graphql.org/June2018/#example-fce18): > GraphQL services may provide an additional entry to errors with key extensions. -> This entry, if set, must have a map as its value. This entry is reserved for implementors to add +> This entry, if set, must have a map as its value. This entry is reserved for implementer to add > additional information to errors however they see fit, and there are no additional restrictions on > its contents. @@ -9,11 +9,11 @@ To quote the [graphql-spec](https://spec.graphql.org/June2018/#example-fce18): I would recommend on checking out this [async-graphql example](https://github.com/async-graphql/examples/blob/master/actix-web/error-extensions/src/main.rs) as a quickstart. ## General Concept -In async-graphql all user-facing errors are cast to the `FieldError` type which by default provides -the error message exposed by `std::fmt::Display`. However `FieldError` actually provides an additional -field `Option` which - if some valid `serde_json::Map` - will be exposed as the extensions key to any error. +In `async-graphql` all user-facing errors are cast to the `FieldError` type which by default provides +the error message exposed by `std::fmt::Display`. However `FieldError` also provides an additional +field `Option` which - if given some valid `serde_json::Map` - will be exposed as the extensions key to any error. -A resolver like this: +A resolver looks like this: ```rust async fn parse_with_extensions(&self) -> Result { @@ -41,7 +41,7 @@ may then return a response like this: ## ErrorExtensions -Constructing new `FieldError`s by hand quickly becomes tedious. That is why async_graphql provides +Constructing new `FieldError`s by hand quickly becomes tedious. That is why `async-graphql` provides two convenience traits for casting your errors to the appropriate `FieldError` with extensions. @@ -62,8 +62,7 @@ If you find yourself attaching extensions to your errors all over the place you implementing the trait on your custom error type directly. ```rust -#[macro_use] -extern crate thiserror; +use thiserror::Error; #[derive(Debug, Error)] pub enum MyError { @@ -102,7 +101,6 @@ async fn parse_with_extensions_result(&self) -> FieldResult { // OR Err(MyError::NotFound.extend_with(|_| json!({ "on_the_fly": "some_more_info" }))) } - ``` ```json @@ -119,12 +117,8 @@ async fn parse_with_extensions_result(&self) -> FieldResult { } ] } - ``` - - - ## ResultExt This trait enables you to call `extend_err` directly on results. So the above code becomes less verbose. @@ -176,7 +170,7 @@ Expected response: ### Pitfalls Rust does not provide stable trait specialization yet. That is why `ErrorExtensions` is actually implemented for `&E where E: std::fmt::Display` -instead of `E: std::fmt::Display` to provide some specialization through +instead of `E: std::fmt::Display`. Some specialization is provided through [Autoref-based stable specialization](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md). The disadvantage is that the below code does **NOT** compile: diff --git a/docs/en/src/error_handling.md b/docs/en/src/error_handling.md index e01662fd..d39e33c8 100644 --- a/docs/en/src/error_handling.md +++ b/docs/en/src/error_handling.md @@ -1,15 +1,15 @@ # Error handling -Resolve can return a `FieldResult`, following is the definition: +Resolve can return a `FieldResult`, which has the following definition: ```rust type FieldResult = std::result::Result; ``` -Any `Error` that implements `std::fmt::Display` can be converted to `FieldError` and you can extend error message. +Any `Error` that implements `std::fmt::Display` can be converted to `FieldError` and you can extend the error message. -Following example shows how to parse an input string to integer. When parsing failed, it would return error and attach error message. -See [ErrorExtensions](error_extensions.md) sections of this book for more details. +The following example shows how to parse an input string to an integer. When parsing fails, it will return an error and attach an error message. +See the [Error Extensions](error_extensions.md) section of this book for more details. ```rust use async_graphql::*; diff --git a/docs/en/src/input_value_validators.md b/docs/en/src/input_value_validators.md index aaaf181b..15c28b72 100644 --- a/docs/en/src/input_value_validators.md +++ b/docs/en/src/input_value_validators.md @@ -1,11 +1,10 @@ # Input value validators -Arguments to a query ([InputObject](define_input_object.md)) are called `Input Objects` in GraphQL. If the provided input type does not match for a query, the query will return a type mismatch error. But sometimes we want to provide more restrictions on specific types of values. For example, we might want to require that an argument is a valid email address. Async-graphql provides an input validators to solve this problem. +Arguments to a query ([InputObject](define_input_object.md)) are called `Input Objects` in GraphQL. If the provided input type does not match for a query, the query will return a type mismatch error. But sometimes we want to provide more restrictions on specific types of values. For example, we might want to require that an argument is a valid email address. `Async-graphql` provides an input validators to solve this problem. An input validator can be combined via `and` and `or` operators. - The following is an input validator which checks that a `String` is a valid Email or MAC address: @@ -22,7 +21,6 @@ impl Query { } ``` - The following example verifies that the `i32` parameter `a` is greater than 10 and less than 100, or else equal to 0: ```rust diff --git a/docs/en/src/integrations.md b/docs/en/src/integrations.md index 639d2bec..5ed956a9 100644 --- a/docs/en/src/integrations.md +++ b/docs/en/src/integrations.md @@ -1,9 +1,9 @@ # Integrations -`Async-Graphql` supports several common Rust web servers. +`Async-graphql` supports several common Rust web servers. - Actix-web [async-graphql-actix-web](https://crates.io/crates/async-graphql-actix-web) - Warp [async-graphql-warp](https://crates.io/crates/async-graphql-warp) - Tide [async-graphql-tide](https://crates.io/crates/async-graphql-tide) -**Even if the server you are currently using is not in the above list, it is quite simple to implement similar functionality yourself** \ No newline at end of file +**Even if the server you are currently using is not in the above list, it is quite simple to implement similar functionality yourself.** diff --git a/docs/en/src/integrations_to_actix_web.md b/docs/en/src/integrations_to_actix_web.md index 6db435fd..c147398e 100644 --- a/docs/en/src/integrations_to_actix_web.md +++ b/docs/en/src/integrations_to_actix_web.md @@ -1,8 +1,8 @@ # Actix-web -`Async-graphql-actix-web` provides an implementation of `actix_web::FromRequest` for `GQLRequest`. This is actually an abstraction around `QueryBuilder` and you can call `GQLRequest::into_inner` to convert it into a `QueryBuilder`。 +`Async-graphql-actix-web` provides an implementation of `actix_web::FromRequest` for `GQLRequest`. This is actually an abstraction around `QueryBuilder` and you can call `GQLRequest::into_inner` to convert it into a `QueryBuilder`. -`WSSubscription` is an Actor that supports WebSocket subscriptions。 +`WSSubscription` is an Actor that supports WebSocket subscriptions. ## Request example diff --git a/docs/en/src/introduction.md b/docs/en/src/introduction.md index db6ccfb7..7e85c252 100644 --- a/docs/en/src/introduction.md +++ b/docs/en/src/introduction.md @@ -35,4 +35,4 @@ cd benchmark cargo bench ``` -Now HTML report is available at `benchmark/target/criterion/report` +Now a HTML report is available at `benchmark/target/criterion/report`. diff --git a/docs/en/src/merging_objects.md b/docs/en/src/merging_objects.md index 7756d787..8c4867d3 100644 --- a/docs/en/src/merging_objects.md +++ b/docs/en/src/merging_objects.md @@ -18,9 +18,9 @@ impl Query { } ``` -Instead, the `#[derive(GQLMergedObject)]` macro allows you to split an object's resolvers across multiple file by merging 2 or more `#[Object]` implementations into one. +Instead, the `#[derive(GQLMergedObject)]`/`#[MergedObject]` macro allows you to split an object's resolvers across multiple modules or files by merging 2 or more `#[Object]` implementations into one. -**Tip:** Every `#[Object]` needs a unique name even in a GQLMergedObject so make sure to give each object your merging it's own name. +**Tip:** Every `#[Object]` needs a unique name, even in a `GQLMergedObject`, so make sure to give each object you're merging its own name. **Note:** This works for queries and mutations. For subscriptions, see "Merging Subscriptions" below. @@ -51,7 +51,7 @@ let schema = Schema::new( # Merging Subscriptions -Along with `GQLMergedObject`, you can derive `GQLMergedSubscription` to merge separate `#[Subscription]` blocks. +Along with `GQLMergedObject`, you can derive `GQLMergedSubscription` or use `#[MergedSubscription]` to merge separate `#[Subscription]` blocks. Like merging Objects, each subscription block requires a unique name. diff --git a/docs/en/src/procedural_macros.md b/docs/en/src/procedural_macros.md index 33e415d5..8a840327 100644 --- a/docs/en/src/procedural_macros.md +++ b/docs/en/src/procedural_macros.md @@ -1,6 +1,6 @@ # Two ways to define types -I think you have discovered that defining a GraphqlQL type can be use attribute macro or derive. +I think you have discovered that GraphQL types can be defined using both an attribute macro and a derive. The following is the corresponding table: @@ -14,7 +14,7 @@ The following is the corresponding table: |Merged Object|MergedObject|GQLMergedObject| |Merged Subscription|MergedSubscription|GQLMergedSubscription| -The advantage of attribute macro is that you can provide some parameters at the same time, for example: +The advantage of the attribute macro is that you can provide parameters at the same time, for example: ```rust #[SimpleObject(name = "ABC")] @@ -23,7 +23,7 @@ struct MyObject { } ``` -**But it does not support conditional compilation**, for example: +**However, attribute macros do not support conditional compilation**. The following does not work: ```rust #[SimpleObject] @@ -36,7 +36,7 @@ struct MyObject { } ``` -**Derive can support conditional compilation**, but it needs to provide parameters separately, for example: +Deriving, on the other hand, does support conditional compilation, but as derive macros can't take parameters you need to provide them separately. For example: ```rust #[derive(SimpleObject)] @@ -46,4 +46,4 @@ struct MyObject { } ``` -_Which way to define the type is up to you, I prefer to use derive._ \ No newline at end of file +_Which way you use to define types is up to you, personally I prefer to use derive._ diff --git a/docs/en/src/query_and_mutation.md b/docs/en/src/query_and_mutation.md index bdbc50e6..504398d6 100644 --- a/docs/en/src/query_and_mutation.md +++ b/docs/en/src/query_and_mutation.md @@ -2,8 +2,7 @@ ## Query root object -The query root object is a GraphQL object with a definition similar to other objects. resolver functions for all fields of the query object are executed concurrently. - +The query root object is a GraphQL object with a definition similar to other objects. Resolver functions for all fields of the query object are executed concurrently. ```rust use async_graphql::*; diff --git a/docs/en/src/quickstart.md b/docs/en/src/quickstart.md index 6e0e9014..6c35b51b 100644 --- a/docs/en/src/quickstart.md +++ b/docs/en/src/quickstart.md @@ -4,10 +4,10 @@ ```toml [dependencies] -async-graphql = "1.11.0" -async-graphql-actix-web = "1.3.0" # If you need to integrate into actix-web -async-graphql-warp = "1.3.0" # If you need to integrate into warp -async-graphql-tide = "1.2.0" # If you need to integrate into tide +async-graphql = "1.17.15" +async-graphql-actix-web = "1.17.3" # If you need to integrate into actix-web +async-graphql-warp = "1.17.3" # If you need to integrate into warp +async-graphql-tide = "1.17.9" # If you need to integrate into tide ``` ## Write a Schema @@ -30,12 +30,11 @@ impl Query { a + b } } - ``` ## Execute the query -In our example, there is only Query without Mutation and Subscription, so we create the Schema with `EmptyMutation` and `EmptySubscription`, and then call `Schema::execute` to execute the Query. +In our example, there is only a Query without a Mutation or Subscription, so we create the Schema with `EmptyMutation` and `EmptySubscription`, and then call `Schema::execute` to execute the Query. ```rust let schema = Schema::new(Query, EmptyMutation, EmptySubscription); @@ -44,7 +43,7 @@ let res = schema.execute("{ add(a: 10, b: 20) }"); ## Output the query results as JSON -Query returns `async_graphql::Result` with `async_graphql::http::GQLResponse ` wrapped, can be directly converted to JSON. +`Schema::execute` returns `async_graphql::Result` with `async_graphql::http::GQLResponse` wrapped, and it can be directly converted to JSON. ```rust let json = serde_json::to_string(&async_graphql::http::GQLResponse(res)); @@ -52,4 +51,4 @@ let json = serde_json::to_string(&async_graphql::http::GQLResponse(res)); ## Web server integration -Please refer to https://github.com/async-graphql/examples. +Please refer to . diff --git a/docs/en/src/subscription.md b/docs/en/src/subscription.md index ec0bbfdb..b9499d6e 100644 --- a/docs/en/src/subscription.md +++ b/docs/en/src/subscription.md @@ -1,8 +1,8 @@ # Subscription -The definition of the subscription root object is slightly different from other root objects. Its resolver function always returns a Stream, and the field parameters are usually used as data filtering conditions. +The definition of the subscription root object is slightly different from other root objects. Its resolver function always returns a [Stream](https://docs.rs/futures-core/~0.3/futures_core/stream/trait.Stream.html), and the field parameters are usually used as data filtering conditions. -The following example subscribes to an integer stream, which generates one integer per second. The parameter step specifies the integer step size with a default of 1 +The following example subscribes to an integer stream, which generates one integer per second. The parameter `step` specifies the integer step size with a default of 1. ```rust use async_graphql::*; diff --git a/docs/en/src/typesystem.md b/docs/en/src/typesystem.md index 21dbbf95..6ba9ae87 100644 --- a/docs/en/src/typesystem.md +++ b/docs/en/src/typesystem.md @@ -1,3 +1,3 @@ # Type System -`Async-graphql` implemented conversion from GraphQL Object to Rust struct, and it's easy to use. \ No newline at end of file +`Async-graphql` implements conversions from GraphQL Objects to Rust structs, and it's easy to use. diff --git a/docs/index.html b/docs/index.html index 1f6c7541..00e0f5a7 100644 --- a/docs/index.html +++ b/docs/index.html @@ -12,14 +12,11 @@ - - - - - +

Async-graphql Book

+

This book is available in multiple languages:

+