diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 0ddfa620..20e78044 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -48,7 +48,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi - Reworked [`chrono` crate] integration GraphQL scalars according to [graphql-scalars.dev] specs: ([#1010]) - Disabled `chrono` [Cargo feature] by default. - Removed `scalar-naivetime` [Cargo feature]. -- Removed lifetime parameter from `ParseError`, `GraphlQLError`, `GraphQLBatchRequest` and `GraphQLRequest`. ([#528]) +- Removed lifetime parameter from `ParseError`, `GraphlQLError`, `GraphQLBatchRequest` and `GraphQLRequest`. ([#1081], [#528]) ### Added @@ -72,8 +72,10 @@ All user visible changes to `juniper` crate will be documented in this file. Thi - Unsupported expressions in `graphql_value!` macro. ([#996], [#503]) - Incorrect GraphQL list coercion rules: `null` cannot be coerced to an `[Int!]!` or `[Int]!`. ([#1004]) - All procedural macros expansion inside `macro_rules!`. ([#1054], [#1051]) +- Incorrect input value coercion with defaults. ([#1080], [#1073]) [#503]: /../../issues/503 +[#528]: /../../issues/528 [#750]: /../../issues/750 [#798]: /../../issues/798 [#918]: /../../issues/918 @@ -101,6 +103,9 @@ All user visible changes to `juniper` crate will be documented in this file. Thi [#1054]: /../../pull/1054 [#1057]: /../../pull/1057 [#1060]: /../../pull/1060 +[#1073]: /../../issues/1073 +[#1080]: /../../pull/1080 +[#1081]: /../../pull/1081 [ba1ed85b]: /../../commit/ba1ed85b3c3dd77fbae7baf6bc4e693321a94083 diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index ea2c0fec..2336138b 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -295,25 +295,36 @@ impl InputValue { Self::Object(o) } - /// Resolve all variables to their values. + /// Resolves all variables of this [`InputValue`] to their actual `values`. + /// + /// If a variable is not present in the `values`: + /// - Returns [`None`] in case this is an [`InputValue::Variable`]. + /// - Skips field in case of an [`InputValue::Object`] field. + /// - Replaces with an [`InputValue::Null`] in case of an + /// [`InputValue::List`] element. + /// + /// This is done, because for an [`InputValue::Variable`] (or an + /// [`InputValue::Object`] field) a default value can be used later, if it's + /// provided. While on contrary, a single [`InputValue::List`] element + /// cannot have a default value. #[must_use] - pub fn into_const(self, vars: &Variables) -> Self + pub fn into_const(self, values: &Variables) -> Option where S: Clone, { match self { - Self::Variable(v) => vars.get(&v).map_or_else(InputValue::null, Clone::clone), - Self::List(l) => Self::List( + Self::Variable(v) => values.get(&v).cloned(), + Self::List(l) => Some(Self::List( l.into_iter() - .map(|s| s.map(|v| v.into_const(vars))) + .map(|s| s.map(|v| v.into_const(values).unwrap_or_else(Self::null))) .collect(), - ), - Self::Object(o) => Self::Object( + )), + Self::Object(o) => Some(Self::Object( o.into_iter() - .map(|(sk, sv)| (sk, sv.map(|v| v.into_const(vars)))) + .filter_map(|(sk, sv)| sv.and_then(|v| v.into_const(values)).map(|sv| (sk, sv))) .collect(), - ), - v => v, + )), + v => Some(v), } } diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 777d8062..887091c7 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -1227,9 +1227,6 @@ impl<'r, S: 'r> Registry<'r, S> { } /// Creates an [`Argument`] with the provided default `value`. - /// - /// When called with type `T`, the actual [`Argument`] will be given the - /// type `Option`. pub fn arg_with_default( &mut self, name: &str, @@ -1240,7 +1237,7 @@ impl<'r, S: 'r> Registry<'r, S> { T: GraphQLType + ToInputValue + FromInputValue, S: ScalarValue, { - Argument::new(name, self.get_type::>(info)).default_value(value.to_input_value()) + Argument::new(name, self.get_type::(info)).default_value(value.to_input_value()) } fn insert_placeholder(&mut self, name: Name, of_type: Type<'r>) { diff --git a/juniper/src/executor_tests/introspection/input_object.rs b/juniper/src/executor_tests/introspection/input_object.rs index 450f774d..cf8c0d20 100644 --- a/juniper/src/executor_tests/introspection/input_object.rs +++ b/juniper/src/executor_tests/introspection/input_object.rs @@ -462,6 +462,9 @@ async fn field_with_defaults_introspection() { name type { name + ofType { + name + } } defaultValue } @@ -477,12 +480,12 @@ async fn field_with_defaults_introspection() { assert_eq!(fields.len(), 2); assert!(fields.contains(&graphql_value!({ "name": "fieldOne", - "type": {"name": "Int"}, + "type": {"name": null, "ofType": {"name": "Int"}}, "defaultValue": "123", }))); assert!(fields.contains(&graphql_value!({ "name": "fieldTwo", - "type": {"name": "Int"}, + "type": {"name": null, "ofType": {"name": "Int"}}, "defaultValue": "456", }))); }) diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs index ed59efc7..e78995ec 100644 --- a/juniper/src/executor_tests/introspection/mod.rs +++ b/juniper/src/executor_tests/introspection/mod.rs @@ -444,9 +444,13 @@ async fn object_introspection() { "name": "second", "description": "The second number", "type": { - "name": "Int", - "kind": "SCALAR", - "ofType": null, + "name": null, + "kind": "NON_NULL", + "ofType": { + "name": "Int", + "kind": "SCALAR", + "ofType": null, + }, }, "defaultValue": "123", }], diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index 023bd61e..0073d0e2 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -75,6 +75,12 @@ impl TestType { format!("{:?}", input) } + fn nullable_field_with_default_argument_value( + #[graphql(default = "Hello World".to_owned())] input: Option, + ) -> String { + format!("{:?}", input) + } + fn field_with_nested_object_input(input: Option) -> String { format!("{:?}", input) } @@ -791,13 +797,14 @@ async fn default_argument_when_not_provided() { } #[tokio::test] -async fn default_argument_when_nullable_variable_not_provided() { - run_query( - r#"query q($input: String) { fieldWithDefaultArgumentValue(input: $input) }"#, +async fn provided_variable_overwrites_default_value() { + run_variable_query( + r#"query q($input: String!) { fieldWithDefaultArgumentValue(input: $input) }"#, + graphql_vars! {"input": "Overwritten"}, |result| { assert_eq!( result.get_field_value("fieldWithDefaultArgumentValue"), - Some(&graphql_value!(r#""Hello World""#)), + Some(&graphql_value!(r#""Overwritten""#)), ); }, ) @@ -805,14 +812,28 @@ async fn default_argument_when_nullable_variable_not_provided() { } #[tokio::test] -async fn default_argument_when_nullable_variable_set_to_null() { +async fn default_argument_when_nullable_variable_not_provided() { + run_query( + r#"query q($input: String) { nullableFieldWithDefaultArgumentValue(input: $input) }"#, + |result| { + assert_eq!( + result.get_field_value("nullableFieldWithDefaultArgumentValue"), + Some(&graphql_value!(r#"Some("Hello World")"#)), + ); + }, + ) + .await; +} + +#[tokio::test] +async fn null_when_nullable_variable_of_argument_with_default_value_set_to_null() { run_variable_query( - r#"query q($input: String) { fieldWithDefaultArgumentValue(input: $input) }"#, + r#"query q($input: String) { nullableFieldWithDefaultArgumentValue(input: $input) }"#, graphql_vars! {"input": null}, |result| { assert_eq!( - result.get_field_value("fieldWithDefaultArgumentValue"), - Some(&graphql_value!(r#""Hello World""#)), + result.get_field_value("nullableFieldWithDefaultArgumentValue"), + Some(&graphql_value!(r#"None"#)), ); }, ) diff --git a/juniper/src/parser/utils.rs b/juniper/src/parser/utils.rs index fdad1679..645c3014 100644 --- a/juniper/src/parser/utils.rs +++ b/juniper/src/parser/utils.rs @@ -81,7 +81,7 @@ impl Spanning { } } - /// Modify the contents of the spanned item + /// Modify the contents of the spanned item. pub fn map O>(self, f: F) -> Spanning { Spanning { item: f(self.item), @@ -89,6 +89,13 @@ impl Spanning { end: self.end, } } + + /// Modifies the contents of the spanned item in case `f` returns [`Some`], + /// or returns [`None`] otherwise. + pub fn and_then Option>(self, f: F) -> Option> { + let (start, end) = (self.start, self.end); + f(self.item).map(|item| Spanning { item, start, end }) + } } impl fmt::Display for Spanning { diff --git a/juniper/src/schema/schema.rs b/juniper/src/schema/schema.rs index 825bb43a..09f10bbd 100644 --- a/juniper/src/schema/schema.rs +++ b/juniper/src/schema/schema.rs @@ -211,13 +211,19 @@ impl<'a, S: ScalarValue + 'a> TypeType<'a, S> { } } - fn fields(&self, #[graphql(default)] include_deprecated: bool) -> Option>> { + fn fields( + &self, + #[graphql(default = false)] include_deprecated: Option, + ) -> Option>> { match self { TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. })) | TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) => Some( fields .iter() - .filter(|f| include_deprecated || !f.deprecation_status.is_deprecated()) + .filter(|f| { + include_deprecated.unwrap_or_default() + || !f.deprecation_status.is_deprecated() + }) .filter(|f| !f.name.starts_with("__")) .collect(), ), @@ -302,12 +308,18 @@ impl<'a, S: ScalarValue + 'a> TypeType<'a, S> { } } - fn enum_values(&self, #[graphql(default)] include_deprecated: bool) -> Option> { + fn enum_values( + &self, + #[graphql(default = false)] include_deprecated: Option, + ) -> Option> { match self { TypeType::Concrete(&MetaType::Enum(EnumMeta { ref values, .. })) => Some( values .iter() - .filter(|f| include_deprecated || !f.deprecation_status.is_deprecated()) + .filter(|f| { + include_deprecated.unwrap_or_default() + || !f.deprecation_status.is_deprecated() + }) .collect(), ), _ => None, diff --git a/juniper/src/types/async_await.rs b/juniper/src/types/async_await.rs index 80c653cf..ebb948ca 100644 --- a/juniper/src/types/async_await.rs +++ b/juniper/src/types/async_await.rs @@ -242,7 +242,9 @@ where f.arguments.as_ref().map(|m| { m.item .iter() - .map(|&(ref k, ref v)| (k.item, v.item.clone().into_const(exec_vars))) + .filter_map(|&(ref k, ref v)| { + v.item.clone().into_const(exec_vars).map(|v| (k.item, v)) + }) .collect() }), &meta_field.arguments, diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs index e43134f1..b46315b7 100644 --- a/juniper/src/types/base.rs +++ b/juniper/src/types/base.rs @@ -49,7 +49,6 @@ pub enum TypeKind { /// ## Input objects /// /// Represents complex values provided in queries _into_ the system. - #[graphql(name = "INPUT_OBJECT")] InputObject, /// ## List types @@ -63,7 +62,6 @@ pub enum TypeKind { /// /// In GraphQL, nullable types are the default. By putting a `!` after a\ /// type, it becomes non-nullable. - #[graphql(name = "NON_NULL")] NonNull, } @@ -89,7 +87,7 @@ impl<'a, S> Arguments<'a, S> { if let (Some(args), Some(meta_args)) = (&mut args, meta_args) { for arg in meta_args { let arg_name = arg.name.as_str(); - if args.get(arg_name).map_or(true, InputValue::is_null) { + if args.get(arg_name).is_none() { if let Some(val) = arg.default_value.as_ref() { args.insert(arg_name, val.clone()); } @@ -474,8 +472,8 @@ where f.arguments.as_ref().map(|m| { m.item .iter() - .map(|&(ref k, ref v)| { - (k.item, v.item.clone().into_const(exec_vars)) + .filter_map(|&(ref k, ref v)| { + v.item.clone().into_const(exec_vars).map(|v| (k.item, v)) }) .collect() }), @@ -608,7 +606,7 @@ where .arguments .iter() .flat_map(|m| m.item.get("if")) - .flat_map(|v| v.item.clone().into_const(vars).convert()) + .filter_map(|v| v.item.clone().into_const(vars)?.convert().ok()) .next() .unwrap(); diff --git a/juniper/src/types/subscriptions.rs b/juniper/src/types/subscriptions.rs index 29140579..e1f42fdf 100644 --- a/juniper/src/types/subscriptions.rs +++ b/juniper/src/types/subscriptions.rs @@ -316,7 +316,9 @@ where f.arguments.as_ref().map(|m| { m.item .iter() - .map(|&(ref k, ref v)| (k.item, v.item.clone().into_const(exec_vars))) + .filter_map(|&(ref k, ref v)| { + v.item.clone().into_const(exec_vars).map(|v| (k.item, v)) + }) .collect() }), &meta_field.arguments, diff --git a/juniper/src/types/utilities.rs b/juniper/src/types/utilities.rs index 2ed03b1f..2781418a 100644 --- a/juniper/src/types/utilities.rs +++ b/juniper/src/types/utilities.rs @@ -71,7 +71,7 @@ where let mut remaining_required_fields = input_fields .iter() .filter_map(|f| { - if f.arg_type.is_non_null() { + if f.arg_type.is_non_null() && f.default_value.is_none() { Some(&f.name) } else { None diff --git a/juniper/src/validation/rules/provided_non_null_arguments.rs b/juniper/src/validation/rules/provided_non_null_arguments.rs index 7c2cb266..eb76c150 100644 --- a/juniper/src/validation/rules/provided_non_null_arguments.rs +++ b/juniper/src/validation/rules/provided_non_null_arguments.rs @@ -26,6 +26,7 @@ where { for meta_arg in meta_args { if meta_arg.arg_type.is_non_null() + && meta_arg.default_value.is_none() && field .item .arguments diff --git a/juniper_codegen/src/common/default.rs b/juniper_codegen/src/common/default.rs index 03fb68a0..2354345a 100644 --- a/juniper_codegen/src/common/default.rs +++ b/juniper_codegen/src/common/default.rs @@ -57,9 +57,11 @@ impl ToTokens for Value { match self { Self::Default => quote! { ::std::default::Default::default() - } - .to_tokens(into), - Self::Expr(expr) => expr.to_tokens(into), + }, + Self::Expr(expr) => quote! { + (#expr).into() + }, } + .to_tokens(into) } } diff --git a/tests/integration/src/codegen/input_object_derive.rs b/tests/integration/src/codegen/input_object_derive.rs index 085815b7..9f537f92 100644 --- a/tests/integration/src/codegen/input_object_derive.rs +++ b/tests/integration/src/codegen/input_object_derive.rs @@ -1,6 +1,9 @@ //! Tests for `#[derive(GraphQLInputObject)]` macro. -use juniper::{execute, graphql_object, graphql_value, graphql_vars, GraphQLInputObject}; +use juniper::{ + execute, graphql_object, graphql_value, graphql_vars, parser::SourcePosition, GraphQLError, + GraphQLInputObject, RuleError, +}; use crate::util::schema; @@ -148,16 +151,56 @@ mod default_value { #[tokio::test] async fn resolves() { - const DOC: &str = r#"{ - x(point: { y: 20 }) - x2: x(point: { x: 20 }) + const DOC: &str = r#"query q($ve_num: Float!) { + literal_implicit_other_number: x(point: { y: 20 }) + literal_explicit_number: x(point: { x: 20 }) + literal_implicit_all: x(point: {}) + variable_explicit_number: x(point: { x: $ve_num }) }"#; let schema = schema(QueryRoot); + assert_eq!( + execute(DOC, None, &schema, &graphql_vars! {"ve_num": 40}, &()).await, + Ok(( + graphql_value!({ + "literal_implicit_other_number": 10.0, + "literal_explicit_number": 20.0, + "literal_implicit_all": 10.0, + "variable_explicit_number": 40.0, + }), + vec![], + )), + ); + } + + #[tokio::test] + async fn errs_on_explicit_null_literal() { + const DOC: &str = r#"{ x(point: { x: 20, y: null }) }"#; + + let schema = schema(QueryRoot); + assert_eq!( execute(DOC, None, &schema, &graphql_vars! {}, &()).await, - Ok((graphql_value!({"x": 10.0, "x2": 20.0}), vec![])), + Err(GraphQLError::ValidationError(vec![RuleError::new( + "Invalid value for argument \"point\", expected type \"Point2D!\"", + &[SourcePosition::new(11, 0, 11)], + )])) + ); + } + + #[tokio::test] + async fn errs_on_missing_variable() { + const DOC: &str = r#"query q($x: Float!){ x(point: { x: $x }) }"#; + + let schema = schema(QueryRoot); + + assert_eq!( + execute(DOC, None, &schema, &graphql_vars! {}, &()).await, + Err(GraphQLError::ValidationError(vec![RuleError::new( + "Variable \"$x\" of required type \"Float!\" was not provided.", + &[SourcePosition::new(8, 0, 8)], + )])) ); } @@ -196,6 +239,140 @@ mod default_value { let schema = schema(QueryRoot); + assert_eq!( + execute(DOC, None, &schema, &graphql_vars! {}, &()).await, + Ok(( + graphql_value!({"__type": {"inputFields": [{ + "name": "x", + "description": null, + "type": {"ofType": {"name": "Float"}}, + "defaultValue": "10", + }, { + "name": "y", + "description": null, + "type": {"ofType": {"name": "Float"}}, + "defaultValue": "10", + }]}}), + vec![], + )), + ); + } +} + +mod default_nullable_value { + use super::*; + + #[derive(GraphQLInputObject)] + struct Point2D { + #[graphql(default = 10.0)] + x: Option, + #[graphql(default = 10.0)] + y: Option, + } + + struct QueryRoot; + + #[graphql_object] + impl QueryRoot { + fn x(point: Point2D) -> Option { + point.x + } + } + + #[tokio::test] + async fn resolves() { + const DOC: &str = r#"query q( + $ve_num: Float, + $ve_null: Float, + $vi: Float, + $vde_num: Float = 40, + $vde_null: Float = 50, + $vdi: Float = 60, + ) { + literal_implicit_other_number: x(point: { y: 20 }) + literal_explicit_number: x(point: { x: 20 }) + literal_implicit_all: x(point: {}) + literal_explicit_null: x(point: { x: null }) + literal_implicit_other_null: x(point: { y: null }) + variable_explicit_number: x(point: { x: $ve_num }) + variable_explicit_null: x(point: { x: $ve_null }) + variable_implicit: x(point: { x: $vi }) + variable_default_explicit_number: x(point: { x: $vde_num }) + variable_default_explicit_null: x(point: { x: $vde_null }) + variable_default_implicit: x(point: { x: $vdi }) + }"#; + + let schema = schema(QueryRoot); + + assert_eq!( + execute( + DOC, + None, + &schema, + &graphql_vars! { + "ve_num": 30.0, + "ve_null": null, + "vde_num": 100, + "vde_null": null, + }, + &(), + ) + .await, + Ok(( + graphql_value!({ + "literal_implicit_other_number": 10.0, + "literal_explicit_number": 20.0, + "literal_implicit_all": 10.0, + "literal_explicit_null": null, + "literal_implicit_other_null": 10.0, + "variable_explicit_number": 30.0, + "variable_explicit_null": null, + "variable_implicit": 10.0, + "variable_default_explicit_number": 100.0, + "variable_default_explicit_null": null, + "variable_default_implicit": 60.0, + }), + vec![], + )), + ); + } + + #[tokio::test] + async fn is_graphql_input_object() { + const DOC: &str = r#"{ + __type(name: "Point2D") { + kind + } + }"#; + + let schema = schema(QueryRoot); + + assert_eq!( + execute(DOC, None, &schema, &graphql_vars! {}, &()).await, + Ok((graphql_value!({"__type": {"kind": "INPUT_OBJECT"}}), vec![])), + ); + } + + #[tokio::test] + async fn has_input_fields() { + const DOC: &str = r#"{ + __type(name: "Point2D") { + inputFields { + name + description + type { + name + ofType { + name + } + } + defaultValue + } + } + }"#; + + let schema = schema(QueryRoot); + assert_eq!( execute(DOC, None, &schema, &graphql_vars! {}, &()).await, Ok(( @@ -203,13 +380,13 @@ mod default_value { { "name": "x", "description": null, - "type": {"ofType": null}, + "type": {"name": "Float", "ofType": null}, "defaultValue": "10", }, { "name": "y", "description": null, - "type": {"ofType": null}, + "type": {"name": "Float", "ofType": null}, "defaultValue": "10", }, ]}}), diff --git a/tests/integration/src/codegen/interface_attr_trait.rs b/tests/integration/src/codegen/interface_attr_trait.rs index 150820fa..7892a027 100644 --- a/tests/integration/src/codegen/interface_attr_trait.rs +++ b/tests/integration/src/codegen/interface_attr_trait.rs @@ -1311,21 +1311,21 @@ mod default_argument { "args": [{ "name": "first", "defaultValue": r#""""#, - "type": {"name": "String", "ofType": null}, + "type": {"name": null, "ofType": {"name": "String"}}, }, { "name": "second", "defaultValue": r#""second""#, - "type": {"name": "String", "ofType": null}, + "type": {"name": null, "ofType": {"name": "String"}}, }, { "name": "third", "defaultValue": r#""t""#, - "type": {"name": "String", "ofType": null}, + "type": {"name": null, "ofType": {"name": "String"}}, }], }, { "args": [{ "name": "coord", "defaultValue": "{x: 1}", - "type": {"name": "Point", "ofType": null}, + "type": {"name": null, "ofType": {"name": "Point"}}, }], }]}}), vec![], diff --git a/tests/integration/src/codegen/object_attr.rs b/tests/integration/src/codegen/object_attr.rs index 9161cc55..5a10710c 100644 --- a/tests/integration/src/codegen/object_attr.rs +++ b/tests/integration/src/codegen/object_attr.rs @@ -1045,10 +1045,10 @@ mod default_argument { impl Human { fn id( #[graphql(default)] arg1: i32, - #[graphql(default = "second".to_string())] arg2: String, + #[graphql(default = "second".to_string())] arg2: Option, #[graphql(default = true)] r#arg3: bool, ) -> String { - format!("{}|{}&{}", arg1, arg2, r#arg3) + format!("{}|{:?}&{}", arg1, arg2, r#arg3) } fn info(#[graphql(default = Point { x: 1 })] coord: Point) -> i32 { @@ -1069,20 +1069,72 @@ mod default_argument { async fn resolves_id_field() { let schema = schema(QueryRoot); - for (input, expected) in &[ - ("{ human { id } }", "0|second&true"), - ("{ human { id(arg1: 1) } }", "1|second&true"), - (r#"{ human { id(arg2: "") } }"#, "0|&true"), - (r#"{ human { id(arg1: 2, arg2: "") } }"#, "2|&true"), + for (input, expected, vars) in &[ + ( + "{ human { id } }", + r#"0|Some("second")&true"#, + graphql_vars! {}, + ), + ( + "{ human { id(arg1: 1) } }", + r#"1|Some("second")&true"#, + graphql_vars! {}, + ), + ( + r#"{ human { id(arg2: "other") } }"#, + r#"0|Some("other")&true"#, + graphql_vars! {}, + ), + ( + "{ human { id(arg2: null) } }", + r#"0|None&true"#, + graphql_vars! {}, + ), + ( + "query q($arg2: String) { human { id(arg2: $arg2) } }", + r#"0|Some("second")&true"#, + graphql_vars! {}, + ), + ( + "query q($arg2: String) { human{ id(arg2: $arg2) } }", + r#"0|None&true"#, + graphql_vars! { "arg2": null }, + ), + ( + "query q($arg2: String) { human{ id(arg2: $arg2) } }", + r#"0|Some("other")&true"#, + graphql_vars! { "arg2": "other" }, + ), + ( + r#"query q($arg2: String = "other") { human { id(arg2: $arg2) } }"#, + r#"0|Some("other")&true"#, + graphql_vars! {}, + ), + ( + r#"query q($arg2: String = "other") { human { id(arg2: $arg2) } }"#, + r#"0|None&true"#, + graphql_vars! { "arg2": null }, + ), + ( + r#"query q($arg2: String = "other") { human { id(arg2: $arg2) } }"#, + r#"0|Some("hello")&true"#, + graphql_vars! { "arg2": "hello" }, + ), + ( + r#"{ human { id(arg1: 2, arg2: "") } }"#, + r#"2|Some("")&true"#, + graphql_vars! {}, + ), ( r#"{ human { id(arg1: 1, arg2: "", arg3: false) } }"#, - "1|&false", + r#"1|Some("")&false"#, + graphql_vars! {}, ), ] { let expected: &str = *expected; assert_eq!( - execute(*input, None, &schema, &graphql_vars! {}, &()).await, + execute(*input, None, &schema, &vars, &(),).await, Ok((graphql_value!({"human": {"id": expected}}), vec![])), ); } @@ -1133,7 +1185,7 @@ mod default_argument { "args": [{ "name": "arg1", "defaultValue": "0", - "type": {"name": "Int", "ofType": null}, + "type": {"name": null, "ofType": {"name": "Int"}}, }, { "name": "arg2", "defaultValue": r#""second""#, @@ -1141,13 +1193,13 @@ mod default_argument { }, { "name": "arg3", "defaultValue": "true", - "type": {"name": "Boolean", "ofType": null}, + "type": {"name": null, "ofType": {"name": "Boolean"}}, }], }, { "args": [{ "name": "coord", "defaultValue": "{x: 1}", - "type": {"name": "Point", "ofType": null}, + "type": {"name": null, "ofType": {"name": "Point"}}, }], }]}}), vec![], diff --git a/tests/integration/src/codegen/subscription_attr.rs b/tests/integration/src/codegen/subscription_attr.rs index 306b8c8e..47535fab 100644 --- a/tests/integration/src/codegen/subscription_attr.rs +++ b/tests/integration/src/codegen/subscription_attr.rs @@ -607,21 +607,21 @@ mod default_argument { "args": [{ "name": "arg1", "defaultValue": "0", - "type": {"name": "Int", "ofType": null}, + "type": {"name": null, "ofType": {"name": "Int"}}, }, { "name": "arg2", "defaultValue": r#""second""#, - "type": {"name": "String", "ofType": null}, + "type": {"name": null, "ofType": {"name": "String"}}, }, { "name": "arg3", "defaultValue": "true", - "type": {"name": "Boolean", "ofType": null}, + "type": {"name": null, "ofType": {"name": "Boolean"}}, }], }, { "args": [{ "name": "coord", "defaultValue": "{x: 1}", - "type": {"name": "Point", "ofType": null}, + "type": {"name": null, "ofType": {"name": "Point"}}, }], }]}}), vec![],