Support directives on variables definitions (#1005, #1000)

This commit is contained in:
ilslv 2021-12-21 20:14:34 +03:00 committed by GitHub
parent 18b10af9f3
commit eb83b60c9a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 96 additions and 16 deletions

View file

@ -23,6 +23,7 @@
- Add `specified_by_url` attribute argument to `#[derive(GraphQLScalarValue)]` and `#[graphql_scalar]` macros. ([#1003](https://github.com/graphql-rust/juniper/pull/1003), [#1000](https://github.com/graphql-rust/juniper/pull/1000))
- Support `isRepeatable` field on directives. ([#1003](https://github.com/graphql-rust/juniper/pull/1003), [#1000](https://github.com/graphql-rust/juniper/pull/1000))
- Support `__Schema.description`, `__Type.specifiedByURL` and `__Directive.isRepeatable` fields in introspection. ([#1003](https://github.com/graphql-rust/juniper/pull/1003), [#1000](https://github.com/graphql-rust/juniper/pull/1000))
- Support directives on variables definitions. ([#1005](https://github.com/graphql-rust/juniper/pull/1005))
## Fixes

View file

@ -49,6 +49,7 @@ pub enum InputValue<S = DefaultScalarValue> {
pub struct VariableDefinition<'a, S> {
pub var_type: Spanning<Type<'a>>,
pub default_value: Option<Spanning<InputValue<S>>>,
pub directives: Option<Vec<Spanning<Directive<'a, S>>>>,
}
#[derive(Clone, PartialEq, Debug)]

View file

@ -451,6 +451,8 @@ where
None
};
let directives = parse_directives(parser, schema)?;
Ok(Spanning::start_end(
&start_pos,
&default_value
@ -462,6 +464,7 @@ where
VariableDefinition {
var_type,
default_value,
directives: directives.map(|s| s.item),
},
),
))

View file

@ -87,6 +87,8 @@ pub enum DirectiveLocation {
FragmentDefinition,
#[graphql(name = "FIELD_DEFINITION")]
FieldDefinition,
#[graphql(name = "VARIABLE_DEFINITION")]
VariableDefinition,
#[graphql(name = "FRAGMENT_SPREAD")]
FragmentSpread,
#[graphql(name = "INLINE_FRAGMENT")]
@ -588,27 +590,28 @@ where
impl fmt::Display for DirectiveLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
DirectiveLocation::Query => "query",
DirectiveLocation::Mutation => "mutation",
DirectiveLocation::Subscription => "subscription",
DirectiveLocation::Field => "field",
DirectiveLocation::FieldDefinition => "field definition",
DirectiveLocation::FragmentDefinition => "fragment definition",
DirectiveLocation::FragmentSpread => "fragment spread",
DirectiveLocation::InlineFragment => "inline fragment",
DirectiveLocation::Scalar => "scalar",
DirectiveLocation::EnumValue => "enum value",
f.write_str(match self {
Self::Query => "query",
Self::Mutation => "mutation",
Self::Subscription => "subscription",
Self::Field => "field",
Self::FieldDefinition => "field definition",
Self::FragmentDefinition => "fragment definition",
Self::FragmentSpread => "fragment spread",
Self::InlineFragment => "inline fragment",
Self::VariableDefinition => "variable definition",
Self::Scalar => "scalar",
Self::EnumValue => "enum value",
})
}
}
impl<'a, S> fmt::Display for TypeType<'a, S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
TypeType::Concrete(t) => f.write_str(t.name().unwrap()),
TypeType::List(ref i, _) => write!(f, "[{}]", i),
TypeType::NonNull(ref i) => write!(f, "{}!", i),
match self {
Self::Concrete(t) => f.write_str(t.name().unwrap()),
Self::List(i, _) => write!(f, "[{}]", i),
Self::NonNull(i) => write!(f, "{}!", i),
}
}
}

View file

@ -1056,6 +1056,12 @@ pub(crate) fn schema_introspection_result() -> Value {
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "VARIABLE_DEFINITION",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "FRAGMENT_SPREAD",
"description": null,
@ -2387,6 +2393,11 @@ pub(crate) fn schema_introspection_result_without_descriptions() -> Value {
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "VARIABLE_DEFINITION",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "FRAGMENT_SPREAD",
"isDeprecated": false,

View file

@ -1,5 +1,8 @@
use crate::{
ast::{Directive, Field, Fragment, FragmentSpread, InlineFragment, Operation, OperationType},
ast::{
Directive, Field, Fragment, FragmentSpread, InlineFragment, Operation, OperationType,
VariableDefinition,
},
parser::Spanning,
schema::model::DirectiveLocation,
validation::{ValidatorContext, Visitor},
@ -106,6 +109,24 @@ where
assert_eq!(top, Some(DirectiveLocation::InlineFragment));
}
fn enter_variable_definition(
&mut self,
_: &mut ValidatorContext<'a, S>,
_: &'a (Spanning<&'a str>, VariableDefinition<S>),
) {
self.location_stack
.push(DirectiveLocation::VariableDefinition);
}
fn exit_variable_definition(
&mut self,
_: &mut ValidatorContext<'a, S>,
_: &'a (Spanning<&'a str>, VariableDefinition<S>),
) {
let top = self.location_stack.pop();
assert_eq!(top, Some(DirectiveLocation::VariableDefinition));
}
fn enter_directive(
&mut self,
ctx: &mut ValidatorContext<'a, S>,
@ -206,6 +227,33 @@ mod tests {
);
}
#[test]
fn with_unknown_directive_on_var_definition() {
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"query Foo(
$var1: Int = 1 @skip(if: true) @unknown,
$var2: String @deprecated
) {
name
}"#,
&[
RuleError::new(
&misplaced_error_message("skip", &DirectiveLocation::VariableDefinition),
&[SourcePosition::new(42, 1, 31)],
),
RuleError::new(
&unknown_error_message("unknown"),
&[SourcePosition::new(58, 1, 47)],
),
RuleError::new(
&misplaced_error_message("deprecated", &DirectiveLocation::VariableDefinition),
&[SourcePosition::new(98, 2, 30)],
),
],
);
}
#[test]
fn with_many_unknown_directives() {
expect_fails_rule::<_, _, DefaultScalarValue>(

View file

@ -141,6 +141,19 @@ fn visit_variable_definitions<'a, S, V>(
visit_input_value(v, ctx, default_value);
}
if let Some(dirs) = &def.1.directives {
for directive in dirs {
let directive_arguments = ctx
.schema
.directive_by_name(directive.item.name.item)
.map(|d| &d.arguments);
v.enter_directive(ctx, directive);
visit_arguments(v, ctx, directive_arguments, &directive.item.arguments);
v.exit_directive(ctx, directive);
}
}
v.exit_variable_definition(ctx, def);
})
}