Rework connection types

This commit is contained in:
Sunli 2022-04-19 12:25:11 +08:00
parent 548655ee71
commit 88ba75ec70
181 changed files with 1806 additions and 1477 deletions

View File

@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
include:
# - { rust: stable, os: ubuntu-latest }
# - { rust: stable, os: ubuntu-latest }
- { rust: 1.56.1, os: ubuntu-latest }
steps:
- name: Checkout
@ -55,7 +55,7 @@ jobs:
override: true
components: rustfmt
- name: Check format
run: cargo fmt --all -- --check
run: cargo +nightly fmt --all -- --check
clippy:
name: Run clippy - Rust (${{ matrix.rust }}) on ${{ matrix.os }}
@ -108,7 +108,6 @@ jobs:
- name: Clean examples
run: cargo clean
working-directory: ./examples
# build-nightly:
# name: Build nightly - Rust (${{ matrix.rust }}) on ${{ matrix.os }}
# runs-on: ${{ matrix.os }}

View File

@ -1,2 +1,12 @@
edition = "2021"
newline_style = "unix"
# comments
normalize_comments=true
wrap_comments=true
format_code_in_doc_comments=true
# imports
imports_granularity="Crate"
group_imports="StdExternalCrate"
# report
#report_fixme="Unnumbered"
#report_todo="Unnumbered"

View File

