This commit is contained in:
sunli 2020-03-25 11:39:28 +08:00
parent 0769513c8b
commit b0369860dd
42 changed files with 285 additions and 659 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "async-graphql"
version = "1.5.0"
version = "1.5.1"
authors = ["sunli <scott_s829@163.com>"]
edition = "2018"
description = "The GraphQL server library implemented by rust"
@ -18,21 +18,20 @@ default = ["bson", "chrono", "uuid", "url", "validators"]
validators = ["regex", "once_cell"]
[dependencies]
async-graphql-derive = { path = "async-graphql-derive", version = "1.5.0" }
async-graphql-derive = { path = "async-graphql-derive", version = "1.5.1" }
graphql-parser = "0.2.3"
anyhow = "1.0.26"
thiserror = "1.0.11"
async-trait = "0.1.24"
serde = "1.0.104"
serde_derive = "1.0.104"
serde_json = { version = "1.0.48", features = ["raw_value"] }
serde_json = "1.0.48"
fnv = "1.0.6"
bytes = "0.5.4"
Inflector = "0.11.4"
base64 = "0.12.0"
byteorder = "1.3.4"
itoa = "0.4.5"
ryu = "1.0.3"
futures = "0.3.0"
once_cell = { version = "1.3.1", optional = true }
regex = { version = "1.3.5", optional = true }
bson = { version = "0.14.1", optional = true }

View File

@ -1,6 +1,6 @@
[package]
name = "async-graphql-actix-web"
version = "0.6.0"
version = "0.6.1"
authors = ["sunli <scott_s829@163.com>"]
edition = "2018"
description = "async-graphql for actix-web"
@ -13,7 +13,7 @@ keywords = ["futures", "async", "graphql"]
categories = ["network-programming", "asynchronous"]
[dependencies]
async-graphql = { path = "..", version = "1.5.0" }
async-graphql = { path = "..", version = "1.5.1" }
actix-web = "2.0.0"
actix-multipart = "0.2.0"
actix-web-actors = "2.0.0"

View File

@ -250,10 +250,9 @@ where
let mut gql_req = web::Json::<GQLRequest>::from_request(&req, &mut payload.0)
.await?
.into_inner();
let prepared = match gql_req.prepare(&schema) {
Ok(prepared) => prepared,
Err(err) => return Ok(web::Json(GQLResponse(Err(err))).respond_to(&req).await?),
};
let prepared = gql_req
.prepare(&schema)
.map_err(actix_web::error::ErrorBadRequest)?;
let mut cache_control = prepared.cache_control().value();
let gql_resp = prepared.execute().await;
if gql_resp.is_err() {

View File

@ -1,6 +1,6 @@
[package]
name = "async-graphql-derive"
version = "1.5.0"
version = "1.5.1"
authors = ["sunli <scott_s829@163.com>"]
edition = "2018"
description = "Macros for async-graphql"

View File

@ -112,8 +112,8 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
#[#crate_name::async_trait::async_trait]
impl #crate_name::OutputValueType for #ident {
async fn resolve(value: &Self, _: &#crate_name::ContextSelectionSet<'_>, w: &mut #crate_name::JsonWriter) -> #crate_name::Result<()> {
#crate_name::EnumType::resolve_enum(value, w)
async fn resolve(value: &Self, _: &#crate_name::ContextSelectionSet<'_>) -> #crate_name::Result<#crate_name::serde_json::Value> {
#crate_name::EnumType::resolve_enum(value)
}
}
};

View File

@ -37,7 +37,7 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
.unwrap_or_else(|| quote! {None});
let mut registry_types = Vec::new();
let mut possible_types = Vec::new();
let mut inline_fragment_resolvers = Vec::new();
let mut collect_inline_fields = Vec::new();
for field in &fields.unnamed {
if let Type::Path(p) = &field.ty {
@ -58,13 +58,13 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
possible_types.push(quote! {
possible_types.insert(<#p as #crate_name::Type>::type_name().to_string());
});
inline_fragment_resolvers.push(quote! {
if name == <#p as #crate_name::Type>::type_name() {
if let #ident::#enum_name(obj) = self {
return #crate_name::do_resolve(ctx, obj, w).await;
}
return Ok(());
}
collect_inline_fields.push(quote! {
// if name == <#p as #crate_name::Type>::type_name() {
// if let #ident::#enum_name(obj) = self {
// return &obj;
// }
// unreachable!()
// }
});
} else {
return Err(Error::new_spanned(field, "Invalid type"));
@ -205,7 +205,7 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
if field.name.as_str() == #name {
#(#get_params)*
let ctx_obj = ctx.with_item(&field.selection_set);
return #crate_name::OutputValueType::resolve(&#resolve_obj, &ctx_obj, w).await.
return #crate_name::OutputValueType::resolve(&#resolve_obj, &ctx_obj).await.
map_err(|err| err.with_position(field.position).into());
}
});
@ -250,7 +250,7 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
#[#crate_name::async_trait::async_trait]
impl #generics #crate_name::ObjectType for #ident #generics {
async fn resolve_field(&self, ctx: &#crate_name::Context<'_>, field: &#crate_name::graphql_parser::query::Field, w: &mut #crate_name::JsonWriter) -> #crate_name::Result<()> {
async fn resolve_field(&self, ctx: &#crate_name::Context<'_>, field: &#crate_name::graphql_parser::query::Field) -> #crate_name::Result<#crate_name::serde_json::Value> {
use #crate_name::ErrorWithPosition;
#(#resolvers)*
@ -262,8 +262,13 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
.with_position(field.position));
}
async fn resolve_inline_fragment(&self, name: &str, ctx: &#crate_name::ContextSelectionSet<'_>, w: &mut #crate_name::JsonWriter) -> #crate_name::Result<()> {
#(#inline_fragment_resolvers)*
fn collect_inline_fields<'a>(
&'a self,
name: &str,
ctx: #crate_name::ContextSelectionSet<'a>,
futures: &mut Vec<#crate_name::BoxFieldFuture<'a>>,
) -> #crate_name::Result<()> {
#(#collect_inline_fields)*
#crate_name::anyhow::bail!(#crate_name::QueryError::UnrecognizedInlineFragment {
object: #gql_typename.to_string(),
name: name.to_string(),
@ -273,11 +278,8 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
#[#crate_name::async_trait::async_trait]
impl #generics #crate_name::OutputValueType for #ident #generics {
async fn resolve(value: &Self, ctx: &#crate_name::ContextSelectionSet<'_>, w: &mut #crate_name::JsonWriter) -> #crate_name::Result<()> {
w.begin_object();
#crate_name::do_resolve(ctx, value, w).await?;
w.end_object();
Ok(())
async fn resolve(value: &Self, ctx: &#crate_name::ContextSelectionSet<'_>) -> #crate_name::Result<#crate_name::serde_json::Value> {
#crate_name::do_resolve(ctx, value).await
}
}
};

View File

