Add support for default values in input objects

This commit is contained in:
Magnus Hallin 2017-02-19 12:08:52 +01:00
parent e0c6b03171
commit 0b07dbe99a
4 changed files with 165 additions and 13 deletions

View file

@ -330,13 +330,34 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
None => return Err(GraphQLError::UnknownOperationName),
};
let default_variable_values = op.item.variable_definitions
.map(|defs| defs.item.items.iter().filter_map(
|&(ref name, ref def)| def.default_value.as_ref().map(
|i| (name.item.to_owned(), i.item.clone())))
.collect::<HashMap<String, InputValue>>());
let errors = RwLock::new(Vec::new());
let value;
{
let mut all_vars;
let mut final_vars = variables;
if let Some(defaults) = default_variable_values {
all_vars = variables.clone();
for (name, value) in defaults {
if !all_vars.contains_key(&name) {
all_vars.insert(name, value);
}
}
final_vars = &all_vars;
}
let executor = Executor {
fragments: &fragments.iter().map(|f| (f.item.name.item, &f.item)).collect(),
variables: variables,
variables: final_vars,
current_selection_set: Some(&op.item.selection_set[..]),
schema: &root_node.schema,
context: context,

View file

@ -57,6 +57,13 @@ graphql_input_object!(
}
);
graphql_input_object!(
#[derive(Debug)]
struct InputWithDefaults {
a = 123: i64,
}
);
graphql_object!(TestType: () |&self| {
field field_with_object_input(input: Option<TestInputObject>) -> String {
format!("{:?}", input)
@ -97,6 +104,10 @@ graphql_object!(TestType: () |&self| {
field example_input(arg: ExampleInputObject) -> String {
format!("a: {:?}, b: {:?}", arg.a, arg.b)
}
field input_with_defaults(arg: InputWithDefaults) -> String {
format!("a: {:?}", arg.a)
}
});
fn run_variable_query<F>(query: &str, vars: Variables, f: F)
@ -891,3 +902,46 @@ fn does_not_allow_null_variable_for_required_field() {
),
]));
}
#[test]
fn input_object_with_default_values() {
run_query(
r#"{ inputWithDefaults(arg: {a: 1}) }"#,
|result| {
assert_eq!(
result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#)));
});
run_variable_query(
r#"query q($var: Int!) { inputWithDefaults(arg: {a: $var}) }"#,
vec![
("var".to_owned(), InputValue::int(1)),
].into_iter().collect(),
|result| {
assert_eq!(
result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#)));
});
run_variable_query(
r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
vec![
].into_iter().collect(),
|result| {
assert_eq!(
result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#)));
});
run_variable_query(
r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
vec![
("var".to_owned(), InputValue::int(2)),
].into_iter().collect(),
|result| {
assert_eq!(
result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 2"#)));
});
}

View file

@ -45,17 +45,19 @@ macro_rules! graphql_input_object {
(
@generate_from_input_value,
$name:tt, $var:tt,
( $($field_name:ident : $field_type:ty $(as $descr:tt)* $(,)* ),* )
( $($field_name:ident $(= $default:tt)* : $field_type:ty $(as $descr:tt)* $(,)* ),* )
) => {
Some($name {
$( $field_name: {
let n: String = $crate::to_camel_case(stringify!($field_name));
let v: Option<&&$crate::InputValue> = $var.get(&n[..]);
if let Some(v) = v {
$crate::FromInputValue::from(v).unwrap()
} else {
$crate::FromInputValue::from(&$crate::InputValue::null()).unwrap()
println!("Found variable for {:?}: {:?} in {:?}", n, v, $var);
match v {
$( Some(&&InputValue::Null) | None if true => $default, )*
Some(v) => $crate::FromInputValue::from(v).unwrap(),
_ => $crate::FromInputValue::from(&$crate::InputValue::null()).unwrap()
}
} ),*
})
@ -65,26 +67,53 @@ macro_rules! graphql_input_object {
(
@generate_struct_fields,
( $($meta:tt)* ), ( $($pubmod:tt)* ), $name:tt,
( $($field_name:ident : $field_type:ty $(as $descr:tt)* $(,)* ),* )
( $($field_name:ident $(= $default:tt)* : $field_type:ty $(as $descr:tt)* $(,)* ),* )
) => {
$($meta)* $($pubmod)* struct $name {
$( $field_name: $field_type, )*
}
};
// Generate the input field meta list, i.e. &[Argument].
// Generate single field meta for field with default value
(
@generate_single_meta_field,
$reg:tt,
( $field_name:ident = $default:tt : $field_type:ty $(as $descr:tt)* )
) => {
graphql_input_object!(
@apply_description,
$($descr)*,
$reg.arg_with_default::<$field_type>(
&$crate::to_camel_case(stringify!($field_name)),
&$default))
};
// Generate single field meta for field without default value
(
@generate_single_meta_field,
$reg:tt,
( $field_name:ident : $field_type:ty $(as $descr:tt)* )
) => {
graphql_input_object!(
@apply_description,
$($descr)*,
$reg.arg::<$field_type>(
&$crate::to_camel_case(stringify!($field_name))))
};
// Generate the input field meta list, i.e. &[Argument] for
(
@generate_meta_fields,
$reg:tt,
( $($field_name:ident : $field_type:ty $(as $descr:tt)* $(,)* ),* )
( $($field_name:ident $(= $default:tt)* : $field_type:ty $(as $descr:tt)* $(,)* ),* )
) => {
&[
$(
graphql_input_object!(
@apply_description,
$($descr)*,
$reg.arg::<$field_type>(
&$crate::to_camel_case(stringify!($field_name))))
@generate_single_meta_field,
$reg,
( $field_name $(= $default)* : $field_type $(as $descr)* )
)
),*
]
};

View file

@ -78,6 +78,13 @@ graphql_input_object!(
}
);
graphql_input_object!(
struct FieldWithDefaults {
field_one = 123: i64,
field_two = 456: i64 as "The second field",
}
);
graphql_object!(Root: () |&self| {
field test_field(
a1: DefaultName,
@ -90,6 +97,7 @@ graphql_object!(Root: () |&self| {
a8: PublicWithDescription,
a9: NamedPublicWithDescription,
a10: NamedPublic,
a11: FieldWithDefaults,
) -> i64 {
0
}
@ -414,3 +422,43 @@ fn field_description_introspection() {
].into_iter().collect())));
});
}
#[test]
fn field_with_defaults_introspection() {
let doc = r#"
{
__type(name: "FieldWithDefaults") {
name
inputFields {
name
type {
name
}
defaultValue
}
}
}
"#;
run_type_info_query(doc, |type_info, fields| {
assert_eq!(type_info.get("name"), Some(&Value::string("FieldWithDefaults")));
assert_eq!(fields.len(), 2);
assert!(fields.contains(&Value::object(vec![
("name", Value::string("fieldOne")),
("type", Value::object(vec![
("name", Value::string("Int")),
].into_iter().collect())),
("defaultValue", Value::string("123")),
].into_iter().collect())));
assert!(fields.contains(&Value::object(vec![
("name", Value::string("fieldTwo")),
("type", Value::object(vec![
("name", Value::string("Int")),
].into_iter().collect())),
("defaultValue", Value::string("456")),
].into_iter().collect())));
});
}