Skip to content

Commit 0b07dbe

Browse files
committed
Add support for default values in input objects
1 parent e0c6b03 commit 0b07dbe

File tree

4 files changed

+165
-13
lines changed

4 files changed

+165
-13
lines changed

src/executor.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,13 +330,34 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
330330
None => return Err(GraphQLError::UnknownOperationName),
331331
};
332332

333+
let default_variable_values = op.item.variable_definitions
334+
.map(|defs| defs.item.items.iter().filter_map(
335+
|&(ref name, ref def)| def.default_value.as_ref().map(
336+
|i| (name.item.to_owned(), i.item.clone())))
337+
.collect::<HashMap<String, InputValue>>());
338+
333339
let errors = RwLock::new(Vec::new());
334340
let value;
335341

336342
{
343+
let mut all_vars;
344+
let mut final_vars = variables;
345+
346+
if let Some(defaults) = default_variable_values {
347+
all_vars = variables.clone();
348+
349+
for (name, value) in defaults {
350+
if !all_vars.contains_key(&name) {
351+
all_vars.insert(name, value);
352+
}
353+
}
354+
355+
final_vars = &all_vars;
356+
}
357+
337358
let executor = Executor {
338359
fragments: &fragments.iter().map(|f| (f.item.name.item, &f.item)).collect(),
339-
variables: variables,
360+
variables: final_vars,
340361
current_selection_set: Some(&op.item.selection_set[..]),
341362
schema: &root_node.schema,
342363
context: context,

src/executor_tests/variables.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ graphql_input_object!(
5757
}
5858
);
5959

60+
graphql_input_object!(
61+
#[derive(Debug)]
62+
struct InputWithDefaults {
63+
a = 123: i64,
64+
}
65+
);
66+
6067
graphql_object!(TestType: () |&self| {
6168
field field_with_object_input(input: Option<TestInputObject>) -> String {
6269
format!("{:?}", input)
@@ -97,6 +104,10 @@ graphql_object!(TestType: () |&self| {
97104
field example_input(arg: ExampleInputObject) -> String {
98105
format!("a: {:?}, b: {:?}", arg.a, arg.b)
99106
}
107+
108+
field input_with_defaults(arg: InputWithDefaults) -> String {
109+
format!("a: {:?}", arg.a)
110+
}
100111
});
101112

102113
fn run_variable_query<F>(query: &str, vars: Variables, f: F)
@@ -891,3 +902,46 @@ fn does_not_allow_null_variable_for_required_field() {
891902
),
892903
]));
893904
}
905+
906+
#[test]
907+
fn input_object_with_default_values() {
908+
run_query(
909+
r#"{ inputWithDefaults(arg: {a: 1}) }"#,
910+
|result| {
911+
assert_eq!(
912+
result.get("inputWithDefaults"),
913+
Some(&Value::string(r#"a: 1"#)));
914+
});
915+
916+
run_variable_query(
917+
r#"query q($var: Int!) { inputWithDefaults(arg: {a: $var}) }"#,
918+
vec![
919+
("var".to_owned(), InputValue::int(1)),
920+
].into_iter().collect(),
921+
|result| {
922+
assert_eq!(
923+
result.get("inputWithDefaults"),
924+
Some(&Value::string(r#"a: 1"#)));
925+
});
926+
927+
run_variable_query(
928+
r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
929+
vec![
930+
].into_iter().collect(),
931+
|result| {
932+
assert_eq!(
933+
result.get("inputWithDefaults"),
934+
Some(&Value::string(r#"a: 1"#)));
935+
});
936+
937+
run_variable_query(
938+
r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
939+
vec![
940+
("var".to_owned(), InputValue::int(2)),
941+
].into_iter().collect(),
942+
|result| {
943+
assert_eq!(
944+
result.get("inputWithDefaults"),
945+
Some(&Value::string(r#"a: 2"#)));
946+
});
947+
}

src/macros/input_object.rs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,19 @@ macro_rules! graphql_input_object {
4545
(
4646
@generate_from_input_value,
4747
$name:tt, $var:tt,
48-
( $($field_name:ident : $field_type:ty $(as $descr:tt)* $(,)* ),* )
48+
( $($field_name:ident $(= $default:tt)* : $field_type:ty $(as $descr:tt)* $(,)* ),* )
4949
) => {
5050
Some($name {
5151
$( $field_name: {
5252
let n: String = $crate::to_camel_case(stringify!($field_name));
5353
let v: Option<&&$crate::InputValue> = $var.get(&n[..]);
5454

55-
if let Some(v) = v {
56-
$crate::FromInputValue::from(v).unwrap()
57-
} else {
58-
$crate::FromInputValue::from(&$crate::InputValue::null()).unwrap()
55+
println!("Found variable for {:?}: {:?} in {:?}", n, v, $var);
56+
57+
match v {
58+
$( Some(&&InputValue::Null) | None if true => $default, )*
59+
Some(v) => $crate::FromInputValue::from(v).unwrap(),
60+
_ => $crate::FromInputValue::from(&$crate::InputValue::null()).unwrap()
5961
}
6062
} ),*
6163
})
@@ -65,26 +67,53 @@ macro_rules! graphql_input_object {
6567
(
6668
@generate_struct_fields,
6769
( $($meta:tt)* ), ( $($pubmod:tt)* ), $name:tt,
68-
( $($field_name:ident : $field_type:ty $(as $descr:tt)* $(,)* ),* )
70+
( $($field_name:ident $(= $default:tt)* : $field_type:ty $(as $descr:tt)* $(,)* ),* )
6971
) => {
7072
$($meta)* $($pubmod)* struct $name {
7173
$( $field_name: $field_type, )*
7274
}
7375
};
7476

75-
// Generate the input field meta list, i.e. &[Argument].
77+
// Generate single field meta for field with default value
78+
(
79+
@generate_single_meta_field,
80+
$reg:tt,
81+
( $field_name:ident = $default:tt : $field_type:ty $(as $descr:tt)* )
82+
) => {
83+
graphql_input_object!(
84+
@apply_description,
85+
$($descr)*,
86+
$reg.arg_with_default::<$field_type>(
87+
&$crate::to_camel_case(stringify!($field_name)),
88+
&$default))
89+
};
90+
91+
// Generate single field meta for field without default value
92+
(
93+
@generate_single_meta_field,
94+
$reg:tt,
95+
( $field_name:ident : $field_type:ty $(as $descr:tt)* )
96+
) => {
97+
graphql_input_object!(
98+
@apply_description,
99+
$($descr)*,
100+
$reg.arg::<$field_type>(
101+
&$crate::to_camel_case(stringify!($field_name))))
102+
};
103+
104+
// Generate the input field meta list, i.e. &[Argument] for
76105
(
77106
@generate_meta_fields,
78107
$reg:tt,
79-
( $($field_name:ident : $field_type:ty $(as $descr:tt)* $(,)* ),* )
108+
( $($field_name:ident $(= $default:tt)* : $field_type:ty $(as $descr:tt)* $(,)* ),* )
80109
) => {
81110
&[
82111
$(
83112
graphql_input_object!(
84-
@apply_description,
85-
$($descr)*,
86-
$reg.arg::<$field_type>(
87-
&$crate::to_camel_case(stringify!($field_name))))
113+
@generate_single_meta_field,
114+
$reg,
115+
( $field_name $(= $default)* : $field_type $(as $descr)* )
116+
)
88117
),*
89118
]
90119
};

src/macros/tests/input_object.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ graphql_input_object!(
7878
}
7979
);
8080

81+
graphql_input_object!(
82+
struct FieldWithDefaults {
83+
field_one = 123: i64,
84+
field_two = 456: i64 as "The second field",
85+
}
86+
);
87+
8188
graphql_object!(Root: () |&self| {
8289
field test_field(
8390
a1: DefaultName,
@@ -90,6 +97,7 @@ graphql_object!(Root: () |&self| {
9097
a8: PublicWithDescription,
9198
a9: NamedPublicWithDescription,
9299
a10: NamedPublic,
100+
a11: FieldWithDefaults,
93101
) -> i64 {
94102
0
95103
}
@@ -414,3 +422,43 @@ fn field_description_introspection() {
414422
].into_iter().collect())));
415423
});
416424
}
425+
426+
#[test]
427+
fn field_with_defaults_introspection() {
428+
let doc = r#"
429+
{
430+
__type(name: "FieldWithDefaults") {
431+
name
432+
inputFields {
433+
name
434+
type {
435+
name
436+
}
437+
defaultValue
438+
}
439+
}
440+
}
441+
"#;
442+
443+
run_type_info_query(doc, |type_info, fields| {
444+
assert_eq!(type_info.get("name"), Some(&Value::string("FieldWithDefaults")));
445+
446+
assert_eq!(fields.len(), 2);
447+
448+
assert!(fields.contains(&Value::object(vec![
449+
("name", Value::string("fieldOne")),
450+
("type", Value::object(vec![
451+
("name", Value::string("Int")),
452+
].into_iter().collect())),
453+
("defaultValue", Value::string("123")),
454+
].into_iter().collect())));
455+
456+
assert!(fields.contains(&Value::object(vec![
457+
("name", Value::string("fieldTwo")),
458+
("type", Value::object(vec![
459+
("name", Value::string("Int")),
460+
].into_iter().collect())),
461+
("defaultValue", Value::string("456")),
462+
].into_iter().collect())));
463+
});
464+
}

0 commit comments

Comments
 (0)