parent
5332db0a4b
commit
0c8bcf582f
18 changed files with 366 additions and 72 deletions
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -295,25 +295,36 @@ impl<S> InputValue<S> {
|
|||
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<S>) -> Self
|
||||
pub fn into_const(self, values: &Variables<S>) -> Option<Self>
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T>`.
|
||||
pub fn arg_with_default<T>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
|
@ -1240,7 +1237,7 @@ impl<'r, S: 'r> Registry<'r, S> {
|
|||
T: GraphQLType<S> + ToInputValue<S> + FromInputValue<S>,
|
||||
S: ScalarValue,
|
||||
{
|
||||
Argument::new(name, self.get_type::<Option<T>>(info)).default_value(value.to_input_value())
|
||||
Argument::new(name, self.get_type::<T>(info)).default_value(value.to_input_value())
|
||||
}
|
||||
|
||||
fn insert_placeholder(&mut self, name: Name, of_type: Type<'r>) {
|
||||
|
|
|
@ -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",
|
||||
})));
|
||||
})
|
||||
|
|
|
@ -444,10 +444,14 @@ async fn object_introspection() {
|
|||
"name": "second",
|
||||
"description": "The second number",
|
||||
"type": {
|
||||
"name": null,
|
||||
"kind": "NON_NULL",
|
||||
"ofType": {
|
||||
"name": "Int",
|
||||
"kind": "SCALAR",
|
||||
"ofType": null,
|
||||
},
|
||||
},
|
||||
"defaultValue": "123",
|
||||
}],
|
||||
"type": {
|
||||
|
|
|
@ -75,6 +75,12 @@ impl TestType {
|
|||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
fn nullable_field_with_default_argument_value(
|
||||
#[graphql(default = "Hello World".to_owned())] input: Option<String>,
|
||||
) -> String {
|
||||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
fn field_with_nested_object_input(input: Option<TestNestedInputObject>) -> 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"#)),
|
||||
);
|
||||
},
|
||||
)
|
||||
|
|
|
@ -81,7 +81,7 @@ impl<T> Spanning<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Modify the contents of the spanned item
|
||||
/// Modify the contents of the spanned item.
|
||||
pub fn map<O, F: Fn(T) -> O>(self, f: F) -> Spanning<O> {
|
||||
Spanning {
|
||||
item: f(self.item),
|
||||
|
@ -89,6 +89,13 @@ impl<T> Spanning<T> {
|
|||
end: self.end,
|
||||
}
|
||||
}
|
||||
|
||||
/// Modifies the contents of the spanned item in case `f` returns [`Some`],
|
||||
/// or returns [`None`] otherwise.
|
||||
pub fn and_then<O, F: Fn(T) -> Option<O>>(self, f: F) -> Option<Spanning<O>> {
|
||||
let (start, end) = (self.start, self.end);
|
||||
f(self.item).map(|item| Spanning { item, start, end })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for Spanning<T> {
|
||||
|
|
|
@ -211,13 +211,19 @@ impl<'a, S: ScalarValue + 'a> TypeType<'a, S> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fields(&self, #[graphql(default)] include_deprecated: bool) -> Option<Vec<&Field<S>>> {
|
||||
fn fields(
|
||||
&self,
|
||||
#[graphql(default = false)] include_deprecated: Option<bool>,
|
||||
) -> Option<Vec<&Field<S>>> {
|
||||
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<Vec<&EnumValue>> {
|
||||
fn enum_values(
|
||||
&self,
|
||||
#[graphql(default = false)] include_deprecated: Option<bool>,
|
||||
) -> Option<Vec<&EnumValue>> {
|
||||
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -57,9 +57,11 @@ impl ToTokens for Value {
|
|||
match self {
|
||||
Self::Default => quote! {
|
||||
::std::default::Default::default()
|
||||
},
|
||||
Self::Expr(expr) => quote! {
|
||||
(#expr).into()
|
||||
},
|
||||
}
|
||||
.to_tokens(into),
|
||||
Self::Expr(expr) => expr.to_tokens(into),
|
||||
}
|
||||
.to_tokens(into)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<f64>,
|
||||
#[graphql(default = 10.0)]
|
||||
y: Option<f64>,
|
||||
}
|
||||
|
||||
struct QueryRoot;
|
||||
|
||||
#[graphql_object]
|
||||
impl QueryRoot {
|
||||
fn x(point: Point2D) -> Option<f64> {
|
||||
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",
|
||||
},
|
||||
]}}),
|
||||
|
|
|
@ -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![],
|
||||
|
|
|
@ -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<String>,
|
||||
#[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![],
|
||||
|
|
|
@ -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![],
|
||||
|
|
Loading…
Reference in a new issue