Remove dependency on serde_derive, satisfy Clippy, improve docs
This commit is contained in:
parent
e8383550ba
commit
02f7a5fbbc
|
@ -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 = [
|
||||
|
|
|
@ -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};
|
||||
|
|
89
src/error.rs
89
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<T: Display> From<T> 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<T, InputValueError>`.
|
||||
pub type InputValueResult<T> = std::result::Result<T, InputValueError>;
|
||||
|
||||
/// FieldError type
|
||||
/// An error in a field resolver.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FieldError(pub String, pub Option<serde_json::Value>);
|
||||
|
||||
|
@ -71,23 +72,17 @@ impl FieldError {
|
|||
}
|
||||
}
|
||||
|
||||
/// FieldResult type
|
||||
/// An alias for `Result<T, InputValueError>`.
|
||||
pub type FieldResult<T> = std::result::Result<T, FieldError>;
|
||||
|
||||
impl<E> From<E> for FieldError
|
||||
where
|
||||
E: std::fmt::Display + Send + Sync + 'static,
|
||||
{
|
||||
impl<E: Display> From<E> 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<C>(self, cb: C) -> FieldError
|
||||
where
|
||||
|
@ -125,15 +120,14 @@ impl<E: std::fmt::Display> ErrorExtensions for &E {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub trait ResultExt<T, E>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn extend_err<CB>(self, cb: CB) -> FieldResult<T>
|
||||
/// Extend a `Result`'s error value with [`ErrorExtensions`](trait.ErrorExtensions.html).
|
||||
pub trait ResultExt<T, E>: Sized {
|
||||
/// Extend the error value of the result with the callback.
|
||||
fn extend_err<C>(self, cb: C) -> FieldResult<T>
|
||||
where
|
||||
CB: FnOnce(&E) -> serde_json::Value;
|
||||
C: FnOnce(&E) -> serde_json::Value;
|
||||
|
||||
/// Extend the result to a `FieldResult`.
|
||||
fn extend(self) -> FieldResult<T>;
|
||||
}
|
||||
|
||||
|
@ -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<serde_json::Value>,
|
||||
},
|
||||
|
||||
|
@ -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<serde_json::Value>,
|
||||
/// The query error.
|
||||
err: QueryError,
|
||||
},
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
18
src/guard.rs
18
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<R: Guard>(self, other: R) -> And<Self, R> {
|
||||
|
@ -22,7 +24,7 @@ pub trait GuardExt: Guard + Sized {
|
|||
|
||||
impl<T: Guard> GuardExt for T {}
|
||||
|
||||
/// Guard for `GuardExt::and`
|
||||
/// Guard for [`GuardExt::and`](trait.GuardExt.html#method.and).
|
||||
pub struct And<A: Guard, B: Guard>(A, B);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -35,10 +37,12 @@ impl<A: Guard + Send + Sync, B: Guard + Send + Sync> Guard for And<A, B> {
|
|||
|
||||
/// 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<T: Send + Sync> {
|
||||
#[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<T: Send + Sync>: PostGuard<T> + Sized {
|
|||
|
||||
impl<T: PostGuard<R>, R: Send + Sync> PostGuardExt<R> for T {}
|
||||
|
||||
/// PostGuard for `PostGuardExt<T>::and`
|
||||
/// PostGuard for [`PostGuardExt<T>::and`](trait.PostGuardExt.html#method.and).
|
||||
pub struct PostAnd<T: Send + Sync, A: PostGuard<T>, B: PostGuard<T>>(A, B, PhantomData<T>);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Generate the page for GraphQL Playground
|
||||
|
|
105
src/lib.rs
105
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<T>, such as `Vec<i32>`
|
||||
/// - Slice<T>, such as `&[i32]`
|
||||
/// - Option<T>, such as `Option<i32>`
|
||||
/// - Object and &Object
|
||||
/// - Enum
|
||||
/// - FieldResult<T, E>, such as `FieldResult<i32, E>`
|
||||
/// - Scalar values, such as `i32` and `bool`. `usize`, `isize`, `u128` and `i128` are not
|
||||
/// supported
|
||||
/// - `Vec<T>`, such as `Vec<i32>`
|
||||
/// - Slices, such as `&[i32]`
|
||||
/// - `Option<T>`, such as `Option<i32>`
|
||||
/// - GraphQL objects.
|
||||
/// - GraphQL enums.
|
||||
/// - References to any of the above types, such as `&i32` or `&Option<String>`.
|
||||
/// - `FieldResult<T, E>`, such as `FieldResult<i32, E>`
|
||||
///
|
||||
/// # 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;
|
||||
|
|
|
@ -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<T: Serialize + Send + Sync> OutputValueType for OutputJson<T> {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[async_std::test]
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ pub async fn test_optional_type() {
|
|||
#[Object]
|
||||
impl Root {
|
||||
async fn value1(&self) -> Option<i32> {
|
||||
self.value1.clone()
|
||||
self.value1
|
||||
}
|
||||
|
||||
async fn value1_ref(&self) -> &Option<i32> {
|
||||
|
@ -23,7 +23,7 @@ pub async fn test_optional_type() {
|
|||
}
|
||||
|
||||
async fn value2(&self) -> Option<i32> {
|
||||
self.value2.clone()
|
||||
self.value2
|
||||
}
|
||||
|
||||
async fn value2_ref(&self) -> &Option<i32> {
|
||||
|
@ -35,7 +35,7 @@ pub async fn test_optional_type() {
|
|||
}
|
||||
|
||||
async fn test_input<'a>(&self, input: MyInput) -> Option<i32> {
|
||||
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!({
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user