@ -211,7 +211,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
if field.name.as_str() == #field_name {
#(#get_params)*
let ctx_obj = ctx.with_item(&field.selection_set);
return #crate_name::OutputValueType::resolve(&#resolve_obj, &ctx_obj, w).await.
return #crate_name::OutputValueType::resolve(&#resolve_obj, &ctx_obj).await.
map_err(|err| err.with_position(field.position).into());
}
});
@ -256,7 +256,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
#[#crate_name::async_trait::async_trait]
impl#generics #crate_name::ObjectType for #self_ty {
async fn resolve_field(&self, ctx: &#crate_name::Context<'_>, field: &#crate_name::graphql_parser::query::Field, w: &mut #crate_name::JsonWriter) -> #crate_name::Result<()> {
async fn resolve_field(&self, ctx: &#crate_name::Context<'_>, field: &#crate_name::graphql_parser::query::Field) -> #crate_name::Result<#crate_name::serde_json::Value> {
use #crate_name::ErrorWithPosition;
#(#resolvers)*
@ -267,22 +267,12 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
}
.with_position(field.position));
}
async fn resolve_inline_fragment(&self, name: &str, ctx: &#crate_name::ContextSelectionSet<'_>, _w: &mut #crate_name::JsonWriter) -> #crate_name::Result<()> {
#crate_name::anyhow::bail!(#crate_name::QueryError::UnrecognizedInlineFragment {
object: #gql_typename.to_string(),
name: name.to_string(),
});
}
}
#[#crate_name::async_trait::async_trait]
impl #generics #crate_name::OutputValueType for #self_ty {
async fn resolve(value: &Self, ctx: &#crate_name::ContextSelectionSet<'_>, w: &mut #crate_name::JsonWriter) -> #crate_name::Result<()> {
w.begin_object();
#crate_name::do_resolve(ctx, value, w).await?;
w.end_object();
Ok(())
async fn resolve(value: &Self, ctx: &#crate_name::ContextSelectionSet<'_>) -> #crate_name::Result<#crate_name::serde_json::Value> {
#crate_name::do_resolve(ctx, value).await
}
}
};

View File

@ -211,22 +211,12 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
if let Some(msg) = msg.downcast_ref::<#ty>() {
#(#get_params)*
if self.#ident(msg, #(#use_params)*) {
let mut w = #crate_name::JsonWriter::default();
let ctx_selection_set = ctx_field.with_item(&field.selection_set);
w.begin_object();
w.begin_object_key();
w.string(ctx_field.result_name());
w.end_object_key();
w.begin_object_value();
#crate_name::OutputValueType::resolve(msg, &ctx_selection_set, &mut w).await?;
w.end_object_value();
w.end_object();
return Ok(Some(w.into_string()));
let value =
#crate_name::OutputValueType::resolve(msg, &ctx_selection_set).await?;
let mut res = #crate_name::serde_json::Map::new();
res.insert(ctx_field.result_name(), value);
return Ok(Some(res.into()));
}
}
});
@ -275,7 +265,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
ctx: &#crate_name::ContextBase<'_, ()>,
types: &std::collections::HashMap<std::any::TypeId, #crate_name::graphql_parser::query::Field>,
msg: &(dyn std::any::Any + Send + Sync),
) -> #crate_name::Result<Option<String>> {
) -> #crate_name::Result<Option<#crate_name::serde_json::Value>> {
let tid = msg.type_id();
if let Some(field) = types.get(&tid) {
let ctx_field = ctx.with_item(field);

View File

@ -34,7 +34,7 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
.unwrap_or_else(|| quote! {None});
let mut registry_types = Vec::new();
let mut possible_types = Vec::new();
let mut inline_fragment_resolvers = Vec::new();
let mut collect_inline_fields = Vec::new();
for field in &fields.unnamed {
if let Type::Path(p) = &field.ty {
@ -54,13 +54,13 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
possible_types.push(quote! {
possible_types.insert(<#p as #crate_name::Type>::type_name().to_string());
});
inline_fragment_resolvers.push(quote! {
if name == <#p as #crate_name::Type>::type_name() {
if let #ident::#enum_name(obj) = self {
#crate_name::do_resolve(ctx, obj, result).await?;
}
return Ok(());
}
collect_inline_fields.push(quote! {
// if name == <#p as #crate_name::Type>::type_name() {
// if let #ident::#enum_name(obj) = self {
// return &obj;
// }
// unreachable!()
// }
});
} else {
return Err(Error::new_spanned(field, "Invalid type"));
@ -106,9 +106,14 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
.with_position(field.position));
}
async fn resolve_inline_fragment(&self, name: &str, ctx: &#crate_name::ContextSelectionSet<'_>, result: &mut #crate_name::serde_json::Map<String, #crate_name::serde_json::Value>) -> #crate_name::Result<()> {
#(#inline_fragment_resolvers)*
anyhow::bail!(#crate_name::QueryError::UnrecognizedInlineFragment {
fn collect_inline_fields<'a>(
&'a self,
name: &str,
ctx: #crate_name::ContextSelectionSet<'a>,
futures: &mut Vec<#crate_name::BoxFieldFuture<'a>>,
) -> #crate_name::Result<()> {
#(#collect_inline_fields)*
#crate_name::anyhow::bail!(#crate_name::QueryError::UnrecognizedInlineFragment {
object: #gql_typename.to_string(),
name: name.to_string(),
});
@ -118,10 +123,7 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
#[#crate_name::async_trait::async_trait]
impl #generics #crate_name::OutputValueType for #ident #generics {
async fn resolve(value: &Self, ctx: &#crate_name::ContextSelectionSet<'_>) -> #crate_name::Result<#crate_name::serde_json::Value> {
w.begin_object();
#crate_name::do_resolve(ctx, value).await?;
w.end_object();
Ok(())
#crate_name::do_resolve(ctx, value).await
}
}
};

View File

