Skip to content

Commit

Permalink
WIP: fix shallow input args validation
Browse files Browse the repository at this point in the history
  • Loading branch information
karatakis committed Aug 26, 2024
1 parent 9a50f13 commit b894528
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 23 deletions.
9 changes: 1 addition & 8 deletions src/core/jit/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions src/core/jit/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
51 changes: 48 additions & 3 deletions src/core/jit/input_resolver.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -15,6 +15,16 @@ pub trait InputResolvable {
) -> Result<Self::Output, ResolveInputError>;
}

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;

Expand Down Expand Up @@ -45,7 +55,7 @@ impl<Input> InputResolver<Input> {
impl<Input, Output> InputResolver<Input>
where
Input: Clone,
Output: Clone,
Output: Clone + OutputTrait,
Input: InputResolvable<Output = Output>,
{
pub fn resolve_input(
Expand All @@ -57,7 +67,42 @@ where
.as_parent()
.iter()
.map(|field| field.clone().try_map(|value| value.resolve(variables)))
.collect::<Result<_, _>>()?;
.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::<Result<_, _>>()?;

Ok(Field { args, ..field })
}
Err(err) => Err(err),
})
.collect::<Result<Vec<_>, _>>()?;

Ok(OperationPlan::new(
new_fields,
Expand Down
18 changes: 18 additions & 0 deletions tests/core/snapshots/graphql-conformance-001.md_5.snap
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
}
23 changes: 11 additions & 12 deletions tests/execution/graphql-conformance-001.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
```

0 comments on commit b894528

Please sign in to comment.