@ -1,8 +1,10 @@
use std::fmt::{self, Display, Formatter};
use darling::ast::{Data, Fields};
use darling::util::{Ignored, SpannedValue};
use darling::{FromDeriveInput, FromField, FromMeta, FromVariant};
use darling::{
ast::{Data, Fields},
util::{Ignored, SpannedValue},
FromDeriveInput, FromField, FromMeta, FromVariant,
};
use inflector::Inflector;
use syn::{
Attribute, Generics, Ident, Lit, LitBool, LitStr, Meta, NestedMeta, Path, Type, Visibility,
@ -186,6 +188,8 @@ pub struct SimpleObject {
#[darling(default)]
pub name: Option<String>,
#[darling(default)]
pub name_type: bool,
#[darling(default)]
pub rename_fields: Option<RenameRule>,
#[darling(default)]
pub rename_args: Option<RenameRule>,
@ -226,6 +230,7 @@ pub struct Argument {
pub struct Object {
pub internal: bool,
pub name: Option<String>,
pub name_type: bool,
pub rename_fields: Option<RenameRule>,
pub rename_args: Option<RenameRule>,
pub cache_control: CacheControl,

View File

@ -1,20 +1,21 @@
use std::{iter::FromIterator, str::FromStr};
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use std::iter::FromIterator;
use std::str::FromStr;
use syn::ext::IdentExt;
use syn::{
punctuated::Punctuated, Block, Error, FnArg, ImplItem, ItemImpl, Pat, ReturnType, Token, Type,
TypeReference,
ext::IdentExt, punctuated::Punctuated, Block, Error, FnArg, ImplItem, ItemImpl, Pat,
ReturnType, Token, Type, TypeReference,
};
use crate::args::{self, ComplexityType, RenameRuleExt, RenameTarget};
use crate::output_type::OutputType;
use crate::utils::{
extract_input_args, gen_deprecation, generate_default, generate_guards, get_cfg_attrs,
get_crate_name, get_rustdoc, get_type_path_and_name, parse_complexity_expr,
parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
use crate::{
args::{self, ComplexityType, RenameRuleExt, RenameTarget},
output_type::OutputType,
utils::{
extract_input_args, gen_deprecation, generate_default, generate_guards, get_cfg_attrs,
get_crate_name, get_rustdoc, get_type_path_and_name, parse_complexity_expr,
parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
},
};
pub fn generate(

View File

@ -1,8 +1,10 @@
use proc_macro::TokenStream;
use quote::quote;
use crate::args;
use crate::utils::{get_crate_name, get_rustdoc, GeneratorResult};
use crate::{
args,
utils::{get_crate_name, get_rustdoc, GeneratorResult},
};
pub fn generate(desc_args: &args::Description) -> GeneratorResult<TokenStream> {
let crate_name = get_crate_name(desc_args.internal);

View File

@ -1,13 +1,14 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::ext::IdentExt;
use syn::{Error, FnArg, ItemFn, Pat};
use syn::{ext::IdentExt, Error, FnArg, ItemFn, Pat};
use crate::args;
use crate::args::{Argument, RenameRuleExt, RenameTarget};
use crate::utils::{
generate_default, get_crate_name, get_rustdoc, parse_graphql_attrs, remove_graphql_attrs,
visible_fn, GeneratorResult,
use crate::{
args,
args::{Argument, RenameRuleExt, RenameTarget},
utils::{
generate_default, get_crate_name, get_rustdoc, parse_graphql_attrs, remove_graphql_attrs,
visible_fn, GeneratorResult,
},
};
pub fn generate(

View File

@ -1,11 +1,12 @@
use darling::ast::Data;
use proc_macro::TokenStream;
use quote::quote;
use syn::ext::IdentExt;
use syn::Error;
use syn::{ext::IdentExt, Error};
use crate::args::{self, RenameRuleExt, RenameTarget};
use crate::utils::{gen_deprecation, get_crate_name, get_rustdoc, visible_fn, GeneratorResult};
use crate::{
args::{self, RenameRuleExt, RenameTarget},
utils::{gen_deprecation, get_crate_name, get_rustdoc, visible_fn, GeneratorResult},
};
pub fn generate(enum_args: &args::Enum) -> GeneratorResult<TokenStream> {
let crate_name = get_crate_name(enum_args.internal);

View File

@ -1,11 +1,12 @@
use darling::ast::Data;
use proc_macro::TokenStream;
use quote::quote;
use syn::ext::IdentExt;
use syn::Error;
use syn::{ext::IdentExt, Error};
use crate::args::{self, RenameRuleExt, RenameTarget};
use crate::utils::{generate_default, get_crate_name, get_rustdoc, visible_fn, GeneratorResult};
use crate::{
args::{self, RenameRuleExt, RenameTarget},
utils::{generate_default, get_crate_name, get_rustdoc, visible_fn, GeneratorResult},
};
pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream> {
let crate_name = get_crate_name(object_args.internal);

View File

@ -4,14 +4,15 @@ use darling::ast::{Data, Style};
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::visit_mut::VisitMut;
use syn::{Error, Type};
use syn::{visit_mut::VisitMut, Error, Type};
use crate::args::{self, InterfaceField, InterfaceFieldArgument, RenameRuleExt, RenameTarget};
use crate::output_type::OutputType;
use crate::utils::{
gen_deprecation, generate_default, get_crate_name, get_rustdoc, visible_fn, GeneratorResult,
RemoveLifetime,
use crate::{
args::{self, InterfaceField, InterfaceFieldArgument, RenameRuleExt, RenameTarget},
output_type::OutputType,
utils::{
gen_deprecation, generate_default, get_crate_name, get_rustdoc, visible_fn,
GeneratorResult, RemoveLifetime,
},
};
pub fn generate(interface_args: &args::Interface) -> GeneratorResult<TokenStream> {

View File

@ -26,8 +26,7 @@ mod validators;
use darling::{FromDeriveInput, FromMeta};
use proc_macro::TokenStream;
use syn::parse_macro_input;
use syn::{AttributeArgs, DeriveInput, ItemFn, ItemImpl};
use syn::{parse_macro_input, AttributeArgs, DeriveInput, ItemFn, ItemImpl};
#[proc_macro_attribute]
#[allow(non_snake_case)]

View File

@ -4,8 +4,10 @@ use proc_macro2::Span;
use quote::quote;
use syn::{Error, LitInt};
use crate::args::{self, RenameTarget};
use crate::utils::{get_crate_name, get_rustdoc, visible_fn, GeneratorResult};
use crate::{
args::{self, RenameTarget},
utils::{get_crate_name, get_rustdoc, visible_fn, GeneratorResult},
};
pub fn generate(object_args: &args::MergedObject) -> GeneratorResult<TokenStream> {
let crate_name = get_crate_name(object_args.internal);

View File

@ -4,8 +4,10 @@ use proc_macro2::Span;
use quote::quote;
use syn::{Error, LitInt};
use crate::args::{self, RenameTarget};
use crate::utils::{get_crate_name, get_rustdoc, visible_fn, GeneratorResult};
use crate::{
args::{self, RenameTarget},
utils::{get_crate_name, get_rustdoc, visible_fn, GeneratorResult},
};
pub fn generate(object_args: &args::MergedSubscription) -> GeneratorResult<TokenStream> {
let crate_name = get_crate_name(object_args.internal);

View File

@ -3,8 +3,10 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::Error;
use crate::args::{self, NewTypeName, RenameTarget};
use crate::utils::{get_crate_name, get_rustdoc, visible_fn, GeneratorResult};
use crate::{
args::{self, NewTypeName, RenameTarget},
utils::{get_crate_name, get_rustdoc, visible_fn, GeneratorResult},
};
pub fn generate(newtype_args: &args::NewType) -> GeneratorResult<TokenStream> {
let crate_name = get_crate_name(newtype_args.internal);

View File

@ -1,20 +1,21 @@
use std::{iter::FromIterator, str::FromStr};
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use std::iter::FromIterator;
use std::str::FromStr;
use syn::ext::IdentExt;
use syn::{
punctuated::Punctuated, Block, Error, FnArg, ImplItem, ItemImpl, Pat, ReturnType, Token, Type,
TypeReference,
ext::IdentExt, punctuated::Punctuated, Block, Error, FnArg, ImplItem, ItemImpl, Pat,
ReturnType, Token, Type, TypeReference,
};
use crate::args::{self, ComplexityType, RenameRuleExt, RenameTarget};
use crate::output_type::OutputType;
use crate::utils::{
extract_input_args, gen_deprecation, generate_default, generate_guards, get_cfg_attrs,
get_crate_name, get_rustdoc, get_type_path_and_name, parse_complexity_expr,
parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
use crate::{
args::{self, ComplexityType, RenameRuleExt, RenameTarget},
output_type::OutputType,
utils::{
extract_input_args, gen_deprecation, generate_default, generate_guards, get_cfg_attrs,
get_crate_name, get_rustdoc, get_type_path_and_name, parse_complexity_expr,
parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
},
};
pub fn generate(
@ -25,10 +26,18 @@ pub fn generate(
let (self_ty, self_name) = get_type_path_and_name(item_impl.self_ty.as_ref())?;
let (impl_generics, _, where_clause) = item_impl.generics.split_for_impl();
let extends = object_args.extends;
let gql_typename = object_args
.name
.clone()
.unwrap_or_else(|| RenameTarget::Type.rename(self_name.clone()));
let gql_typename = if !object_args.name_type {
object_args
.name
.as_ref()
.map(|name| quote!(::std::borrow::Cow::Borrowed(#name)))
.unwrap_or_else(|| {
let name = RenameTarget::Type.rename(self_name.clone());
quote!(::std::borrow::Cow::Borrowed(#name))
})
} else {
quote!(<Self as #crate_name::TypeName>::type_name())
};
let desc = if object_args.use_type_description {
quote! { ::std::option::Option::Some(<Self as #crate_name::Description>::description()) }
@ -286,7 +295,6 @@ pub fn generate(
let ident = &method.sig.ident;
schema_fields.push(quote! {
#crate_name::static_assertions::assert_impl_one!(#ty: #crate_name::ObjectType);
<#ty>::create_type_info(registry);
if let #crate_name::registry::MetaType::Object { fields: obj_fields, .. } =
registry.create_fake_output_type::<#ty>() {
@ -681,12 +689,12 @@ pub fn generate(
#[#crate_name::async_trait::async_trait]
impl #impl_generics #crate_name::OutputType for #self_ty #where_clause {
fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
::std::borrow::Cow::Borrowed(#gql_typename)
#gql_typename
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
let ty = registry.create_output_type::<Self, _>(#crate_name::registry::MetaTypeId::Object, |registry| #crate_name::registry::MetaType::Object {
name: ::std::borrow::ToOwned::to_owned(#gql_typename),
name: ::std::borrow::Cow::into_owned(#gql_typename),
description: #desc,
fields: {
let mut fields = #crate_name::indexmap::IndexMap::new();

View File

@ -1,12 +1,15 @@
use std::collections::HashSet;
use darling::ast::{Data, Style};
use proc_macro::TokenStream;
use quote::quote;
use std::collections::HashSet;
use syn::{Error, Type};
use crate::args;
use crate::args::{RenameRuleExt, RenameTarget};
use crate::utils::{get_crate_name, get_rustdoc, visible_fn, GeneratorResult};
use crate::{
args,
args::{RenameRuleExt, RenameTarget},
utils::{get_crate_name, get_rustdoc, visible_fn, GeneratorResult},
};
pub fn generate(object_args: &args::OneofObject) -> GeneratorResult<TokenStream> {
let crate_name = get_crate_name(object_args.internal);

View File

@ -2,9 +2,9 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::ItemImpl;
use crate::args::{self, RenameTarget};
use crate::utils::{
get_crate_name, get_rustdoc, get_type_path_and_name, visible_fn, GeneratorResult,
use crate::{
args::{self, RenameTarget},
utils::{get_crate_name, get_rustdoc, get_type_path_and_name, visible_fn, GeneratorResult},
};
pub fn generate(

View File

@ -1,14 +1,15 @@
use std::str::FromStr;
use darling::ast::Data;
use proc_macro::TokenStream;
use quote::quote;
use std::str::FromStr;
use syn::ext::IdentExt;
use syn::visit::Visit;
use syn::{Error, Ident, LifetimeDef, Path, Type};
use syn::{ext::IdentExt, visit::Visit, Error, Ident, LifetimeDef, Path, Type};
use crate::args::{self, RenameRuleExt, RenameTarget, SimpleObjectField};
use crate::utils::{
gen_deprecation, generate_guards, get_crate_name, get_rustdoc, visible_fn, GeneratorResult,
use crate::{
args::{self, RenameRuleExt, RenameTarget, SimpleObjectField},
utils::{
gen_deprecation, generate_guards, get_crate_name, get_rustdoc, visible_fn, GeneratorResult,
},
};
#[derive(Debug)]
@ -29,10 +30,18 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
let ident = &object_args.ident;
let (impl_generics, ty_generics, where_clause) = object_args.generics.split_for_impl();
let extends = object_args.extends;
let gql_typename = object_args
.name
.clone()
.unwrap_or_else(|| RenameTarget::Type.rename(ident.to_string()));
let gql_typename = if !object_args.name_type {
object_args
.name
.as_ref()
.map(|name| quote!(::std::borrow::Cow::Borrowed(#name)))
.unwrap_or_else(|| {
let name = RenameTarget::Type.rename(ident.to_string());
quote!(::std::borrow::Cow::Borrowed(#name))
})
} else {
quote!(<Self as #crate_name::TypeName>::type_name())
};
let desc = get_rustdoc(&object_args.attrs)?
.map(|s| quote! { ::std::option::Option::Some(#s) })
@ -172,7 +181,6 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
});
} else {
schema_fields.push(quote! {
#crate_name::static_assertions::assert_impl_one!(#ty: #crate_name::ObjectType);
#ty::create_type_info(registry);
if let #crate_name::registry::MetaType::Object { fields: obj_fields, .. } =
registry.create_fake_output_type::<#ty>() {
@ -307,12 +315,12 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
#[#crate_name::async_trait::async_trait]
impl #impl_generics #crate_name::OutputType for #ident #ty_generics #where_clause {
fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
::std::borrow::Cow::Borrowed(#gql_typename)
#gql_typename
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
registry.create_output_type::<Self, _>(#crate_name::registry::MetaTypeId::Object, |registry| #crate_name::registry::MetaType::Object {
name: ::std::borrow::ToOwned::to_owned(#gql_typename),
name: ::std::borrow::Cow::into_owned(#gql_typename),
description: #desc,
fields: {
let mut fields = #crate_name::indexmap::IndexMap::new();

View File

@ -1,14 +1,18 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::ext::IdentExt;
use syn::{Block, Error, ImplItem, ItemImpl, ReturnType, Type, TypeImplTrait, TypeParamBound};
use syn::{
ext::IdentExt, Block, Error, ImplItem, ItemImpl, ReturnType, Type, TypeImplTrait,
TypeParamBound,
};
use crate::args::{self, ComplexityType, RenameRuleExt, RenameTarget, SubscriptionField};
use crate::output_type::OutputType;
use crate::utils::{
extract_input_args, gen_deprecation, generate_default, generate_guards, get_cfg_attrs,
get_crate_name, get_rustdoc, get_type_path_and_name, parse_complexity_expr,
parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
use crate::{
args::{self, ComplexityType, RenameRuleExt, RenameTarget, SubscriptionField},
output_type::OutputType,
utils::{
extract_input_args, gen_deprecation, generate_default, generate_guards, get_cfg_attrs,
get_crate_name, get_rustdoc, get_type_path_and_name, parse_complexity_expr,
parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
},
};
pub fn generate(

View File

@ -1,12 +1,14 @@
use std::collections::HashSet;
use darling::ast::{Data, Style};
use proc_macro::TokenStream;
use quote::quote;
use std::collections::HashSet;
use syn::visit_mut::VisitMut;
use syn::{Error, Type};
use syn::{visit_mut::VisitMut, Error, Type};
use crate::args::{self, RenameTarget};
use crate::utils::{get_crate_name, get_rustdoc, visible_fn, GeneratorResult, RemoveLifetime};
use crate::{
args::{self, RenameTarget},
utils::{get_crate_name, get_rustdoc, visible_fn, GeneratorResult, RemoveLifetime},
};
pub fn generate(union_args: &args::Union) -> GeneratorResult<TokenStream> {
let crate_name = get_crate_name(union_args.internal);

View File

@ -1,15 +1,13 @@
use std::collections::HashSet;
use darling::util::SpannedValue;
use darling::FromMeta;
use darling::{util::SpannedValue, FromMeta};
use proc_macro2::{Span, TokenStream, TokenTree};
use proc_macro_crate::{crate_name, FoundCrate};
use quote::quote;
use syn::visit::Visit;
use syn::visit_mut::VisitMut;
use syn::{
visit_mut, Attribute, Error, Expr, ExprPath, FnArg, Ident, ImplItemMethod, Lifetime, Lit,
LitStr, Meta, Pat, PatIdent, Type, TypeGroup, TypeParamBound, TypeReference,
visit::Visit, visit_mut, visit_mut::VisitMut, Attribute, Error, Expr, ExprPath, FnArg, Ident,
ImplItemMethod, Lifetime, Lit, LitStr, Meta, Pat, PatIdent, Type, TypeGroup, TypeParamBound,
TypeReference,
};
use thiserror::Error;

View File

@ -1,5 +1,4 @@
use darling::util::SpannedValue;
use darling::FromMeta;
use darling::{util::SpannedValue, FromMeta};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{Error, Expr, Lit, Result};

View File

@ -1,22 +1,27 @@
use actix_http::body::BoxBody;
use actix_web::error::JsonPayloadError;
use std::future::Future;
use std::io::{self, ErrorKind};
use std::pin::Pin;
use std::{
future::Future,
io::{self, ErrorKind},
pin::Pin,
};
use actix_http::error::PayloadError;
use actix_web::dev::Payload;
use actix_web::http::{Method, StatusCode};
use actix_web::{http, Error, FromRequest, HttpRequest, HttpResponse, Responder, Result};
use futures_util::future::{self, FutureExt};
use futures_util::{StreamExt, TryStreamExt};
use async_graphql::http::MultipartOptions;
use async_graphql::ParseRequestError;
use actix_http::{body::BoxBody, error::PayloadError};
use actix_web::{
dev::Payload,
error::JsonPayloadError,
http,
http::{Method, StatusCode},
Error, FromRequest, HttpRequest, HttpResponse, Responder, Result,
};
use async_graphql::{http::MultipartOptions, ParseRequestError};
use futures_util::{
future::{self, FutureExt},
StreamExt, TryStreamExt,
};
/// Extractor for GraphQL request.
///
/// `async_graphql::http::MultipartOptions` allows to configure extraction process.
/// `async_graphql::http::MultipartOptions` allows to configure extraction
/// process.
pub struct GraphQLRequest(pub async_graphql::Request);
impl GraphQLRequest {
@ -47,7 +52,8 @@ impl FromRequest for GraphQLRequest {
/// Extractor for GraphQL batch request.
///
/// `async_graphql::http::MultipartOptions` allows to configure extraction process.
/// `async_graphql::http::MultipartOptions` allows to configure extraction
/// process.
pub struct GraphQLBatchRequest(pub async_graphql::BatchRequest);
impl GraphQLBatchRequest {
@ -138,8 +144,8 @@ impl FromRequest for GraphQLBatchRequest {
/// Responder for a GraphQL response.
///
/// This contains a batch response, but since regular responses are a type of batch response it
/// works for both.
/// This contains a batch response, but since regular responses are a type of
/// batch response it works for both.
pub struct GraphQLResponse(pub async_graphql::BatchResponse);
impl From<async_graphql::Response> for GraphQLResponse {
@ -156,9 +162,10 @@ impl From<async_graphql::BatchResponse> for GraphQLResponse {
#[cfg(feature = "cbor")]
mod cbor {
use actix_web::{http::StatusCode, ResponseError};
use core::fmt;
use actix_web::{http::StatusCode, ResponseError};
#[derive(Debug)]
pub struct Error(pub serde_cbor::Error);
impl fmt::Display for Error {

View File

@ -1,21 +1,21 @@
use std::future::Future;
use std::str::FromStr;
use std::time::{Duration, Instant};
use std::{
future::Future,
str::FromStr,
time::{Duration, Instant},
};
use actix::{
Actor, ActorContext, AsyncContext, ContextFutureSpawner, StreamHandler, WrapFuture, WrapStream,
Actor, ActorContext, ActorFutureExt, ActorStreamExt, AsyncContext, ContextFutureSpawner,
StreamHandler, WrapFuture, WrapStream,
};
use actix::{ActorFutureExt, ActorStreamExt};
use actix_http::error::PayloadError;
use actix_http::ws;
use actix_web::web::Bytes;
use actix_web::{Error, HttpRequest, HttpResponse};
use actix_http::{error::PayloadError, ws};
use actix_web::{web::Bytes, Error, HttpRequest, HttpResponse};
use actix_web_actors::ws::{CloseReason, Message, ProtocolError, WebsocketContext};
use futures_util::future::Ready;
use futures_util::stream::Stream;
use async_graphql::http::{WebSocket, WebSocketProtocols, WsMessage, ALL_WEBSOCKET_PROTOCOLS};
use async_graphql::{Data, ObjectType, Result, Schema, SubscriptionType};
use async_graphql::{
http::{WebSocket, WebSocketProtocols, WsMessage, ALL_WEBSOCKET_PROTOCOLS},
Data, ObjectType, Result, Schema, SubscriptionType,
};
use futures_util::{future::Ready, stream::Stream};
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
@ -63,17 +63,19 @@ where
OnInit: Fn(serde_json::Value) -> OnInitFut + Unpin + Send + 'static,
OnInitFut: Future<Output = async_graphql::Result<Data>> + Send + 'static,
{
/// Specify the initial subscription context data, usually you can get something from the
/// incoming request to create it.
/// Specify the initial subscription context data, usually you can get
/// something from the incoming request to create it.
#[must_use]
pub fn with_data(self, data: Data) -> Self {
Self { data, ..self }
}
/// Specify a callback function to be called when the connection is initialized.
/// Specify a callback function to be called when the connection is
/// initialized.
///
/// You can get something from the payload of [`GQL_CONNECTION_INIT` message](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init) to create [`Data`].
/// The data returned by this callback function will be merged with the data specified by [`with_data`].
/// The data returned by this callback function will be merged with the data
/// specified by [`with_data`].
pub fn on_connection_init<OnConnInit2, Fut>(
self,
callback: OnConnInit2,

View File

@ -1,9 +1,7 @@
use actix_http::Method;
use actix_web::dev::Service;
use actix_web::{guard, test, web, web::Data, App};
use serde_json::json;
use actix_web::{dev::Service, guard, test, web, web::Data, App};
use async_graphql::*;
use serde_json::json;
use test_utils::*;
mod test_utils;

View File

@ -1,6 +1,6 @@
use actix_web::{web, HttpRequest, HttpResponse};
use async_graphql::http::{playground_source, GraphQLPlaygroundConfig};
use async_graphql::{
http::{playground_source, GraphQLPlaygroundConfig},
Context, EmptyMutation, EmptySubscription, Object, ObjectType, Schema, SubscriptionType,
};
use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};

View File

@ -1,12 +1,11 @@
use std::io::ErrorKind;
use async_graphql::futures_util::TryStreamExt;
use async_graphql::http::MultipartOptions;
use async_graphql::ParseRequestError;
use axum::http::Method;
use async_graphql::{futures_util::TryStreamExt, http::MultipartOptions, ParseRequestError};
use axum::{
extract::{BodyStream, FromRequest, RequestParts},
http, BoxError,
http,
http::Method,
BoxError,
};
use bytes::Bytes;
use tokio_util::compat::TokioAsyncReadCompatExt;
@ -25,10 +24,12 @@ impl GraphQLRequest {
/// Rejection response types.
pub mod rejection {
use async_graphql::ParseRequestError;
use axum::body::{boxed, Body, BoxBody};
use axum::http;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::{
body::{boxed, Body, BoxBody},
http,
http::StatusCode,
response::IntoResponse,
};
/// Rejection used for [`GraphQLRequest`](GraphQLRequest).
pub struct GraphQLRejection(pub ParseRequestError);

View File

@ -1,12 +1,14 @@
use axum::body::{boxed, Body, BoxBody};
use axum::http;
use axum::http::{HeaderValue, Response};
use axum::response::IntoResponse;
use axum::{
body::{boxed, Body, BoxBody},
http,
http::{HeaderValue, Response},
response::IntoResponse,
};
/// Responder for a GraphQL response.
///
/// This contains a batch response, but since regular responses are a type of batch response it
/// works for both.
/// This contains a batch response, but since regular responses are a type of
/// batch response it works for both.
pub struct GraphQLResponse(pub async_graphql::BatchResponse);
impl From<async_graphql::Response> for GraphQLResponse {

View File

@ -1,20 +1,26 @@
use std::borrow::Cow;
use std::convert::Infallible;
use std::future::Future;
use std::str::FromStr;
use std::{borrow::Cow, convert::Infallible, future::Future, str::FromStr};
use async_graphql::futures_util::task::{Context, Poll};
use async_graphql::http::{WebSocketProtocols, WsMessage, ALL_WEBSOCKET_PROTOCOLS};
use async_graphql::{Data, ObjectType, Result, Schema, SubscriptionType};
use axum::body::{boxed, BoxBody, HttpBody};
use axum::extract::ws::{CloseFrame, Message};
use axum::extract::{FromRequest, RequestParts, WebSocketUpgrade};
use axum::http::{self, Request, Response, StatusCode};
use axum::response::IntoResponse;
use axum::Error;
use futures_util::future::{BoxFuture, Ready};
use futures_util::stream::{SplitSink, SplitStream};
use futures_util::{future, Sink, SinkExt, Stream, StreamExt};
use async_graphql::{
futures_util::task::{Context, Poll},
http::{WebSocketProtocols, WsMessage, ALL_WEBSOCKET_PROTOCOLS},
Data, ObjectType, Result, Schema, SubscriptionType,
};
use axum::{
body::{boxed, BoxBody, HttpBody},
extract::{
ws::{CloseFrame, Message},
FromRequest, RequestParts, WebSocketUpgrade,
},
http::{self, Request, Response, StatusCode},
response::IntoResponse,
Error,
};
use futures_util::{
future,
future::{BoxFuture, Ready},
stream::{SplitSink, SplitStream},
Sink, SinkExt, Stream, StreamExt,
};
use tower_service::Service;
/// A GraphQL protocol extractor.
@ -191,17 +197,19 @@ where
OnConnInit: Fn(serde_json::Value) -> OnConnInitFut + Send + Sync + 'static,
OnConnInitFut: Future<Output = async_graphql::Result<Data>> + Send + 'static,
{
/// Specify the initial subscription context data, usually you can get something from the
/// incoming request to create it.
/// Specify the initial subscription context data, usually you can get
/// something from the incoming request to create it.
#[must_use]
pub fn with_data(self, data: Data) -> Self {
Self { data, ..self }
}
/// Specify a callback function to be called when the connection is initialized.
/// Specify a callback function to be called when the connection is
/// initialized.
///
/// You can get something from the payload of [`GQL_CONNECTION_INIT` message](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init) to create [`Data`].
/// The data returned by this callback function will be merged with the data specified by [`with_data`].
/// The data returned by this callback function will be merged with the data
/// specified by [`with_data`].
pub fn on_connection_init<OnConnInit2, Fut>(
self,
callback: OnConnInit2,

View File

@ -1,23 +1,31 @@
use async_graphql::http::MultipartOptions;
use poem::error::BadRequest;
use poem::http::{header, Method};
use poem::web::Query;
use poem::{async_trait, FromRequest, Request, RequestBody, Result};
use poem::{
async_trait,
error::BadRequest,
http::{header, Method},
web::Query,
FromRequest, Request, RequestBody, Result,
};
use tokio_util::compat::TokioAsyncReadCompatExt;
/// An extractor for GraphQL request.
///
/// You can just use the extractor as in the example below, but I would recommend using
/// the [`GraphQL`](crate::GraphQL) endpoint because it is easier to integrate.
/// You can just use the extractor as in the example below, but I would
/// recommend using the [`GraphQL`](crate::GraphQL) endpoint because it is
/// easier to integrate.
///
/// # Example
///
/// ```
/// use poem::{handler, Route, post, EndpointExt};
/// use poem::web::{Json, Data};
/// use poem::middleware::AddData;
/// use async_graphql_poem::GraphQLRequest;
/// use async_graphql::{EmptyMutation, EmptySubscription, Object, Schema};
/// use async_graphql_poem::GraphQLRequest;
/// use poem::{
/// handler,
/// middleware::AddData,
/// post,
/// web::{Data, Json},
/// EndpointExt, Route,
/// };
///
/// struct Query;
///

View File

@ -8,9 +8,9 @@ use crate::{GraphQLBatchRequest, GraphQLBatchResponse};
/// # Example
///
/// ```
/// use poem::{Route, post};
/// use async_graphql_poem::GraphQL;
/// use async_graphql::{EmptyMutation, EmptySubscription, Object, Schema};
/// use async_graphql_poem::GraphQL;
/// use poem::{post, Route};
///
/// struct Query;
///

View File

@ -1,5 +1,4 @@
use poem::web::Json;
use poem::{IntoResponse, Response};
use poem::{web::Json, IntoResponse, Response};
/// Response for `async_graphql::Request`.
pub struct GraphQLResponse(pub async_graphql::Response);

View File

@ -1,15 +1,19 @@
use std::io::Error as IoError;
use std::str::FromStr;
use std::{io::Error as IoError, str::FromStr};
use async_graphql::http::{WebSocketProtocols, WsMessage, ALL_WEBSOCKET_PROTOCOLS};
use async_graphql::{Data, ObjectType, Schema, SubscriptionType};
use futures_util::future::{self, Ready};
use futures_util::stream::{SplitSink, SplitStream};
use futures_util::{Future, Sink, SinkExt, Stream, StreamExt};
use poem::http::StatusCode;
use poem::web::websocket::{Message, WebSocket};
use async_graphql::{
http::{WebSocketProtocols, WsMessage, ALL_WEBSOCKET_PROTOCOLS},
Data, ObjectType, Schema, SubscriptionType,
};
use futures_util::{
future::{self, Ready},
stream::{SplitSink, SplitStream},
Future, Sink, SinkExt, Stream, StreamExt,
};
use poem::{
http, Endpoint, Error, FromRequest, IntoResponse, Request, RequestBody, Response, Result,
http,
http::StatusCode,
web::websocket::{Message, WebSocket},
Endpoint, Error, FromRequest, IntoResponse, Request, RequestBody, Response, Result,
};
/// A GraphQL protocol extractor.
@ -39,10 +43,10 @@ impl<'a> FromRequest<'a> for GraphQLProtocol {
/// # Example
///
/// ```
/// use poem::{Route, get};
/// use async_graphql_poem::GraphQLSubscription;
/// use async_graphql::{EmptyMutation, Object, Schema, Subscription};
/// use futures_util::{Stream, stream};
/// use async_graphql_poem::GraphQLSubscription;
/// use futures_util::{stream, Stream};
/// use poem::{get, Route};
///
/// struct Query;
///
@ -181,17 +185,19 @@ where
OnConnInit: Fn(serde_json::Value) -> OnConnInitFut + Send + Sync + 'static,
OnConnInitFut: Future<Output = async_graphql::Result<Data>> + Send + 'static,
{
/// Specify the initial subscription context data, usually you can get something from the
/// incoming request to create it.
/// Specify the initial subscription context data, usually you can get
/// something from the incoming request to create it.
#[must_use]
pub fn with_data(self, data: Data) -> Self {
Self { data, ..self }
}
/// Specify a callback function to be called when the connection is initialized.
/// Specify a callback function to be called when the connection is
/// initialized.
///
/// You can get something from the payload of [`GQL_CONNECTION_INIT` message](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init) to create [`Data`].
/// The data returned by this callback function will be merged with the data specified by [`with_data`].
/// The data returned by this callback function will be merged with the data
/// specified by [`with_data`].
pub fn on_connection_init<OnConnInit2, Fut>(
self,
callback: OnConnInit2,

View File

@ -1,10 +1,11 @@
//! Async-graphql integration with Rocket.
//!
//! Note: This integrates with the unreleased version 0.5 of Rocket, and so breaking changes in
//! both this library and Rocket are to be expected.
//! Note: This integrates with the unreleased version 0.5 of Rocket, and so
//! breaking changes in both this library and Rocket are to be expected.
//!
//! To configure options for sending and receiving multipart requests, add your instance of
//! `MultipartOptions` to the state managed by Rocket (`.manage(your_multipart_options)`).
//! To configure options for sending and receiving multipart requests, add your
//! instance of `MultipartOptions` to the state managed by Rocket
//! (`.manage(your_multipart_options)`).
//!
//! **[Full Example](<https://github.com/async-graphql/examples/blob/master/rocket/starwars/src/main.rs>)**
@ -14,8 +15,9 @@
use core::any::Any;
use std::io::Cursor;
use async_graphql::http::MultipartOptions;
use async_graphql::{ObjectType, ParseRequestError, Schema, SubscriptionType};
use async_graphql::{
http::MultipartOptions, ObjectType, ParseRequestError, Schema, SubscriptionType,
};
use rocket::{
data::{self, Data, FromData, ToByteUnit},
form::FromForm,
@ -185,11 +187,11 @@ impl<'r> FromData<'r> for GraphQLRequest {
}
}
/// Wrapper around `async-graphql::Response` that is a Rocket responder so it can be returned from
/// a routing function in Rocket.
/// Wrapper around `async-graphql::Response` that is a Rocket responder so it
/// can be returned from a routing function in Rocket.
///
/// It contains a `BatchResponse` but since a response is a type of batch response it works for
/// both.
/// It contains a `BatchResponse` but since a response is a type of batch
/// response it works for both.
#[derive(Debug)]
pub struct GraphQLResponse(pub async_graphql::BatchResponse);

View File

@ -11,20 +11,20 @@
#[cfg(feature = "websocket")]
mod subscription;
use async_graphql::http::MultipartOptions;
use async_graphql::{ObjectType, ParseRequestError, Schema, SubscriptionType};
use tide::utils::async_trait;
use async_graphql::{
http::MultipartOptions, ObjectType, ParseRequestError, Schema, SubscriptionType,
};
#[cfg(feature = "websocket")]
pub use subscription::GraphQLSubscription;
use tide::{
http::{
headers::{self, HeaderValue},
Method,
},
utils::async_trait,
Body, Request, Response, StatusCode,
};
#[cfg(feature = "websocket")]
pub use subscription::GraphQLSubscription;
/// Create a new GraphQL endpoint with the schema.
///
/// Default multipart options are used and batch operations are supported.
@ -106,7 +106,8 @@ pub async fn receive_request<State: Clone + Send + Sync + 'static>(
receive_request_opts(request, Default::default()).await
}
/// Convert a Tide request to a GraphQL request with options on how to receive multipart.
/// Convert a Tide request to a GraphQL request with options on how to receive
/// multipart.
pub async fn receive_request_opts<State: Clone + Send + Sync + 'static>(
request: Request<State>,
opts: MultipartOptions,
@ -124,7 +125,8 @@ pub async fn receive_batch_request<State: Clone + Send + Sync + 'static>(
receive_batch_request_opts(request, Default::default()).await
}
/// Convert a Tide request to a GraphQL batch request with options on how to receive multipart.
/// Convert a Tide request to a GraphQL batch request with options on how to
/// receive multipart.
pub async fn receive_batch_request_opts<State: Clone + Send + Sync + 'static>(
mut request: Request<State>,
opts: MultipartOptions,

View File

@ -1,15 +1,12 @@
use std::future::Future;
use std::str::FromStr;
use std::{future::Future, str::FromStr};
use async_graphql::http::{
WebSocket as AGWebSocket, WebSocketProtocols, WsMessage, ALL_WEBSOCKET_PROTOCOLS,
use async_graphql::{
http::{WebSocket as AGWebSocket, WebSocketProtocols, WsMessage, ALL_WEBSOCKET_PROTOCOLS},
Data, ObjectType, Result, Schema, SubscriptionType,
};
use async_graphql::{Data, ObjectType, Result, Schema, SubscriptionType};
use futures_util::future::Ready;
use futures_util::{future, StreamExt};
use futures_util::{future, future::Ready, StreamExt};
use tide::Endpoint;
use tide_websockets::tungstenite::protocol::CloseFrame;
use tide_websockets::Message;
use tide_websockets::{tungstenite::protocol::CloseFrame, Message};
/// A GraphQL subscription endpoint builder.
#[cfg_attr(docsrs, doc(cfg(feature = "websocket")))]
@ -49,10 +46,12 @@ where
OnConnInit: Fn(serde_json::Value) -> OnConnInitFut + Clone + Send + Sync + 'static,
OnConnInitFut: Future<Output = async_graphql::Result<Data>> + Send + 'static,
{
/// Specify a callback function to be called when the connection is initialized.
/// Specify a callback function to be called when the connection is
/// initialized.
///
/// You can get something from the payload of [`GQL_CONNECTION_INIT` message](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init) to create [`Data`].
/// The data returned by this callback function will be merged with the data specified by [`with_data`].
/// The data returned by this callback function will be merged with the data
/// specified by [`with_data`].
pub fn on_connection_init<OnConnInit2, Fut>(
self,
callback: OnConnInit2,

View File

@ -1,6 +1,7 @@
use reqwest::Client;
use std::time::Duration;
use reqwest::Client;
pub fn client() -> Client {
Client::builder().no_proxy().build().unwrap()
}

View File

@ -1,17 +1,15 @@
use std::io;
use std::io::ErrorKind;
use std::{io, io::ErrorKind};
use async_graphql::http::MultipartOptions;
use async_graphql::{BatchRequest, ObjectType, Schema, SubscriptionType};
use async_graphql::{http::MultipartOptions, BatchRequest, ObjectType, Schema, SubscriptionType};
use futures_util::TryStreamExt;
use warp::reply::Response as WarpResponse;
use warp::{Buf, Filter, Rejection, Reply};
use warp::{reply::Response as WarpResponse, Buf, Filter, Rejection, Reply};
use crate::GraphQLBadRequest;
/// GraphQL batch request filter
///
/// It outputs a tuple containing the `async_graphql::Schema` and `async_graphql::BatchRequest`.
/// It outputs a tuple containing the `async_graphql::Schema` and
/// `async_graphql::BatchRequest`.
pub fn graphql_batch<Query, Mutation, Subscription>(
schema: Schema<Query, Mutation, Subscription>,
) -> impl Filter<Extract = ((Schema<Query, Mutation, Subscription>, BatchRequest),), Error = Rejection>
@ -24,7 +22,8 @@ where
graphql_batch_opts(schema, Default::default())
}
/// Similar to graphql_batch, but you can set the options with :`async_graphql::MultipartOptions`.
/// Similar to graphql_batch, but you can set the options with
/// :`async_graphql::MultipartOptions`.
pub fn graphql_batch_opts<Query, Mutation, Subscription>(
schema: Schema<Query, Mutation, Subscription>,
opts: MultipartOptions,

View File

@ -1,16 +1,21 @@
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::{
error::Error,
fmt::{self, Display, Formatter},
};
use async_graphql::ParseRequestError;
use warp::http::{Response, StatusCode};
use warp::hyper::Body;
use warp::reject::Reject;
use warp::Reply;
use warp::{
http::{Response, StatusCode},
hyper::Body,
reject::Reject,
Reply,
};
/// Bad request error.
///
/// It's a wrapper of `async_graphql::ParseRequestError`. It is also a `Reply` - by default it just
/// returns a response containing the error message in plain text.
/// It's a wrapper of `async_graphql::ParseRequestError`. It is also a `Reply` -
/// by default it just returns a response containing the error message in plain
/// text.
#[derive(Debug)]
pub struct GraphQLBadRequest(pub ParseRequestError);

View File

@ -1,24 +1,25 @@
use async_graphql::http::MultipartOptions;
use async_graphql::{BatchRequest, ObjectType, Request, Schema, SubscriptionType};
use warp::reply::Response as WarpResponse;
use warp::{Filter, Rejection, Reply};
use async_graphql::{
http::MultipartOptions, BatchRequest, ObjectType, Request, Schema, SubscriptionType,
};
use warp::{reply::Response as WarpResponse, Filter, Rejection, Reply};
use crate::{graphql_batch_opts, GraphQLBadRequest, GraphQLBatchResponse};
/// GraphQL request filter
///
/// It outputs a tuple containing the `async_graphql::Schema` and `async_graphql::Request`.
/// It outputs a tuple containing the `async_graphql::Schema` and
/// `async_graphql::Request`.
///
/// # Examples
///
/// *[Full Example](<https://github.com/async-graphql/examples/blob/master/warp/starwars/src/main.rs>)*
///
/// ```no_run
/// use std::convert::Infallible;
///
/// use async_graphql::*;
/// use async_graphql_warp::*;
/// use warp::Filter;
/// use std::convert::Infallible;
///
/// struct QueryRoot;
///
@ -33,10 +34,13 @@ use crate::{graphql_batch_opts, GraphQLBadRequest, GraphQLBatchResponse};
///
/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
/// let filter = async_graphql_warp::graphql(schema)
/// .and_then(|(schema, request): (MySchema, async_graphql::Request)| async move {
/// Ok::<_, Infallible>(async_graphql_warp::GraphQLResponse::from(schema.execute(request).await))
/// });
/// let filter = async_graphql_warp::graphql(schema).and_then(
/// |(schema, request): (MySchema, async_graphql::Request)| async move {
/// Ok::<_, Infallible>(async_graphql_warp::GraphQLResponse::from(
/// schema.execute(request).await,
/// ))
/// },
/// );
/// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await;
/// # });
/// ```
@ -57,7 +61,8 @@ where
graphql_opts(schema, Default::default())
}
/// Similar to graphql, but you can set the options `async_graphql::MultipartOptions`.
/// Similar to graphql, but you can set the options
/// `async_graphql::MultipartOptions`.
pub fn graphql_opts<Query, Mutation, Subscription>(
schema: Schema<Query, Mutation, Subscription>,
opts: MultipartOptions,

View File

@ -1,25 +1,28 @@
use std::future::Future;
use std::str::FromStr;
use std::{future::Future, str::FromStr};
use async_graphql::http::{WebSocketProtocols, WsMessage};
use async_graphql::{Data, ObjectType, Result, Schema, SubscriptionType};
use futures_util::future::Ready;
use futures_util::stream::{SplitSink, SplitStream};
use futures_util::{future, Sink, Stream, StreamExt};
use warp::filters::ws;
use warp::ws::Message;
use warp::{Error, Filter, Rejection, Reply};
use async_graphql::{
http::{WebSocketProtocols, WsMessage},
Data, ObjectType, Result, Schema, SubscriptionType,
};
use futures_util::{
future,
future::Ready,
stream::{SplitSink, SplitStream},
Sink, Stream, StreamExt,
};
use warp::{filters::ws, ws::Message, Error, Filter, Rejection, Reply};
/// GraphQL subscription filter
///
/// # Examples
///
/// ```no_run
/// use std::time::Duration;
///
/// use async_graphql::*;
/// use async_graphql_warp::*;
/// use warp::Filter;
/// use futures_util::stream::{Stream, StreamExt};
/// use std::time::Duration;
/// use warp::Filter;
///
/// struct QueryRoot;
///
@ -48,8 +51,8 @@ use warp::{Error, Filter, Rejection, Reply};
///
/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
/// let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot);
/// let filter = async_graphql_warp::graphql_subscription(schema)
/// .or(warp::any().map(|| "Hello, World!"));
/// let filter =
/// async_graphql_warp::graphql_subscription(schema).or(warp::any().map(|| "Hello, World!"));
/// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await;
/// # });
/// ```
@ -80,7 +83,8 @@ where
})
}
/// Create a `Filter` that parse [WebSocketProtocols] from `sec-websocket-protocol` header.
/// Create a `Filter` that parse [WebSocketProtocols] from
/// `sec-websocket-protocol` header.
pub fn graphql_protocol() -> impl Filter<Extract = (WebSocketProtocols,), Error = Rejection> + Clone
{
warp::header::optional::<String>("sec-websocket-protocol").map(|protocols: Option<String>| {
@ -105,11 +109,12 @@ fn default_on_connection_init(_: serde_json::Value) -> Ready<async_graphql::Resu
/// # Examples
///
/// ```no_run
/// use std::time::Duration;
///
/// use async_graphql::*;
/// use async_graphql_warp::*;
/// use warp::{Filter, ws};
/// use futures_util::stream::{Stream, StreamExt};
/// use std::time::Duration;
/// use warp::{ws, Filter};
///
/// struct QueryRoot;
///
@ -144,9 +149,8 @@ fn default_on_connection_init(_: serde_json::Value) -> Ready<async_graphql::Resu
/// .map(move |ws: ws::Ws, protocol| {
/// let schema = schema.clone();
///
/// let reply = ws.on_upgrade(move |socket| {
/// GraphQLWebSocket::new(socket, schema, protocol).serve()
/// });
/// let reply = ws
/// .on_upgrade(move |socket| GraphQLWebSocket::new(socket, schema, protocol).serve());
///
/// warp::reply::with_header(
/// reply,
@ -231,17 +235,19 @@ where
OnConnInit: Fn(serde_json::Value) -> OnConnInitFut + Send + Sync + 'static,
OnConnInitFut: Future<Output = async_graphql::Result<Data>> + Send + 'static,
{
/// Specify the initial subscription context data, usually you can get something from the
/// incoming request to create it.
/// Specify the initial subscription context data, usually you can get
/// something from the incoming request to create it.
#[must_use]
pub fn with_data(self, data: Data) -> Self {
Self { data, ..self }
}
/// Specify a callback function to be called when the connection is initialized.
/// Specify a callback function to be called when the connection is
/// initialized.
///
/// You can get something from the payload of [`GQL_CONNECTION_INIT` message](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init) to create [`Data`].
/// The data returned by this callback function will be merged with the data specified by [`with_data`].
/// The data returned by this callback function will be merged with the data
/// specified by [`with_data`].
pub fn on_connection_init<OnConnInit2, Fut>(
self,
callback: OnConnInit2,

View File

@ -9,15 +9,15 @@
#![allow(clippy::needless_question_mark)]
#![forbid(unsafe_code)]
use crate::types::OperationType;
use async_graphql_value::Name;
use pest::error::LineColLocation;
use pest::RuleType;
use serde::{Serialize, Serializer};
use std::fmt::{self, Display, Formatter};
use async_graphql_value::Name;
pub use parse::{parse_query, parse_schema};
use pest::{error::LineColLocation, RuleType};
pub use pos::{Pos, Positioned};
use serde::{Serialize, Serializer};
use crate::types::OperationType;
pub mod types;

View File

@ -1,6 +1,7 @@
use super::*;
use async_graphql_value::Name;
use super::*;
/// Parse a GraphQL query document.
///
/// # Errors
@ -399,9 +400,10 @@ fn parse_type_condition(
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use super::*;
#[test]
fn test_parser() {
for entry in fs::read_dir("tests/executables").unwrap() {

View File

@ -2,15 +2,21 @@
//!
//! This module's structure mirrors `types`.
use crate::pos::{PositionCalculator, Positioned};
use crate::types::*;
use crate::{Error, Result};
use pest::iterators::{Pair, Pairs};
use pest::Parser;
use pest_derive::Parser;
use std::collections::hash_map::{self, HashMap};
use pest::{
iterators::{Pair, Pairs},
Parser,
};
use pest_derive::Parser;
use utils::*;
use crate::{
pos::{PositionCalculator, Positioned},
types::*,
Error, Result,
};
mod executable;
mod service;
mod utils;

View File

@ -398,9 +398,10 @@ fn parse_input_value_definition(
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use super::*;
#[test]
fn test_parser() {
for entry in fs::read_dir("tests/services").unwrap() {

View File

@ -1,6 +1,7 @@
use pest::iterators::{Pair, Pairs};
use super::Rule;
use crate::Result;
use pest::iterators::{Pair, Pairs};
pub(super) fn next_if_rule<'a>(pairs: &mut Pairs<'a, Rule>, rule: Rule) -> Option<Pair<'a, Rule>> {
if pairs.peek().map_or(false, |pair| pair.as_rule() == rule) {

View File

@ -1,11 +1,13 @@
use pest::iterators::Pair;
use pest::RuleType;
use std::{
borrow::{Borrow, BorrowMut},
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
str::Chars,
};
use pest::{iterators::Pair, RuleType};
use serde::{Deserialize, Serialize};
use std::borrow::{Borrow, BorrowMut};
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::str::Chars;
/// Original position of an element in source code.
///
@ -56,8 +58,8 @@ impl<T> Positioned<T> {
/// Get the inner node.
///
/// This is most useful in callback chains where `Positioned::into_inner` is easier to read than
/// `|positioned| positioned.node`.
/// This is most useful in callback chains where `Positioned::into_inner` is
/// easier to read than `|positioned| positioned.node`.
#[inline]
pub fn into_inner(self) -> T {
self.node

View File

@ -1,8 +1,9 @@
//! Executable document-related GraphQL types.
use super::*;
use async_graphql_value::{ConstValue, Name, Value};
use super::*;
/// An executable GraphQL file or request string.
///
/// [Reference](https://spec.graphql.org/October2021/#ExecutableDocument).
@ -36,10 +37,10 @@ impl DocumentOperations {
}
}
// TODO: This is not implemented as I would like to later implement IntoIterator for
// DocumentOperations (not a reference) without having a breaking change.
// TODO: This is not implemented as I would like to later implement IntoIterator
// for DocumentOperations (not a reference) without having a breaking change.
//
//impl<'a> IntoIterator for &'a DocumentOperations {
// impl<'a> IntoIterator for &'a DocumentOperations {
// type Item = &'a Positioned<OperationDefinition>;
// type IntoIter = OperationsIter<'a>;
//
@ -91,7 +92,8 @@ enum OperationsIterInner<'a> {
Multiple(hash_map::Iter<'a, Name, Positioned<OperationDefinition>>),
}
/// A GraphQL operation, such as `mutation($content:String!) { makePost(content: $content) { id } }`.
/// A GraphQL operation, such as `mutation($content:String!) { makePost(content:
/// $content) { id } }`.
///
/// [Reference](https://spec.graphql.org/October2021/#OperationDefinition).
#[derive(Debug, Clone)]
@ -106,7 +108,8 @@ pub struct OperationDefinition {
pub selection_set: Positioned<SelectionSet>,
}
/// A variable definition inside a list of variable definitions, for example `$name:String!`.
/// A variable definition inside a list of variable definitions, for example
/// `$name:String!`.
///
/// [Reference](https://spec.graphql.org/October2021/#VariableDefinition).
#[derive(Debug, Clone)]
@ -122,8 +125,8 @@ pub struct VariableDefinition {
}
impl VariableDefinition {
/// Get the default value of the variable; this is `default_value` if it is present,
/// `Value::Null` if it is nullable and `None` otherwise.
/// Get the default value of the variable; this is `default_value` if it is
/// present, `Value::Null` if it is nullable and `None` otherwise.
#[must_use]
pub fn default_value(&self) -> Option<&ConstValue> {
self.default_value.as_ref().map(|value| &value.node).or({
@ -145,12 +148,14 @@ pub struct SelectionSet {
pub items: Vec<Positioned<Selection>>,
}
/// A part of an object to be selected; a single field, a fragment spread or an inline fragment.
/// A part of an object to be selected; a single field, a fragment spread or an
/// inline fragment.
///
/// [Reference](https://spec.graphql.org/October2021/#Selection).
#[derive(Debug, Clone)]
pub enum Selection {
/// Select a single field, such as `name` or `weightKilos: weight(unit: KILOGRAMS)`.
/// Select a single field, such as `name` or `weightKilos: weight(unit:
/// KILOGRAMS)`.
Field(Positioned<Field>),
/// Select using a fragment.
FragmentSpread(Positioned<FragmentSpread>),
@ -179,7 +184,8 @@ impl Selection {
}
}
/// A field being selected on an object, such as `name` or `weightKilos: weight(unit: KILOGRAMS)`.
/// A field being selected on an object, such as `name` or `weightKilos:
/// weight(unit: KILOGRAMS)`.
///
/// [Reference](https://spec.graphql.org/October2021/#Field).
#[derive(Debug, Clone)]
@ -192,13 +198,14 @@ pub struct Field {
pub arguments: Vec<(Positioned<Name>, Positioned<Value>)>,
/// The directives in the field selector.
pub directives: Vec<Positioned<Directive>>,
/// The subfields being selected in this field, if it is an object. Empty if no fields are
/// being selected.
/// The subfields being selected in this field, if it is an object. Empty if
/// no fields are being selected.
pub selection_set: Positioned<SelectionSet>,
}
impl Field {
/// Get the response key of the field. This is the alias if present and the name otherwise.
/// Get the response key of the field. This is the alias if present and the
/// name otherwise.
#[must_use]
pub fn response_key(&self) -> &Positioned<Name> {
self.alias.as_ref().unwrap_or(&self.name)
@ -238,7 +245,8 @@ pub struct InlineFragment {
pub selection_set: Positioned<SelectionSet>,
}
/// The definition of a fragment, such as `fragment userFields on User { name age }`.
/// The definition of a fragment, such as `fragment userFields on User { name
/// age }`.
///
/// [Reference](https://spec.graphql.org/October2021/#FragmentDefinition).
#[derive(Debug, Clone)]

View File

@ -1,22 +1,26 @@
//! GraphQL types.
//!
//! The two root types are [`ExecutableDocument`](struct.ExecutableDocument.html) and
//! [`ServiceDocument`](struct.ServiceDocument.html), representing an executable GraphQL query and a
//! GraphQL service respectively.
//! The two root types are
//! [`ExecutableDocument`](struct.ExecutableDocument.html) and
//! [`ServiceDocument`](struct.ServiceDocument.html), representing an executable
//! GraphQL query and a GraphQL service respectively.
//!
//! This follows the [June 2018 edition of the GraphQL spec](https://spec.graphql.org/October2021/).
mod executable;
mod service;
use crate::pos::Positioned;
use async_graphql_value::{ConstValue, Name, Value};
use std::collections::{hash_map, HashMap};
use std::fmt::{self, Display, Formatter, Write};
use std::{
collections::{hash_map, HashMap},
fmt::{self, Display, Formatter, Write},
};
use async_graphql_value::{ConstValue, Name, Value};
pub use executable::*;
pub use service::*;
use crate::pos::Positioned;
/// The type of an operation; `query`, `mutation` or `subscription`.
///
/// [Reference](https://spec.graphql.org/October2021/#OperationType).
@ -82,8 +86,8 @@ impl Display for Type {
}
}
/// A GraphQL base type, for example `String` or `[String!]`. This does not include whether the
/// type is nullable; for that see [Type](struct.Type.html).
/// A GraphQL base type, for example `String` or `[String!]`. This does not
/// include whether the type is nullable; for that see [Type](struct.Type.html).
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum BaseType {
/// A named type, such as `String`.
@ -101,9 +105,10 @@ impl Display for BaseType {
}
}
/// A const GraphQL directive, such as `@deprecated(reason: "Use the other field)`. This differs
/// from [`Directive`](struct.Directive.html) in that it uses [`ConstValue`](enum.ConstValue.html)
/// instead of [`Value`](enum.Value.html).
/// A const GraphQL directive, such as `@deprecated(reason: "Use the other
/// field)`. This differs from [`Directive`](struct.Directive.html) in that it
/// uses [`ConstValue`](enum.ConstValue.html) instead of
/// [`Value`](enum.Value.html).
///
/// [Reference](https://spec.graphql.org/October2021/#Directive).
#[derive(Debug, Clone)]

View File

@ -1,8 +1,9 @@
//! Service-related GraphQL types.
use super::*;
use async_graphql_value::Name;
use super::*;
/// A GraphQL file or request string defining a GraphQL service.
///
/// [Reference](https://spec.graphql.org/October2021/#Document).
@ -52,7 +53,8 @@ pub struct SchemaDefinition {
pub struct TypeDefinition {
/// Whether the type is an extension of another type.
pub extend: bool,
/// The description of the type, if present. This is never present on an extension type.
/// The description of the type, if present. This is never present on an
/// extension type.
pub description: Option<Positioned<String>>,
/// The name of the type.
pub name: Positioned<Name>,
@ -195,11 +197,14 @@ pub struct DirectiveDefinition {
/// [Reference](https://spec.graphql.org/October2021/#DirectiveLocation).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DirectiveLocation {
/// A [query](enum.OperationType.html#variant.Query) [operation](struct.OperationDefinition.html).
/// A [query](enum.OperationType.html#variant.Query)
/// [operation](struct.OperationDefinition.html).
Query,
/// A [mutation](enum.OperationType.html#variant.Mutation) [operation](struct.OperationDefinition.html).
/// A [mutation](enum.OperationType.html#variant.Mutation)
/// [operation](struct.OperationDefinition.html).
Mutation,
/// A [subscription](enum.OperationType.html#variant.Subscription) [operation](struct.OperationDefinition.html).
/// A [subscription](enum.OperationType.html#variant.Subscription)
/// [operation](struct.OperationDefinition.html).
Subscription,
/// A [field](struct.Field.html).
Field,
@ -217,8 +222,8 @@ pub enum DirectiveLocation {
Object,
/// A [field definition](struct.FieldDefinition.html).
FieldDefinition,
/// An [input value definition](struct.InputFieldDefinition.html) as the arguments of a field
/// but not an input object.
/// An [input value definition](struct.InputFieldDefinition.html) as the
/// arguments of a field but not an input object.
ArgumentDefinition,
/// An [interface](struct.InterfaceType.html).
Interface,
@ -230,8 +235,8 @@ pub enum DirectiveLocation {
EnumValue,
/// An [input object](struct.InputObjectType.html).
InputObject,
/// An [input value definition](struct.InputValueDefinition.html) on an input object but not a
/// field.
/// An [input value definition](struct.InputValueDefinition.html) on an
/// input object but not a field.
InputFieldDefinition,
/// An [variable definition](struct.VariableDefinition.html).
VariableDefinition,

View File

@ -1,11 +1,10 @@
use std::borrow::Cow;
use std::sync::Arc;
use std::{borrow::Cow, sync::Arc};
use async_graphql_value::ConstValue;
use crate::parser::types::Field;
use crate::registry::{self, Registry};
use crate::{
parser::types::Field,
registry::{self, Registry},
ContainerType, Context, ContextSelectionSet, Error, InputValueError, InputValueResult,
Positioned, Result, ServerResult, Value,
};
@ -15,6 +14,12 @@ pub trait Description {
fn description() -> &'static str;
}
/// Used to specify the GraphQL Type name.
pub trait TypeName: Send + Sync {
/// Returns a GraphQL type name.
fn type_name() -> Cow<'static, str>;
}
/// Represents a GraphQL input type.
pub trait InputType: Send + Sync + Sized {
/// The raw type used for validator.
@ -67,7 +72,8 @@ pub trait OutputType: Send + Sync {
/// Introspection type name
///
/// Is the return value of field `__typename`, the interface and union should return the current type, and the others return `Type::type_name`.
/// Is the return value of field `__typename`, the interface and union
/// should return the current type, and the others return `Type::type_name`.
fn introspection_type_name(&self) -> Cow<'static, str> {
Self::type_name()
}

View File

@ -1,24 +1,30 @@
//! Query context.
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt::{self, Debug, Display, Formatter};
use std::ops::Deref;
use std::sync::{Arc, Mutex};
use std::{
any::{Any, TypeId},
collections::HashMap,
fmt::{self, Debug, Display, Formatter},
ops::Deref,
sync::{Arc, Mutex},
};
use async_graphql_value::{Value as InputValue, Variables};
use fnv::FnvHashMap;
use http::header::{AsHeaderName, HeaderMap, IntoHeaderName};
use http::HeaderValue;
use serde::ser::{SerializeSeq, Serializer};
use serde::Serialize;
use crate::parser::types::{
Directive, Field, FragmentDefinition, OperationDefinition, Selection, SelectionSet,
use http::{
header::{AsHeaderName, HeaderMap, IntoHeaderName},
HeaderValue,
};
use crate::schema::SchemaEnv;
use crate::{extensions::Extensions, schema::IntrospectionMode};
use serde::{
ser::{SerializeSeq, Serializer},
Serialize,
};
use crate::{
extensions::Extensions,
parser::types::{
Directive, Field, FragmentDefinition, OperationDefinition, Selection, SelectionSet,
},
schema::{IntrospectionMode, SchemaEnv},
Error, InputType, Lookahead, Name, OneofObjectType, PathSegment, Pos, Positioned, Result,
ServerError, ServerResult, UploadValue, Value,
};
@ -27,7 +33,8 @@ use crate::{
pub trait DataContext<'a> {
/// Gets the global data defined in the `Context` or `Schema`.
///
/// If both `Schema` and `Query` have the same data type, the data in the `Query` is obtained.
/// If both `Schema` and `Query` have the same data type, the data in the
/// `Query` is obtained.
///
/// # Errors
///
@ -41,7 +48,8 @@ pub trait DataContext<'a> {
/// It will panic if the specified data type does not exist.
fn data_unchecked<D: Any + Send + Sync>(&self) -> &'a D;
/// Gets the global data defined in the `Context` or `Schema` or `None` if the specified type data does not exist.
/// Gets the global data defined in the `Context` or `Schema` or `None` if
/// the specified type data does not exist.
fn data_opt<D: Any + Send + Sync>(&self) -> Option<&'a D>;
}
@ -87,8 +95,8 @@ pub type ContextDirective<'a> = ContextBase<'a, &'a Positioned<Directive>>;
/// A segment in the path to the current query.
///
/// This is a borrowed form of [`PathSegment`](enum.PathSegment.html) used during execution instead
/// of passed back when errors occur.
/// This is a borrowed form of [`PathSegment`](enum.PathSegment.html) used
/// during execution instead of passed back when errors occur.
#[derive(Debug, Clone, Copy, Serialize)]
#[serde(untagged)]
pub enum QueryPathSegment<'a> {
@ -138,7 +146,8 @@ impl<'a> Display for QueryPathNode<'a> {
impl<'a> QueryPathNode<'a> {
/// Get the current field name.
///
/// This traverses all the parents of the node until it finds one that is a field name.
/// This traverses all the parents of the node until it finds one that is a
/// field name.
pub fn field_name(&self) -> &str {
std::iter::once(self)
.chain(self.parents())
@ -192,12 +201,14 @@ impl<'a> QueryPathNode<'a> {
}
}
/// An iterator over the parents of a [`QueryPathNode`](struct.QueryPathNode.html).
/// An iterator over the parents of a
/// [`QueryPathNode`](struct.QueryPathNode.html).
#[derive(Debug, Clone)]
pub struct Parents<'a>(&'a QueryPathNode<'a>);
impl<'a> Parents<'a> {
/// Get the current query path node, which the next call to `next` will get the parents of.
/// Get the current query path node, which the next call to `next` will get
/// the parents of.
#[must_use]
pub fn current(&self) -> &'a QueryPathNode<'a> {
self.0
@ -344,14 +355,16 @@ impl<'a, T> ContextBase<'a, T> {
/// Report a resolver error.
///
/// When implementing `OutputType`, if an error occurs, call this function to report this error and return `Value::Null`.
/// When implementing `OutputType`, if an error occurs, call this function
/// to report this error and return `Value::Null`.
pub fn add_error(&self, error: ServerError) {
self.query_env.errors.lock().unwrap().push(error);
}
/// Gets the global data defined in the `Context` or `Schema`.
///
/// If both `Schema` and `Query` have the same data type, the data in the `Query` is obtained.
/// If both `Schema` and `Query` have the same data type, the data in the
/// `Query` is obtained.
///
/// # Errors
///
@ -375,7 +388,8 @@ impl<'a, T> ContextBase<'a, T> {
.unwrap_or_else(|| panic!("Data `{}` does not exist.", std::any::type_name::<D>()))
}
/// Gets the global data defined in the `Context` or `Schema` or `None` if the specified type data does not exist.
/// Gets the global data defined in the `Context` or `Schema` or `None` if
/// the specified type data does not exist.
pub fn data_opt<D: Any + Send + Sync>(&self) -> Option<&'a D> {
self.query_env
.ctx_data
@ -391,15 +405,14 @@ impl<'a, T> ContextBase<'a, T> {
/// # Examples
///
/// ```no_run
/// use async_graphql::*;
/// use ::http::header::ACCESS_CONTROL_ALLOW_ORIGIN;
/// use async_graphql::*;
///
/// struct Query;
///
/// #[Object]
/// impl Query {
/// async fn greet(&self, ctx: &Context<'_>) -> String {
///
/// let header_exists = ctx.http_header_contains("Access-Control-Allow-Origin");
/// assert!(!header_exists);
///
@ -422,27 +435,27 @@ impl<'a, T> ContextBase<'a, T> {
/// Sets a HTTP header to response.
///
/// If the header was not currently set on the response, then `None` is returned.
/// If the header was not currently set on the response, then `None` is
/// returned.
///
/// If the response already contained this header then the new value is associated with this key
/// and __all the previous values are removed__, however only a the first previous
/// value is returned.
/// If the response already contained this header then the new value is
/// associated with this key and __all the previous values are
/// removed__, however only a the first previous value is returned.
///
/// See [`http::HeaderMap`] for more details on the underlying implementation
/// See [`http::HeaderMap`] for more details on the underlying
/// implementation
///
/// # Examples
///
/// ```no_run
/// use ::http::{header::ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue};
/// use async_graphql::*;
/// use ::http::header::ACCESS_CONTROL_ALLOW_ORIGIN;
/// use ::http::HeaderValue;
///
/// struct Query;
///
/// #[Object]
/// impl Query {
/// async fn greet(&self, ctx: &Context<'_>) -> String {
///
/// // Headers can be inserted using the `http` constants
/// let was_in_headers = ctx.insert_http_header(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
/// assert_eq!(was_in_headers, None);
@ -479,19 +492,22 @@ impl<'a, T> ContextBase<'a, T> {
/// Sets a HTTP header to response.
///
/// If the header was not currently set on the response, then `false` is returned.
/// If the header was not currently set on the response, then `false` is
/// returned.
///
/// If the response did have this header then the new value is appended to the end of the
/// list of values currently associated with the key, however the key is not updated
/// _(which is important for types that can be `==` without being identical)_.
/// If the response did have this header then the new value is appended to
/// the end of the list of values currently associated with the key,
/// however the key is not updated _(which is important for types that
/// can be `==` without being identical)_.
///
/// See [`http::HeaderMap`] for more details on the underlying implementation
/// See [`http::HeaderMap`] for more details on the underlying
/// implementation
///
/// # Examples
///
/// ```no_run
/// use async_graphql::*;
/// use ::http::header::SET_COOKIE;
/// use async_graphql::*;
///
/// struct Query;
///
@ -679,7 +695,11 @@ impl<'a> ContextBase<'a, &'a Positioned<Field>> {
/// #[Object]
/// impl Query {
/// async fn obj(&self, ctx: &Context<'_>) -> MyObj {
/// let fields = ctx.field().selection_set().map(|field| field.name()).collect::<Vec<_>>();
/// let fields = ctx
/// .field()
/// .selection_set()
/// .map(|field| field.name())
/// .collect::<Vec<_>>();
/// assert_eq!(fields, vec!["a", "b", "c"]);
/// MyObj { a: 1, b: 2, c: 3 }
/// }
@ -689,9 +709,11 @@ impl<'a> ContextBase<'a, &'a Positioned<Field>> {
/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
/// assert!(schema.execute("{ obj { a b c }}").await.is_ok());
/// assert!(schema.execute("{ obj { a ... { b c } }}").await.is_ok());
/// assert!(schema.execute("{ obj { a ... BC }} fragment BC on MyObj { b c }").await.is_ok());
/// assert!(schema
/// .execute("{ obj { a ... BC }} fragment BC on MyObj { b c }")
/// .await
/// .is_ok());
/// # });
///
/// ```
pub fn field(&self) -> SelectionField {
SelectionField {

View File

@ -1,7 +1,7 @@
use crate::extensions::ResolveFut;
use crate::parser::types::Directive;
use crate::registry::Registry;
use crate::{Context, ContextDirective, ServerResult, Value};
use crate::{
extensions::ResolveFut, parser::types::Directive, registry::Registry, Context,
ContextDirective, ServerResult, Value,
};
#[doc(hidden)]
pub trait CustomDirectiveFactory: Send + Sync + 'static {

View File

@ -1,8 +1,9 @@
use std::borrow::Cow;
use std::collections::hash_map::RandomState;
use std::collections::HashMap;
use std::hash::{BuildHasher, Hash};
use std::marker::PhantomData;
use std::{
borrow::Cow,
collections::{hash_map::RandomState, HashMap},
hash::{BuildHasher, Hash},
marker::PhantomData,
};
/// Factory for creating cache storage.
pub trait CacheFactory: Send + Sync + 'static {
@ -23,10 +24,12 @@ pub trait CacheStorage: Send + Sync + 'static {
/// The value type of the record.
type Value: Send + Sync + Clone + 'static;
/// Returns a reference to the value of the key in the cache or None if it is not present in the cache.
/// Returns a reference to the value of the key in the cache or None if it
/// is not present in the cache.
fn get(&mut self, key: &Self::Key) -> Option<&Self::Value>;
/// Puts a key-value pair into the cache. If the key already exists in the cache, then it updates the key's value.
/// Puts a key-value pair into the cache. If the key already exists in the
/// cache, then it updates the key's value.
fn insert(&mut self, key: Cow<'_, Self::Key>, val: Cow<'_, Self::Value>);
/// Removes the value corresponding to the key from the cache.

View File

@ -58,21 +58,24 @@
mod cache;
use std::any::{Any, TypeId};
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::{
any::{Any, TypeId},
borrow::Cow,
collections::{HashMap, HashSet},
hash::Hash,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
time::Duration,
};
pub use cache::{CacheFactory, CacheStorage, HashMapCache, LruCache, NoCache};
use fnv::FnvHashMap;
use futures_channel::oneshot;
use futures_timer::Delay;
use futures_util::future::BoxFuture;
pub use cache::{CacheFactory, CacheStorage, HashMapCache, LruCache, NoCache};
#[allow(clippy::type_complexity)]
struct ResSender<K: Send + Sync + Hash + Eq + Clone + 'static, T: Loader<K>> {
use_cache_values: HashMap<K, T::Value>,
@ -231,9 +234,11 @@ impl<T, C: CacheFactory> DataLoader<T, C> {
Self { delay, ..self }
}
/// pub fn Specify the max batch size for loading data, the default is `1000`.
/// pub fn Specify the max batch size for loading data, the default is
/// `1000`.
///
/// If the keys waiting to be loaded reach the threshold, they are loaded immediately.
/// If the keys waiting to be loaded reach the threshold, they are loaded
/// immediately.
#[must_use]
pub fn max_batch_size(self, max_batch_size: usize) -> Self {
Self {
@ -387,7 +392,8 @@ impl<T, C: CacheFactory> DataLoader<T, C> {
/// Feed some data into the cache.
///
/// **NOTE: If the cache type is [NoCache], this function will not take effect. **
/// **NOTE: If the cache type is [NoCache], this function will not take
/// effect. **
pub async fn feed_many<K, I>(&self, values: I)
where
K: Send + Sync + Hash + Eq + Clone + 'static,
@ -410,7 +416,8 @@ impl<T, C: CacheFactory> DataLoader<T, C> {
/// Feed some data into the cache.
///
/// **NOTE: If the cache type is [NoCache], this function will not take effect. **
/// **NOTE: If the cache type is [NoCache], this function will not take
/// effect. **
pub async fn feed_one<K>(&self, key: K, value: T::Value)
where
K: Send + Sync + Hash + Eq + Clone + 'static,
@ -421,7 +428,8 @@ impl<T, C: CacheFactory> DataLoader<T, C> {
/// Clears the cache.
///
/// **NOTE: If the cache type is [NoCache], this function will not take effect. **
/// **NOTE: If the cache type is [NoCache], this function will not take
/// effect. **
pub fn clear<K>(&self)
where
K: Send + Sync + Hash + Eq + Clone + 'static,
@ -440,10 +448,12 @@ impl<T, C: CacheFactory> DataLoader<T, C> {
#[cfg(test)]
mod tests {
use super::*;
use fnv::FnvBuildHasher;
use std::sync::Arc;
use fnv::FnvBuildHasher;
use super::*;
struct MyLoader;
#[async_trait::async_trait]

View File

@ -1,8 +1,10 @@
use std::any::Any;
use std::collections::BTreeMap;
use std::fmt::{self, Debug, Display, Formatter};
use std::marker::PhantomData;
use std::sync::Arc;
use std::{
any::Any,
collections::BTreeMap,
fmt::{self, Debug, Display, Formatter},
marker::PhantomData,
sync::Arc,
};
use serde::{Deserialize, Serialize};
use thiserror::Error;
@ -91,22 +93,31 @@ impl ServerError {
/// # Examples
///
/// ```rust
/// use async_graphql::*;
/// use std::io::ErrorKind;
///
/// use async_graphql::*;
///
/// struct Query;
///
/// #[Object]
/// impl Query {
/// async fn value(&self) -> Result<i32> {
/// Err(Error::new_with_source(std::io::Error::new(ErrorKind::Other, "my error")))
/// Err(Error::new_with_source(std::io::Error::new(
/// ErrorKind::Other,
/// "my error",
/// )))
/// }
/// }
///
/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
///
/// # tokio::runtime::Runtime::new().unwrap().block_on(async move {
/// let err = schema.execute("{ value }").await.into_result().unwrap_err().remove(0);
/// let err = schema
/// .execute("{ value }")
/// .await
/// .into_result()
/// .unwrap_err()
/// .remove(0);
/// assert!(err.source::<std::io::Error>().is_some());
/// # });
/// ```
@ -147,8 +158,8 @@ impl From<parser::Error> for ServerError {
/// A segment of path to a resolver.
///
/// This is like [`QueryPathSegment`](enum.QueryPathSegment.html), but owned and used as a part of
/// errors instead of during execution.
/// This is like [`QueryPathSegment`](enum.QueryPathSegment.html), but owned and
/// used as a part of errors instead of during execution.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PathSegment {
@ -163,7 +174,8 @@ pub type ServerResult<T> = std::result::Result<T, ServerError>;
/// An error parsing an input value.
///
/// This type is generic over T as it uses T's type name when converting to a regular error.
/// This type is generic over T as it uses T's type name when converting to a
/// regular error.
#[derive(Debug)]
pub struct InputValueError<T> {
message: String,
@ -190,8 +202,8 @@ impl<T: InputType> InputValueError<T> {
/// A custom error message.
///
/// Any type that implements `Display` is automatically converted to this if you use the `?`
/// operator.
/// Any type that implements `Display` is automatically converted to this if
/// you use the `?` operator.
#[must_use]
pub fn custom(msg: impl Display) -> Self {
Self::new(format!(r#"Failed to parse "{}": {}"#, T::type_name(), msg))
@ -263,8 +275,8 @@ impl Error {
}
}
/// Create an error with a type that implements `Display`, and it will also set the
/// `source` of the error to this value.
/// Create an error with a type that implements `Display`, and it will also
/// set the `source` of the error to this value.
pub fn new_with_source(source: impl Display + Send + Sync + 'static) -> Self {
Self {
message: source.to_string(),
@ -339,7 +351,8 @@ pub enum ParseRequestError {
#[error("Payload too large")]
PayloadTooLarge,
/// The request is a batch request, but the server does not support batch requests.
/// The request is a batch request, but the server does not support batch
/// requests.
#[error("Batch requests are not supported")]
UnsupportedBatch,
}
@ -397,8 +410,8 @@ impl ErrorExtensions for Error {
}
}
// implementing for &E instead of E gives the user the possibility to implement for E which does
// not conflict with this implementation acting as a fallback.
// implementing for &E instead of E gives the user the possibility to implement
// for E which does not conflict with this implementation acting as a fallback.
impl<E: Display> ErrorExtensions for &E {
fn extend(&self) -> Error {
Error {
@ -409,7 +422,8 @@ impl<E: Display> ErrorExtensions for &E {
}
}
/// Extend a `Result`'s error value with [`ErrorExtensions`](trait.ErrorExtensions.html).
/// 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) -> Result<T>
@ -420,8 +434,8 @@ pub trait ResultExt<T, E>: Sized {
fn extend(self) -> Result<T>;
}
// This is implemented on E and not &E which means it cannot be used on foreign types.
// (see example).
// This is implemented on E and not &E which means it cannot be used on foreign
// types. (see example).
impl<T, E> ResultExt<T, E> for std::result::Result<T, E>
where
E: ErrorExtensions + Send + Sync + 'static,

View File

@ -2,14 +2,15 @@ use std::sync::Arc;
use futures_util::lock::Mutex;
use crate::extensions::{
Extension, ExtensionContext, ExtensionFactory, NextRequest, NextValidation,
use crate::{
extensions::{Extension, ExtensionContext, ExtensionFactory, NextRequest, NextValidation},
value, Response, ServerError, ValidationResult,
};
use crate::{value, Response, ServerError, ValidationResult};
/// Analyzer extension
///
/// This extension will output the `analyzer` field containing `complexity` and `depth` in the response extension of each query.
/// This extension will output the `analyzer` field containing `complexity` and
/// `depth` in the response extension of each query.
pub struct Analyzer;
impl ExtensionFactory for Analyzer {

View File

@ -6,8 +6,10 @@ use futures_util::lock::Mutex;
use serde::Deserialize;
use sha2::{Digest, Sha256};
use crate::extensions::{Extension, ExtensionContext, ExtensionFactory, NextPrepareRequest};
use crate::{from_value, Request, ServerError, ServerResult};
use crate::{
extensions::{Extension, ExtensionContext, ExtensionFactory, NextPrepareRequest},
from_value, Request, ServerError, ServerResult,
};
#[derive(Deserialize)]
struct PersistedQuery {

View File

@ -2,13 +2,14 @@ use std::sync::Arc;
use chrono::{DateTime, Utc};
use futures_util::lock::Mutex;
use serde::ser::SerializeMap;
use serde::{Serialize, Serializer};
use serde::{ser::SerializeMap, Serialize, Serializer};
use crate::extensions::{
Extension, ExtensionContext, ExtensionFactory, NextExecute, NextResolve, ResolveInfo,
use crate::{
extensions::{
Extension, ExtensionContext, ExtensionFactory, NextExecute, NextResolve, ResolveInfo,
},
value, Response, ServerResult, Value,
};
use crate::{value, Response, ServerResult, Value};
struct ResolveState {
path: Vec<String>,
@ -38,10 +39,11 @@ impl Serialize for ResolveState {
/// Apollo tracing extension for performance tracing
///
/// Apollo Tracing works by including data in the extensions field of the GraphQL response, which is
/// reserved by the GraphQL spec for extra information that a server wants to return. That way, you
/// have access to performance traces alongside the data returned by your query.
/// It's already supported by `Apollo Engine`, and we're excited to see what other kinds of
/// Apollo Tracing works by including data in the extensions field of the
/// GraphQL response, which is reserved by the GraphQL spec for extra
/// information that a server wants to return. That way, you have access to
/// performance traces alongside the data returned by your query. It's already
/// supported by `Apollo Engine`, and we're excited to see what other kinds of
/// integrations people can build on top of this format.
#[cfg_attr(docsrs, doc(cfg(feature = "apollo_tracing")))]
pub struct ApolloTracing;

View File

@ -1,11 +1,10 @@
use std::fmt::Write;
use std::sync::Arc;
use std::{fmt::Write, sync::Arc};
use crate::extensions::{
Extension, ExtensionContext, ExtensionFactory, NextExecute, NextParseQuery,
use crate::{
extensions::{Extension, ExtensionContext, ExtensionFactory, NextExecute, NextParseQuery},
parser::types::{ExecutableDocument, OperationType, Selection},
PathSegment, Response, ServerResult, Variables,
};
use crate::parser::types::{ExecutableDocument, OperationType, Selection};
use crate::{PathSegment, Response, ServerResult, Variables};
/// Logger extension
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]

View File

@ -12,6 +12,14 @@ mod opentelemetry;
#[cfg(feature = "tracing")]
mod tracing;
use std::{
any::{Any, TypeId},
future::Future,
sync::Arc,
};
use futures_util::stream::BoxStream;
pub use self::analyzer::Analyzer;
#[cfg(feature = "apollo_tracing")]
pub use self::apollo_tracing::ApolloTracing;
@ -21,17 +29,9 @@ pub use self::logger::Logger;
pub use self::opentelemetry::OpenTelemetry;
#[cfg(feature = "tracing")]
pub use self::tracing::Tracing;
use std::any::{Any, TypeId};
use std::future::Future;
use std::sync::Arc;
use futures_util::stream::BoxStream;
use crate::parser::types::ExecutableDocument;
use crate::{
Data, DataContext, Error, QueryPathNode, Request, Response, Result, SchemaEnv, ServerError,
ServerResult, ValidationResult, Value, Variables,
parser::types::ExecutableDocument, Data, DataContext, Error, QueryPathNode, Request, Response,
Result, SchemaEnv, ServerError, ServerResult, ValidationResult, Value, Variables,
};
/// Context for extension
@ -73,7 +73,8 @@ impl<'a> ExtensionContext<'a> {
/// Gets the global data defined in the `Context` or `Schema`.
///
/// If both `Schema` and `Query` have the same data type, the data in the `Query` is obtained.
/// If both `Schema` and `Query` have the same data type, the data in the
/// `Query` is obtained.
///
/// # Errors
///
@ -97,7 +98,8 @@ impl<'a> ExtensionContext<'a> {
.unwrap_or_else(|| panic!("Data `{}` does not exist.", std::any::type_name::<D>()))
}
/// Gets the global data defined in the `Context` or `Schema` or `None` if the specified type data does not exist.
/// Gets the global data defined in the `Context` or `Schema` or `None` if
/// the specified type data does not exist.
pub fn data_opt<D: Any + Send + Sync>(&self) -> Option<&'a D> {
self.query_data
.and_then(|query_data| query_data.get(&TypeId::of::<D>()))

View File

@ -2,16 +2,19 @@ use std::sync::Arc;
use async_graphql_parser::types::ExecutableDocument;
use async_graphql_value::Variables;
use futures_util::stream::BoxStream;
use futures_util::TryFutureExt;
use opentelemetry::trace::{FutureExt, SpanKind, TraceContextExt, Tracer};
use opentelemetry::{Context as OpenTelemetryContext, Key};
use crate::extensions::{
Extension, ExtensionContext, ExtensionFactory, NextExecute, NextParseQuery, NextRequest,
NextResolve, NextSubscribe, NextValidation, ResolveInfo,
use futures_util::{stream::BoxStream, TryFutureExt};
use opentelemetry::{
trace::{FutureExt, SpanKind, TraceContextExt, Tracer},
Context as OpenTelemetryContext, Key,
};
use crate::{
extensions::{
Extension, ExtensionContext, ExtensionFactory, NextExecute, NextParseQuery, NextRequest,
NextResolve, NextSubscribe, NextValidation, ResolveInfo,
},
Response, ServerError, ServerResult, ValidationResult, Value,
};
use crate::{Response, ServerError, ServerResult, ValidationResult, Value};
const KEY_SOURCE: Key = Key::from_static_str("graphql.source");
const KEY_VARIABLES: Key = Key::from_static_str("graphql.variables");

View File

@ -1,16 +1,17 @@
use std::sync::Arc;
use futures_util::stream::BoxStream;
use futures_util::TryFutureExt;
use futures_util::{stream::BoxStream, TryFutureExt};
use tracing_futures::Instrument;
use tracinglib::{span, Level};
use crate::extensions::{
Extension, ExtensionContext, ExtensionFactory, NextExecute, NextParseQuery, NextRequest,
NextResolve, NextSubscribe, NextValidation, ResolveInfo,
use crate::{
extensions::{
Extension, ExtensionContext, ExtensionFactory, NextExecute, NextParseQuery, NextRequest,
NextResolve, NextSubscribe, NextValidation, ResolveInfo,
},
parser::types::ExecutableDocument,
Response, ServerError, ServerResult, ValidationResult, Value, Variables,
};
use crate::parser::types::ExecutableDocument;
use crate::{Response, ServerError, ServerResult, ValidationResult, Value, Variables};
/// Tracing extension
///
@ -21,8 +22,7 @@ use crate::{Response, ServerError, ServerResult, ValidationResult, Value, Variab
/// # Examples
///
/// ```no_run
/// use async_graphql::*;
/// use async_graphql::extensions::Tracing;
/// use async_graphql::{extensions::Tracing, *};
///
/// #[derive(SimpleObject)]
/// struct Query {

View File

@ -4,7 +4,8 @@ use crate::{Context, Result};
/// 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.
#[async_trait::async_trait]
pub trait Guard {
/// Check whether the guard will allow access to the field.

View File

@ -5,16 +5,15 @@ mod multipart;
mod playground_source;
mod websocket;
use futures_util::io::{AsyncRead, AsyncReadExt};
pub use graphiql_source::graphiql_source;
use mime;
pub use multipart::MultipartOptions;
pub use playground_source::{playground_source, GraphQLPlaygroundConfig};
pub use websocket::{
ClientMessage, Protocols as WebSocketProtocols, WebSocket, WsMessage, ALL_WEBSOCKET_PROTOCOLS,
};
use futures_util::io::{AsyncRead, AsyncReadExt};
use mime;
use crate::{BatchRequest, ParseRequestError, Request};
/// Receive a GraphQL request from a content type and body.
@ -62,7 +61,8 @@ pub async fn receive_batch_body(
}
/// Recieves a GraphQL query which is either cbor or json but NOT multipart
/// This method is only to avoid recursive calls with [``receive_batch_body``] and [``multipart::receive_batch_multipart``]
/// This method is only to avoid recursive calls with [``receive_batch_body``]
/// and [``multipart::receive_batch_multipart``]
pub(super) async fn receive_batch_body_no_multipart(
content_type: &mime::Mime,
body: impl AsyncRead + Send,

View File

@ -1,10 +1,11 @@
use std::collections::HashMap;
use std::io::{self, Seek, SeekFrom, Write};
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{
collections::HashMap,
io::{self, Seek, SeekFrom, Write},
pin::Pin,
task::{Context, Poll},
};
use futures_util::io::AsyncRead;
use futures_util::stream::Stream;
use futures_util::{io::AsyncRead, stream::Stream};
use multer::{Constraints, Multipart, SizeLimit};
use pin_project_lite::pin_project;
@ -90,8 +91,9 @@ pub(super) async fn receive_batch_multipart(
// Note: we actually differ here from the inoffical spec for this:
// (https://github.com/jaydenseric/graphql-multipart-request-spec#multipart-form-field-structure)
// It says: "map: A JSON encoded map of where files occurred in the operations.
// For each file, the key is the file multipart form field name and the value is an array of operations paths."
// However, I think, that since we accept CBOR as operation, which is valid, we should also accept it
// For each file, the key is the file multipart form field name and the value is
// an array of operations paths." However, I think, that
// since we accept CBOR as operation, which is valid, we should also accept it
// as the mapping for the files.
#[cfg(feature = "cbor")]
(mime::OCTET_STREAM, _) | (mime::APPLICATION, mime::OCTET_STREAM) => {

View File

@ -626,9 +626,10 @@ impl<'a> GraphQLPlaygroundConfig<'a> {
#[cfg(test)]
mod tests {
use super::*;
use indexmap::IndexMap;
use super::*;
#[test]
fn test_with_setting_can_use_any_json_value() {
let settings = GraphQLPlaygroundConfig::new("")

View File

@ -1,15 +1,18 @@
//! WebSocket transport for subscription
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use std::{
collections::HashMap,
future::Future,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use futures_util::future::Ready;
use futures_util::stream::Stream;
use futures_util::FutureExt;
use futures_util::{future::BoxFuture, StreamExt};
use futures_util::{
future::{BoxFuture, Ready},
stream::Stream,
FutureExt, StreamExt,
};
use pin_project_lite::pin_project;
use serde::{Deserialize, Serialize};
@ -29,7 +32,8 @@ pub enum WsMessage {
}
impl WsMessage {
/// Returns the contained [WsMessage::Text] value, consuming the `self` value.
/// Returns the contained [WsMessage::Text] value, consuming the `self`
/// value.
///
/// Because this function may panic, its use is generally discouraged.
///
@ -43,7 +47,8 @@ impl WsMessage {
}
}
/// Returns the contained [WsMessage::Close] value, consuming the `self` value.
/// Returns the contained [WsMessage::Close] value, consuming the `self`
/// value.
///
/// Because this function may panic, its use is generally discouraged.
///
@ -136,8 +141,9 @@ where
/// Specify a connection data.
///
/// This data usually comes from HTTP requests.
/// When the `GQL_CONNECTION_INIT` message is received, this data will be merged with the data
/// returned by the closure specified by `with_initializer` into the final subscription context data.
/// When the `GQL_CONNECTION_INIT` message is received, this data will be
/// merged with the data returned by the closure specified by
/// `with_initializer` into the final subscription context data.
#[must_use]
pub fn connection_data(mut self, data: Data) -> Self {
self.connection_data = Some(data);
@ -146,8 +152,8 @@ where
/// Specify a connection initialize callback function.
///
/// This function if present, will be called with the data sent by the client in the
/// [`GQL_CONNECTION_INIT` message](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init).
/// This function if present, will be called with the data sent by the
/// client in the [`GQL_CONNECTION_INIT` message](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_init).
/// From that point on the returned data will be accessible to all requests.
#[must_use]
pub fn on_connection_init<F, R>(
@ -365,7 +371,7 @@ impl std::str::FromStr for Protocols {
/// A websocket message received from the client
#[derive(Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[allow(clippy::large_enum_variant)] //Request is at fault
#[allow(clippy::large_enum_variant)] // Request is at fault
pub enum ClientMessage {
/// A new connection
ConnectionInit {
@ -377,8 +383,8 @@ pub enum ClientMessage {
Start {
/// Message ID
id: String,
/// The GraphQL Request - this can be modified by protocol implementors to add files
/// uploads.
/// The GraphQL Request - this can be modified by protocol implementors
/// to add files uploads.
payload: Request,
},
/// The end of a Websocket subscription
@ -389,7 +395,8 @@ pub enum ClientMessage {
},
/// Connection terminated by the client
ConnectionTerminate,
/// Useful for detecting failed connections, displaying latency metrics or other types of network probing.
/// Useful for detecting failed connections, displaying latency metrics or
/// other types of network probing.
///
/// https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md#ping
Ping {

View File

@ -43,7 +43,8 @@
//! * Rustfmt friendly (Procedural Macro)
//! * Custom scalars
//! * Minimal overhead
//! * Easy integration ([poem](https://crates.io/crates/poem), actix_web, tide, warp, rocket ...)
//! * Easy integration ([poem](https://crates.io/crates/poem), actix_web, tide,
//! warp, rocket ...)
//! * File upload (Multipart request)
//! * Subscriptions (WebSocket transport)
//! * Custom extensions
@ -56,20 +57,27 @@
//!
//! ## Crate features
//!
//! This crate offers the following features, all of which are not activated by default:
//! This crate offers the following features, all of which are not activated by
//! default:
//!
//! - `apollo_tracing`: Enable the [Apollo tracing extension](extensions/struct.ApolloTracing.html).
//! - `apollo_persisted_queries`: Enable the [Apollo persisted queries extension](extensions/apollo_persisted_queries/struct.ApolloPersistedQueries.html).
//! - `apollo_tracing`: Enable the [Apollo tracing
//! extension](extensions/struct.ApolloTracing.html).
//! - `apollo_persisted_queries`: Enable the [Apollo persisted queries
//! extension](extensions/apollo_persisted_queries/struct.
//! ApolloPersistedQueries.html).
//! - `log`: Enable the [logger extension](extensions/struct.Logger.html).
//! - `tracing`: Enable the [tracing extension](extensions/struct.Tracing.html).
//! - `opentelemetry`: Enable the [OpenTelemetry extension](extensions/struct.OpenTelemetry.html).
//! - `unblock`: Support [asynchronous reader for Upload](types/struct.Upload.html)
//! - `opentelemetry`: Enable the [OpenTelemetry
//! extension](extensions/struct.OpenTelemetry.html).
//! - `unblock`: Support [asynchronous reader for
//! Upload](types/struct.Upload.html)
//! - `bson`: Integrate with the [`bson` crate](https://crates.io/crates/bson).
//! - `chrono`: Integrate with the [`chrono` crate](https://crates.io/crates/chrono).
//! - `chrono-tz`: Integrate with the [`chrono-tz` crate](https://crates.io/crates/chrono-tz).
//! - `url`: Integrate with the [`url` crate](https://crates.io/crates/url).
//! - `uuid`: Integrate with the [`uuid` crate](https://crates.io/crates/uuid).
//! - `string_number`: Enable the [StringNumber](types/struct.StringNumber.html).
//! - `string_number`: Enable the
//! [StringNumber](types/struct.StringNumber.html).
//! - `dataloader`: Support [DataLoader](dataloader/struct.DataLoader.html).
//! - `decimal`: Integrate with the [`rust_decimal` crate](https://crates.io/crates/rust_decimal).
//! - `cbor`: Support for [serde_cbor](https://crates.io/crates/serde_cbor).
@ -125,7 +133,6 @@
//! ```
//!
//! Now a HTML report is available at `benchmark/target/criterion/report`.
//!
#![deny(clippy::all)]
// #![deny(clippy::pedantic)]
@ -192,58 +199,56 @@ pub mod validators;
#[doc(hidden)]
pub mod registry;
#[doc(hidden)]
pub use async_stream;
#[doc(hidden)]
pub use async_trait;
#[doc(hidden)]
pub use context::ContextSelectionSet;
#[doc(hidden)]
pub use futures_util;
#[doc(hidden)]
pub use indexmap;
#[doc(hidden)]
pub use static_assertions;
#[doc(hidden)]
pub use subscription::SubscriptionType;
pub use async_graphql_parser as parser;
pub use async_graphql_value::{
from_value, to_value, value, ConstValue as Value, DeserializerError, Name, Number,
SerializerError, Variables,
};
#[doc(hidden)]
pub use async_stream;
#[doc(hidden)]
pub use async_trait;
pub use base::{
ComplexObject, Description, InputObjectType, InputType, InterfaceType, ObjectType,
OneofObjectType, OutputType, UnionType,
OneofObjectType, OutputType, TypeName, UnionType,
};
#[doc(hidden)]
pub use context::ContextSelectionSet;
pub use context::*;
pub use custom_directive::{CustomDirective, CustomDirectiveFactory};
pub use error::{
Error, ErrorExtensionValues, ErrorExtensions, InputValueError, InputValueResult,
ParseRequestError, PathSegment, Result, ResultExt, ServerError, ServerResult,
};
pub use extensions::ResolveFut;
#[doc(hidden)]
pub use futures_util;
pub use guard::{Guard, GuardExt};
#[doc(hidden)]
pub use indexmap;
pub use look_ahead::Lookahead;
#[doc(no_inline)]
pub use parser::{Pos, Positioned};
pub use registry::CacheControl;
pub use request::{BatchRequest, Request};
#[doc(no_inline)]
pub use resolver_utils::{ContainerType, EnumType, ScalarType};
pub use response::{BatchResponse, Response};
pub use schema::{Schema, SchemaBuilder, SchemaEnv};
#[doc(hidden)]
pub use static_assertions;
#[doc(hidden)]
pub use subscription::SubscriptionType;
pub use types::*;
pub use validation::{ValidationMode, ValidationResult, VisitorContext};
pub use validators::CustomValidator;
pub use context::*;
#[doc(no_inline)]
pub use parser::{Pos, Positioned};
pub use types::*;
/// An alias of [async_graphql::Error](struct.Error.html). Present for backward compatibility
/// reasons.
/// An alias of [async_graphql::Error](struct.Error.html). Present for backward
/// compatibility reasons.
pub type FieldError = Error;
/// An alias of [async_graphql::Result](type.Result.html). Present for backward compatibility
/// reasons.
/// An alias of [async_graphql::Result](type.Result.html). Present for backward
/// compatibility reasons.
pub type FieldResult<T> = Result<T>;
#[doc = include_str!("docs/complex_object.md")]

View File

@ -1,8 +1,9 @@
use std::collections::HashMap;
use crate::parser::types::{Field, FragmentDefinition, Selection, SelectionSet};
use crate::Context;
use crate::{Name, Positioned, SelectionField};
use crate::{
parser::types::{Field, FragmentDefinition, Selection, SelectionSet},
Context, Name, Positioned, SelectionField,
};
/// A selection performed by a query.
pub struct Lookahead<'a> {
@ -24,11 +25,11 @@ impl<'a> Lookahead<'a> {
}
}
/// Get the field of the selection set with the specified name. This will ignore
/// aliases.
/// Get the field of the selection set with the specified name. This will
/// ignore aliases.
///
/// For example, calling `.field("a")` on `{ a { b } }` will return a lookahead that
/// represents `{ b }`.
/// For example, calling `.field("a")` on `{ a { b } }` will return a
/// lookahead that represents `{ b }`.
#[must_use]
pub fn field(&self, name: &str) -> Self {
let mut fields = Vec::new();
@ -49,9 +50,11 @@ impl<'a> Lookahead<'a> {
!self.fields.is_empty()
}
/// Get the `SelectionField`s for each of the fields covered by this `Lookahead`.
/// Get the `SelectionField`s for each of the fields covered by this
/// `Lookahead`.
///
/// There will be multiple fields in situations where the same field is queried twice.
/// There will be multiple fields in situations where the same field is
/// queried twice.
pub fn selection_fields(&self) -> Vec<SelectionField<'a>> {
self.fields
.iter()
@ -75,7 +78,8 @@ impl<'a> From<SelectionField<'a>> for Lookahead<'a> {
}
/// Convert a slice of `SelectionField`s to a `Lookahead`.
/// Assumes all `SelectionField`s are from the same query and thus have the same fragments.
/// Assumes all `SelectionField`s are from the same query and thus have the same
/// fragments.
///
/// Fails if either no `SelectionField`s were provided.
impl<'a> TryFrom<&[SelectionField<'a>]> for Lookahead<'a> {

View File

@ -1,9 +1,9 @@
use std::collections::HashSet;
use crate::model::__InputValue;
use crate::{registry, Enum, Object};
use crate::{model::__InputValue, registry, Enum, Object};
/// A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.
/// A Directive can be adjacent to many parts of the GraphQL language, a
/// __DirectiveLocation describes one such possible adjacencies.
#[derive(Debug, Enum, Copy, Clone, Eq, PartialEq)]
#[graphql(internal, name = "__DirectiveLocation")]
#[allow(non_camel_case_types)]
@ -72,9 +72,13 @@ pub struct __Directive<'a> {
pub directive: &'a registry::MetaDirective,
}
/// A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
/// A Directive provides a way to describe alternate runtime execution and type
/// validation behavior in a GraphQL document.
///
/// In some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.
/// In some cases, you need to provide options to alter GraphQL's execution
/// behavior in ways field arguments will not suffice, such as conditionally
/// including or skipping a field. Directives provide this by describing
/// additional information to the executor.
#[Object(internal, name = "__Directive")]
impl<'a> __Directive<'a> {
#[inline]

View File

@ -5,7 +5,9 @@ pub struct __EnumValue<'a> {
pub value: &'a registry::MetaEnumValue,
}
/// One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.
/// One possible value for a given Enum. Enum values are unique values, not a
/// placeholder for a string or numeric value. However an Enum value is returned
/// in a JSON response as a string.
#[Object(internal, name = "__EnumValue")]
impl<'a> __EnumValue<'a> {
#[inline]

View File

@ -1,8 +1,11 @@
use std::collections::HashSet;
use crate::model::{__InputValue, __Type};
use crate::registry::is_visible;
use crate::{registry, Context, Object};
use crate::{
model::{__InputValue, __Type},
registry,
registry::is_visible,
Context, Object,
};
pub struct __Field<'a> {
pub registry: &'a registry::Registry,
@ -10,7 +13,8 @@ pub struct __Field<'a> {
pub field: &'a registry::MetaField,
}
/// Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.
/// Object and Interface types are described by a list of Fields, each of which
/// has a name, potentially a list of arguments, and a return type.
#[Object(internal, name = "__Field")]
impl<'a> __Field<'a> {
#[inline]

View File

@ -1,7 +1,6 @@
use std::collections::HashSet;
use crate::model::__Type;
use crate::{registry, Object};
use crate::{model::__Type, registry, Object};
pub struct __InputValue<'a> {
pub registry: &'a registry::Registry,
@ -9,7 +8,9 @@ pub struct __InputValue<'a> {
pub input_value: &'a registry::MetaInputValue,
}
/// Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.
/// Arguments provided to Fields or Directives and the input fields of an
/// InputObject are represented as Input Values which describe their type and
/// optionally a default value.
#[Object(internal, name = "__InputValue")]
impl<'a> __InputValue<'a> {
#[inline]

View File

@ -7,10 +7,12 @@ pub enum __TypeKind {
/// Indicates this type is a scalar.
Scalar,
/// Indicates this type is an object. `fields` and `interfaces` are valid fields.
/// Indicates this type is an object. `fields` and `interfaces` are valid
/// fields.
Object,
/// Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.
/// Indicates this type is an interface. `fields` and `possibleTypes` are
/// valid fields.
Interface,
/// Indicates this type is a union. `possibleTypes` is a valid field.

View File

@ -1,7 +1,9 @@
use std::collections::HashSet;
use crate::model::{__Directive, __Type};
use crate::{registry, Object};
use crate::{
model::{__Directive, __Type},
registry, Object,
};
pub struct __Schema<'a> {
registry: &'a registry::Registry,
@ -17,7 +19,9 @@ impl<'a> __Schema<'a> {
}
}
/// A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.
/// A GraphQL Schema defines the capabilities of a GraphQL server. It exposes
/// all available types and directives on the server, as well as the entry
/// points for query, mutation, and subscription operations.
#[Object(internal, name = "__Schema")]
impl<'a> __Schema<'a> {
/// A list of all types supported by this server.
@ -51,7 +55,8 @@ impl<'a> __Schema<'a> {
)
}
/// If this server supports mutation, the type that mutation operations will be rooted at.
/// If this server supports mutation, the type that mutation operations will
/// be rooted at.
#[inline]
async fn mutation_type(&self) -> Option<__Type<'a>> {
self.registry.mutation_type.as_ref().and_then(|ty| {
@ -67,7 +72,8 @@ impl<'a> __Schema<'a> {
})
}
/// If this server support subscription, the type that subscription operations will be rooted at.
/// If this server support subscription, the type that subscription
/// operations will be rooted at.
#[inline]
async fn subscription_type(&self) -> Option<__Type<'a>> {
self.registry.subscription_type.as_ref().and_then(|ty| {

View File

@ -1,8 +1,11 @@
use std::collections::HashSet;
use crate::model::{__EnumValue, __Field, __InputValue, __TypeKind};
use crate::registry::is_visible;
use crate::{registry, Context, Object};
use crate::{
model::{__EnumValue, __Field, __InputValue, __TypeKind},
registry,
registry::is_visible,
Context, Object,
};
enum TypeDetail<'a> {
Named(&'a registry::MetaType),
@ -59,9 +62,15 @@ impl<'a> __Type<'a> {
}
}
/// The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.
/// The fundamental unit of any GraphQL Schema is the type. There are many kinds
/// of types in GraphQL as represented by the `__TypeKind` enum.
///
/// Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.
/// Depending on the kind of a type, certain fields describe information about
/// that type. Scalar types provide no information beyond a name and
/// description, while Enum types provide their values. Object and Interface
/// types provide the fields they describe. Abstract types, Union and Interface,
/// provide the Object types possible at runtime. List and NonNull types compose
/// other types.
#[Object(internal, name = "__Type")]
impl<'a> __Type<'a> {
#[inline]

View File

@ -22,9 +22,42 @@
///
/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
/// assert_eq!(schema.execute("{ value1 }").await.into_result().unwrap().cache_control, CacheControl { public: true, max_age: 30 });
/// assert_eq!(schema.execute("{ value2 }").await.into_result().unwrap().cache_control, CacheControl { public: false, max_age: 60 });
/// assert_eq!(schema.execute("{ value1 value2 }").await.into_result().unwrap().cache_control, CacheControl { public: false, max_age: 30 });
/// assert_eq!(
/// schema
/// .execute("{ value1 }")
/// .await
/// .into_result()
/// .unwrap()
/// .cache_control,
/// CacheControl {
/// public: true,
/// max_age: 30
/// }
/// );
/// assert_eq!(
/// schema
/// .execute("{ value2 }")
/// .await
/// .into_result()
/// .unwrap()
/// .cache_control,
/// CacheControl {
/// public: false,
/// max_age: 60
/// }
/// );
/// assert_eq!(
/// schema
/// .execute("{ value1 value2 }")
/// .await
/// .into_result()
/// .unwrap()
/// .cache_control,
/// CacheControl {
/// public: false,
/// max_age: 30
/// }
/// );
/// # });
/// ```
#[derive(Clone, Copy, PartialEq, Eq, Debug)]

View File

@ -2,23 +2,22 @@ mod cache_control;
mod export_sdl;
mod stringify_exec_doc;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt::{self, Display, Formatter};
use indexmap::map::IndexMap;
use indexmap::set::IndexSet;
pub use crate::model::__DirectiveLocation;
use crate::{
model, Any, Context, InputType, OutputType, Positioned, ServerResult, SubscriptionType, Value,
VisitorContext,
};
use crate::{
parser::types::{BaseType as ParsedBaseType, Field, Type as ParsedType, VariableDefinition},
schema::IntrospectionMode,
use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
fmt::{self, Display, Formatter},
};
pub use cache_control::CacheControl;
use indexmap::{map::IndexMap, set::IndexSet};
pub use crate::model::__DirectiveLocation;
use crate::{
model,
parser::types::{BaseType as ParsedBaseType, Field, Type as ParsedType, VariableDefinition},
schema::IntrospectionMode,
Any, Context, InputType, OutputType, Positioned, ServerResult, SubscriptionType, Value,
VisitorContext,
};
fn strip_brackets(type_name: &str) -> Option<&str> {
type_name
@ -482,7 +481,8 @@ impl Registry {
}
}
None => {
// Inserting a fake type before calling the function allows recursive types to exist.
// Inserting a fake type before calling the function allows recursive types to
// exist.
self.types.insert(
name.to_string(),
MetaType::Object {

View File

@ -2,11 +2,13 @@ use std::fmt::{Error, Result as FmtResult, Write};
use async_graphql_value::ConstValue;
use crate::parser::types::{
ExecutableDocument, FragmentDefinition, OperationType, Selection, SelectionSet,
use crate::{
parser::types::{
ExecutableDocument, FragmentDefinition, OperationType, Selection, SelectionSet,
},
registry::{MetaInputValue, MetaType, MetaTypeName, Registry},
Variables,
};
use crate::registry::{MetaInputValue, MetaType, MetaTypeName, Registry};
use crate::Variables;
impl Registry {
pub(crate) fn stringify_exec_doc(
@ -219,8 +221,7 @@ impl Registry {
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse_query;
use crate::*;
use crate::{parser::parse_query, *};
#[test]
fn test_stringify() {

View File

@ -1,18 +1,22 @@
use std::any::Any;
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
use std::{
any::Any,
collections::HashMap,
fmt::{self, Debug, Formatter},
};
use serde::{Deserialize, Deserializer, Serialize};
use crate::parser::parse_query;
use crate::parser::types::ExecutableDocument;
use crate::schema::IntrospectionMode;
use crate::{Data, ParseRequestError, ServerError, UploadValue, Value, Variables};
use crate::{
parser::{parse_query, types::ExecutableDocument},
schema::IntrospectionMode,
Data, ParseRequestError, ServerError, UploadValue, Value, Variables,
};
/// GraphQL request.
///
/// This can be deserialized from a structure of the query string, the operation name and the
/// variables. The names are all in `camelCase` (e.g. `operationName`).
/// This can be deserialized from a structure of the query string, the operation
/// name and the variables. The names are all in `camelCase` (e.g.
/// `operationName`).
#[non_exhaustive]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -45,14 +49,16 @@ pub struct Request {
/// Disable introspection queries for this request.
/// This option has priority over `introspection_mode` when set to true.
/// `introspection_mode` has priority when `disable_introspection` set to `false`.
/// `introspection_mode` has priority when `disable_introspection` set to
/// `false`.
#[serde(skip)]
pub disable_introspection: bool,
#[serde(skip)]
pub(crate) parsed_query: Option<ExecutableDocument>,
/// Sets the introspection mode for this request (defaults to [IntrospectionMode::Enabled]).
/// Sets the introspection mode for this request (defaults to
/// [IntrospectionMode::Enabled]).
#[serde(skip)]
pub introspection_mode: IntrospectionMode,
}
@ -114,8 +120,8 @@ impl Request {
#[inline]
/// Performs parsing of query ahead of execution.
///
/// This effectively allows to inspect query information, before passing request to schema for
/// execution as long as query is valid.
/// This effectively allows to inspect query information, before passing
/// request to schema for execution as long as query is valid.
pub fn parsed_query(&mut self) -> Result<&ExecutableDocument, ServerError> {
if self.parsed_query.is_none() {
match parse_query(&self.query) {
@ -124,17 +130,17 @@ impl Request {
}
}
//forbid_unsafe effectively bans optimize away else branch here so use unwrap
//but this unwrap never panics
// forbid_unsafe effectively bans optimize away else branch here so use unwrap
// but this unwrap never panics
Ok(self.parsed_query.as_ref().unwrap())
}
/// Set a variable to an upload value.
///
/// `var_path` is a dot-separated path to the item that begins with `variables`, for example
/// `variables.files.2.content` is equivalent to the Rust code
/// `request.variables["files"][2]["content"]`. If no variable exists at the path this function
/// won't do anything.
/// `var_path` is a dot-separated path to the item that begins with
/// `variables`, for example `variables.files.2.content` is equivalent
/// to the Rust code `request.variables["files"][2]["content"]`. If no
/// variable exists at the path this function won't do anything.
pub fn set_upload(&mut self, var_path: &str, upload: UploadValue) {
fn variable_path<'a>(variables: &'a mut Variables, path: &str) -> Option<&'a mut Value> {
let mut parts = path.strip_prefix("variables.")?.split('.');
@ -178,12 +184,13 @@ impl Debug for Request {
}
}
/// Batch support for GraphQL requests, which is either a single query, or an array of queries
/// Batch support for GraphQL requests, which is either a single query, or an
/// array of queries
///
/// **Reference:** <https://www.apollographql.com/blog/batching-client-graphql-queries-a685f5bcd41b/>
#[derive(Debug, Deserialize)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)] //Request is at fault
#[allow(clippy::large_enum_variant)] // Request is at fault
pub enum BatchRequest {
/// Single query
Single(Request),
@ -198,8 +205,8 @@ impl BatchRequest {
///
/// # Errors
///
/// Fails if the batch request is a list of requests with a message saying that batch requests
/// aren't supported.
/// Fails if the batch request is a list of requests with a message saying
/// that batch requests aren't supported.
pub fn into_single(self) -> Result<Request, ParseRequestError> {
match self {
Self::Single(req) => Ok(req),

View File

@ -1,21 +1,17 @@
use futures_util::FutureExt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::{future::Future, pin::Pin, sync::Arc};
use futures_util::FutureExt;
use indexmap::IndexMap;
use crate::extensions::ResolveInfo;
use crate::parser::types::Selection;
use crate::{
Context, ContextBase, ContextSelectionSet, Error, Name, OutputType, ServerError, ServerResult,
Value,
extensions::ResolveInfo, parser::types::Selection, Context, ContextBase, ContextSelectionSet,
Error, Name, OutputType, ServerError, ServerResult, Value,
};
/// Represents a GraphQL container object.
///
/// This helper trait allows the type to call `resolve_container` on itself in its
/// `OutputType::resolve` implementation.
/// This helper trait allows the type to call `resolve_container` on itself in
/// its `OutputType::resolve` implementation.
#[async_trait::async_trait]
pub trait ContainerType: OutputType {
/// This function returns true of type `EmptyMutation` only.
@ -24,15 +20,17 @@ pub trait ContainerType: OutputType {
false
}
/// Resolves a field value and outputs it as a json value `async_graphql::Value`.
/// Resolves a field value and outputs it as a json value
/// `async_graphql::Value`.
///
/// If the field was not found returns None.
async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>>;
/// Collect all the fields of the container that are queried in the selection set.
/// Collect all the fields of the container that are queried in the
/// selection set.
///
/// Objects do not have to override this, but interfaces and unions must call it on their
/// internal type.
/// Objects do not have to override this, but interfaces and unions must
/// call it on their internal type.
fn collect_all_fields<'a>(
&'a self,
ctx: &ContextSelectionSet<'a>,
@ -175,7 +173,8 @@ type BoxFieldFuture<'a> = Pin<Box<dyn Future<Output = ServerResult<(Name, Value)
pub struct Fields<'a>(Vec<BoxFieldFuture<'a>>);
impl<'a> Fields<'a> {
/// Add another set of fields to this set of fields using the given container.
/// Add another set of fields to this set of fields using the given
/// container.
pub fn add_set<T: ContainerType + ?Sized>(
&mut self,
ctx: &ContextSelectionSet<'a>,

View File

@ -38,7 +38,8 @@ pub fn parse_enum<T: EnumType + InputType>(value: Value) -> InputValueResult<T>
/// Convert the enum value into a GraphQL value.
///
/// This can be used to implement `InputType::to_value` or `OutputType::resolve`.
/// This can be used to implement `InputType::to_value` or
/// `OutputType::resolve`.
pub fn enum_value<T: EnumType>(value: T) -> Value {
let item = T::items().iter().find(|item| item.value == value).unwrap();
Value::Enum(Name::new(item.name))

View File

@ -1,6 +1,7 @@
use crate::extensions::ResolveInfo;
use crate::parser::types::Field;
use crate::{ContextSelectionSet, OutputType, Positioned, ServerResult, Value};
use crate::{
extensions::ResolveInfo, parser::types::Field, ContextSelectionSet, OutputType, Positioned,
ServerResult, Value,
};
/// Resolve an list by executing each of the items concurrently.
pub async fn resolve_list<'a, T: OutputType + 'a>(

View File

@ -33,7 +33,8 @@ pub trait ScalarType: Sized + Send {
/// Checks for a valid scalar value.
///
/// Implementing this function can find incorrect input values during the verification phase, which can improve performance.
/// Implementing this function can find incorrect input values during the
/// verification phase, which can improve performance.
fn is_valid(_value: &Value) -> bool {
true
}
@ -44,8 +45,11 @@ pub trait ScalarType: Sized + Send {
/// Define a scalar
///
/// If your type implemented `serde::Serialize` and `serde::Deserialize`, then you can use this macro to define a scalar more simply.
/// It helps you implement the `ScalarType::parse` and `ScalarType::to_value` functions by calling the [from_value](fn.from_value.html) and [to_value](fn.to_value.html) functions.
/// If your type implemented `serde::Serialize` and `serde::Deserialize`, then
/// you can use this macro to define a scalar more simply. It helps you
/// implement the `ScalarType::parse` and `ScalarType::to_value` functions by
/// calling the [from_value](fn.from_value.html) and
/// [to_value](fn.to_value.html) functions.
///
/// # Examples
///

View File

@ -1,7 +1,9 @@
use std::collections::BTreeMap;
use http::header::{HeaderMap, HeaderName};
use http::HeaderValue;
use http::{
header::{HeaderMap, HeaderName},
HeaderValue,
};
use serde::{Deserialize, Serialize};
use crate::{CacheControl, Result, ServerError, Value};
@ -87,8 +89,8 @@ impl Response {
!self.is_ok()
}
/// Extract the error from the response. Only if the `error` field is empty will this return
/// `Ok`.
/// Extract the error from the response. Only if the `error` field is empty
/// will this return `Ok`.
#[inline]
pub fn into_result(self) -> Result<Self, Vec<ServerError>> {
if self.is_err() {

View File

@ -1,28 +1,26 @@
use std::any::Any;
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
use std::{any::Any, collections::HashMap, ops::Deref, sync::Arc};
use futures_util::stream::{self, Stream, StreamExt};
use indexmap::map::IndexMap;
use crate::custom_directive::CustomDirectiveFactory;
use crate::extensions::{ExtensionFactory, Extensions};
use crate::model::__DirectiveLocation;
use crate::parser::types::{Directive, DocumentOperations, OperationType, Selection, SelectionSet};
use crate::parser::{parse_query, Positioned};
use crate::registry::{MetaDirective, MetaInputValue, Registry};
use crate::resolver_utils::{resolve_container, resolve_container_serial};
use crate::subscription::collect_subscription_streams;
use crate::types::QueryRoot;
use crate::validation::{check_rules, ValidationMode};
use crate::{
context::{Data, QueryEnvInner},
EmptyMutation, EmptySubscription,
};
use crate::{
BatchRequest, BatchResponse, CacheControl, ContextBase, InputType, ObjectType, OutputType,
QueryEnv, Request, Response, ServerError, SubscriptionType, Variables, ID,
custom_directive::CustomDirectiveFactory,
extensions::{ExtensionFactory, Extensions},
model::__DirectiveLocation,
parser::{
parse_query,
types::{Directive, DocumentOperations, OperationType, Selection, SelectionSet},
Positioned,
},
registry::{MetaDirective, MetaInputValue, Registry},
resolver_utils::{resolve_container, resolve_container_serial},
subscription::collect_subscription_streams,
types::QueryRoot,
validation::{check_rules, ValidationMode},
BatchRequest, BatchResponse, CacheControl, ContextBase, EmptyMutation, EmptySubscription,
InputType, ObjectType, OutputType, QueryEnv, Request, Response, ServerError, SubscriptionType,
Variables, ID,
};
/// Introspection mode
@ -56,7 +54,8 @@ pub struct SchemaBuilder<Query, Mutation, Subscription> {
impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription> {
/// Manually register a input type in the schema.
///
/// You can use this function to register schema types that are not directly referenced.
/// You can use this function to register schema types that are not directly
/// referenced.
#[must_use]
pub fn register_input_type<T: InputType>(mut self) -> Self {
T::create_type_info(&mut self.registry);
@ -65,7 +64,8 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
/// Manually register a output type in the schema.
///
/// You can use this function to register schema types that are not directly referenced.
/// You can use this function to register schema types that are not directly
/// referenced.
#[must_use]
pub fn register_output_type<T: OutputType>(mut self) -> Self {
T::create_type_info(&mut self.registry);
@ -79,14 +79,16 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
self
}
/// Only process introspection queries, everything else is processed as an error.
/// Only process introspection queries, everything else is processed as an
/// error.
#[must_use]
pub fn introspection_only(mut self) -> Self {
self.registry.introspection_mode = IntrospectionMode::IntrospectionOnly;
self
}
/// Set the maximum complexity a query can have. By default, there is no limit.
/// Set the maximum complexity a query can have. By default, there is no
/// limit.
#[must_use]
pub fn limit_complexity(mut self, complexity: usize) -> Self {
self.complexity = Some(complexity);
@ -116,7 +118,7 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
/// }
/// }
///
/// let schema = Schema::build(Query, EmptyMutation,EmptySubscription)
/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
/// .extension(extensions::Logger)
/// .finish();
/// ```
@ -126,7 +128,8 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
self
}
/// Add a global data that can be accessed in the `Schema`. You access it with `Context::data`.
/// Add a global data that can be accessed in the `Schema`. You access it
/// with `Context::data`.
#[must_use]
pub fn data<D: Any + Send + Sync>(mut self, data: D) -> Self {
self.data.insert(data);
@ -140,7 +143,8 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
self
}
/// Enable federation, which is automatically enabled if the Query has least one entity definition.
/// Enable federation, which is automatically enabled if the Query has least
/// one entity definition.
#[must_use]
pub fn enable_federation(mut self) -> Self {
self.registry.enable_federation = true;
@ -149,7 +153,8 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
/// Make the Federation SDL include subscriptions.
///
/// Note: Not included by default, in order to be compatible with Apollo Server.
/// Note: Not included by default, in order to be compatible with Apollo
/// Server.
#[must_use]
pub fn enable_subscription_in_federation(mut self) -> Self {
self.registry.federation_subscription = true;
@ -426,9 +431,10 @@ where
/// Get all names in this schema
///
/// Maybe you want to serialize a custom binary protocol. In order to minimize message size, a dictionary
/// is usually used to compress type names, field names, directive names, and parameter names. This function gets all the names,
/// so you can create this dictionary.
/// Maybe you want to serialize a custom binary protocol. In order to
/// minimize message size, a dictionary is usually used to compress type
/// names, field names, directive names, and parameter names. This function
/// gets all the names, so you can create this dictionary.
pub fn names(&self) -> Vec<String> {
self.0.env.registry.names()
}

View File

@ -1,12 +1,12 @@
use std::borrow::Cow;
use std::pin::Pin;
use std::{borrow::Cow, pin::Pin};
use futures_util::stream::{Stream, StreamExt};
use crate::parser::types::{Selection, TypeCondition};
use crate::registry::Registry;
use crate::{
registry, Context, ContextSelectionSet, PathSegment, Response, ServerError, ServerResult,
parser::types::{Selection, TypeCondition},
registry,
registry::Registry,
Context, ContextSelectionSet, PathSegment, Response, ServerError, ServerResult,
};
/// A GraphQL subscription object

View File

@ -2,11 +2,13 @@ use crate::{InputValueResult, Scalar, ScalarType, Value};
/// Any scalar (For [Apollo Federation](https://www.apollographql.com/docs/apollo-server/federation/introduction))
///
/// The `Any` scalar is used to pass representations of entities from external services into the root `_entities` field for execution.
/// The `Any` scalar is used to pass representations of entities from external
/// services into the root `_entities` field for execution.
#[derive(Clone, PartialEq, Debug)]
pub struct Any(pub Value);
/// The `_Any` scalar is used to pass representations of entities from external services into the root `_entities` field for execution.
/// The `_Any` scalar is used to pass representations of entities from external
/// services into the root `_entities` field for execution.
#[Scalar(internal, name = "_Any")]
impl ScalarType for Any {
fn parse(value: Value) -> InputValueResult<Self> {

View File

@ -1,34 +1,56 @@
use std::borrow::Cow;
use std::{borrow::Cow, marker::PhantomData};
use futures_util::stream::{Stream, StreamExt, TryStreamExt};
use indexmap::map::IndexMap;
use crate::connection::edge::Edge;
use crate::connection::page_info::PageInfo;
use crate::parser::types::Field;
use crate::registry::MetaTypeId;
use crate::resolver_utils::{resolve_container, ContainerType};
use crate::types::connection::{CursorType, EmptyFields};
use crate::{
registry, Context, ContextSelectionSet, ObjectType, OutputType, Positioned, Result,
ServerResult, Value,
connection::{edge::Edge, ConnectionNameType, EdgeNameType, PageInfo},
types::connection::{CursorType, EmptyFields},
Object, ObjectType, OutputType, TypeName,
};
/// Connection type
///
/// Connection is the result of a query for `connection::query`.
pub struct Connection<C, T, EC = EmptyFields, EE = EmptyFields> {
pub struct Connection<
Name,
EdgeName,
Cursor,
Node,
ConnectionFields = EmptyFields,
EdgeFields = EmptyFields,
> where
Name: ConnectionNameType,
EdgeName: EdgeNameType,
Cursor: CursorType + Send + Sync,
Node: OutputType,
ConnectionFields: ObjectType,
EdgeFields: ObjectType,
{
_mark1: PhantomData<Name>,
_mark2: PhantomData<EdgeName>,
/// All edges of the current page.
pub edges: Vec<Edge<C, T, EE>>,
additional_fields: EC,
has_previous_page: bool,
has_next_page: bool,
pub edges: Vec<Edge<EdgeName, Cursor, Node, EdgeFields>>,
/// Additional fields for connection object.
pub additional_fields: ConnectionFields,
/// If `true` means has previous page.
pub has_previous_page: bool,
/// If `false` means has next page.
pub has_next_page: bool,
}
impl<C, T, EE> Connection<C, T, EmptyFields, EE> {
impl<Name, EdgeName, Cursor, Node, EdgeFields>
Connection<Name, EdgeName, Cursor, Node, EmptyFields, EdgeFields>
where
Name: ConnectionNameType,
EdgeName: EdgeNameType,
Cursor: CursorType + Send + Sync,
Node: OutputType,
EdgeFields: ObjectType,
{
/// Create a new connection.
#[inline]
pub fn new(has_previous_page: bool, has_next_page: bool) -> Self {
Connection {
_mark1: PhantomData,
_mark2: PhantomData,
additional_fields: EmptyFields,
has_previous_page,
has_next_page,
@ -37,14 +59,26 @@ impl<C, T, EE> Connection<C, T, EmptyFields, EE> {
}
}
impl<C, T, EC, EE> Connection<C, T, EC, EE> {
impl<Name, EdgeName, Cursor, Node, ConnectionFields, EdgeFields>
Connection<Name, EdgeName, Cursor, Node, ConnectionFields, EdgeFields>
where
Name: ConnectionNameType,
EdgeName: EdgeNameType,
Cursor: CursorType + Send + Sync,
Node: OutputType,
ConnectionFields: ObjectType,
EdgeFields: ObjectType,
{
/// Create a new connection, it can have some additional fields.
#[inline]
pub fn with_additional_fields(
has_previous_page: bool,
has_next_page: bool,
additional_fields: EC,
additional_fields: ConnectionFields,
) -> Self {
Connection {
_mark1: PhantomData,
_mark2: PhantomData,
additional_fields,
has_previous_page,
has_next_page,
@ -53,198 +87,52 @@ impl<C, T, EC, EE> Connection<C, T, EC, EE> {
}
}
impl<C, T, EC, EE> Connection<C, T, EC, EE> {
/// Convert the edge type and return a new `Connection`.
pub fn map<T2, EE2, F>(self, mut f: F) -> Connection<C, T2, EC, EE2>
where
F: FnMut(Edge<C, T, EE>) -> Edge<C, T2, EE2>,
{
let mut new_edges = Vec::with_capacity(self.edges.len());
for edge in self.edges {
new_edges.push(f(edge));
}
Connection {
edges: new_edges,
additional_fields: self.additional_fields,
#[Object(internal, name_type)]
impl<Name, EdgeName, Cursor, Node, ConnectionFields, EdgeFields>
Connection<Name, EdgeName, Cursor, Node, ConnectionFields, EdgeFields>
where
Name: ConnectionNameType,
EdgeName: EdgeNameType,
Cursor: CursorType + Send + Sync,
Node: OutputType,
ConnectionFields: ObjectType,
EdgeFields: ObjectType,
{
/// Information to aid in pagination.
async fn page_info(&self) -> PageInfo {
PageInfo {
has_previous_page: self.has_previous_page,
has_next_page: self.has_next_page,
start_cursor: self.edges.first().map(|edge| edge.cursor.0.encode_cursor()),
end_cursor: self.edges.last().map(|edge| edge.cursor.0.encode_cursor()),
}
}
/// Convert the node type and return a new `Connection`.
pub fn map_node<T2, F>(self, mut f: F) -> Connection<C, T2, EC, EE>
where
F: FnMut(T) -> T2,
{
self.map(|edge| Edge {
cursor: edge.cursor,
node: f(edge.node),
additional_fields: edge.additional_fields,
})
/// A list of edges.
#[inline]
async fn edges(&self) -> &[Edge<EdgeName, Cursor, Node, EdgeFields>] {
&self.edges
}
/// Append edges with `IntoIterator<Item = Edge<C, T, EE>>`
pub fn append<I>(&mut self, iter: I)
where
I: IntoIterator<Item = Edge<C, T, EE>>,
{
self.edges.extend(iter);
}
/// Append edges with `IntoIterator<Item = Result<Edge<C, T, EE>, E>>`
pub fn try_append<I, E>(&mut self, iter: I) -> Result<(), E>
where
I: IntoIterator<Item = Result<Edge<C, T, EE>, E>>,
{
for edge in iter {
self.edges.push(edge?);
}
Ok(())
}
/// Append edges with `Stream<Item = Result<Edge<C, T, EE>>>`
pub async fn append_stream<S>(&mut self, stream: S)
where
S: Stream<Item = Edge<C, T, EE>> + Unpin,
{
self.edges.extend(stream.collect::<Vec<_>>().await);
}
/// Append edges with `Stream<Item = Result<Edge<C, T, EE>, E>>`
pub async fn try_append_stream<S, E>(&mut self, stream: S) -> Result<(), E>
where
S: Stream<Item = Result<Edge<C, T, EE>, E>> + Unpin,
{
self.edges.extend(stream.try_collect::<Vec<_>>().await?);
Ok(())
#[graphql(flatten)]
#[inline]
async fn additional_fields(&self) -> &ConnectionFields {
&self.additional_fields
}
}
#[async_trait::async_trait]
impl<C, T, EC, EE> ContainerType for Connection<C, T, EC, EE>
impl<Name, EdgeName, Cursor, Node, ConnectionFields, EdgeFields> TypeName
for Connection<Name, EdgeName, Cursor, Node, ConnectionFields, EdgeFields>
where
C: CursorType + Send + Sync,
T: OutputType,
EC: ObjectType,
EE: ObjectType,
{
async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
if ctx.item.node.name.node == "pageInfo" {
let page_info = PageInfo {
has_previous_page: self.has_previous_page,
has_next_page: self.has_next_page,
start_cursor: self.edges.first().map(|edge| edge.cursor.encode_cursor()),
end_cursor: self.edges.last().map(|edge| edge.cursor.encode_cursor()),
};
let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
return OutputType::resolve(&page_info, &ctx_obj, ctx.item)
.await
.map(Some);
} else if ctx.item.node.name.node == "edges" {
let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
return OutputType::resolve(&self.edges, &ctx_obj, ctx.item)
.await
.map(Some);
}
self.additional_fields.resolve_field(ctx).await
}
}
#[async_trait::async_trait]
impl<C, T, EC, EE> OutputType for Connection<C, T, EC, EE>
where
C: CursorType + Send + Sync,
T: OutputType,
EC: ObjectType,
EE: ObjectType,
Name: ConnectionNameType,
EdgeName: EdgeNameType,
Cursor: CursorType + Send + Sync,
Node: OutputType,
ConnectionFields: ObjectType,
EdgeFields: ObjectType,
{
#[inline]
fn type_name() -> Cow<'static, str> {
Cow::Owned(format!("{}Connection", T::type_name()))
}
fn create_type_info(registry: &mut registry::Registry) -> String {
registry.create_output_type::<Self, _>(MetaTypeId::Object,|registry| {
EC::create_type_info(registry);
let additional_fields = if let Some(registry::MetaType::Object { fields, .. }) =
registry.types.remove(EC::type_name().as_ref())
{
fields
} else {
unreachable!()
};
registry::MetaType::Object {
name: Self::type_name().to_string(),
description: None,
fields: {
let mut fields = IndexMap::new();
fields.insert(
"pageInfo".to_string(),
registry::MetaField {
name: "pageInfo".to_string(),
description: Some("Information to aid in pagination."),
args: Default::default(),
ty: PageInfo::create_type_info(registry),
deprecation: Default::default(),
cache_control: Default::default(),
external: false,
requires: None,
provides: None,
visible: None,
compute_complexity: None,
oneof: false,
},
);
fields.insert(
"edges".to_string(),
registry::MetaField {
name: "edges".to_string(),
description: Some("A list of edges."),
args: Default::default(),
ty: <Option<Vec<Option<Edge<C, T, EE>>>> as OutputType>::create_type_info(
registry,
),
deprecation: Default::default(),
cache_control: Default::default(),
external: false,
requires: None,
provides: None,
visible: None,
compute_complexity: None,
oneof: false,
},
);
fields.extend(additional_fields);
fields
},
cache_control: Default::default(),
extends: false,
keys: None,
visible: None,
is_subscription: false,
rust_typename: std::any::type_name::<Self>(),
}
})
}
async fn resolve(
&self,
ctx: &ContextSelectionSet<'_>,
_field: &Positioned<Field>,
) -> ServerResult<Value> {
resolve_container(ctx, self).await
Name::type_name::<Node>().into()
}
}
impl<C, T, EC, EE> ObjectType for Connection<C, T, EC, EE>
where
C: CursorType + Send + Sync,
T: OutputType,
EC: ObjectType,
EE: ObjectType,
{
}

View File

@ -1,6 +1,4 @@
use std::convert::Infallible;
use std::fmt::Display;
use std::num::ParseIntError;
use std::{convert::Infallible, fmt::Display, num::ParseIntError};
use crate::ID;

View File

@ -1,155 +1,104 @@
use std::borrow::Cow;
use std::{borrow::Cow, marker::PhantomData};
use indexmap::map::IndexMap;
use crate::connection::EmptyFields;
use crate::parser::types::Field;
use crate::registry::MetaTypeId;
use crate::resolver_utils::{resolve_container, ContainerType};
use crate::types::connection::CursorType;
use crate::{
registry, Context, ContextSelectionSet, ObjectType, OutputType, Positioned, ServerResult, Value,
connection::EmptyFields,
types::connection::{CursorType, EdgeNameType},
InputValueError, InputValueResult, ObjectType, OutputType, Scalar, ScalarType, SimpleObject,
TypeName, Value,
};
/// The edge type output by the data source
pub struct Edge<C, T, E> {
pub(crate) cursor: C,
pub(crate) node: T,
pub(crate) additional_fields: E,
pub(crate) struct CursorScalar<T: CursorType>(pub(crate) T);
#[Scalar(internal, name = "String")]
impl<T: CursorType + Send + Sync> ScalarType for CursorScalar<T> {
fn parse(value: Value) -> InputValueResult<Self> {
match value {
Value::String(s) => T::decode_cursor(&s)
.map(Self)
.map_err(InputValueError::custom),
_ => Err(InputValueError::expected_type(value)),
}
}
fn is_valid(value: &Value) -> bool {
matches!(value, Value::String(_))
}
fn to_value(&self) -> Value {
Value::String(self.0.encode_cursor())
}
}
impl<C, T, E> Edge<C, T, E> {
/// An edge in a connection.
#[derive(SimpleObject)]
#[graphql(internal, name_type)]
pub struct Edge<Name, Cursor, Node, EdgeFields>
where
Name: EdgeNameType,
Cursor: CursorType + Send + Sync,
Node: OutputType,
EdgeFields: ObjectType,
{
#[graphql(skip)]
_mark: PhantomData<Name>,
/// A cursor for use in pagination
pub(crate) cursor: CursorScalar<Cursor>,
/// "The item at the end of the edge
pub(crate) node: Node,
#[graphql(flatten)]
pub(crate) additional_fields: EdgeFields,
}
impl<Name, Cursor, Node, EdgeFields> TypeName for Edge<Name, Cursor, Node, EdgeFields>
where
Name: EdgeNameType,
Cursor: CursorType + Send + Sync,
Node: OutputType,
EdgeFields: ObjectType,
{
#[inline]
fn type_name() -> Cow<'static, str> {
Name::type_name::<Node>().into()
}
}
impl<Name, Cursor, Node, EdgeFields> Edge<Name, Cursor, Node, EdgeFields>
where
Name: EdgeNameType,
Cursor: CursorType + Send + Sync,
Node: OutputType,
EdgeFields: ObjectType,
{
/// Create a new edge, it can have some additional fields.
pub fn with_additional_fields(cursor: C, node: T, additional_fields: E) -> Self {
#[inline]
pub fn with_additional_fields(
cursor: Cursor,
node: Node,
additional_fields: EdgeFields,
) -> Self {
Self {
cursor,
_mark: PhantomData,
cursor: CursorScalar(cursor),
node,
additional_fields,
}
}
}
impl<C: CursorType, T> Edge<C, T, EmptyFields> {
impl<Name, Cursor, Node> Edge<Name, Cursor, Node, EmptyFields>
where
Name: EdgeNameType,
Cursor: CursorType + Send + Sync,
Node: OutputType,
{
/// Create a new edge.
pub fn new(cursor: C, node: T) -> Self {
#[inline]
pub fn new(cursor: Cursor, node: Node) -> Self {
Self {
cursor,
_mark: PhantomData,
cursor: CursorScalar(cursor),
node,
additional_fields: EmptyFields,
}
}
}
#[async_trait::async_trait]
impl<C, T, E> ContainerType for Edge<C, T, E>
where
C: CursorType + Send + Sync,
T: OutputType,
E: ObjectType,
{
async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
if ctx.item.node.name.node == "node" {
let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
return OutputType::resolve(&self.node, &ctx_obj, ctx.item)
.await
.map(Some);
} else if ctx.item.node.name.node == "cursor" {
return Ok(Some(Value::String(self.cursor.encode_cursor())));
}
self.additional_fields.resolve_field(ctx).await
}
}
#[async_trait::async_trait]
impl<C, T, E> OutputType for Edge<C, T, E>
where
C: CursorType + Send + Sync,
T: OutputType,
E: ObjectType,
{
fn type_name() -> Cow<'static, str> {
Cow::Owned(format!("{}Edge", T::type_name()))
}
fn create_type_info(registry: &mut registry::Registry) -> String {
registry.create_output_type::<Self, _>(MetaTypeId::Object, |registry| {
let additional_fields = if let registry::MetaType::Object { fields, .. } =
registry.create_fake_output_type::<E>()
{
fields
} else {
unreachable!()
};
registry::MetaType::Object {
name: Self::type_name().to_string(),
description: Some("An edge in a connection."),
fields: {
let mut fields = IndexMap::new();
fields.insert(
"node".to_string(),
registry::MetaField {
name: "node".to_string(),
description: Some("The item at the end of the edge"),
args: Default::default(),
ty: T::create_type_info(registry),
deprecation: Default::default(),
cache_control: Default::default(),
external: false,
requires: None,
provides: None,
visible: None,
compute_complexity: None,
oneof: false,
},
);
fields.insert(
"cursor".to_string(),
registry::MetaField {
name: "cursor".to_string(),
description: Some("A cursor for use in pagination"),
args: Default::default(),
ty: String::create_type_info(registry),
deprecation: Default::default(),
cache_control: Default::default(),
external: false,
requires: None,
provides: None,
visible: None,
compute_complexity: None,
oneof: false,
},
);
fields.extend(additional_fields);
fields
},
cache_control: Default::default(),
extends: false,
keys: None,
visible: None,
is_subscription: false,
rust_typename: std::any::type_name::<Self>(),
}
})
}
async fn resolve(
&self,
ctx: &ContextSelectionSet<'_>,
_field: &Positioned<Field>,
) -> ServerResult<Value> {
resolve_container(ctx, self).await
}
}
impl<C, T, E> ObjectType for Edge<C, T, E>
where
C: CursorType + Send + Sync,
T: OutputType,
E: ObjectType,
{
}

View File

@ -5,26 +5,57 @@ mod cursor;
mod edge;
mod page_info;
use std::fmt::Display;
use std::future::Future;
use std::{fmt::Display, future::Future};
pub use connection_type::Connection;
pub use cursor::CursorType;
pub use edge::Edge;
pub use page_info::PageInfo;
use crate::{Error, Result, SimpleObject};
use crate::{Error, ObjectType, OutputType, Result, SimpleObject};
/// Empty additional fields
#[derive(SimpleObject)]
#[graphql(internal, fake)]
pub struct EmptyFields;
/// Used to specify the edge name.
pub trait EdgeNameType: Send + Sync {
/// Returns the edge type name.
fn type_name<T: OutputType>() -> String;
}
/// Name the edge type by default with the default format.
pub struct DefaultEdgeName;
impl EdgeNameType for DefaultEdgeName {
fn type_name<T: OutputType>() -> String {
format!("{}Edge", T::type_name())
}
}
/// Used to specify the connection name.
pub trait ConnectionNameType: Send + Sync {
/// Returns the connection type name.
fn type_name<T: OutputType>() -> String;
}
/// Name the connection type by default with the default format.
pub struct DefaultConnectionName;
impl ConnectionNameType for DefaultConnectionName {
fn type_name<T: OutputType>() -> String {
format!("{}Connection", T::type_name())
}
}
/// Parses the parameters and executes the query.
///
/// # Examples
///
/// ```rust
/// use std::borrow::Cow;
///
/// use async_graphql::*;
/// use async_graphql::types::connection::*;
///
@ -44,7 +75,7 @@ pub struct EmptyFields;
/// before: Option<String>,
/// first: Option<i32>,
/// last: Option<i32>
/// ) -> Result<Connection<usize, i32, EmptyFields, Diff>> {
/// ) -> Result<Connection<DefaultConnectionName, DefaultEdgeName, usize, i32, EmptyFields, Diff>> {
/// query(after, before, first, last, |after, before, first, last| async move {
/// let mut start = after.map(|after| after + 1).unwrap_or(0);
/// let mut end = before.unwrap_or(10000);
@ -59,7 +90,7 @@ pub struct EmptyFields;
/// };
/// }
/// let mut connection = Connection::new(start > 0, end < 10000);
/// connection.append(
/// connection.edges.extend(
/// (start..end).into_iter().map(|n|
/// Edge::with_additional_fields(n, n as i32, Diff{ diff: (10000 - n) as i32 })),
/// );
@ -90,31 +121,108 @@ pub struct EmptyFields;
/// }));
/// # });
/// ```
pub async fn query<Cursor, Node, ConnectionFields, EdgeFields, F, R, E>(
///
/// # Custom connection and edge type names
///
/// ```
/// use async_graphql::{connection::*, *};
///
/// #[derive(SimpleObject)]
/// struct MyObj {
/// a: i32,
/// b: String,
/// }
///
/// // Use to custom connection name
/// struct MyConnectionName;
///
/// impl ConnectionNameType for MyConnectionName {
/// fn type_name<T: OutputType>() -> String {
/// "MyConnection".to_string()
/// }
/// }
///
/// // Use to custom edge name
/// struct MyEdgeName;
///
/// impl EdgeNameType for MyEdgeName {
/// fn type_name<T: OutputType>() -> String {
/// "MyEdge".to_string()
/// }
/// }
///
/// struct Query;
///
/// #[Object]
/// impl Query {
/// async fn numbers(
/// &self,
/// after: Option<String>,
/// before: Option<String>,
/// first: Option<i32>,
/// last: Option<i32>,
/// ) -> Connection<MyConnectionName, MyEdgeName, usize, MyObj> {
/// let mut connection = Connection::new(false, false);
/// connection.edges.push(Edge::new(1, MyObj { a: 100, b: "abc".to_string() }));
/// connection
/// }
/// }
///
/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
///
/// let query = r#"{
/// numbers(first: 2) {
/// __typename
/// edges { __typename node { a b } }
/// }
/// }"#;
/// let data = schema.execute(query).await.into_result().unwrap().data;
/// assert_eq!(data, value!({
/// "numbers": {
/// "__typename": "MyConnection",
/// "edges": [
/// {"__typename": "MyEdge", "node": { "a": 100, "b": "abc" }},
/// ]
/// },
/// }));
/// # });
/// ```
pub async fn query<Name, EdgeName, Cursor, Node, ConnectionFields, EdgeFields, F, R, E>(
after: Option<String>,
before: Option<String>,
first: Option<i32>,
last: Option<i32>,
f: F,
) -> Result<Connection<Cursor, Node, ConnectionFields, EdgeFields>>
) -> Result<Connection<Name, EdgeName, Cursor, Node, ConnectionFields, EdgeFields>>
where
Name: ConnectionNameType,
EdgeName: EdgeNameType,
Cursor: CursorType + Send + Sync,
<Cursor as CursorType>::Error: Display + Send + Sync + 'static,
Node: OutputType,
ConnectionFields: ObjectType,
EdgeFields: ObjectType,
F: FnOnce(Option<Cursor>, Option<Cursor>, Option<usize>, Option<usize>) -> R,
R: Future<Output = Result<Connection<Cursor, Node, ConnectionFields, EdgeFields>, E>>,
R: Future<
Output = Result<Connection<Name, EdgeName, Cursor, Node, ConnectionFields, EdgeFields>, E>,
>,
E: Into<Error>,
{
query_with(after, before, first, last, f).await
}
/// Parses the parameters and executes the query and return a custom `Connection` type.
/// Parses the parameters and executes the query and return a custom
/// `Connection` type.
///
/// `Connection<T>` and `Edge<T>` have certain limitations. For example, you cannot customize
/// the name of the type, so you can use this function to execute the query and return a customized `Connection` type.
/// `Connection<T>` and `Edge<T>` have certain limitations. For example, you
/// cannot customize the name of the type, so you can use this function to
/// execute the query and return a customized `Connection` type.
///
/// # Examples
///
/// ```rust
///
/// use async_graphql::*;
/// use async_graphql::types::connection::*;
///
@ -172,29 +280,27 @@ where
/// }
/// }
///
/// #[tokio::main]
/// async fn main() {
/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
///
/// assert_eq!(schema.execute("{ numbers(first: 2) { edges { node diff } } }").await.into_result().unwrap().data, value!({
/// "numbers": {
/// "edges": [
/// {"node": 0, "diff": 10000},
/// {"node": 1, "diff": 9999},
/// ]
/// },
/// }));
///
/// assert_eq!(schema.execute("{ numbers(last: 2) { edges { node diff } } }").await.into_result().unwrap().data, value!({
/// "numbers": {
/// "edges": [
/// {"node": 9998, "diff": 2},
/// {"node": 9999, "diff": 1},
/// ]
/// },
/// }));
/// }
/// assert_eq!(schema.execute("{ numbers(first: 2) { edges { node diff } } }").await.into_result().unwrap().data, value!({
/// "numbers": {
/// "edges": [
/// {"node": 0, "diff": 10000},
/// {"node": 1, "diff": 9999},
/// ]
/// },
/// }));
///
/// assert_eq!(schema.execute("{ numbers(last: 2) { edges { node diff } } }").await.into_result().unwrap().data, value!({
/// "numbers": {
/// "edges": [
/// {"node": 9998, "diff": 2},
/// {"node": 9999, "diff": 1},
/// ]
/// },
/// }));
/// # });
/// ```
pub async fn query_with<Cursor, T, F, R, E>(
after: Option<String>,

View File

@ -1,16 +1,14 @@
use std::borrow::Cow;
use crate::parser::types::Field;
use crate::registry::MetaTypeId;
use crate::resolver_utils::ContainerType;
use crate::{
registry, Context, ContextSelectionSet, ObjectType, OutputType, Positioned, ServerError,
ServerResult, Value,
parser::types::Field, registry, registry::MetaTypeId, resolver_utils::ContainerType, Context,
ContextSelectionSet, ObjectType, OutputType, Positioned, ServerError, ServerResult, Value,
};
/// Empty mutation
///
/// Only the parameters used to construct the Schema, representing an unconfigured mutation.
/// Only the parameters used to construct the Schema, representing an
/// unconfigured mutation.
///
/// # Examples
///

View File

@ -1,5 +1,4 @@
use std::borrow::Cow;
use std::pin::Pin;
use std::{borrow::Cow, pin::Pin};
use futures_util::stream::{self, Stream};
@ -7,7 +6,8 @@ use crate::{registry, Context, Response, ServerError, SubscriptionType};
/// Empty subscription
///
/// Only the parameters used to construct the Schema, representing an unconfigured subscription.
/// Only the parameters used to construct the Schema, representing an
/// unconfigured subscription.
#[derive(Default, Copy, Clone)]
pub struct EmptySubscription;

View File

@ -1,9 +1,8 @@
use bson::{oid::ObjectId, Bson, Document};
#[cfg(feature = "chrono")]
use bson::DateTime as UtcDateTime;
#[cfg(feature = "bson-uuid")]
use bson::Uuid;
use bson::{oid::ObjectId, Bson, Document};
#[cfg(feature = "chrono")]
use chrono::{DateTime, Utc};

View File

@ -1,7 +1,8 @@
use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
/// The `Char` scalar type represents a unicode char.
/// The input and output values are a string, and there can only be one unicode character in this string.
/// The input and output values are a string, and there can only be one unicode
/// character in this string.
#[Scalar(internal)]
impl ScalarType for char {
fn parse(value: Value) -> InputValueResult<Self> {

View File

@ -29,9 +29,10 @@ where
#[cfg(test)]
mod test {
use crate::*;
use std::borrow::Cow;
use crate::*;
#[tokio::test]
async fn test_cow_type() {
struct Query {

View File

@ -1,17 +1,12 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt::Display;
use std::str::FromStr;
use std::{borrow::Cow, collections::BTreeMap, fmt::Display, str::FromStr};
use async_graphql_parser::types::Field;
use async_graphql_parser::Positioned;
use async_graphql_parser::{types::Field, Positioned};
use async_graphql_value::{from_value, to_value};
use indexmap::IndexMap;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde::{de::DeserializeOwned, Serialize};
use crate::registry::{MetaType, MetaTypeId, Registry};
use crate::{
registry::{MetaType, MetaTypeId, Registry},
ContextSelectionSet, InputType, InputValueError, InputValueResult, Name, OutputType,
ServerResult, Value,
};

View File

@ -1,21 +1,16 @@
use std::borrow::Cow;
use std::fmt::Display;
use std::hash::Hash;
use std::str::FromStr;
use std::{
borrow::Cow, collections::HashMap as StdHashMap, fmt::Display, hash::Hash, str::FromStr,
};
use async_graphql_parser::types::Field;
use async_graphql_parser::Positioned;
use async_graphql_parser::{types::Field, Positioned};
use async_graphql_value::{from_value, to_value};
use hashbrown::HashMap;
use indexmap::IndexMap;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::collections::HashMap as StdHashMap;
use serde::{de::DeserializeOwned, Serialize};
use crate::registry::Registry;
use crate::{
ContextSelectionSet, InputType, InputValueError, InputValueResult, Name, OutputType,
ServerResult, Value,
registry::Registry, ContextSelectionSet, InputType, InputValueError, InputValueResult, Name,
OutputType, ServerResult, Value,
};
impl<K, V> InputType for HashMap<K, V>

View File

@ -1,18 +1,18 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Display;
use std::hash::{BuildHasher, Hash};
use std::str::FromStr;
use std::{
borrow::Cow,
collections::HashMap,
fmt::Display,
hash::{BuildHasher, Hash},
str::FromStr,
};
use async_graphql_parser::types::Field;
use async_graphql_parser::Positioned;
use async_graphql_parser::{types::Field, Positioned};
use async_graphql_value::{from_value, to_value};
use indexmap::IndexMap;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde::{de::DeserializeOwned, Serialize};
use crate::registry::{MetaType, MetaTypeId, Registry};
use crate::{
registry::{MetaType, MetaTypeId, Registry},
ContextSelectionSet, InputType, InputValueError, InputValueResult, Name, OutputType,
ServerResult, Value,
};

Some files were not shown because too many files have changed in this diff Show More