@ -161,7 +161,7 @@ pub fn parse_validator(crate_name: &TokenStream, args: &MetaList) -> Result<Toke
return Err(Error::new_spanned(ls,
"Only one validator can be defined. You can connect combine validators with `and` or `or`"));
}
if ls.nested.is_empty() {
if ls.nested.len() == 0 {
return Err(Error::new_spanned(
ls,
"At least one validator must be defined",

View File

@ -1,7 +1,9 @@
use crate::registry::Registry;
use crate::{registry, Context, ContextSelectionSet, JsonWriter, QueryError, Result, ID};
use crate::{registry, Context, ContextSelectionSet, QueryError, Result, ID};
use graphql_parser::query::{Field, Value};
use std::borrow::Cow;
use std::future::Future;
use std::pin::Pin;
/// Represents a GraphQL type
///
@ -50,10 +52,13 @@ pub trait InputValueType: Type + Sized {
#[async_trait::async_trait]
pub trait OutputValueType: Type {
/// Resolve an output value to `serde_json::Value`.
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>, w: &mut JsonWriter)
-> Result<()>;
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value>;
}
#[allow(missing_docs)]
pub type BoxFieldFuture<'a> =
Pin<Box<dyn Future<Output = Result<(String, serde_json::Value)>> + 'a + Send>>;
/// Represents a GraphQL object
#[async_trait::async_trait]
pub trait ObjectType: OutputValueType {
@ -64,20 +69,20 @@ pub trait ObjectType: OutputValueType {
}
/// Resolves a field value and outputs it as a json value `serde_json::Value`.
async fn resolve_field(
&self,
ctx: &Context<'_>,
field: &Field,
w: &mut JsonWriter,
) -> Result<()>;
async fn resolve_field(&self, ctx: &Context<'_>, field: &Field) -> Result<serde_json::Value>;
/// Resolve an inline fragment with the `name`.
async fn resolve_inline_fragment(
&self,
/// Collect the fields with the `name` inline object
fn collect_inline_fields<'a>(
&'a self,
name: &str,
ctx: &ContextSelectionSet<'_>,
w: &mut JsonWriter,
) -> Result<()>;
_ctx: ContextSelectionSet<'a>,
_futures: &mut Vec<BoxFieldFuture<'a>>,
) -> Result<()> {
anyhow::bail!(QueryError::UnrecognizedInlineFragment {
object: Self::type_name().to_string(),
name: name.to_string(),
});
}
}
/// Represents a GraphQL input object
@ -107,9 +112,8 @@ pub trait InputObjectType: InputValueType {}
/// }
/// }
///
/// fn to_json(&self, w: &mut JsonWriter) -> Result<()> {
/// w.int(self.0 as i64);
/// Ok(())
/// fn to_json(&self) -> Result<serde_json::Value> {
/// Ok(self.0.into())
/// }
/// }
///
@ -135,7 +139,7 @@ pub trait Scalar: Sized + Send {
}
/// Convert the scalar value to json value.
fn to_json(&self, w: &mut JsonWriter) -> Result<()>;
fn to_json(&self) -> Result<serde_json::Value>;
}
#[macro_export]
@ -168,9 +172,8 @@ macro_rules! impl_scalar_internal {
async fn resolve(
value: &Self,
_: &crate::ContextSelectionSet<'_>,
w: &mut crate::JsonWriter,
) -> crate::Result<()> {
value.to_json(w)
) -> crate::Result<serde_json::Value> {
value.to_json()
}
}
};
@ -206,9 +209,8 @@ macro_rules! impl_scalar {
async fn resolve(
value: &Self,
_: &async_graphql::ContextSelectionSet<'_>,
w: &mut async_graphql::JsonWriter,
) -> async_graphql::Result<()> {
value.to_json(w)
) -> async_graphql::Result<serde_json::Value> {
value.to_json()
}
}
};
@ -227,11 +229,7 @@ impl<T: Type + Send + Sync> Type for &T {
#[async_trait::async_trait]
impl<T: OutputValueType + Send + Sync> OutputValueType for &T {
#[allow(clippy::trivially_copy_pass_by_ref)]
async fn resolve(
value: &Self,
ctx: &ContextSelectionSet<'_>,
w: &mut JsonWriter,
) -> Result<()> {
T::resolve(*value, ctx, w).await
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
T::resolve(*value, ctx).await
}
}

View File

@ -144,6 +144,7 @@ pub type ContextSelectionSet<'a> = ContextBase<'a, &'a SelectionSet>;
pub type Context<'a> = ContextBase<'a, &'a Field>;
/// Query context
#[derive(Clone)]
pub struct ContextBase<'a, T> {
pub(crate) item: T,
pub(crate) variables: &'a Variables,
@ -333,10 +334,7 @@ impl<'a> ContextBase<'a, &'a Field> {
}
#[doc(hidden)]
pub fn result_name(&self) -> &str {
self.item
.alias
.as_deref()
.unwrap_or_else(|| self.name.as_str())
pub fn result_name(&self) -> String {
self.item.alias.clone().unwrap_or_else(|| self.name.clone())
}
}

View File

@ -74,7 +74,7 @@ impl GQLRequest {
}
/// Serializable query result type
pub struct GQLResponse(pub Result<String>);
pub struct GQLResponse(pub Result<serde_json::Value>);
impl Serialize for GQLResponse {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
@ -82,13 +82,10 @@ impl Serialize for GQLResponse {
Ok(res) => {
let mut map = serializer.serialize_map(None)?;
map.serialize_key("data")?;
map.serialize_value(
&serde_json::value::RawValue::from_string(res.clone()).unwrap(),
)?;
map.serialize_value(&res)?;
map.end()
}
Err(err) => {
println!("err: {}", err);
let mut map = serializer.serialize_map(None)?;
map.serialize_key("errors")?;
map.serialize_value(&GQLError(err))?;
@ -242,7 +239,7 @@ mod tests {
#[test]
fn test_response_data() {
let resp = GQLResponse(Ok(serde_json::to_string(&json!({"ok": true})).unwrap()));
let resp = GQLResponse(Ok(json!({"ok": true})));
assert_eq!(
serde_json::to_value(resp).unwrap(),
json! ({

View File

@ -1,203 +0,0 @@
#![allow(missing_docs)]
use serde_json::ser::CharEscape;
const BB: u8 = b'b'; // \x08
const TT: u8 = b't'; // \x09
const NN: u8 = b'n'; // \x0A
const FF: u8 = b'f'; // \x0C
const RR: u8 = b'r'; // \x0D
const QU: u8 = b'"'; // \x22
const BS: u8 = b'\\'; // \x5C
const UU: u8 = b'u'; // \x00...\x1F except the ones above
const __: u8 = 0;
#[inline]
fn from_escape_table(escape: u8, byte: u8) -> CharEscape {
match escape {
self::BB => CharEscape::Backspace,
self::TT => CharEscape::Tab,
self::NN => CharEscape::LineFeed,
self::FF => CharEscape::FormFeed,
self::RR => CharEscape::CarriageReturn,
self::QU => CharEscape::Quote,
self::BS => CharEscape::ReverseSolidus,
self::UU => CharEscape::AsciiControl(byte),
_ => unreachable!(),
}
}
static ESCAPE: [u8; 256] = [
// 1 2 3 4 5 6 7 8 9 A B C D E F
UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0
UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, // 1
__, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4
__, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F
];
#[inline]
fn write_char_escape(data: &mut Vec<u8>, char_escape: CharEscape) {
use self::CharEscape::*;
let s = match char_escape {
Quote => b"\\\"",
ReverseSolidus => b"\\\\",
Solidus => b"\\/",
Backspace => b"\\b",
FormFeed => b"\\f",
LineFeed => b"\\n",
CarriageReturn => b"\\r",
Tab => b"\\t",
AsciiControl(byte) => {
static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";
let bytes = &[
b'\\',
b'u',
b'0',
b'0',
HEX_DIGITS[(byte >> 4) as usize],
HEX_DIGITS[(byte & 0xF) as usize],
];
data.extend_from_slice(bytes);
return;
}
};
data.extend_from_slice(s);
}
/// JSON Writer
#[derive(Default)]
pub struct JsonWriter {
data: Vec<u8>,
}
impl JsonWriter {
#[inline]
pub fn into_string(self) -> String {
unsafe { String::from_utf8_unchecked(self.data) }
}
#[inline]
pub fn null(&mut self) {
self.data.extend_from_slice(b"null");
}
#[inline]
pub fn bool(&mut self, value: bool) {
if value {
self.data.extend_from_slice(b"true");
} else {
self.data.extend_from_slice(b"false");
}
}
#[inline]
pub fn int(&mut self, value: i64) {
let mut buffer = itoa::Buffer::new();
self.data.extend_from_slice(buffer.format(value).as_bytes());
}
#[inline]
pub fn float(&mut self, value: f64) {
let mut buffer = ryu::Buffer::new();
let s = buffer.format_finite(value);
self.data.extend_from_slice(s.as_bytes());
}
#[inline]
pub fn string(&mut self, value: &str) {
self.data.push(b'"');
let bytes = value.as_bytes();
let mut start = 0;
for (i, &byte) in bytes.iter().enumerate() {
let escape = ESCAPE[byte as usize];
if escape == 0 {
continue;
}
if start < i {
self.data.extend_from_slice(&bytes[start..i]);
}
let char_escape = from_escape_table(escape, byte);
write_char_escape(&mut self.data, char_escape);
start = i + 1;
}
if start != bytes.len() {
self.data.extend_from_slice(&bytes[start..]);
}
self.data.push(b'"');
}
#[inline]
pub fn begin_array(&mut self) {
self.data.push(b'[');
}
#[inline]
pub fn end_array(&mut self) {
if let Some(lc) = self.data.last_mut() {
if *lc == b',' {
*lc = b']';
return;
}
}
self.data.push(b']');
}
#[inline]
pub fn begin_array_value(&mut self) {}
#[inline]
pub fn end_array_value(&mut self) {
self.data.push(b',');
}
#[inline]
pub fn begin_object(&mut self) {
self.data.push(b'{');
}
#[inline]
pub fn end_object(&mut self) {
if let Some(lc) = self.data.last_mut() {
if *lc == b',' {
*lc = b'}';
return;
}
}
self.data.push(b'}');
}
#[inline]
pub fn begin_object_key(&mut self) {}
#[inline]
pub fn end_object_key(&mut self) {}
#[inline]
pub fn begin_object_value(&mut self) {
self.data.push(b':');
}
#[inline]
pub fn end_object_value(&mut self) {
self.data.push(b',');
}
}

View File

@ -69,7 +69,6 @@ extern crate serde_derive;
mod base;
mod context;
mod error;
mod json_writer;
mod model;
mod query;
mod resolver;
@ -98,7 +97,6 @@ pub use base::{Scalar, Type};
pub use context::{Context, Variables};
pub use error::{ErrorWithPosition, PositionError, QueryError, QueryParseError};
pub use graphql_parser::query::Value;
pub use json_writer::JsonWriter;
pub use query::{PreparedQuery, QueryBuilder};
pub use registry::CacheControl;
pub use scalars::ID;
@ -121,11 +119,11 @@ pub use context::ContextSelectionSet;
#[doc(hidden)]
pub mod registry;
#[doc(hidden)]
pub use base::{InputObjectType, InputValueType, ObjectType, OutputValueType};
pub use base::{BoxFieldFuture, InputObjectType, InputValueType, ObjectType, OutputValueType};
#[doc(hidden)]
pub use context::ContextBase;
#[doc(hidden)]
pub use resolver::do_resolve;
pub use resolver::{collect_fields, do_resolve};
#[doc(hidden)]
pub use subscription::{Subscribe, SubscriptionType};
#[doc(hidden)]
@ -216,13 +214,13 @@ pub use types::{EnumItem, EnumType};
/// #[async_std::main]
/// async fn main() {
/// let schema = Schema::new(MyObject{ value: 10 }, EmptyMutation, EmptySubscription);
/// let res = serde_json::from_str::<serde_json::Value>(&schema.query(r#"{
/// let res = schema.query(r#"{
/// value
/// valueRef
/// valueWithError
/// valueWithArg1: valueWithArg
/// valueWithArg2: valueWithArg(a: 99)
/// }"#).execute().await.unwrap()).unwrap();
/// }"#).execute().await.unwrap();
/// assert_eq!(res, serde_json::json!({
/// "value": 10,
/// "valueRef": 10,
@ -283,7 +281,7 @@ pub use async_graphql_derive::Object;
/// #[async_std::main]
/// async fn main() {
/// let schema = Schema::new(MyObject{ value1: MyEnum::A, value2: MyEnum::B }, EmptyMutation, EmptySubscription);
/// let res = serde_json::from_str::<serde_json::Value>(&schema.query("{ value1 value2 }").execute().await.unwrap()).unwrap();
/// let res = schema.query("{ value1 value2 }").execute().await.unwrap();
/// assert_eq!(res, serde_json::json!({ "value1": "A", "value2": "b" }));
/// }
/// ```
@ -332,11 +330,11 @@ pub use async_graphql_derive::Enum;
/// #[async_std::main]
/// async fn main() {
/// let schema = Schema::new(MyObject, EmptyMutation, EmptySubscription);
/// let res = serde_json::from_str::<serde_json::Value>(&schema.query(r#"
/// let res = schema.query(r#"
/// {
/// value1: value(input:{a:9, b:3})
/// value2: value(input:{a:9})
/// }"#).execute().await.unwrap()).unwrap();
/// }"#).execute().await.unwrap();
/// assert_eq!(res, serde_json::json!({ "value1": 27, "value2": 90 }));
/// }
/// ```
@ -437,14 +435,14 @@ pub use async_graphql_derive::InputObject;
/// #[async_std::main]
/// async fn main() {
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription).data("hello".to_string());
/// let res = serde_json::from_str::<serde_json::Value>(&schema.query(r#"
/// let res = schema.query(r#"
/// {
/// typeA {
/// valueA
/// valueB
/// valueC(a: 3, b: 2)
/// }
/// }"#).execute().await.unwrap()).unwrap();
/// }"#).execute().await.unwrap();
/// assert_eq!(res, serde_json::json!({
/// "typeA": {
/// "valueA": "hello",

View File

@ -2,7 +2,7 @@ use crate::context::Data;
use crate::registry::{CacheControl, Registry};
use crate::types::QueryRoot;
use crate::validation::check_rules;
use crate::{ContextBase, JsonWriter, OutputValueType, Result};
use crate::{ContextBase, OutputValueType, Result};
use crate::{ObjectType, QueryError, QueryParseError, Variables};
use bytes::Bytes;
use graphql_parser::parse_query;
@ -111,7 +111,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
}
/// Execute the query.
pub async fn execute(self) -> Result<String>
pub async fn execute(self) -> Result<serde_json::Value>
where
Query: ObjectType + Send + Sync,
Mutation: ObjectType + Send + Sync,
@ -160,7 +160,7 @@ impl<'a, Query, Mutation> PreparedQuery<'a, Query, Mutation> {
}
/// Execute the query.
pub async fn execute(self) -> Result<String>
pub async fn execute(self) -> Result<serde_json::Value>
where
Query: ObjectType + Send + Sync,
Mutation: ObjectType + Send + Sync,
@ -173,13 +173,11 @@ impl<'a, Query, Mutation> PreparedQuery<'a, Query, Mutation> {
data: self.data,
fragments: &self.fragments,
};
let mut w = JsonWriter::default();
match self.root {
Root::Query(query) => OutputValueType::resolve(query, &ctx, &mut w).await?,
Root::Mutation(mutation) => OutputValueType::resolve(mutation, &ctx, &mut w).await?,
Root::Query(query) => OutputValueType::resolve(query, &ctx).await,
Root::Mutation(mutation) => OutputValueType::resolve(mutation, &ctx).await,
}
Ok(w.into_string())
}
/// Get cache control value

View File

@ -1,103 +1,91 @@
use crate::{ContextSelectionSet, ErrorWithPosition, JsonWriter, ObjectType, QueryError, Result};
use crate::base::BoxFieldFuture;
use crate::{ContextSelectionSet, Error, ErrorWithPosition, ObjectType, QueryError, Result};
use futures::{future, TryFutureExt};
use graphql_parser::query::{Selection, TypeCondition};
use std::future::Future;
use std::pin::Pin;
struct Resolver<'a, T> {
ctx: &'a ContextSelectionSet<'a>,
obj: &'a T,
w: &'a mut JsonWriter,
}
impl<'a, T: ObjectType + Send + Sync> Resolver<'a, T> {
pub fn resolve(&'a mut self) -> Pin<Box<dyn Future<Output = Result<()>> + 'a + Send>> {
Box::pin(async move {
if self.ctx.items.is_empty() {
anyhow::bail!(QueryError::MustHaveSubFields {
object: T::type_name().to_string(),
}
.with_position(self.ctx.span.0));
}
for selection in &self.ctx.item.items {
match selection {
Selection::Field(field) => {
if self.ctx.is_skip(&field.directives)? {
continue;
}
let ctx_field = self.ctx.with_item(field);
if field.name.as_str() == "__typename" {
self.w.begin_object_key();
self.w.string(ctx_field.result_name());
self.w.end_object_key();
self.w.begin_object_value();
self.w.string(&T::type_name());
self.w.end_object_value();
continue;
}
self.w.begin_object_key();
self.w.string(ctx_field.result_name());
self.w.end_object_key();
self.w.begin_object_value();
self.obj.resolve_field(&ctx_field, field, self.w).await?;
self.w.end_object_value();
}
Selection::FragmentSpread(fragment_spread) => {
if self.ctx.is_skip(&fragment_spread.directives)? {
continue;
}
if let Some(fragment) =
self.ctx.fragments.get(&fragment_spread.fragment_name)
{
let mut r = Resolver {
ctx: &self.ctx.with_item(&fragment.selection_set),
obj: self.obj,
w: self.w,
};
r.resolve().await?;
} else {
return Err(QueryError::UnknownFragment {
name: fragment_spread.fragment_name.clone(),
}
.into());
}
}
Selection::InlineFragment(inline_fragment) => {
if self.ctx.is_skip(&inline_fragment.directives)? {
continue;
}
if let Some(TypeCondition::On(name)) = &inline_fragment.type_condition {
self.obj
.resolve_inline_fragment(
&name,
&self.ctx.with_item(&inline_fragment.selection_set),
self.w,
)
.await?;
}
}
}
}
Ok(())
})
}
}
use std::iter::FromIterator;
#[allow(missing_docs)]
#[inline]
pub async fn do_resolve<'a, T: ObjectType + Send + Sync>(
ctx: &'a ContextSelectionSet<'a>,
root: &'a T,
w: &mut JsonWriter,
) -> Result<serde_json::Value> {
let mut futures = Vec::new();
collect_fields(ctx.clone(), root, &mut futures)?;
let res = futures::future::try_join_all(futures).await?;
let map = serde_json::Map::from_iter(res);
Ok(map.into())
}
#[allow(missing_docs)]
pub fn collect_fields<'a, T: ObjectType + Send + Sync>(
ctx: ContextSelectionSet<'a>,
root: &'a T,
futures: &mut Vec<BoxFieldFuture<'a>>,
) -> Result<()> {
Resolver { ctx, obj: root, w }.resolve().await?;
if ctx.items.is_empty() {
anyhow::bail!(QueryError::MustHaveSubFields {
object: T::type_name().to_string(),
}
.with_position(ctx.span.0));
}
for selection in &ctx.item.items {
match selection {
Selection::Field(field) => {
if ctx.is_skip(&field.directives)? {
continue;
}
let ctx_field = ctx.with_item(field);
let field_name = ctx_field.result_name();
if field.name.as_str() == "__typename" {
// Get the typename
futures.push(Box::pin(
future::ok::<serde_json::Value, Error>(T::type_name().to_string().into())
.map_ok(move |value| (field_name, value)),
));
continue;
}
futures.push(Box::pin({
let ctx_field = ctx_field.clone();
async move {
root.resolve_field(&ctx_field, field)
.map_ok(move |value| (field_name, value))
.await
}
}))
}
Selection::FragmentSpread(fragment_spread) => {
if ctx.is_skip(&fragment_spread.directives)? {
continue;
}
if let Some(fragment) = ctx.fragments.get(&fragment_spread.fragment_name) {
collect_fields(ctx.with_item(&fragment.selection_set), root, futures)?;
} else {
return Err(QueryError::UnknownFragment {
name: fragment_spread.fragment_name.clone(),
}
.into());
}
}
Selection::InlineFragment(inline_fragment) => {
if ctx.is_skip(&inline_fragment.directives)? {
continue;
}
if let Some(TypeCondition::On(name)) = &inline_fragment.type_condition {
root.collect_inline_fields(
name,
ctx.with_item(&inline_fragment.selection_set),
futures,
)?;
}
}
}
}
Ok(())
}

View File

@ -1,4 +1,4 @@
use crate::{impl_scalar_internal, JsonWriter, Result, Scalar, Value};
use crate::{impl_scalar_internal, Result, Scalar, Value};
impl Scalar for bool {
fn type_name() -> &'static str {
@ -16,9 +16,8 @@ impl Scalar for bool {
}
}
fn to_json(&self, w: &mut JsonWriter) -> Result<()> {
w.bool(*self);
Ok(())
fn to_json(&self) -> Result<serde_json::Value> {
Ok((*self).into())
}
}

View File

@ -1,4 +1,4 @@
use crate::{impl_scalar_internal, JsonWriter, Result, Scalar, Value};
use crate::{impl_scalar_internal, Result, Scalar, Value};
use bson::oid::ObjectId;
impl Scalar for ObjectId {
@ -13,9 +13,8 @@ impl Scalar for ObjectId {
}
}
fn to_json(&self, w: &mut JsonWriter) -> Result<()> {
w.string(&self.to_string());
Ok(())
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_string().into())
}
}

View File

@ -1,4 +1,4 @@
use crate::{impl_scalar_internal, JsonWriter, Result, Scalar, Value};
use crate::{impl_scalar_internal, Result, Scalar, Value};
use chrono::{DateTime, TimeZone, Utc};
/// Implement the DateTime<Utc> scalar
@ -16,9 +16,8 @@ impl Scalar for DateTime<Utc> {
}
}
fn to_json(&self, w: &mut JsonWriter) -> Result<()> {
w.string(&self.to_rfc3339());
Ok(())
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_rfc3339().into())
}
}

