diff --git a/src/core/jit/context.rs b/src/core/jit/context.rs index f52b34223b..757f5d9ff9 100644 --- a/src/core/jit/context.rs +++ b/src/core/jit/context.rs @@ -70,16 +70,9 @@ impl<'a, Input: Clone, Output> Context<'a, Input, Output> { for arg in field.args.iter() { let name = arg.name.as_str(); - let value = arg - .value - .clone() - // TODO: default value resolution should happen in the InputResolver - .or_else(|| arg.default_value.clone()); + let value = arg.value.clone(); if let Some(value) = value { arg_map.insert(Name::new(name), value); - } else if !arg.type_of.is_nullable() { - // TODO: throw error here - todo!() } } Some(arg_map) diff --git a/src/core/jit/error.rs b/src/core/jit/error.rs index 8689b61ca4..2183bcbeeb 100644 --- a/src/core/jit/error.rs +++ b/src/core/jit/error.rs @@ -20,6 +20,11 @@ pub enum BuildError { pub enum ResolveInputError { #[error("Variable `{0}` is not defined")] VariableIsNotFound(String), + #[error("Argument `{arg_name}` for field `{field_name}` is required")] + ArgumentIsRequired { + arg_name: String, + field_name: String, + }, } #[derive(Error, Debug, Clone)] diff --git a/src/core/jit/input_resolver.rs b/src/core/jit/input_resolver.rs index 286070ed39..9c3bd20002 100644 --- a/src/core/jit/input_resolver.rs +++ b/src/core/jit/input_resolver.rs @@ -1,6 +1,6 @@ use async_graphql_value::{ConstValue, Value}; -use super::{OperationPlan, ResolveInputError, Variables}; +use super::{Arg, Field, OperationPlan, ResolveInputError, Variables}; /// Trait to represent conversion from some dynamic type (with variables) /// to the resolved variant based on the additional provided info. @@ -15,6 +15,16 @@ pub trait InputResolvable { ) -> Result; } +pub trait OutputTrait { + fn is_null_value(&self) -> bool; +} + +impl OutputTrait for ConstValue { + fn is_null_value(&self) -> bool { + self.eq(&ConstValue::Null) + } +} + impl InputResolvable for Value { type Output = ConstValue; @@ -45,7 +55,7 @@ impl InputResolver { impl InputResolver where Input: Clone, - Output: Clone, + Output: Clone + OutputTrait, Input: InputResolvable, { pub fn resolve_input( @@ -57,7 +67,42 @@ where .as_parent() .iter() .map(|field| field.clone().try_map(|value| value.resolve(variables))) - .collect::>()?; + .map(|field| match field { + Ok(field) => { + let args = field + .args + .into_iter() + .map(|arg| { + // TODO: this should recursively check the InputType for field presence + if arg + .value + .as_ref() + .map(|val| val.is_null_value()) + .unwrap_or(true) + && !arg.type_of.is_nullable() + { + let default_value = arg.default_value.clone(); + match default_value { + Some(value) => Ok(Arg { value: Some(value), ..arg }), + None => Err(ResolveInputError::ArgumentIsRequired { + arg_name: arg.name, + field_name: field.output_name.clone(), + }), + } + } else if arg.value.is_none() { + let default_value = arg.default_value.clone(); + Ok(Arg { value: default_value, ..arg }) + } else { + Ok(arg) + } + }) + .collect::>()?; + + Ok(Field { args, ..field }) + } + Err(err) => Err(err), + }) + .collect::, _>>()?; Ok(OperationPlan::new( new_fields, diff --git a/tests/core/snapshots/graphql-conformance-001.md_5.snap b/tests/core/snapshots/graphql-conformance-001.md_5.snap new file mode 100644 index 0000000000..92f6de675e --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-001.md_5.snap @@ -0,0 +1,18 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "Build error: ResolveInputError: Argument `id` for field `user` is required" + } + ] + } +} diff --git a/tests/execution/graphql-conformance-001.md b/tests/execution/graphql-conformance-001.md index c92262e53c..eaf8cbb478 100644 --- a/tests/execution/graphql-conformance-001.md +++ b/tests/execution/graphql-conformance-001.md @@ -114,16 +114,15 @@ type User { } # Negative: missing input -# TODO: expect error that user.id input is missing -# - method: POST -# url: http://localhost:8080/graphql -# body: -# query: | -# query { -# user { -# id -# name -# city -# } -# } +- method: POST + url: http://localhost:8080/graphql + body: + query: | + query { + user { + id + name + city + } + } ```