8000 fix validation failure with variables in multiple operations · async-graphql/async-graphql@56f0cda · GitHub
[go: up one dir, main page]

Skip to content

Commit 56f0cda

Browse files
committed
fix validation failure with variables in multiple operations
When a document with multiple operations has variables with different types, a validation error would occur, because the validation checked that the type of the variable matched the type of *all* places it's used. In multi-operation documents, variables only apply to the selected operation. Don't check argument types that are variables when in unselected operations.
1 parent 60e353e commit 56f0cda

File tree

8 files changed

+70
-6
lines changed

8 files changed

+70
-6
lines changed

src/schema.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,7 @@ pub(crate) async fn prepare_request(
872872
registry,
873873
&document,
874874
Some(&request.variables),
875+
request.operation_name.as_deref(),
875876
validation_mode,
876877
complexity,
877878
depth,

src/validation/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub(crate) fn check_rules(
4343
registry: &Registry,
4444
doc: &ExecutableDocument,
4545
variables: Option<&Variables>,
46+
operation_name: Option<&str>,
4647
mode: ValidationMode,
4748
limit_complexity: Option<usize>,
4849
limit_depth: Option<usize>,
@@ -51,9 +52,9 @@ pub(crate) fn check_rules(
5152
let mut complexity = 0;
5253
let mut depth = 0;
5354

55+
let mut ctx = VisitorContext::new(registry, doc, variables, operation_name);
5456
let errors = match mode {
5557
ValidationMode::Strict => {
56-
let mut ctx = VisitorContext::new(registry, doc, variables);
5758
let mut visitor = VisitorNil
5859
.with(rules::ArgumentsOfCorrectType::default())
5960
.with(rules::DefaultValuesOfCorrectType)
@@ -89,7 +90,6 @@ pub(crate) fn check_rules(
8990
ctx.errors
9091
}
9192
ValidationMode::Fast => {
92-
let mut ctx = VisitorContext::new(registry, doc, variables);
9393
let mut visitor = VisitorNil
9494
.with(rules::NoFragmentCycles::default())
9595
.with(rules::UploadFile)

src/validation/rules/arguments_of_correct_type.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,23 @@ use crate::{
1515
#[derive(Default)]
1616
pub struct ArgumentsOfCorrectType<'a> {
1717
current_args: Option<&'a IndexMap<String, MetaInputValue>>,
18+
in_unselected_operation: bool,
1819
}
1920

2021
impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> {
22+
fn enter_operation_definition(
23+
&mut self,
24+
ctx: &mut VisitorContext<'a>,
25+
name: Option<&'a Name>,
26+
_operation_definition: &'a Positioned<async_graphql_parser::types::OperationDefinition>,
27+
) {
28+
self.in_unselected_operation =
29+
ctx.operation_name
30+
.zip(name)
31+
.is_some_and(|(selected_operation, current_operation)| {
32+
selected_operation != current_operation.as_str()
33+
})
34+
}
2135
fn enter_directive(
2236
&mut self,
2337
ctx: &mut VisitorContext<'a>,
@@ -52,7 +66,11 @@ impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> {
5266
.node
5367
.clone()
5468
.into_const_with(|var_name| {
55-
ctx.variables
69+
// Don't check variables if we're in an unselected operation, since the
70+
// variables don't apply to this operation.
71+
(!self.in_unselected_operation)
72+
.then(|| ctx.variables)
73+
.flatten()
5674
.and_then(|variables| variables.get(&var_name))
5775
.cloned()
5876
.ok_or(())

src/validation/test_harness.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ where
385385
{
386386
let schema = TEST_HARNESS.get_or_init(|| Schema::new(Query, Mutation, Subscription));
387387
let registry = &schema.0.env.registry;
388-
let mut ctx = VisitorContext::new(registry, doc, None);
388+
let mut ctx = VisitorContext::new(registry, doc, None, None);
389389
let mut visitor = factory();
390390
visit(&mut visitor, &mut ctx, doc);
391391
if ctx.errors.is_empty() {

src/validation/visitor.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::{
1919
pub struct VisitorContext<'a> {
2020
pub(crate) registry: &'a registry::Registry,
2121
pub(crate) variables: Option<&'a Variables>,
22+
pub(crate) operation_name: Option<&'a str>,
2223
pub(crate) errors: Vec<RuleError>,
2324
type_stack: Vec<Option<&'a registry::MetaType>>,
2425
input_type: Vec<Option<MetaTypeName<'a>>>,
@@ -30,10 +31,12 @@ impl<'a> VisitorContext<'a> {
3031
registry: &'a registry::Registry,
3132
doc: &'a ExecutableDocument,
3233
variables: Option<&'a Variables>,
34+
operation_name: Option<&'a str>,
3335
) -> Self {
3436
Self {
3537
registry,
3638
variables,
39+
operation_name,
3740
errors: Default::default(),
3841
type_stack: Default::default(),
3942
input_type: Default::default(),

src/validation/visitors/complexity.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ mod tests {
194194
let registry =
195195
Schema::<Query, EmptyMutation, Subscription>::create_registry(Default::default());
196196
let doc = parse_query(query).unwrap();
197-
let mut ctx = VisitorContext::new(&registry, &doc, None);
197+
let mut ctx = VisitorContext::new(&registry, &doc, None, None);
198198
let mut complexity = 0;
199199
let mut complexity_calculate = ComplexityCalculate::new(&mut complexity);
200200
visit(&mut complexity_calculate, &mut ctx, &doc);

src/validation/visitors/depth.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ mod tests {
7878
let registry =
7979
Schema::<Query, EmptyMutation, EmptySubscription>::create_registry(Default::default());
8080
let doc = parse_query(query).unwrap();
81-
let mut ctx = VisitorContext::new(&registry, &doc, None);
81+
let mut ctx = VisitorContext::new(&registry, &doc, None, None);
8282
let mut depth = 0;
8383
let mut depth_calculate = DepthCalculate::new(&mut depth);
8484
visit(&mut depth_calculate, &mut ctx, &doc);

tests/variables.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,45 @@ pub async fn test_variables_invalid_type_with_value() {
409409
Some("Unknown type \"invalid\"")
410410
);
411411
}
412+
413+
/// Ensure we only validated parameters against their variables for the
414+
/// selected operation. Variables with the same name can have different
415+
/// types in different operations.
416+
#[tokio::test]
417+
pub async fn test_variables_different_type_in_different_operations() {
418+
struct Query;
419+
420+
#[Object]
421+
impl Query {
422+
pub async fn int_val(&self, value: i32) -> i32 {
423+
value
424+
}
425+
426+
pub async fn bool_val(&self, value: bool) -> bool {
427+
value
428+
}
429+
}
430+
431+
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
432+
let query = Request::new(
433+
r#"
434+
query QueryInt($input: Int!) {
435+
intVal(value: $input)
436+
}
437+
query QueryBool($input: Boolean!) {
438+
boolVal(value: $input)
439+
}
440+
"#,
441+
)
442+
.operation_name("QueryInt")
443+
.variables(Variables::from_value(value!({
444+
"input": 10,
445+
})));
446+
let resp = schema.execute(query).await;
447+
assert_eq!(
448+
resp.into_result().unwrap().data,
449+
value!({
450+
"intVal": 10,
451+
})
452+
);
453+
}

0 commit comments

Comments
 (0)
0