View File

@ -1,4 +1,4 @@
use crate::{impl_scalar_internal, JsonWriter, Result, Scalar, Value};
use crate::{impl_scalar_internal, Result, Scalar, Value};
macro_rules! impl_float_scalars {
($($ty:ty),*) => {
@ -20,9 +20,8 @@ macro_rules! impl_float_scalars {
}
}
fn to_json(&self, w: &mut JsonWriter) -> Result<()> {
w.float(*self as f64);
Ok(())
fn to_json(&self) -> Result<serde_json::Value> {
Ok((*self).into())
}
}

View File

@ -1,4 +1,4 @@
use crate::{impl_scalar_internal, JsonWriter, Result, Scalar, Value};
use crate::{impl_scalar_internal, Result, Scalar, Value};
use std::ops::{Deref, DerefMut};
/// ID scalar
@ -58,9 +58,8 @@ impl Scalar for ID {
}
}
fn to_json(&self, w: &mut JsonWriter) -> Result<()> {
w.string(&self.0);
Ok(())
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.0.clone().into())
}
}

View File

@ -1,4 +1,4 @@
use crate::{impl_scalar_internal, JsonWriter, Result, Scalar, Value};
use crate::{impl_scalar_internal, Result, Scalar, Value};
macro_rules! impl_integer_scalars {
($($ty:ty),*) => {
@ -19,9 +19,8 @@ macro_rules! impl_integer_scalars {
}
}
fn to_json(&self, w: &mut JsonWriter) -> Result<()> {
w.int(*self as i64);
Ok(())
fn to_json(&self) -> Result<serde_json::Value> {
Ok((*self).into())
}
}

View File

@ -1,6 +1,6 @@
use crate::{
impl_scalar_internal, registry, ContextSelectionSet, JsonWriter, OutputValueType, Result,
Scalar, Type, Value,
impl_scalar_internal, registry, ContextSelectionSet, OutputValueType, Result, Scalar, Type,
Value,
};
use std::borrow::Cow;
@ -29,9 +29,8 @@ impl Scalar for String {
}
}
fn to_json(&self, w: &mut JsonWriter) -> Result<()> {
w.string(self.as_str());
Ok(())
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.clone().into())
}
}
@ -56,8 +55,7 @@ impl<'a> Type for &'a str {
#[async_trait::async_trait]
impl<'a> OutputValueType for &'a str {
async fn resolve(value: &Self, _: &ContextSelectionSet<'_>, w: &mut JsonWriter) -> Result<()> {
w.string(*value);
Ok(())
async fn resolve(value: &Self, _: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
Ok((*value).into())
}
}

View File

@ -1,4 +1,4 @@
use crate::{impl_scalar_internal, JsonWriter, Result, Scalar, Value};
use crate::{impl_scalar_internal, Result, Scalar, Value};
use url::Url;
impl Scalar for Url {
@ -13,9 +13,8 @@ impl Scalar for Url {
}
}
fn to_json(&self, w: &mut JsonWriter) -> Result<()> {
w.string(&self.to_string());
Ok(())
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_string().into())
}
}

