Merge branch 'graphql-2021'
This commit is contained in:
commit
4793c362c3
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## Unreleased
|
||||
|
||||
- Use Rust `2021` edition.
|
||||
- Subscription typename - [GraphQL - October 2021] [#681](https://github.com/async-graphql/async-graphql/issues/681)
|
||||
- Allow directive on variable definition - [GraphQL - October 2021] [#678](https://github.com/async-graphql/async-graphql/issues/678)
|
||||
- Specified By - [GraphQL - October 2021] [#677](https://github.com/async-graphql/async-graphql/issues/677)
|
||||
- Add `specified_by_url` for `Tz`, `DateTime<Tz>`, `Url`, `Uuid` and `Upload` scalars.
|
||||
|
||||
## [2.10.8] 2021-10-26
|
||||
|
||||
|
|
|
@ -446,6 +446,7 @@ pub struct Scalar {
|
|||
pub name: Option<String>,
|
||||
pub use_type_description: bool,
|
||||
pub visible: Option<Visible>,
|
||||
pub specified_by_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(FromMeta, Default)]
|
||||
|
@ -652,6 +653,8 @@ pub struct NewType {
|
|||
pub name: NewTypeName,
|
||||
#[darling(default)]
|
||||
pub visible: Option<Visible>,
|
||||
#[darling(default)]
|
||||
pub specified_by_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(FromMeta, Default)]
|
||||
|
|
|
@ -90,6 +90,7 @@ pub fn generate(object_args: &args::MergedObject) -> GeneratorResult<TokenStream
|
|||
extends: #extends,
|
||||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ pub fn generate(object_args: &args::MergedSubscription) -> GeneratorResult<Token
|
|||
extends: #extends,
|
||||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -38,12 +38,18 @@ pub fn generate(newtype_args: &args::NewType) -> GeneratorResult<TokenStream> {
|
|||
None => quote! { <#inner_ty as #crate_name::Type>::type_name() },
|
||||
};
|
||||
let create_type_info = if let Some(name) = &gql_typename {
|
||||
let specified_by_url = match &newtype_args.specified_by_url {
|
||||
Some(specified_by_url) => quote! { ::std::option::Option::Some(#specified_by_url) },
|
||||
None => quote! { ::std::option::Option::None },
|
||||
};
|
||||
|
||||
quote! {
|
||||
registry.create_type::<#ident, _>(|_| #crate_name::registry::MetaType::Scalar {
|
||||
name: ::std::borrow::ToOwned::to_owned(#name),
|
||||
description: #desc,
|
||||
is_valid: |value| <#ident as #crate_name::ScalarType>::is_valid(value),
|
||||
visible: #visible,
|
||||
specified_by_url: #specified_by_url,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -563,6 +563,7 @@ pub fn generate(
|
|||
extends: #extends,
|
||||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: false,
|
||||
});
|
||||
#(#create_entity_types)*
|
||||
#(#add_keys)*
|
||||
|
@ -630,6 +631,7 @@ pub fn generate(
|
|||
extends: #extends,
|
||||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: false,
|
||||
});
|
||||
#(#create_entity_types)*
|
||||
#(#add_keys)*
|
||||
|
|
|
@ -30,6 +30,11 @@ pub fn generate(
|
|||
let generic = &item_impl.generics;
|
||||
let where_clause = &item_impl.generics.where_clause;
|
||||
let visible = visible_fn(&scalar_args.visible);
|
||||
let specified_by_url = match &scalar_args.specified_by_url {
|
||||
Some(specified_by_url) => quote! { ::std::option::Option::Some(#specified_by_url) },
|
||||
None => quote! { ::std::option::Option::None },
|
||||
};
|
||||
|
||||
let expanded = quote! {
|
||||
#item_impl
|
||||
|
||||
|
@ -45,6 +50,7 @@ pub fn generate(
|
|||
description: #desc,
|
||||
is_valid: |value| <#self_ty as #crate_name::ScalarType>::is_valid(value),
|
||||
visible: #visible,
|
||||
specified_by_url: #specified_by_url,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -293,6 +293,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
|||
extends: #extends,
|
||||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -343,6 +344,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
|||
extends: #extends,
|
||||
keys: ::std::option::Option::None,
|
||||
visible: #visible,
|
||||
is_subscription: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -456,6 +456,7 @@ pub fn generate(
|
|||
extends: #extends,
|
||||
keys: ::std::option::Option::None,
|
||||
visible: ::std::option::Option::None,
|
||||
is_subscription: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ executable_definition = { operation_definition | fragment_definition }
|
|||
operation_definition = { named_operation_definition | selection_set }
|
||||
named_operation_definition = { operation_type ~ name? ~ variable_definitions? ~ directives? ~ selection_set }
|
||||
variable_definitions = { "(" ~ variable_definition* ~ ")" }
|
||||
variable_definition = { variable ~ ":" ~ type_ ~ default_value? }
|
||||
variable_definition = { variable ~ ":" ~ type_ ~ directives? ~ default_value? }
|
||||
|
||||
selection_set = { "{" ~ selection+ ~ "}" }
|
||||
selection = { field | inline_fragment | fragment_spread }
|
||||
|
@ -86,6 +86,7 @@ directive_location = {
|
|||
| "FRAGMENT_DEFINITION"
|
||||
| "FRAGMENT_SPREAD"
|
||||
| "INLINE_FRAGMENT"
|
||||
| "VARIABLE_DEFINITION"
|
||||
| "SCHEMA"
|
||||
| "SCALAR"
|
||||
| "OBJECT"
|
||||
|
@ -115,7 +116,7 @@ value = { variable | number | string | boolean | null | enum_value |
|
|||
|
||||
variable = { "$" ~ name }
|
||||
|
||||
number = @{ float | int }
|
||||
number = @{ (float | int) ~ !name_start }
|
||||
float = { int ~ ((fractional ~ exponent) | fractional | exponent) }
|
||||
fractional = { "." ~ ASCII_DIGIT+ }
|
||||
exponent = { ("E" | "e") ~ ("+" | "-")? ~ ASCII_DIGIT+ }
|
||||
|
@ -164,4 +165,5 @@ arguments = { "(" ~ argument+ ~ ")" }
|
|||
const_argument = { name ~ ":" ~ const_value }
|
||||
argument = { name ~ ":" ~ value }
|
||||
|
||||
name = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHA | ASCII_DIGIT | "_")* }
|
||||
name_start = @{ (ASCII_ALPHA | "_") }
|
||||
name = @{ name_start ~ (ASCII_ALPHA | ASCII_DIGIT | "_")* }
|
||||
|
|
|
@ -208,6 +208,8 @@ fn parse_variable_definition(
|
|||
|
||||
let variable = parse_variable(pairs.next().unwrap(), pc)?;
|
||||
let var_type = parse_type(pairs.next().unwrap(), pc)?;
|
||||
|
||||
let directives = parse_opt_directives(&mut pairs, pc)?;
|
||||
let default_value = parse_if_rule(&mut pairs, Rule::default_value, |pair| {
|
||||
parse_default_value(pair, pc)
|
||||
})?;
|
||||
|
@ -218,6 +220,7 @@ fn parse_variable_definition(
|
|||
VariableDefinition {
|
||||
name: variable,
|
||||
var_type,
|
||||
directives,
|
||||
default_value,
|
||||
},
|
||||
pos,
|
||||
|
|
|
@ -312,3 +312,21 @@ fn parse_name(pair: Pair<Rule>, pc: &mut PositionCalculator) -> Result<Positione
|
|||
debug_assert_eq!(pair.as_rule(), Rule::name);
|
||||
Ok(Positioned::new(Name::new(pair.as_str()), pc.step(&pair)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_number_lookahead_restrictions() {
|
||||
GraphQLParser::parse(Rule::const_list, "[123 abc]").unwrap();
|
||||
GraphQLParser::parse(Rule::const_list, "[123.0123 abc]").unwrap();
|
||||
GraphQLParser::parse(Rule::const_list, "[123.0123e7 abc]").unwrap();
|
||||
GraphQLParser::parse(Rule::const_list, "[123.0123e77 abc]").unwrap();
|
||||
|
||||
assert!(GraphQLParser::parse(Rule::const_list, "[123abc]").is_err());
|
||||
assert!(GraphQLParser::parse(Rule::const_list, "[123.0123abc]").is_err());
|
||||
assert!(GraphQLParser::parse(Rule::const_list, "[123.0123e7abc]").is_err());
|
||||
assert!(GraphQLParser::parse(Rule::const_list, "[123.0123e77abc]").is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -323,6 +323,7 @@ fn parse_directive_definition(
|
|||
"FRAGMENT_DEFINITION" => DirectiveLocation::FragmentDefinition,
|
||||
"FRAGMENT_SPREAD" => DirectiveLocation::FragmentSpread,
|
||||
"INLINE_FRAGMENT" => DirectiveLocation::InlineFragment,
|
||||
"VARIABLE_DEFINITION" => DirectiveLocation::VariableDefinition,
|
||||
"SCHEMA" => DirectiveLocation::Schema,
|
||||
"SCALAR" => DirectiveLocation::Scalar,
|
||||
"OBJECT" => DirectiveLocation::Object,
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::str::Chars;
|
|||
/// Original position of an element in source code.
|
||||
///
|
||||
/// You can serialize and deserialize it to the GraphQL `locations` format
|
||||
/// ([reference](https://spec.graphql.org/June2018/#sec-Errors)).
|
||||
/// ([reference](https://spec.graphql.org/October2021/#sec-Errors)).
|
||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Default, Hash, Serialize, Deserialize)]
|
||||
pub struct Pos {
|
||||
/// One-based line number.
|
||||
|
|
|
@ -5,7 +5,7 @@ use async_graphql_value::{ConstValue, Name, Value};
|
|||
|
||||
/// An executable GraphQL file or request string.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#ExecutableDocument).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#ExecutableDocument).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExecutableDocument {
|
||||
/// The operations of the document.
|
||||
|
@ -93,7 +93,7 @@ enum OperationsIterInner<'a> {
|
|||
|
||||
/// A GraphQL operation, such as `mutation($content:String!) { makePost(content: $content) { id } }`.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#OperationDefinition).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#OperationDefinition).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OperationDefinition {
|
||||
/// The type of operation.
|
||||
|
@ -108,13 +108,15 @@ pub struct OperationDefinition {
|
|||
|
||||
/// A variable definition inside a list of variable definitions, for example `$name:String!`.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#VariableDefinition).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#VariableDefinition).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VariableDefinition {
|
||||
/// The name of the variable, without the preceding `$`.
|
||||
pub name: Positioned<Name>,
|
||||
/// The type of the variable.
|
||||
pub var_type: Positioned<Type>,
|
||||
/// The variable's directives.
|
||||
pub directives: Vec<Positioned<Directive>>,
|
||||
/// The optional default value of the variable.
|
||||
pub default_value: Option<Positioned<ConstValue>>,
|
||||
}
|
||||
|
@ -139,7 +141,7 @@ impl VariableDefinition {
|
|||
|
||||
/// A set of fields to be selected, for example `{ name age }`.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#SelectionSet).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#SelectionSet).
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct SelectionSet {
|
||||
/// The fields to be selected.
|
||||
|
@ -148,7 +150,7 @@ pub struct SelectionSet {
|
|||
|
||||
/// A part of an object to be selected; a single field, a fragment spread or an inline fragment.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#Selection).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#Selection).
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Selection {
|
||||
/// Select a single field, such as `name` or `weightKilos: weight(unit: KILOGRAMS)`.
|
||||
|
@ -182,7 +184,7 @@ impl Selection {
|
|||
|
||||
/// A field being selected on an object, such as `name` or `weightKilos: weight(unit: KILOGRAMS)`.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#Field).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#Field).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Field {
|
||||
/// The optional field alias.
|
||||
|
@ -217,7 +219,7 @@ impl Field {
|
|||
|
||||
/// A fragment selector, such as `... userFields`.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#FragmentSpread).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#FragmentSpread).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FragmentSpread {
|
||||
/// The name of the fragment being selected.
|
||||
|
@ -228,7 +230,7 @@ pub struct FragmentSpread {
|
|||
|
||||
/// An inline fragment selector, such as `... on User { name }`.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#InlineFragment).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#InlineFragment).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InlineFragment {
|
||||
/// The type condition.
|
||||
|
@ -241,7 +243,7 @@ pub struct InlineFragment {
|
|||
|
||||
/// The definition of a fragment, such as `fragment userFields on User { name age }`.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#FragmentDefinition).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#FragmentDefinition).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FragmentDefinition {
|
||||
/// The type this fragment operates on.
|
||||
|
@ -254,7 +256,7 @@ pub struct FragmentDefinition {
|
|||
|
||||
/// A type a fragment can apply to (`on` followed by the type).
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#TypeCondition).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#TypeCondition).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TypeCondition {
|
||||
/// The type this fragment applies to.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! [`ServiceDocument`](struct.ServiceDocument.html), representing an executable GraphQL query and a
|
||||
//! GraphQL service respectively.
|
||||
//!
|
||||
//! This follows the [June 2018 edition of the GraphQL spec](https://spec.graphql.org/June2018/).
|
||||
//! This follows the [June 2018 edition of the GraphQL spec](https://spec.graphql.org/October2021/).
|
||||
|
||||
mod executable;
|
||||
mod service;
|
||||
|
@ -19,7 +19,7 @@ pub use service::*;
|
|||
|
||||
/// The type of an operation; `query`, `mutation` or `subscription`.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#OperationType).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#OperationType).
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub enum OperationType {
|
||||
/// A query.
|
||||
|
@ -42,7 +42,7 @@ impl Display for OperationType {
|
|||
|
||||
/// A GraphQL type, for example `String` or `[String!]!`.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#Type).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#Type).
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Type {
|
||||
/// The base type.
|
||||
|
@ -105,7 +105,7 @@ impl Display for BaseType {
|
|||
/// from [`Directive`](struct.Directive.html) in that it uses [`ConstValue`](enum.ConstValue.html)
|
||||
/// instead of [`Value`](enum.Value.html).
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#Directive).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#Directive).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConstDirective {
|
||||
/// The name of the directive.
|
||||
|
@ -140,7 +140,7 @@ impl ConstDirective {
|
|||
|
||||
/// A GraphQL directive, such as `@deprecated(reason: "Use the other field")`.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#Directive).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#Directive).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Directive {
|
||||
/// The name of the directive.
|
||||
|
|
|
@ -5,7 +5,7 @@ use async_graphql_value::Name;
|
|||
|
||||
/// A GraphQL file or request string defining a GraphQL service.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#Document).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#Document).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServiceDocument {
|
||||
/// The definitions of this document.
|
||||
|
@ -14,8 +14,8 @@ pub struct ServiceDocument {
|
|||
|
||||
/// A definition concerning the type system of a GraphQL service.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#TypeSystemDefinition). This enum also covers
|
||||
/// [extensions](https://spec.graphql.org/June2018/#TypeSystemExtension).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#TypeSystemDefinition). This enum also covers
|
||||
/// [extensions](https://spec.graphql.org/October2021/#TypeSystemExtension).
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TypeSystemDefinition {
|
||||
/// The definition of the schema of the service.
|
||||
|
@ -28,8 +28,8 @@ pub enum TypeSystemDefinition {
|
|||
|
||||
/// The definition of the schema in a GraphQL service.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#SchemaDefinition). This also covers
|
||||
/// [extensions](https://spec.graphql.org/June2018/#SchemaExtension).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#SchemaDefinition). This also covers
|
||||
/// [extensions](https://spec.graphql.org/October2021/#SchemaExtension).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SchemaDefinition {
|
||||
/// Whether the schema is an extension of another schema.
|
||||
|
@ -46,8 +46,8 @@ pub struct SchemaDefinition {
|
|||
|
||||
/// The definition of a type in a GraphQL service.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#TypeDefinition). This also covers
|
||||
/// [extensions](https://spec.graphql.org/June2018/#TypeExtension).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#TypeDefinition). This also covers
|
||||
/// [extensions](https://spec.graphql.org/October2021/#TypeExtension).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TypeDefinition {
|
||||
/// Whether the type is an extension of another type.
|
||||
|
@ -81,7 +81,7 @@ pub enum TypeKind {
|
|||
|
||||
/// The definition of an object type.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#ObjectType).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#ObjectType).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjectType {
|
||||
/// The interfaces implemented by the object.
|
||||
|
@ -92,7 +92,7 @@ pub struct ObjectType {
|
|||
|
||||
/// The definition of a field inside an object or interface.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#FieldDefinition).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#FieldDefinition).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FieldDefinition {
|
||||
/// The description of the field.
|
||||
|
@ -109,7 +109,7 @@ pub struct FieldDefinition {
|
|||
|
||||
/// The definition of an interface type.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#InterfaceType).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#InterfaceType).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InterfaceType {
|
||||
/// The interfaces implemented by the interface.
|
||||
|
@ -120,7 +120,7 @@ pub struct InterfaceType {
|
|||
|
||||
/// The definition of a union type.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#UnionType).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#UnionType).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnionType {
|
||||
/// The member types of the union.
|
||||
|
@ -129,7 +129,7 @@ pub struct UnionType {
|
|||
|
||||
/// The definition of an enum.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#EnumType).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#EnumType).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnumType {
|
||||
/// The possible values of the enum.
|
||||
|
@ -138,7 +138,7 @@ pub struct EnumType {
|
|||
|
||||
/// The definition of a value inside an enum.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#EnumValueDefinition).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#EnumValueDefinition).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnumValueDefinition {
|
||||
/// The description of the argument.
|
||||
|
@ -151,7 +151,7 @@ pub struct EnumValueDefinition {
|
|||
|
||||
/// The definition of an input object.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#InputObjectType).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#InputObjectType).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InputObjectType {
|
||||
/// The fields of the input object.
|
||||
|
@ -160,7 +160,7 @@ pub struct InputObjectType {
|
|||
|
||||
/// The definition of an input value inside the arguments of a field.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#InputValueDefinition).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#InputValueDefinition).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InputValueDefinition {
|
||||
/// The description of the argument.
|
||||
|
@ -177,7 +177,7 @@ pub struct InputValueDefinition {
|
|||
|
||||
/// The definition of a directive in a service.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#DirectiveDefinition).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#DirectiveDefinition).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DirectiveDefinition {
|
||||
/// The description of the directive.
|
||||
|
@ -192,7 +192,7 @@ pub struct DirectiveDefinition {
|
|||
|
||||
/// Where a directive can apply to.
|
||||
///
|
||||
/// [Reference](https://spec.graphql.org/June2018/#DirectiveLocation).
|
||||
/// [Reference](https://spec.graphql.org/October2021/#DirectiveLocation).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DirectiveLocation {
|
||||
/// A [query](enum.OperationType.html#variant.Query) [operation](struct.OperationDefinition.html).
|
||||
|
@ -233,4 +233,6 @@ pub enum DirectiveLocation {
|
|||
/// An [input value definition](struct.InputValueDefinition.html) on an input object but not a
|
||||
/// field.
|
||||
InputFieldDefinition,
|
||||
/// An [variable definition](struct.VariableDefinition.html).
|
||||
VariableDefinition,
|
||||
}
|
||||
|
|
3
parser/tests/executables/variable_directive.graphql
Normal file
3
parser/tests/executables/variable_directive.graphql
Normal file
|
@ -0,0 +1,3 @@
|
|||
query Foo($a: Int @directive = 10, $b: Int @directive) {
|
||||
value
|
||||
}
|
|
@ -4,3 +4,5 @@ directive @test1(service: String!) on FIELD_DEFINITION
|
|||
directive @test2(service: String!) on FIELD
|
||||
directive @test3(service: String!) on ENUM_VALUE
|
||||
directive @test4(service: String!) on ENUM
|
||||
|
||||
directive @test5(service: String!) on VARIABLE_DEFINITION
|
||||
|
|
|
@ -987,6 +987,7 @@ pub use async_graphql_derive::Subscription;
|
|||
/// | Attribute | description | Type | Optional |
|
||||
/// |-------------|---------------------------|----------|----------|
|
||||
/// | name | Scalar name | string | Y |
|
||||
/// | specified_by_url | Provide a specification URL for this scalar type, it must link to a human-readable specification of the data format, serialization and coercion rules for this scalar. | string | Y |
|
||||
///
|
||||
pub use async_graphql_derive::Scalar;
|
||||
|
||||
|
@ -1000,8 +1001,9 @@ pub use async_graphql_derive::Scalar;
|
|||
/// |-------------|---------------------------|----------|----------|
|
||||
/// | name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | string | Y |
|
||||
/// | name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | bool | Y |
|
||||
/// | visible(Only valid for new scalars.) | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y |
|
||||
/// | visible(Only valid for new scalars.) | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
|
||||
/// | visible(Only valid for new scalars) | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y |
|
||||
/// | visible(Only valid for new scalars) | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
|
||||
/// | specified_by_url(Only valid for new scalars) | Provide a specification URL for this scalar type, it must link to a human-readable specification of the data format, serialization and coercion rules for this scalar. | string | Y |
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
@ -218,4 +218,16 @@ impl<'a> __Type<'a> {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[graphql(name = "specifiedByURL")]
|
||||
async fn specified_by_url(&self) -> Option<&'a str> {
|
||||
if let TypeDetail::Named(registry::MetaType::Scalar {
|
||||
specified_by_url, ..
|
||||
}) = &self.detail
|
||||
{
|
||||
*specified_by_url
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -189,6 +189,7 @@ pub enum MetaType {
|
|||
description: Option<&'static str>,
|
||||
is_valid: fn(value: &Value) -> bool,
|
||||
visible: Option<MetaVisibleFn>,
|
||||
specified_by_url: Option<&'static str>,
|
||||
},
|
||||
Object {
|
||||
name: String,
|
||||
|
@ -198,6 +199,7 @@ pub enum MetaType {
|
|||
extends: bool,
|
||||
keys: Option<Vec<String>>,
|
||||
visible: Option<MetaVisibleFn>,
|
||||
is_subscription: bool,
|
||||
},
|
||||
Interface {
|
||||
name: String,
|
||||
|
@ -374,6 +376,7 @@ impl Registry {
|
|||
extends: false,
|
||||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
},
|
||||
);
|
||||
let ty = f(self);
|
||||
|
@ -507,6 +510,7 @@ impl Registry {
|
|||
extends: false,
|
||||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@ pub trait ScalarType: Sized + Send {
|
|||
/// // Rename to `MV` and add description.
|
||||
/// // scalar!(MyValue, "MV", "This is my value");
|
||||
///
|
||||
/// // Rename to `MV`, add description and specifiedByURL.
|
||||
/// // scalar!(MyValue, "MV", "This is my value", "https://tools.ietf.org/html/rfc4122");
|
||||
///
|
||||
/// struct Query;
|
||||
///
|
||||
/// #[Object]
|
||||
|
@ -92,23 +95,47 @@ pub trait ScalarType: Sized + Send {
|
|||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! scalar {
|
||||
($ty:ty, $name:literal, $desc:literal, $specified_by_url:literal) => {
|
||||
$crate::scalar_internal!(
|
||||
$ty,
|
||||
$name,
|
||||
::std::option::Option::Some($desc),
|
||||
::std::option::Option::Some($specified_by_url)
|
||||
);
|
||||
};
|
||||
|
||||
($ty:ty, $name:literal, $desc:literal) => {
|
||||
$crate::scalar_internal!($ty, $name, ::std::option::Option::Some($desc));
|
||||
$crate::scalar_internal!(
|
||||
$ty,
|
||||
$name,
|
||||
::std::option::Option::Some($desc),
|
||||
::std::option::Option::None
|
||||
);
|
||||
};
|
||||
|
||||
($ty:ty, $name:literal) => {
|
||||
$crate::scalar_internal!($ty, $name, ::std::option::Option::None);
|
||||
$crate::scalar_internal!(
|
||||
$ty,
|
||||
$name,
|
||||
::std::option::Option::None,
|
||||
::std::option::Option::None
|
||||
);
|
||||
};
|
||||
|
||||
($ty:ty) => {
|
||||
$crate::scalar_internal!($ty, ::std::stringify!($ty), ::std::option::Option::None);
|
||||
$crate::scalar_internal!(
|
||||
$ty,
|
||||
::std::stringify!($ty),
|
||||
::std::option::Option::None,
|
||||
::std::option::Option::None
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! scalar_internal {
|
||||
($ty:ty, $name:expr, $desc:expr) => {
|
||||
($ty:ty, $name:expr, $desc:expr, $specified_by_url:expr) => {
|
||||
impl $crate::Type for $ty {
|
||||
fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
|
||||
::std::borrow::Cow::Borrowed($name)
|
||||
|
@ -122,6 +149,7 @@ macro_rules! scalar_internal {
|
|||
description: $desc,
|
||||
is_valid: |value| <$ty as $crate::ScalarType>::is_valid(value),
|
||||
visible: ::std::option::Option::None,
|
||||
specified_by_url: $specified_by_url,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,6 +190,7 @@ where
|
|||
extends: false,
|
||||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ where
|
|||
extends: false,
|
||||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ impl Type for EmptyMutation {
|
|||
extends: false,
|
||||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ impl Type for EmptySubscription {
|
|||
extends: false,
|
||||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
6
src/types/external/chrono_tz.rs
vendored
6
src/types/external/chrono_tz.rs
vendored
|
@ -2,7 +2,11 @@ use chrono_tz::Tz;
|
|||
|
||||
use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
|
||||
|
||||
#[Scalar(internal, name = "TimeZone")]
|
||||
#[Scalar(
|
||||
internal,
|
||||
name = "TimeZone",
|
||||
specified_by_url = "http://www.iana.org/time-zones"
|
||||
)]
|
||||
impl ScalarType for Tz {
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match value {
|
||||
|
|
18
src/types/external/datetime.rs
vendored
18
src/types/external/datetime.rs
vendored
|
@ -5,7 +5,11 @@ use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
|
|||
/// Implement the DateTime<FixedOffset> scalar
|
||||
///
|
||||
/// The input/output is a string in RFC3339 format.
|
||||
#[Scalar(internal, name = "DateTime")]
|
||||
#[Scalar(
|
||||
internal,
|
||||
name = "DateTime",
|
||||
specified_by_url = "https://datatracker.ietf.org/doc/html/rfc3339"
|
||||
)]
|
||||
impl ScalarType for DateTime<FixedOffset> {
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match &value {
|
||||
|
@ -22,7 +26,11 @@ impl ScalarType for DateTime<FixedOffset> {
|
|||
/// Implement the DateTime<Local> scalar
|
||||
///
|
||||
/// The input/output is a string in RFC3339 format.
|
||||
#[Scalar(internal, name = "DateTime")]
|
||||
#[Scalar(
|
||||
internal,
|
||||
name = "DateTime",
|
||||
specified_by_url = "https://datatracker.ietf.org/doc/html/rfc3339"
|
||||
)]
|
||||
impl ScalarType for DateTime<Local> {
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match &value {
|
||||
|
@ -39,7 +47,11 @@ impl ScalarType for DateTime<Local> {
|
|||
/// Implement the DateTime<Utc> scalar
|
||||
///
|
||||
/// The input/output is a string in RFC3339 format.
|
||||
#[Scalar(internal, name = "DateTime")]
|
||||
#[Scalar(
|
||||
internal,
|
||||
name = "DateTime",
|
||||
specified_by_url = "https://datatracker.ietf.org/doc/html/rfc3339"
|
||||
)]
|
||||
impl ScalarType for DateTime<Utc> {
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
match &value {
|
||||
|
|
2
src/types/external/url.rs
vendored
2
src/types/external/url.rs
vendored
|
@ -2,7 +2,7 @@ use url::Url;
|
|||
|
||||
use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
|
||||
|
||||
#[Scalar(internal)]
|
||||
#[Scalar(internal, specified_by_url = "http://url.spec.whatwg.org/")]
|
||||
/// URL is a String implementing the [URL Standard](http://url.spec.whatwg.org/)
|
||||
impl ScalarType for Url {
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
|
|
6
src/types/external/uuid.rs
vendored
6
src/types/external/uuid.rs
vendored
|
@ -2,7 +2,11 @@ use uuid::Uuid;
|
|||
|
||||
use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
|
||||
|
||||
#[Scalar(internal, name = "UUID")]
|
||||
#[Scalar(
|
||||
internal,
|
||||
name = "UUID",
|
||||
specified_by_url = "http://tools.ietf.org/html/rfc4122"
|
||||
)]
|
||||
/// A UUID is a unique 128-bit number, stored as 16 octets. UUIDs are parsed as Strings
|
||||
/// within GraphQL. UUIDs are used to assign unique identifiers to entities without requiring a central
|
||||
/// allocating authority.
|
||||
|
|
|
@ -85,6 +85,7 @@ impl<T> Type for OutputJson<T> {
|
|||
description: None,
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{registry, InputType, InputValueError, InputValueResult, Type, Value}
|
|||
|
||||
/// Similar to `Option`, but it has three states, `undefined`, `null` and `x`.
|
||||
///
|
||||
/// **Reference:** <https://spec.graphql.org/June2018/#sec-Null-Value>
|
||||
/// **Reference:** <https://spec.graphql.org/October2021/#sec-Null-Value>
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
@ -50,6 +50,7 @@ impl<A: Type, B: Type> Type for MergedObject<A, B> {
|
|||
extends: false,
|
||||
keys: None,
|
||||
visible: None,
|
||||
is_subscription: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@ impl Type for Upload {
|
|||
description: None,
|
||||
is_valid: |value| matches!(value, Value::String(_)),
|
||||
visible: None,
|
||||
specified_by_url: Some("https://github.com/jaydenseric/graphql-multipart-request-spec"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -329,4 +329,9 @@ mod tests {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typename_in_subscription_root() {
|
||||
expect_fails_rule!(factory, "subscription { __typename }");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::futures_util::Stream;
|
||||
use crate::parser::types::ExecutableDocument;
|
||||
use crate::validation::visitor::{visit, RuleError, Visitor, VisitorContext};
|
||||
use crate::*;
|
||||
|
@ -345,8 +346,17 @@ impl MutationRoot {
|
|||
}
|
||||
}
|
||||
|
||||
static TEST_HARNESS: Lazy<Schema<QueryRoot, MutationRoot, EmptySubscription>> =
|
||||
Lazy::new(|| Schema::new(QueryRoot, MutationRoot, EmptySubscription));
|
||||
pub struct SubscriptionRoot;
|
||||
|
||||
#[Subscription(internal)]
|
||||
impl SubscriptionRoot {
|
||||
async fn values(&self) -> impl Stream<Item = i32> {
|
||||
futures_util::stream::once(async move { 10 })
|
||||
}
|
||||
}
|
||||
|
||||
static TEST_HARNESS: Lazy<Schema<QueryRoot, MutationRoot, SubscriptionRoot>> =
|
||||
Lazy::new(|| Schema::new(QueryRoot, MutationRoot, SubscriptionRoot));
|
||||
|
||||
pub(crate) fn validate<'a, V, F>(
|
||||
doc: &'a ExecutableDocument,
|
||||
|
|
|
@ -611,6 +611,17 @@ fn visit_selection<'a, V: Visitor<'a>>(
|
|||
visit_field(v, ctx, field);
|
||||
},
|
||||
);
|
||||
} else if ctx.current_type().map(|ty| match ty {
|
||||
MetaType::Object {
|
||||
is_subscription, ..
|
||||
} => *is_subscription,
|
||||
_ => false,
|
||||
}) == Some(true)
|
||||
{
|
||||
ctx.report_error(
|
||||
vec![field.pos],
|
||||
"Unknown field \"__typename\" on type \"Subscription\".",
|
||||
);
|
||||
}
|
||||
}
|
||||
Selection::FragmentSpread(fragment_spread) => {
|
||||
|
|
|
@ -11,7 +11,12 @@ mod test_mod {
|
|||
|
||||
#[tokio::test]
|
||||
pub async fn test_scalar_macro() {
|
||||
scalar!(test_mod::MyValue, "MV", "DESC");
|
||||
scalar!(
|
||||
test_mod::MyValue,
|
||||
"MV",
|
||||
"DESC",
|
||||
"https://tools.ietf.org/html/rfc4122"
|
||||
);
|
||||
|
||||
struct Query;
|
||||
|
||||
|
@ -26,7 +31,7 @@ pub async fn test_scalar_macro() {
|
|||
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute(r#"{ __type(name:"MV") { name description } }"#)
|
||||
.execute(r#"{ __type(name:"MV") { name description specifiedByURL } }"#)
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap()
|
||||
|
@ -35,6 +40,7 @@ pub async fn test_scalar_macro() {
|
|||
"__type": {
|
||||
"name": "MV",
|
||||
"description": "DESC",
|
||||
"specifiedByURL": "https://tools.ietf.org/html/rfc4122",
|
||||
}
|
||||
})
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue
Block a user