Clippy cleanup
This commit is contained in:
parent
ee1e6caac4
commit
2b2be34d4d
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
@ -11,10 +11,10 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Check with clippy
|
|
||||||
run: cargo clippy
|
|
||||||
- name: Check format
|
- name: Check format
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
|
- name: Check with clippy
|
||||||
|
run: cargo clippy --all
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --all --verbose
|
run: cargo build --all --verbose
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
|
|
@ -114,7 +114,7 @@ where
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
if req.method() == Method::GET {
|
if req.method() == Method::GET {
|
||||||
if enable_subscription {
|
if enable_subscription {
|
||||||
if let Some(s) = req.headers().get(&header::UPGRADE) {
|
if let Some(s) = req.headers().get(header::UPGRADE) {
|
||||||
if let Ok(s) = s.to_str() {
|
if let Ok(s) = s.to_str() {
|
||||||
if s.to_ascii_lowercase().contains("websocket") {
|
if s.to_ascii_lowercase().contains("websocket") {
|
||||||
return ws::start_with_protocols(
|
return ws::start_with_protocols(
|
||||||
|
@ -168,14 +168,14 @@ where
|
||||||
let mut gql_request = {
|
let mut gql_request = {
|
||||||
let data = read_multipart(&mut multipart, "operations").await?;
|
let data = read_multipart(&mut multipart, "operations").await?;
|
||||||
serde_json::from_slice::<GQLRequest>(&data)
|
serde_json::from_slice::<GQLRequest>(&data)
|
||||||
.map_err(|err| actix_web::error::ErrorBadRequest(err))?
|
.map_err(actix_web::error::ErrorBadRequest)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// read map
|
// read map
|
||||||
let mut map = {
|
let mut map = {
|
||||||
let data = read_multipart(&mut multipart, "map").await?;
|
let data = read_multipart(&mut multipart, "map").await?;
|
||||||
serde_json::from_slice::<HashMap<String, Vec<String>>>(&data)
|
serde_json::from_slice::<HashMap<String, Vec<String>>>(&data)
|
||||||
.map_err(|err| actix_web::error::ErrorBadRequest(err))?
|
.map_err(actix_web::error::ErrorBadRequest)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut query = match gql_request.prepare(schema) {
|
let mut query = match gql_request.prepare(schema) {
|
||||||
|
@ -202,8 +202,7 @@ where
|
||||||
let content_type = field.content_type().to_string();
|
let content_type = field.content_type().to_string();
|
||||||
let mut data = BytesMut::new();
|
let mut data = BytesMut::new();
|
||||||
while let Some(part) = field.next().await {
|
while let Some(part) = field.next().await {
|
||||||
let part =
|
let part = part.map_err(actix_web::error::ErrorBadRequest)?;
|
||||||
part.map_err(|err| actix_web::error::ErrorBadRequest(err))?;
|
|
||||||
data.extend(&part);
|
data.extend(&part);
|
||||||
|
|
||||||
if data.len() > max_file_size {
|
if data.len() > max_file_size {
|
||||||
|
@ -261,7 +260,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_content_type(headers: &HeaderMap) -> actix_web::Result<Mime> {
|
fn get_content_type(headers: &HeaderMap) -> actix_web::Result<Mime> {
|
||||||
if let Some(content_type) = headers.get(&header::CONTENT_TYPE) {
|
if let Some(content_type) = headers.get(header::CONTENT_TYPE) {
|
||||||
if let Ok(content_type) = content_type.to_str() {
|
if let Ok(content_type) = content_type.to_str() {
|
||||||
if let Ok(ct) = content_type.parse::<Mime>() {
|
if let Ok(ct) = content_type.parse::<Mime>() {
|
||||||
return Ok(ct);
|
return Ok(ct);
|
||||||
|
@ -287,7 +286,7 @@ async fn read_multipart(multipart: &mut Multipart, name: &str) -> actix_web::Res
|
||||||
|
|
||||||
let mut data = BytesMut::new();
|
let mut data = BytesMut::new();
|
||||||
while let Some(part) = field.next().await {
|
while let Some(part) = field.next().await {
|
||||||
let part = part.map_err(|err| actix_web::error::ErrorBadRequest(err))?;
|
let part = part.map_err(actix_web::error::ErrorBadRequest)?;
|
||||||
data.extend(&part);
|
data.extend(&part);
|
||||||
}
|
}
|
||||||
data
|
data
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::utils::parse_value;
|
use crate::utils::{parse_validators, parse_value};
|
||||||
use graphql_parser::query::Value;
|
use graphql_parser::query::Value;
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
use syn::{Attribute, AttributeArgs, Error, Meta, MetaList, NestedMeta, Result, Type};
|
use syn::{Attribute, AttributeArgs, Error, Meta, MetaList, NestedMeta, Result, Type};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -58,6 +60,7 @@ pub struct Argument {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub desc: Option<String>,
|
pub desc: Option<String>,
|
||||||
pub default: Option<Value>,
|
pub default: Option<Value>,
|
||||||
|
pub validators: TokenStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Argument {
|
impl Argument {
|
||||||
|
@ -65,13 +68,13 @@ impl Argument {
|
||||||
let mut name = None;
|
let mut name = None;
|
||||||
let mut desc = None;
|
let mut desc = None;
|
||||||
let mut default = None;
|
let mut default = None;
|
||||||
|
let mut validators = quote! { Default::default() };
|
||||||
|
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
match attr.parse_meta() {
|
match attr.parse_meta() {
|
||||||
Ok(Meta::List(ls)) if ls.path.is_ident("arg") => {
|
Ok(Meta::List(ls)) if ls.path.is_ident("arg") => {
|
||||||
for meta in &ls.nested {
|
for meta in &ls.nested {
|
||||||
match meta {
|
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) => {
|
|
||||||
if nv.path.is_ident("name") {
|
if nv.path.is_ident("name") {
|
||||||
if let syn::Lit::Str(lit) = &nv.lit {
|
if let syn::Lit::Str(lit) = &nv.lit {
|
||||||
name = Some(lit.value());
|
name = Some(lit.value());
|
||||||
|
@ -115,9 +118,9 @@ impl Argument {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validators = parse_validators(&ls)?;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -127,6 +130,7 @@ impl Argument {
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
default,
|
default,
|
||||||
|
validators,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,8 +157,7 @@ impl Field {
|
||||||
Ok(Meta::List(ls)) if ls.path.is_ident("field") => {
|
Ok(Meta::List(ls)) if ls.path.is_ident("field") => {
|
||||||
is_field = true;
|
is_field = true;
|
||||||
for meta in &ls.nested {
|
for meta in &ls.nested {
|
||||||
match meta {
|
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) => {
|
|
||||||
if nv.path.is_ident("name") {
|
if nv.path.is_ident("name") {
|
||||||
if let syn::Lit::Str(lit) = &nv.lit {
|
if let syn::Lit::Str(lit) = &nv.lit {
|
||||||
name = Some(lit.value());
|
name = Some(lit.value());
|
||||||
|
@ -184,8 +187,6 @@ impl Field {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -272,8 +273,7 @@ impl EnumItem {
|
||||||
if attr.path.is_ident("item") {
|
if attr.path.is_ident("item") {
|
||||||
if let Ok(Meta::List(args)) = attr.parse_meta() {
|
if let Ok(Meta::List(args)) = attr.parse_meta() {
|
||||||
for meta in args.nested {
|
for meta in args.nested {
|
||||||
match meta {
|
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) => {
|
|
||||||
if nv.path.is_ident("name") {
|
if nv.path.is_ident("name") {
|
||||||
if let syn::Lit::Str(lit) = nv.lit {
|
if let syn::Lit::Str(lit) = nv.lit {
|
||||||
name = Some(lit.value());
|
name = Some(lit.value());
|
||||||
|
@ -303,8 +303,6 @@ impl EnumItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,6 +322,7 @@ pub struct InputField {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub desc: Option<String>,
|
pub desc: Option<String>,
|
||||||
pub default: Option<Value>,
|
pub default: Option<Value>,
|
||||||
|
pub validators: TokenStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputField {
|
impl InputField {
|
||||||
|
@ -332,18 +331,19 @@ impl InputField {
|
||||||
let mut name = None;
|
let mut name = None;
|
||||||
let mut desc = None;
|
let mut desc = None;
|
||||||
let mut default = None;
|
let mut default = None;
|
||||||
|
let mut validators = quote! { Default::default() };
|
||||||
|
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
if attr.path.is_ident("field") {
|
if attr.path.is_ident("field") {
|
||||||
if let Ok(Meta::List(args)) = attr.parse_meta() {
|
if let Ok(Meta::List(args)) = &attr.parse_meta() {
|
||||||
for meta in args.nested {
|
for meta in &args.nested {
|
||||||
match meta {
|
match meta {
|
||||||
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("internal") => {
|
NestedMeta::Meta(Meta::Path(p)) if p.is_ident("internal") => {
|
||||||
internal = true;
|
internal = true;
|
||||||
}
|
}
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) => {
|
NestedMeta::Meta(Meta::NameValue(nv)) => {
|
||||||
if nv.path.is_ident("name") {
|
if nv.path.is_ident("name") {
|
||||||
if let syn::Lit::Str(lit) = nv.lit {
|
if let syn::Lit::Str(lit) = &nv.lit {
|
||||||
name = Some(lit.value());
|
name = Some(lit.value());
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::new_spanned(
|
return Err(Error::new_spanned(
|
||||||
|
@ -352,7 +352,7 @@ impl InputField {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else if nv.path.is_ident("desc") {
|
} else if nv.path.is_ident("desc") {
|
||||||
if let syn::Lit::Str(lit) = nv.lit {
|
if let syn::Lit::Str(lit) = &nv.lit {
|
||||||
desc = Some(lit.value());
|
desc = Some(lit.value());
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::new_spanned(
|
return Err(Error::new_spanned(
|
||||||
|
@ -361,7 +361,7 @@ impl InputField {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else if nv.path.is_ident("default") {
|
} else if nv.path.is_ident("default") {
|
||||||
if let syn::Lit::Str(lit) = nv.lit {
|
if let syn::Lit::Str(lit) = &nv.lit {
|
||||||
match parse_value(&lit.value()) {
|
match parse_value(&lit.value()) {
|
||||||
Ok(Value::Variable(_)) => {
|
Ok(Value::Variable(_)) => {
|
||||||
return Err(Error::new_spanned(
|
return Err(Error::new_spanned(
|
||||||
|
@ -388,6 +388,8 @@ impl InputField {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validators = parse_validators(&args)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,6 +399,7 @@ impl InputField {
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
default,
|
default,
|
||||||
|
validators,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -468,8 +471,7 @@ impl InterfaceFieldArgument {
|
||||||
let mut default = None;
|
let mut default = None;
|
||||||
|
|
||||||
for meta in &ls.nested {
|
for meta in &ls.nested {
|
||||||
match meta {
|
if let NestedMeta::Meta(Meta::NameValue(nv)) = meta {
|
||||||
NestedMeta::Meta(Meta::NameValue(nv)) => {
|
|
||||||
if nv.path.is_ident("name") {
|
if nv.path.is_ident("name") {
|
||||||
if let syn::Lit::Str(lit) = &nv.lit {
|
if let syn::Lit::Str(lit) = &nv.lit {
|
||||||
name = Some(lit.value());
|
name = Some(lit.value());
|
||||||
|
@ -526,8 +528,6 @@ impl InterfaceFieldArgument {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if name.is_none() {
|
if name.is_none() {
|
||||||
|
|
|
@ -94,6 +94,7 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
|
||||||
description: #desc,
|
description: #desc,
|
||||||
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
||||||
default_value: #default,
|
default_value: #default,
|
||||||
|
validators: Default::default(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,6 +137,7 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
|
||||||
description: #desc,
|
description: #desc,
|
||||||
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
||||||
default_value: #schema_default,
|
default_value: #schema_default,
|
||||||
|
validators: Default::default(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(clippy::cognitive_complexity)]
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
mod args;
|
mod args;
|
||||||
|
|
|
@ -87,8 +87,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
||||||
(_, Type::Reference(TypeReference { elem, .. })) => {
|
(_, Type::Reference(TypeReference { elem, .. })) => {
|
||||||
if let Type::Path(path) = elem.as_ref() {
|
if let Type::Path(path) = elem.as_ref() {
|
||||||
if idx != 1
|
if idx != 1
|
||||||
|| path.path.segments.last().unwrap().ident.to_string()
|
|| path.path.segments.last().unwrap().ident != "Context"
|
||||||
!= "Context"
|
|
||||||
{
|
{
|
||||||
return Err(Error::new_spanned(
|
return Err(Error::new_spanned(
|
||||||
arg,
|
arg,
|
||||||
|
@ -114,6 +113,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
default,
|
default,
|
||||||
|
validators,
|
||||||
},
|
},
|
||||||
) in args
|
) in args
|
||||||
{
|
{
|
||||||
|
@ -138,6 +138,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
||||||
description: #desc,
|
description: #desc,
|
||||||
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
||||||
default_value: #schema_default,
|
default_value: #schema_default,
|
||||||
|
validators: #validators,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -171,9 +172,10 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let ctx_field = match arg_ctx {
|
let ctx_field = if arg_ctx {
|
||||||
true => quote! { &ctx, },
|
quote! { &ctx, }
|
||||||
false => quote! {},
|
} else {
|
||||||
|
quote! {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let field_ident = &method.sig.ident;
|
let field_ident = &method.sig.ident;
|
||||||
|
|
|
@ -12,7 +12,7 @@ impl<'a> OutputType<'a> {
|
||||||
let ty = if let Type::Path(p) = input {
|
let ty = if let Type::Path(p) = input {
|
||||||
if p.path.segments.last().unwrap().ident == "Result" {
|
if p.path.segments.last().unwrap().ident == "Result" {
|
||||||
if let PathArguments::AngleBracketed(args) = &p.path.segments[0].arguments {
|
if let PathArguments::AngleBracketed(args) = &p.path.segments[0].arguments {
|
||||||
if args.args.len() == 0 {
|
if args.args.is_empty() {
|
||||||
return Err(Error::new_spanned(input, "Invalid type"));
|
return Err(Error::new_spanned(input, "Invalid type"));
|
||||||
}
|
}
|
||||||
let mut res = None;
|
let mut res = None;
|
||||||
|
|
|
@ -137,6 +137,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
||||||
name,
|
name,
|
||||||
desc,
|
desc,
|
||||||
default,
|
default,
|
||||||
|
validators,
|
||||||
},
|
},
|
||||||
) in args
|
) in args
|
||||||
{
|
{
|
||||||
|
@ -161,6 +162,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
||||||
description: #desc,
|
description: #desc,
|
||||||
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
ty: <#ty as #crate_name::Type>::create_type_info(registry),
|
||||||
default_value: #schema_default,
|
default_value: #schema_default,
|
||||||
|
validators: #validators,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,16 @@ use graphql_parser::parse_query;
|
||||||
use graphql_parser::query::{Definition, OperationDefinition, ParseError, Query, Value};
|
use graphql_parser::query::{Definition, OperationDefinition, ParseError, Query, Value};
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{Error, Ident, Result};
|
use syn::{Error, Ident, Meta, MetaList, NestedMeta, Result};
|
||||||
|
|
||||||
pub fn get_crate_name(internal: bool) -> TokenStream {
|
pub fn get_crate_name(internal: bool) -> TokenStream {
|
||||||
match internal {
|
if internal {
|
||||||
true => quote! { crate },
|
quote! { crate }
|
||||||
false => {
|
} else {
|
||||||
let id = Ident::new("async_graphql", Span::call_site());
|
let id = Ident::new("async_graphql", Span::call_site());
|
||||||
quote! { #id }
|
quote! { #id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_value(s: &str) -> std::result::Result<Value, ParseError> {
|
pub fn parse_value(s: &str) -> std::result::Result<Value, ParseError> {
|
||||||
let mut doc = parse_query(&format!("query ($a:Int!={}) {{ dummy }}", s))?;
|
let mut doc = parse_query(&format!("query ($a:Int!={}) {{ dummy }}", s))?;
|
||||||
|
@ -97,3 +96,30 @@ pub fn check_reserved_name(name: &str, internal: bool) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_validators(args: &MetaList) -> Result<TokenStream> {
|
||||||
|
let mut validators = Vec::new();
|
||||||
|
for arg in &args.nested {
|
||||||
|
if let NestedMeta::Meta(Meta::List(ls)) = arg {
|
||||||
|
if ls.path.is_ident("validator") {
|
||||||
|
let mut ty = None;
|
||||||
|
let mut params = Vec::new();
|
||||||
|
for item in &ls.nested {
|
||||||
|
match item {
|
||||||
|
NestedMeta::Meta(Meta::Path(p)) => {
|
||||||
|
ty = Some(p);
|
||||||
|
}
|
||||||
|
NestedMeta::Meta(Meta::NameValue(nv)) => {
|
||||||
|
let name = &nv.path;
|
||||||
|
let value = &nv.lit;
|
||||||
|
params.push(quote! { #name: #value });
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validators.push(quote! { Box::new(#ty { #(#params),* }) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(quote! { std::sync::Arc::new(vec![#(#validators)*]) })
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub trait Type {
|
||||||
|
|
||||||
/// Parse `GlobalID`.
|
/// Parse `GlobalID`.
|
||||||
fn from_global_id(id: ID) -> Result<ID> {
|
fn from_global_id(id: ID) -> Result<ID> {
|
||||||
let v: Vec<&str> = id.splitn(2, ":").collect();
|
let v: Vec<&str> = id.splitn(2, ':').collect();
|
||||||
if v.len() != 2 {
|
if v.len() != 2 {
|
||||||
return Err(QueryError::InvalidGlobalID.into());
|
return Err(QueryError::InvalidGlobalID.into());
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,7 @@ macro_rules! impl_scalar_internal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl crate::OutputValueType for $ty {
|
impl crate::OutputValueType for $ty {
|
||||||
async fn resolve(
|
async fn resolve(
|
||||||
|
@ -191,6 +192,7 @@ macro_rules! impl_scalar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
#[async_graphql::async_trait::async_trait]
|
#[async_graphql::async_trait::async_trait]
|
||||||
impl async_graphql::OutputValueType for $ty {
|
impl async_graphql::OutputValueType for $ty {
|
||||||
async fn resolve(
|
async fn resolve(
|
||||||
|
@ -215,6 +217,7 @@ impl<T: Type + Send + Sync> Type for &T {
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<T: OutputValueType + Send + Sync> OutputValueType for &T {
|
impl<T: OutputValueType + Send + Sync> OutputValueType for &T {
|
||||||
|
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||||
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
||||||
T::resolve(*value, ctx).await
|
T::resolve(*value, ctx).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ impl Variables {
|
||||||
content_type: Option<&str>,
|
content_type: Option<&str>,
|
||||||
content: Bytes,
|
content: Bytes,
|
||||||
) {
|
) {
|
||||||
let mut it = var_path.split(".").peekable();
|
let mut it = var_path.split('.').peekable();
|
||||||
|
|
||||||
if let Some(first) = it.next() {
|
if let Some(first) = it.next() {
|
||||||
if first != "variables" {
|
if first != "variables" {
|
||||||
|
@ -85,8 +85,7 @@ impl Variables {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if let Value::Object(obj) = current {
|
||||||
if let Value::Object(obj) = current {
|
|
||||||
if let Some(value) = obj.get_mut(s) {
|
if let Some(value) = obj.get_mut(s) {
|
||||||
if !has_next {
|
if !has_next {
|
||||||
*value = Value::String(file_string(filename, content_type, &content));
|
*value = Value::String(file_string(filename, content_type, &content));
|
||||||
|
@ -101,7 +100,6 @@ impl Variables {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn file_string(filename: &str, content_type: Option<&str>, content: &[u8]) -> String {
|
fn file_string(filename: &str, content_type: Option<&str>, content: &[u8]) -> String {
|
||||||
if let Some(content_type) = content_type {
|
if let Some(content_type) = content_type {
|
||||||
|
@ -119,11 +117,9 @@ fn json_value_to_gql_value(value: serde_json::Value) -> Value {
|
||||||
serde_json::Value::Number(n) if n.is_f64() => Value::Float(n.as_f64().unwrap()),
|
serde_json::Value::Number(n) if n.is_f64() => Value::Float(n.as_f64().unwrap()),
|
||||||
serde_json::Value::Number(n) => Value::Int((n.as_i64().unwrap() as i32).into()),
|
serde_json::Value::Number(n) => Value::Int((n.as_i64().unwrap() as i32).into()),
|
||||||
serde_json::Value::String(s) => Value::String(s),
|
serde_json::Value::String(s) => Value::String(s),
|
||||||
serde_json::Value::Array(ls) => Value::List(
|
serde_json::Value::Array(ls) => {
|
||||||
ls.into_iter()
|
Value::List(ls.into_iter().map(json_value_to_gql_value).collect())
|
||||||
.map(|value| json_value_to_gql_value(value))
|
}
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
serde_json::Value::Object(obj) => Value::Object(
|
serde_json::Value::Object(obj) => Value::Object(
|
||||||
obj.into_iter()
|
obj.into_iter()
|
||||||
.map(|(name, value)| (name, json_value_to_gql_value(value)))
|
.map(|(name, value)| (name, json_value_to_gql_value(value)))
|
||||||
|
@ -172,7 +168,7 @@ impl<'a, T> ContextBase<'a, T> {
|
||||||
item,
|
item,
|
||||||
variables: self.variables,
|
variables: self.variables,
|
||||||
variable_definitions: self.variable_definitions,
|
variable_definitions: self.variable_definitions,
|
||||||
registry: self.registry.clone(),
|
registry: self.registry,
|
||||||
data: self.data,
|
data: self.data,
|
||||||
fragments: self.fragments,
|
fragments: self.fragments,
|
||||||
}
|
}
|
||||||
|
@ -198,10 +194,10 @@ impl<'a, T> ContextBase<'a, T> {
|
||||||
return Ok(default.clone());
|
return Ok(default.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(QueryError::VarNotDefined {
|
Err(QueryError::VarNotDefined {
|
||||||
var_name: name.to_string(),
|
var_name: name.to_string(),
|
||||||
}
|
}
|
||||||
.into());
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_input_value(&self, mut value: Value) -> Result<Value> {
|
fn resolve_input_value(&self, mut value: Value) -> Result<Value> {
|
||||||
|
@ -216,7 +212,7 @@ impl<'a, T> ContextBase<'a, T> {
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
Value::Object(ref mut obj) => {
|
Value::Object(ref mut obj) => {
|
||||||
for (_, value) in obj {
|
for value in obj.values_mut() {
|
||||||
if let Value::Variable(var_name) = value {
|
if let Value::Variable(var_name) = value {
|
||||||
*value = self.var_value(&var_name)?;
|
*value = self.var_value(&var_name)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,6 @@ pub enum QueryError {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Creates a wrapper with an error location
|
/// Creates a wrapper with an error location
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub trait ErrorWithPosition {
|
pub trait ErrorWithPosition {
|
||||||
|
|
|
@ -58,6 +58,8 @@
|
||||||
//! * [GraphQL over WebSocket Protocol](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md)
|
//! * [GraphQL over WebSocket Protocol](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md)
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
#![allow(clippy::needless_doctest_main)]
|
||||||
|
#![allow(clippy::needless_lifetimes)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate thiserror;
|
extern crate thiserror;
|
||||||
|
@ -76,6 +78,9 @@ mod subscription;
|
||||||
mod types;
|
mod types;
|
||||||
mod validation;
|
mod validation;
|
||||||
|
|
||||||
|
/// Input value validators
|
||||||
|
pub mod validators;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use anyhow;
|
pub use anyhow;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
@ -167,14 +167,14 @@ impl<'a, Query, Mutation> PreparedQuery<'a, Query, Mutation> {
|
||||||
item: &self.selection_set,
|
item: &self.selection_set,
|
||||||
variables: &self.variables,
|
variables: &self.variables,
|
||||||
variable_definitions: self.variable_definitions.as_deref(),
|
variable_definitions: self.variable_definitions.as_deref(),
|
||||||
registry: self.registry.clone(),
|
registry: self.registry,
|
||||||
data: self.data,
|
data: self.data,
|
||||||
fragments: &self.fragments,
|
fragments: &self.fragments,
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.root {
|
match self.root {
|
||||||
Root::Query(query) => return OutputValueType::resolve(query, &ctx).await,
|
Root::Query(query) => OutputValueType::resolve(query, &ctx).await,
|
||||||
Root::Mutation(mutation) => return OutputValueType::resolve(mutation, &ctx).await,
|
Root::Mutation(mutation) => OutputValueType::resolve(mutation, &ctx).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
use crate::validators::InputValueValidator;
|
||||||
use crate::{model, Value};
|
use crate::{model, Value};
|
||||||
use graphql_parser::query::Type as ParsedType;
|
use graphql_parser::query::Type as ParsedType;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn parse_non_null(type_name: &str) -> Option<&str> {
|
fn parse_non_null(type_name: &str) -> Option<&str> {
|
||||||
if type_name.ends_with("!") {
|
if type_name.ends_with('!') {
|
||||||
Some(&type_name[..type_name.len() - 1])
|
Some(&type_name[..type_name.len() - 1])
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -11,7 +13,7 @@ fn parse_non_null(type_name: &str) -> Option<&str> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_list(type_name: &str) -> Option<&str> {
|
fn parse_list(type_name: &str) -> Option<&str> {
|
||||||
if type_name.starts_with("[") {
|
if type_name.starts_with('[') {
|
||||||
Some(&type_name[1..type_name.len() - 1])
|
Some(&type_name[1..type_name.len() - 1])
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -58,6 +60,7 @@ pub struct InputValue {
|
||||||
pub description: Option<&'static str>,
|
pub description: Option<&'static str>,
|
||||||
pub ty: String,
|
pub ty: String,
|
||||||
pub default_value: Option<&'static str>,
|
pub default_value: Option<&'static str>,
|
||||||
|
pub validators: Arc<Vec<Box<dyn InputValueValidator>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -108,7 +108,7 @@ pub async fn do_resolve_values<'a, T: ObjectType + Send + Sync>(
|
||||||
Resolver {
|
Resolver {
|
||||||
ctx,
|
ctx,
|
||||||
obj: root,
|
obj: root,
|
||||||
result: result,
|
result,
|
||||||
}
|
}
|
||||||
.resolve()
|
.resolve()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{impl_scalar_internal, Scalar, Result, Value};
|
use crate::{impl_scalar_internal, Result, Scalar, Value};
|
||||||
use bson::oid::ObjectId;
|
use bson::oid::ObjectId;
|
||||||
|
|
||||||
impl Scalar for ObjectId {
|
impl Scalar for ObjectId {
|
||||||
|
|
|
@ -5,12 +5,12 @@ mod integers;
|
||||||
mod string;
|
mod string;
|
||||||
mod url;
|
mod url;
|
||||||
|
|
||||||
|
#[cfg(feature = "bson")]
|
||||||
|
mod bson;
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
mod datetime;
|
mod datetime;
|
||||||
#[cfg(feature = "uuid")]
|
#[cfg(feature = "uuid")]
|
||||||
mod uuid;
|
mod uuid;
|
||||||
#[cfg(feature = "bson")]
|
|
||||||
mod bson;
|
|
||||||
|
|
||||||
pub use id::ID;
|
pub use id::ID;
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ pub use id::ID;
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::ID;
|
use super::ID;
|
||||||
use crate::Type;
|
use crate::Type;
|
||||||
|
use bson::oid::ObjectId;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use bson::oid::ObjectId;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scalar_type() {
|
fn test_scalar_type() {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
const STRING_DESC:&'static str = "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.";
|
const STRING_DESC: &str = "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.";
|
||||||
|
|
||||||
impl Scalar for String {
|
impl Scalar for String {
|
||||||
fn type_name() -> &'static str {
|
fn type_name() -> &'static str {
|
||||||
|
@ -56,6 +56,6 @@ impl<'a> Type for &'a str {
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<'a> OutputValueType for &'a str {
|
impl<'a> OutputValueType for &'a str {
|
||||||
async fn resolve(value: &Self, _: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
async fn resolve(value: &Self, _: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
||||||
Ok(value.to_string().into())
|
Ok((*value).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,8 @@ impl<Query: ObjectType, Mutation: ObjectType, Subscription: SubscriptionType>
|
||||||
name: "if",
|
name: "if",
|
||||||
description: Some("Included when true."),
|
description: Some("Included when true."),
|
||||||
ty: "Boolean!".to_string(),
|
ty: "Boolean!".to_string(),
|
||||||
default_value: None
|
default_value: None,
|
||||||
|
validators: Default::default(),
|
||||||
});
|
});
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
@ -76,7 +77,8 @@ impl<Query: ObjectType, Mutation: ObjectType, Subscription: SubscriptionType>
|
||||||
name: "if",
|
name: "if",
|
||||||
description: Some("Skipped when true."),
|
description: Some("Skipped when true."),
|
||||||
ty: "Boolean!".to_string(),
|
ty: "Boolean!".to_string(),
|
||||||
default_value: None
|
default_value: None,
|
||||||
|
validators: Default::default(),
|
||||||
});
|
});
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub trait SubscriptionType: Type {
|
||||||
/// This function returns true of type `EmptySubscription` only
|
/// This function returns true of type `EmptySubscription` only
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn is_empty() -> bool {
|
fn is_empty() -> bool {
|
||||||
return false;
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_type(field: &Field, types: &mut HashMap<TypeId, Field>) -> Result<()>;
|
fn create_type(field: &Field, types: &mut HashMap<TypeId, Field>) -> Result<()>;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
mod connection;
|
mod connection_type;
|
||||||
mod edge;
|
mod edge;
|
||||||
mod page_info;
|
mod page_info;
|
||||||
mod slice;
|
mod slice;
|
||||||
|
|
||||||
use crate::{Context, ObjectType, QueryError, Result};
|
use crate::{Context, ObjectType, QueryError, Result};
|
||||||
|
|
||||||
pub use connection::Connection;
|
pub use connection_type::Connection;
|
||||||
|
|
||||||
/// Connection query operation
|
/// Connection query operation
|
||||||
pub enum QueryOperation<'a> {
|
pub enum QueryOperation<'a> {
|
||||||
|
|
|
@ -26,7 +26,7 @@ impl<'a, T: Sync> DataSource for &'a [T] {
|
||||||
.and_then(|before| base64::decode(before).ok())
|
.and_then(|before| base64::decode(before).ok())
|
||||||
.and_then(|data| data.as_slice().read_u32::<BE>().ok())
|
.and_then(|data| data.as_slice().read_u32::<BE>().ok())
|
||||||
.map(|idx| idx as usize)
|
.map(|idx| idx as usize)
|
||||||
.unwrap_or(self.len());
|
.unwrap_or_else(|| self.len());
|
||||||
let start = if end < *limit { 0 } else { end - *limit };
|
let start = if end < *limit { 0 } else { end - *limit };
|
||||||
(start, end)
|
(start, end)
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,6 @@ impl ObjectType for EmptyMutation {
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl OutputValueType for EmptyMutation {
|
impl OutputValueType for EmptyMutation {
|
||||||
async fn resolve(_value: &Self, _ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
async fn resolve(_value: &Self, _ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
||||||
return Err(QueryError::NotConfiguredMutations.into());
|
Err(QueryError::NotConfiguredMutations.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,6 @@ impl SubscriptionType for EmptySubscription {
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl OutputValueType for EmptySubscription {
|
impl OutputValueType for EmptySubscription {
|
||||||
async fn resolve(_value: &Self, _ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
async fn resolve(_value: &Self, _ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
||||||
return Err(QueryError::NotConfiguredSubscriptions.into());
|
Err(QueryError::NotConfiguredSubscriptions.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub trait EnumType: Type + Sized + Eq + Send + Copy + Sized + 'static {
|
||||||
let items = Self::items();
|
let items = Self::items();
|
||||||
for item in items {
|
for item in items {
|
||||||
if item.value == *self {
|
if item.value == *self {
|
||||||
return Ok(item.name.clone().into());
|
return Ok(item.name.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
|
@ -31,6 +31,7 @@ impl<T: InputValueType> InputValueType for Vec<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<T: OutputValueType + Send + Sync> OutputValueType for Vec<T> {
|
impl<T: OutputValueType + Send + Sync> OutputValueType for Vec<T> {
|
||||||
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
|
||||||
|
|
|
@ -47,6 +47,7 @@ impl<T: Type> Type for QueryRoot<T> {
|
||||||
description: None,
|
description: None,
|
||||||
ty: "String!".to_string(),
|
ty: "String!".to_string(),
|
||||||
default_value: None,
|
default_value: None,
|
||||||
|
validators: Default::default(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
args
|
args
|
||||||
|
@ -87,7 +88,7 @@ impl<T: ObjectType + Send + Sync> ObjectType for QueryRoot<T> {
|
||||||
.map_err(|err| err.with_position(field.position).into());
|
.map_err(|err| err.with_position(field.position).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.inner.resolve_field(ctx, field).await;
|
self.inner.resolve_field(ctx, field).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn resolve_inline_fragment(
|
async fn resolve_inline_fragment(
|
||||||
|
|
|
@ -37,10 +37,10 @@ impl<'a> InputValueType for Upload {
|
||||||
if let Value::String(s) = value {
|
if let Value::String(s) = value {
|
||||||
if s.starts_with("file:") {
|
if s.starts_with("file:") {
|
||||||
let s = &s[5..];
|
let s = &s[5..];
|
||||||
if let Some(idx) = s.find("|") {
|
if let Some(idx) = s.find('|') {
|
||||||
let name_and_type = &s[..idx];
|
let name_and_type = &s[..idx];
|
||||||
let content = &s[idx + 1..];
|
let content = &s[idx + 1..];
|
||||||
if let Some(type_idx) = name_and_type.find(":") {
|
if let Some(type_idx) = name_and_type.find(':') {
|
||||||
let name = &name_and_type[..type_idx];
|
let name = &name_and_type[..type_idx];
|
||||||
let mime_type = &name_and_type[type_idx + 1..];
|
let mime_type = &name_and_type[type_idx + 1..];
|
||||||
return Some(Self {
|
return Some(Self {
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl<'a> ValidatorContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parent_type(&self) -> Option<&'a registry::Type> {
|
pub fn parent_type(&self) -> Option<&'a registry::Type> {
|
||||||
self.type_stack.get(self.type_stack.len() - 2).map(|t| *t)
|
self.type_stack.get(self.type_stack.len() - 2).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_type(&self) -> &'a registry::Type {
|
pub fn current_type(&self) -> &'a registry::Type {
|
||||||
|
@ -62,6 +62,6 @@ impl<'a> ValidatorContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fragment(&self, name: &str) -> Option<&'a FragmentDefinition> {
|
pub fn fragment(&self, name: &str) -> Option<&'a FragmentDefinition> {
|
||||||
self.fragments.get(name).map(|f| *f)
|
self.fragments.get(name).copied()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,16 @@ impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> {
|
||||||
.current_args
|
.current_args
|
||||||
.and_then(|args| args.get(name).map(|input| input))
|
.and_then(|args| args.get(name).map(|input| input))
|
||||||
{
|
{
|
||||||
|
for validator in arg.validators.iter() {
|
||||||
|
if let Some(reason) = validator.is_valid(value) {
|
||||||
|
ctx.report_error(
|
||||||
|
vec![pos],
|
||||||
|
format!("Invalid value for argument \"{}\", {}", arg.name, reason,),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !is_valid_input_value(ctx.registry, &arg.ty, value) {
|
if !is_valid_input_value(ctx.registry, &arg.ty, value) {
|
||||||
ctx.report_error(
|
ctx.report_error(
|
||||||
vec![pos],
|
vec![pos],
|
||||||
|
|
|
@ -17,8 +17,7 @@ impl<'a> Visitor<'a> for DefaultValuesOfCorrectType {
|
||||||
"Argument \"{}\" has type \"{}\" and is not nullable, so it't can't have a default value",
|
"Argument \"{}\" has type \"{}\" and is not nullable, so it't can't have a default value",
|
||||||
variable_definition.name, variable_definition.var_type,
|
variable_definition.name, variable_definition.var_type,
|
||||||
));
|
));
|
||||||
} else {
|
} else if !is_valid_input_value(
|
||||||
if !is_valid_input_value(
|
|
||||||
ctx.registry,
|
ctx.registry,
|
||||||
&variable_definition.var_type.to_string(),
|
&variable_definition.var_type.to_string(),
|
||||||
value,
|
value,
|
||||||
|
@ -34,4 +33,3 @@ impl<'a> Visitor<'a> for DefaultValuesOfCorrectType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -32,9 +32,8 @@ impl<'a, 'ctx> FindConflicts<'a, 'ctx> {
|
||||||
Selection::Field(field) => {
|
Selection::Field(field) => {
|
||||||
let output_name = field
|
let output_name = field
|
||||||
.alias
|
.alias
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map(|alias| alias.as_str())
|
.unwrap_or_else(|| field.name.as_str());
|
||||||
.unwrap_or(field.name.as_str());
|
|
||||||
self.add_output(output_name, field);
|
self.add_output(output_name, field);
|
||||||
}
|
}
|
||||||
Selection::InlineFragment(inline_fragment) => {
|
Selection::InlineFragment(inline_fragment) => {
|
||||||
|
|
|
@ -11,8 +11,9 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments {
|
||||||
fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Directive) {
|
fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Directive) {
|
||||||
if let Some(schema_directive) = ctx.registry.directives.get(&directive.name) {
|
if let Some(schema_directive) = ctx.registry.directives.get(&directive.name) {
|
||||||
for arg in schema_directive.args.values() {
|
for arg in schema_directive.args.values() {
|
||||||
if TypeName::create(&arg.ty).is_non_null() && arg.default_value.is_none() {
|
if TypeName::create(&arg.ty).is_non_null()
|
||||||
if directive
|
&& arg.default_value.is_none()
|
||||||
|
&& directive
|
||||||
.arguments
|
.arguments
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(name, _)| name == arg.name)
|
.find(|(name, _)| name == arg.name)
|
||||||
|
@ -27,14 +28,14 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) {
|
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Field) {
|
||||||
if let Some(parent_type) = ctx.parent_type() {
|
if let Some(parent_type) = ctx.parent_type() {
|
||||||
if let Some(schema_field) = parent_type.field_by_name(&field.name) {
|
if let Some(schema_field) = parent_type.field_by_name(&field.name) {
|
||||||
for arg in schema_field.args.values() {
|
for arg in schema_field.args.values() {
|
||||||
if TypeName::create(&arg.ty).is_non_null() && arg.default_value.is_none() {
|
if TypeName::create(&arg.ty).is_non_null()
|
||||||
if field
|
&& arg.default_value.is_none()
|
||||||
|
&& field
|
||||||
.arguments
|
.arguments
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(name, _)| name == arg.name)
|
.find(|(name, _)| name == arg.name)
|
||||||
|
@ -51,4 +52,3 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -11,8 +11,7 @@ impl<'a> Visitor<'a> for UploadFile {
|
||||||
ctx: &mut ValidatorContext<'a>,
|
ctx: &mut ValidatorContext<'a>,
|
||||||
operation_definition: &'a OperationDefinition,
|
operation_definition: &'a OperationDefinition,
|
||||||
) {
|
) {
|
||||||
match operation_definition {
|
if let OperationDefinition::Query(query) = operation_definition {
|
||||||
OperationDefinition::Query(query) => {
|
|
||||||
for var in &query.variable_definitions {
|
for var in &query.variable_definitions {
|
||||||
if let Some(ty) = ctx.registry.basic_type_by_parsed_type(&var.var_type) {
|
if let Some(ty) = ctx.registry.basic_type_by_parsed_type(&var.var_type) {
|
||||||
if ty.name() == "Upload" {
|
if ty.name() == "Upload" {
|
||||||
|
@ -24,7 +23,5 @@ impl<'a> Visitor<'a> for UploadFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -461,7 +461,7 @@ fn visit_arguments<'a, V: Visitor<'a>>(
|
||||||
v: &mut V,
|
v: &mut V,
|
||||||
ctx: &mut ValidatorContext<'a>,
|
ctx: &mut ValidatorContext<'a>,
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
arguments: &'a Vec<(Name, Value)>,
|
arguments: &'a [(Name, Value)],
|
||||||
) {
|
) {
|
||||||
for (name, value) in arguments {
|
for (name, value) in arguments {
|
||||||
v.enter_argument(ctx, pos, name, value);
|
v.enter_argument(ctx, pos, name, value);
|
||||||
|
@ -472,7 +472,7 @@ fn visit_arguments<'a, V: Visitor<'a>>(
|
||||||
fn visit_variable_definitions<'a, V: Visitor<'a>>(
|
fn visit_variable_definitions<'a, V: Visitor<'a>>(
|
||||||
v: &mut V,
|
v: &mut V,
|
||||||
ctx: &mut ValidatorContext<'a>,
|
ctx: &mut ValidatorContext<'a>,
|
||||||
variable_definitions: &'a Vec<VariableDefinition>,
|
variable_definitions: &'a [VariableDefinition],
|
||||||
) {
|
) {
|
||||||
for d in variable_definitions {
|
for d in variable_definitions {
|
||||||
v.enter_variable_definition(ctx, d);
|
v.enter_variable_definition(ctx, d);
|
||||||
|
@ -483,7 +483,7 @@ fn visit_variable_definitions<'a, V: Visitor<'a>>(
|
||||||
fn visit_directives<'a, V: Visitor<'a>>(
|
fn visit_directives<'a, V: Visitor<'a>>(
|
||||||
v: &mut V,
|
v: &mut V,
|
||||||
ctx: &mut ValidatorContext<'a>,
|
ctx: &mut ValidatorContext<'a>,
|
||||||
directives: &'a Vec<Directive>,
|
directives: &'a [Directive],
|
||||||
) {
|
) {
|
||||||
for d in directives {
|
for d in directives {
|
||||||
v.enter_directive(ctx, d);
|
v.enter_directive(ctx, d);
|
||||||
|
|
30
src/validators/int_validators.rs
Normal file
30
src/validators/int_validators.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use crate::validators::InputValueValidator;
|
||||||
|
use graphql_parser::query::Value;
|
||||||
|
|
||||||
|
/// Integer range validator
|
||||||
|
pub struct IntRange {
|
||||||
|
/// Minimum value, including this value
|
||||||
|
pub min: i64,
|
||||||
|
|
||||||
|
/// Maximum value, including this value
|
||||||
|
pub max: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputValueValidator for IntRange {
|
||||||
|
fn is_valid(&self, value: &Value) -> Option<String> {
|
||||||
|
if let Value::Int(n) = value {
|
||||||
|
if n.as_i64().unwrap() < self.min || n.as_i64().unwrap() > self.max {
|
||||||
|
Some(format!(
|
||||||
|
"the value is {}, but the range must be between {} and {}",
|
||||||
|
n.as_i64().unwrap(),
|
||||||
|
self.min,
|
||||||
|
self.max
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some("expected type \"Int\"".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/validators/mod.rs
Normal file
16
src/validators/mod.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
mod int_validators;
|
||||||
|
|
||||||
|
use graphql_parser::schema::Value;
|
||||||
|
|
||||||
|
/// Input value validator
|
||||||
|
///
|
||||||
|
/// You can create your own input value validator by implementing this trait.
|
||||||
|
pub trait InputValueValidator
|
||||||
|
where
|
||||||
|
Self: Sync + Send,
|
||||||
|
{
|
||||||
|
/// Check value is valid, returns the reason for the error if it fails, otherwise None.
|
||||||
|
fn is_valid(&self, value: &Value) -> Option<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use int_validators::IntRange;
|
Loading…
Reference in New Issue
Block a user