View File

@ -1,4 +1,4 @@
use crate::{impl_scalar_internal, JsonWriter, Result, Scalar, Value};
use crate::{impl_scalar_internal, Result, Scalar, Value};
use uuid::Uuid;
impl Scalar for Uuid {
@ -13,9 +13,8 @@ impl Scalar for Uuid {
}
}
fn to_json(&self, w: &mut JsonWriter) -> Result<()> {
w.string(&self.to_string());
Ok(())
fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_string().into())
}
}

View File

@ -28,7 +28,7 @@ impl Subscribe {
&self,
schema: &Schema<Query, Mutation, Subscription>,
msg: &(dyn Any + Send + Sync),
) -> Result<Option<String>>
) -> Result<Option<serde_json::Value>>
where
Subscription: SubscriptionType + Sync + Send + 'static,
{
@ -91,7 +91,7 @@ pub trait SubscriptionType: Type {
ctx: &ContextBase<'_, ()>,
types: &HashMap<TypeId, Field>,
msg: &(dyn Any + Send + Sync),
) -> Result<Option<String>>;
) -> Result<Option<serde_json::Value>>;
}
fn create_types<T: SubscriptionType>(

View File

@ -1,7 +1,7 @@
use crate::types::connection::edge::Edge;
use crate::types::connection::page_info::PageInfo;
use crate::{
do_resolve, registry, Context, ContextSelectionSet, ErrorWithPosition, JsonWriter, ObjectType,
do_resolve, registry, Context, ContextSelectionSet, ErrorWithPosition, ObjectType,
OutputValueType, QueryError, Result, Type,
};
use graphql_parser::query::Field;
@ -127,16 +127,11 @@ impl<T: OutputValueType + Send + Sync, E: ObjectType + Sync + Send> Type for Con
impl<T: OutputValueType + Send + Sync, E: ObjectType + Sync + Send> ObjectType
for Connection<T, E>
{
async fn resolve_field(
&self,
ctx: &Context<'_>,
field: &Field,
w: &mut JsonWriter,
) -> Result<()> {
async fn resolve_field(&self, ctx: &Context<'_>, field: &Field) -> Result<serde_json::Value> {
if field.name.as_str() == "pageInfo" {
let ctx_obj = ctx.with_item(&field.selection_set);
let page_info = &self.page_info;
return OutputValueType::resolve(page_info, &ctx_obj, w)
return OutputValueType::resolve(page_info, &ctx_obj)
.await
.map_err(|err| err.with_position(field.position).into());
} else if field.name.as_str() == "edges" {
@ -150,16 +145,14 @@ impl<T: OutputValueType + Send + Sync, E: ObjectType + Sync + Send> ObjectType
node,
})
.collect::<Vec<_>>();
return OutputValueType::resolve(&edges, &ctx_obj, w)
return OutputValueType::resolve(&edges, &ctx_obj)
.await
.map_err(|err| err.with_position(field.position).into());
} else if field.name.as_str() == "totalCount" {
if let Some(total_count) = self.total_count {
w.int(total_count as i64);
} else {
w.null();
}
return Ok(());
return Ok(self
.total_count
.map(|n| (n as i32).into())
.unwrap_or_else(|| serde_json::Value::Null));
} else if field.name.as_str() == T::type_name().to_plural().to_camel_case() {
let ctx_obj = ctx.with_item(&field.selection_set);
let items = self
@ -167,7 +160,7 @@ impl<T: OutputValueType + Send + Sync, E: ObjectType + Sync + Send> ObjectType
.iter()
.map(|(_, _, item)| item)
.collect::<Vec<_>>();
return OutputValueType::resolve(&items, &ctx_obj, w)
return OutputValueType::resolve(&items, &ctx_obj)
.await
.map_err(|err| err.with_position(field.position).into());
}
@ -178,29 +171,13 @@ impl<T: OutputValueType + Send + Sync, E: ObjectType + Sync + Send> ObjectType
}
.with_position(field.position))
}
async fn resolve_inline_fragment(
&self,
name: &str,
_ctx: &ContextSelectionSet<'_>,
_w: &mut JsonWriter,
) -> Result<()> {
anyhow::bail!(QueryError::UnrecognizedInlineFragment {
object: Connection::<T, E>::type_name().to_string(),
name: name.to_string(),
});
}
}
#[async_trait::async_trait]
impl<T: OutputValueType + Send + Sync, E: ObjectType + Sync + Send> OutputValueType
for Connection<T, E>
{
async fn resolve(
value: &Self,
ctx: &ContextSelectionSet<'_>,
w: &mut JsonWriter,
) -> Result<()> {
do_resolve(ctx, value, w).await
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
do_resolve(ctx, value).await
}
}

