2020-05-09 09:55:04 +00:00
|
|
|
use crate::parser::ast::{Directive, Field};
|
2020-05-15 02:08:37 +00:00
|
|
|
use crate::registry::MetaTypeName;
|
2020-03-24 10:54:22 +00:00
|
|
|
use crate::validation::visitor::{Visitor, VisitorContext};
|
2020-05-10 02:59:51 +00:00
|
|
|
use crate::Positioned;
|
2020-03-10 12:35:25 +00:00
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct ProvidedNonNullArguments;
|
|
|
|
|
|
|
|
impl<'a> Visitor<'a> for ProvidedNonNullArguments {
|
2020-05-10 02:59:51 +00:00
|
|
|
fn enter_directive(
|
|
|
|
&mut self,
|
|
|
|
ctx: &mut VisitorContext<'a>,
|
|
|
|
directive: &'a Positioned<Directive>,
|
|
|
|
) {
|
2020-05-12 08:27:06 +00:00
|
|
|
if let Some(schema_directive) = ctx.registry.directives.get(directive.name.node) {
|
2020-03-10 12:35:25 +00:00
|
|
|
for arg in schema_directive.args.values() {
|
2020-05-15 02:08:37 +00:00
|
|
|
if MetaTypeName::create(&arg.ty).is_non_null()
|
2020-03-21 01:32:13 +00:00
|
|
|
&& arg.default_value.is_none()
|
|
|
|
&& directive
|
2020-03-10 12:35:25 +00:00
|
|
|
.arguments
|
|
|
|
.iter()
|
2020-05-12 08:27:06 +00:00
|
|
|
.find(|(name, _)| name.node == arg.name)
|
2020-03-10 12:35:25 +00:00
|
|
|
.is_none()
|
2020-03-21 01:32:13 +00:00
|
|
|
{
|
2020-05-09 09:55:04 +00:00
|
|
|
ctx.report_error(vec![directive.position()],
|
2020-03-10 12:35:25 +00:00
|
|
|
format!(
|
|
|
|
"Directive \"@{}\" argument \"{}\" of type \"{}\" is required but not provided",
|
|
|
|
directive.name, arg.name, arg.ty
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-10 02:59:51 +00:00
|
|
|
fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Positioned<Field>) {
|
2020-03-10 12:35:25 +00:00
|
|
|
if let Some(parent_type) = ctx.parent_type() {
|
|
|
|
if let Some(schema_field) = parent_type.field_by_name(&field.name) {
|
|
|
|
for arg in schema_field.args.values() {
|
2020-05-15 02:08:37 +00:00
|
|
|
if MetaTypeName::create(&arg.ty).is_non_null()
|
2020-03-21 01:32:13 +00:00
|
|
|
&& arg.default_value.is_none()
|
|
|
|
&& field
|
2020-03-10 12:35:25 +00:00
|
|
|
.arguments
|
|
|
|
.iter()
|
2020-05-12 08:27:06 +00:00
|
|
|
.find(|(name, _)| name.node == arg.name)
|
2020-03-10 12:35:25 +00:00
|
|
|
.is_none()
|
2020-03-21 01:32:13 +00:00
|
|
|
{
|
2020-05-09 09:55:04 +00:00
|
|
|
ctx.report_error(vec![field.position()],
|
2020-03-10 12:35:25 +00:00
|
|
|
format!(
|
|
|
|
r#"Field "{}" argument "{}" of type "{}" is required but not provided"#,
|
|
|
|
field.name, arg.name, parent_type.name()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-05 08:00:26 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::validation::test_harness::{expect_fails_rule, expect_passes_rule};
|
|
|
|
|
|
|
|
pub fn factory() -> ProvidedNonNullArguments {
|
|
|
|
ProvidedNonNullArguments
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn ignores_unknown_arguments() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
dog {
|
|
|
|
isHousetrained(unknownArgument: true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn arg_on_optional_arg() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
dog {
|
|
|
|
isHousetrained(atOtherHomes: true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn no_arg_on_optional_arg() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
dog {
|
|
|
|
isHousetrained
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multiple_args() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleReqs(req1: 1, req2: 2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multiple_args_reverse_order() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleReqs(req2: 2, req1: 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn no_args_on_multiple_optional() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleOpts
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn one_arg_on_multiple_optional() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleOpts(opt1: 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn second_arg_on_multiple_optional() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleOpts(opt2: 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn muliple_reqs_on_mixed_list() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleOptAndReq(req1: 3, req2: 4)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multiple_reqs_and_one_opt_on_mixed_list() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn all_reqs_on_opts_on_mixed_list() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_one_non_nullable_argument() {
|
|
|
|
expect_fails_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleReqs(req2: 2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_multiple_non_nullable_arguments() {
|
|
|
|
expect_fails_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleReqs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn incorrect_value_and_missing_argument() {
|
|
|
|
expect_fails_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
complicatedArgs {
|
|
|
|
multipleReqs(req1: "one")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn ignores_unknown_directives() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
dog @unknown
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn with_directives_of_valid_types() {
|
|
|
|
expect_passes_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
dog @include(if: true) {
|
|
|
|
name
|
|
|
|
}
|
|
|
|
human @skip(if: false) {
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn with_directive_with_missing_types() {
|
|
|
|
expect_fails_rule(
|
|
|
|
factory,
|
|
|
|
r#"
|
|
|
|
{
|
|
|
|
dog @include {
|
|
|
|
name @skip
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|