View File

@ -1,6 +1,6 @@
use crate::{
do_resolve, registry, Context, ContextSelectionSet, ErrorWithPosition, JsonWriter, ObjectType,
OutputValueType, QueryError, Result, Type,
do_resolve, registry, Context, ContextSelectionSet, ErrorWithPosition, ObjectType,
OutputValueType, Result, Type,
};
use graphql_parser::query::Field;
use std::borrow::Cow;
@ -78,35 +78,17 @@ where
T: OutputValueType + Send + Sync + 'a,
E: ObjectType + Sync + Send + 'a,
{
async fn resolve_field(
&self,
ctx: &Context<'_>,
field: &Field,
w: &mut JsonWriter,
) -> Result<()> {
async fn resolve_field(&self, ctx: &Context<'_>, field: &Field) -> Result<serde_json::Value> {
if field.name.as_str() == "node" {
let ctx_obj = ctx.with_item(&field.selection_set);
return OutputValueType::resolve(self.node, &ctx_obj, w)
return OutputValueType::resolve(self.node, &ctx_obj)
.await
.map_err(|err| err.with_position(field.position).into());
} else if field.name.as_str() == "cursor" {
w.string(self.cursor);
return Ok(());
return Ok(self.cursor.into());
}
self.extra_type.resolve_field(ctx, field, w).await
}
async fn resolve_inline_fragment(
&self,
name: &str,
_ctx: &ContextSelectionSet<'_>,
_w: &mut JsonWriter,
) -> Result<()> {
anyhow::bail!(QueryError::UnrecognizedInlineFragment {
object: <Edge<T, E> as Type>::type_name().to_string(),
name: name.to_string(),
});
self.extra_type.resolve_field(ctx, field).await
}
}
@ -116,11 +98,7 @@ where
T: OutputValueType + Send + Sync + 'a,
E: ObjectType + Sync + Send + 'a,
{
async fn resolve(
value: &Self,
ctx: &ContextSelectionSet<'_>,
w: &mut JsonWriter,
) -> Result<()> {
do_resolve(ctx, value, w).await
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
do_resolve(ctx, value).await
}
}

View File

@ -106,7 +106,7 @@ impl EmptyEdgeFields {}
/// async fn main() {
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
///
/// assert_eq!(serde_json::from_str::<serde_json::Value>(&schema.query("{ numbers(first: 2) { edges { node } } }").execute().await.unwrap()).unwrap(), serde_json::json!({
/// assert_eq!(schema.query("{ numbers(first: 2) { edges { node } } }").execute().await.unwrap(), serde_json::json!({
/// "numbers": {
/// "edges": [
/// {"node": 0},
@ -115,7 +115,7 @@ impl EmptyEdgeFields {}
/// },
/// }));
///
/// assert_eq!(serde_json::from_str::<serde_json::Value>(&schema.query("{ numbers(last: 2) { edges { node diff } } }").execute().await.unwrap()).unwrap(), serde_json::json!({
/// assert_eq!(schema.query("{ numbers(last: 2) { edges { node diff } } }").execute().await.unwrap(), serde_json::json!({
/// "numbers": {
/// "edges": [
/// {"node": -2, "diff": -1002},

View File

@ -1,6 +1,5 @@
use crate::{
registry, Context, ContextSelectionSet, JsonWriter, ObjectType, OutputValueType, QueryError,
Result, Type,
registry, Context, ContextSelectionSet, ObjectType, OutputValueType, QueryError, Result, Type,
};
use graphql_parser::query::Field;
use std::borrow::Cow;
@ -46,32 +45,14 @@ impl ObjectType for EmptyMutation {
true
}
async fn resolve_field(
&self,
_ctx: &Context<'_>,
_name: &Field,
_w: &mut JsonWriter,
) -> Result<()> {
unreachable!()
}
async fn resolve_inline_fragment(
&self,
_name: &str,
_ctx: &ContextSelectionSet<'_>,
_w: &mut JsonWriter,
) -> Result<()> {
async fn resolve_field(&self, _ctx: &Context<'_>, _name: &Field) -> Result<serde_json::Value> {
unreachable!()
}
}
#[async_trait::async_trait]
impl OutputValueType for EmptyMutation {
async fn resolve(
_value: &Self,
_ctx: &ContextSelectionSet<'_>,
_w: &mut JsonWriter,
) -> Result<()> {
async fn resolve(_value: &Self, _ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
Err(QueryError::NotConfiguredMutations.into())
}
}

View File

@ -1,8 +1,9 @@
use crate::{
registry, ContextBase, ContextSelectionSet, JsonWriter, OutputValueType, QueryError, Result,
registry, ContextBase, ContextSelectionSet, OutputValueType, QueryError, Result,
SubscriptionType, Type,
};
use graphql_parser::query::Field;
use serde_json::Value;
use std::any::{Any, TypeId};
use std::borrow::Cow;
use std::collections::hash_map::RandomState;
@ -43,18 +44,14 @@ impl SubscriptionType for EmptySubscription {
_ctx: &ContextBase<'_, ()>,
_types: &HashMap<TypeId, Field, RandomState>,
_msg: &(dyn Any + Send + Sync),
) -> Result<Option<String>> {
) -> Result<Option<Value>> {
unreachable!()
}
}
#[async_trait::async_trait]
impl OutputValueType for EmptySubscription {
async fn resolve(
_value: &Self,
_ctx: &ContextSelectionSet<'_>,
_w: &mut JsonWriter,
) -> Result<()> {
async fn resolve(_value: &Self, _ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
Err(QueryError::NotConfiguredSubscriptions.into())
}
}

View File

@ -1,4 +1,4 @@
use crate::{JsonWriter, Result, Type};
use crate::{Result, Type};
use graphql_parser::query::Value;
#[allow(missing_docs)]
@ -30,12 +30,11 @@ pub trait EnumType: Type + Sized + Eq + Send + Copy + Sized + 'static {
})
}
fn resolve_enum(&self, w: &mut JsonWriter) -> Result<()> {
fn resolve_enum(&self) -> Result<serde_json::Value> {
let items = Self::items();
for item in items {
if item.value == *self {
w.string(item.name);
return Ok(());
return Ok(item.name.into());
}
}
unreachable!()

View File

@ -1,6 +1,4 @@
use crate::{
registry, ContextSelectionSet, InputValueType, JsonWriter, OutputValueType, Result, Type, Value,
};
use crate::{registry, ContextSelectionSet, InputValueType, OutputValueType, Result, Type, Value};
use std::borrow::Cow;
impl<T: Type> Type for Vec<T> {
@ -36,19 +34,12 @@ impl<T: InputValueType> InputValueType for Vec<T> {
#[allow(clippy::ptr_arg)]
#[async_trait::async_trait]
impl<T: OutputValueType + Send + Sync> OutputValueType for Vec<T> {
async fn resolve(
value: &Self,
ctx: &ContextSelectionSet<'_>,
w: &mut JsonWriter,
) -> Result<()> {
w.begin_array();
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
let mut futures = Vec::with_capacity(value.len());
for item in value {
w.begin_array_value();
OutputValueType::resolve(item, &ctx, w).await?;
w.end_array_value();
futures.push(OutputValueType::resolve(item, &ctx));
}
w.end_array();
Ok(())
Ok(futures::future::try_join_all(futures).await?.into())
}
}
@ -64,19 +55,12 @@ impl<T: Type> Type for &[T] {
#[async_trait::async_trait]
impl<T: OutputValueType + Send + Sync> OutputValueType for &[T] {
async fn resolve(
value: &Self,
ctx: &ContextSelectionSet<'_>,
w: &mut JsonWriter,
) -> Result<()> {
w.begin_array();
for item in value.iter() {
w.begin_array_value();
OutputValueType::resolve(item, &ctx, w).await?;
w.end_array_value();
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
let mut futures = Vec::with_capacity(value.len());
for item in *value {
futures.push(OutputValueType::resolve(item, &ctx));
}
w.end_array();
Ok(())
Ok(futures::future::try_join_all(futures).await?.into())
}
}

View File

@ -1,6 +1,4 @@
use crate::{
registry, ContextSelectionSet, InputValueType, JsonWriter, OutputValueType, Result, Type, Value,
};
use crate::{registry, ContextSelectionSet, InputValueType, OutputValueType, Result, Type, Value};
use std::borrow::Cow;
impl<T: Type> Type for Option<T> {
@ -29,16 +27,12 @@ impl<T: InputValueType> InputValueType for Option<T> {
#[async_trait::async_trait]
impl<T: OutputValueType + Sync> OutputValueType for Option<T> {
async fn resolve(
value: &Self,
ctx: &ContextSelectionSet<'_>,
w: &mut JsonWriter,
) -> Result<()> where {
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> where
{
if let Some(inner) = value {
OutputValueType::resolve(inner, ctx, w).await
OutputValueType::resolve(inner, ctx).await
} else {
w.null();
Ok(())
Ok(serde_json::Value::Null)
}
}
}

View File

@ -1,7 +1,7 @@
use crate::model::{__Schema, __Type};
use crate::{
do_resolve, registry, Context, ContextSelectionSet, ErrorWithPosition, JsonWriter, ObjectType,
OutputValueType, QueryError, Result, Type, Value,
do_resolve, registry, Context, ContextSelectionSet, ErrorWithPosition, ObjectType,
OutputValueType, Result, Type, Value,
};
use graphql_parser::query::Field;
use std::borrow::Cow;
@ -65,12 +65,7 @@ impl<T: Type> Type for QueryRoot<T> {
#[async_trait::async_trait]
impl<T: ObjectType + Send + Sync> ObjectType for QueryRoot<T> {
async fn resolve_field(
&self,
ctx: &Context<'_>,
field: &Field,
w: &mut JsonWriter,
) -> Result<()> {
async fn resolve_field(&self, ctx: &Context<'_>, field: &Field) -> Result<serde_json::Value> {
if field.name.as_str() == "__schema" {
let ctx_obj = ctx.with_item(&field.selection_set);
return OutputValueType::resolve(
@ -78,7 +73,6 @@ impl<T: ObjectType + Send + Sync> ObjectType for QueryRoot<T> {
registry: &ctx.registry,
},
&ctx_obj,
w,
)
.await
.map_err(|err| err.with_position(field.position).into());
@ -91,38 +85,18 @@ impl<T: ObjectType + Send + Sync> ObjectType for QueryRoot<T> {
.get(&type_name)
.map(|ty| __Type::new_simple(ctx.registry, ty)),
&ctx_obj,
w,
)
.await
.map_err(|err| err.with_position(field.position).into());
}
self.inner.resolve_field(ctx, field, w).await
}
async fn resolve_inline_fragment(
&self,
name: &str,
_ctx: &ContextSelectionSet<'_>,
_w: &mut JsonWriter,
) -> Result<()> {
anyhow::bail!(QueryError::UnrecognizedInlineFragment {
object: T::type_name().to_string(),
name: name.to_string(),
});
self.inner.resolve_field(ctx, field).await
}
}
#[async_trait::async_trait]
impl<T: ObjectType + Send + Sync> OutputValueType for QueryRoot<T> {
async fn resolve(
value: &Self,
ctx: &ContextSelectionSet<'_>,
w: &mut JsonWriter,
) -> Result<()> {
w.begin_object();
do_resolve(ctx, value, w).await?;
w.end_object();
Ok(())
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
do_resolve(ctx, value).await
}
}

View File

@ -1,13 +1,13 @@
use crate::error::RuleErrors;
use crate::registry::Registry;
use crate::validation::visitor::{visit, VisitorContext, VisitorNil};
use crate::{CacheControl, Result};
use graphql_parser::query::Document;
mod rules;
mod utils;
mod visitor;
use crate::error::RuleErrors;
use crate::registry::Registry;
use crate::{CacheControl, Result};
use graphql_parser::query::Document;
use visitor::{visit, VisitorContext, VisitorNil};
pub fn check_rules(registry: &Registry, doc: &Document) -> Result<CacheControl> {
let mut ctx = VisitorContext::new(registry, doc);
let mut cache_control = CacheControl::default();

View File

@ -44,8 +44,7 @@ pub async fn test_enum_type() {
"#
);
assert_eq!(
serde_json::from_str::<serde_json::Value>(&schema.query(&query).execute().await.unwrap())
.unwrap(),
schema.query(&query).execute().await.unwrap(),
serde_json::json!({
"value": "A",
"testArg": "A",

View File

@ -81,8 +81,7 @@ pub async fn test_input_object_default_value() {
}}"#
);
assert_eq!(
serde_json::from_str::<serde_json::Value>(&schema.query(&query).execute().await.unwrap())
.unwrap(),
schema.query(&query).execute().await.unwrap(),
serde_json::json!({
"a": {
"a": 999,

View File

@ -52,8 +52,7 @@ pub async fn test_list_type() {
json_value
);
assert_eq!(
serde_json::from_str::<serde_json::Value>(&schema.query(&query).execute().await.unwrap())
.unwrap(),
schema.query(&query).execute().await.unwrap(),
serde_json::json!({
"valueVec": vec![1, 2, 3, 4, 5],
"valueSlice": vec![1, 2, 3, 4, 5],

View File

@ -66,8 +66,7 @@ pub async fn test_optional_type() {
}}"#
);
assert_eq!(
serde_json::from_str::<serde_json::Value>(&schema.query(&query).execute().await.unwrap())
.unwrap(),
schema.query(&query).execute().await.unwrap(),
serde_json::json!({
"value1": 10,
"value1Ref": 10,

View File

@ -35,7 +35,7 @@ macro_rules! test_scalars {
let json_value: serde_json::Value = $value.into();
let query = format!("{{ value testArg(input: {0}) testInput(input: {{value: {0}}}) }}", json_value);
assert_eq!(
serde_json::from_str::<serde_json::Value>(&schema.query(&query).execute().await.unwrap()).unwrap(),
schema.query(&query).execute().await.unwrap(),
serde_json::json!({ "value": $value, "testArg": $value, "testInput": $value })
);
}