Skip to content

Commit

Permalink
Tests for interface schema validation
Browse files Browse the repository at this point in the history
Reviewed By: monicatang

Differential Revision: D58470038

fbshipit-source-id: a681cc70b2f5e3e022a375cc3ba52c5121e0d29b
  • Loading branch information
captbaritone authored and facebook-github-bot committed Jun 13, 2024
1 parent 8fd21e7 commit 767b0ea
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ interface IPerson {
name: String
}
==================================== OUTPUT ===================================
✖︎ Interface field 'IPerson.name' expected but 'Admin' does not provide it.
✖︎ Interface field 'IPerson.name' expected but type 'Admin' does not provide it.

AdminTypeResolvers.js:2:19
1 │ *
Expand Down
4 changes: 2 additions & 2 deletions compiler/crates/schema-validate/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ pub enum SchemaValidationError {
#[error("Type '{0}' can only implement '{1}' once.")]
DuplicateInterfaceImplementation(StringKey, InterfaceName),

#[error("Interface field '{0}.{1}' expected but '{2}' does not provide it.")]
InterfaceFieldNotProvided(InterfaceName, StringKey, StringKey),
#[error("Interface field '{0}.{1}' expected but {2} '{3}' does not provide it.")]
InterfaceFieldNotProvided(InterfaceName, StringKey, StringKey, StringKey),

#[error("Interface field '{0}.{1}' expects type '{2}' but '{3}.{1}' is of type '{4}'.")]
NotASubType(InterfaceName, StringKey, String, StringKey, String),
Expand Down
1 change: 1 addition & 0 deletions compiler/crates/schema-validate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ impl<'schema> ValidationContext<'schema> {
SchemaValidationError::InterfaceFieldNotProvided(
interface.name.item,
field_name,
type_.type_kind(),
typename,
),
*type_.location(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
==================================== INPUT ====================================
# Reports full type, not just inner type

interface Node {
id: ID!
}
Expand All @@ -7,22 +9,118 @@ type Pet implements Node {
id: ID # <--- Missing !
}

# Subtypes for fields are allowed

interface InterfaceA {
some_field: String
}

type TypeA implements InterfaceA {
some_field: String! # More specific type of String! should be allowed
}

# Checks multiple interfaces

interface InterfaceB {
some_field: String
}

interface InterfaceC {
another_field: String
}

type TypeB implements InterfaceB & InterfaceC {
some_field: String
# Oops! Does not implement InterfaceC's field!
}

# Checks interface implements interface

interface InterfaceD {
some_field: String
}

interface InterfaceE implements InterfaceD {
# Oops! Does not implement InterfaceD's field
another_field: String
}

# Checks multi-dimensional lists

interface InterfaceF {
some_field: [[[String]]]
}

type TypeC implements InterfaceF {
some_field: [[[Int]]] # Oops! Should be String in there
}

# Required for global validation

type Query {
node: Node
}
==================================== OUTPUT ===================================
✖︎ Interface field 'Node.id' expects type 'ID!' but 'Pet.id' is of type 'ID'.

validate_implements_interface.graphql:6:3
5 │ type Pet implements Node {
6 │ id: ID # <--- Missing !
validate_implements_interface.graphql:8:3
7 │ type Pet implements Node {
8 │ id: ID # <--- Missing !
│ ^^
7 │ }
9 │ }

ℹ︎ The interface field is defined here:

validate_implements_interface.graphql:2:3
1 │ interface Node {
2 │ id: ID!
validate_implements_interface.graphql:4:3
3 │ interface Node {
4 │ id: ID!
│ ^^
3 │ }
5 │ }

✖︎ Interface field 'InterfaceC.another_field' expected but type 'TypeB' does not provide it.

validate_implements_interface.graphql:31:6
30 │
31 │ type TypeB implements InterfaceB & InterfaceC {
│ ^^^^^
32 │ some_field: String

ℹ︎ The interface field is defined here:

validate_implements_interface.graphql:28:3
27 │ interface InterfaceC {
28 │ another_field: String
│ ^^^^^^^^^^^^^
29 │ }

✖︎ Interface field 'InterfaceD.some_field' expected but interface 'InterfaceE' does not provide it.

validate_implements_interface.graphql:42:11
41 │
42 │ interface InterfaceE implements InterfaceD {
│ ^^^^^^^^^^
43 │ # Oops! Does not implement InterfaceD's field

ℹ︎ The interface field is defined here:

validate_implements_interface.graphql:39:3
38 │ interface InterfaceD {
39 │ some_field: String
│ ^^^^^^^^^^
40 │ }

✖︎ Interface field 'InterfaceF.some_field' expects type '[[[String]]]' but 'TypeC.some_field' is of type '[[[Int]]]'.

validate_implements_interface.graphql:54:3
53 │ type TypeC implements InterfaceF {
54 │ some_field: [[[Int]]] # Oops! Should be String in there
│ ^^^^^^^^^^
55 │ }

ℹ︎ The interface field is defined here:

validate_implements_interface.graphql:50:3
49 │ interface InterfaceF {
50 │ some_field: [[[String]]]
│ ^^^^^^^^^^
51 │ }
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Reports full type, not just inner type

interface Node {
id: ID!
}
Expand All @@ -6,6 +8,54 @@ type Pet implements Node {
id: ID # <--- Missing !
}

# Subtypes for fields are allowed

interface InterfaceA {
some_field: String
}

type TypeA implements InterfaceA {
some_field: String! # More specific type of String! should be allowed
}

# Checks multiple interfaces

interface InterfaceB {
some_field: String
}

interface InterfaceC {
another_field: String
}

type TypeB implements InterfaceB & InterfaceC {
some_field: String
# Oops! Does not implement InterfaceC's field!
}

# Checks interface implements interface

interface InterfaceD {
some_field: String
}

interface InterfaceE implements InterfaceD {
# Oops! Does not implement InterfaceD's field
another_field: String
}

# Checks multi-dimensional lists

interface InterfaceF {
some_field: [[[String]]]
}

type TypeC implements InterfaceF {
some_field: [[[Int]]] # Oops! Should be String in there
}

# Required for global validation

type Query {
node: Node
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ type Fur {
│ ^^^^^^^
4 │ pet: Canine

✖︎ Interface field 'Canine.name' expected but 'Pet' does not provide it.
✖︎ Interface field 'Canine.name' expected but type 'Pet' does not provide it.

validate_object.graphql:13:6
12 │
Expand Down
11 changes: 10 additions & 1 deletion compiler/crates/schema/src/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::fmt;
use std::hash::Hash;
use std::slice::Iter;

use ::intern::string_key::Intern;
use ::intern::string_key::StringKey;
use common::ArgumentName;
use common::DirectiveName;
Expand All @@ -28,7 +29,7 @@ use common::WithLocation;
use graphql_syntax::ConstantValue;
use graphql_syntax::DirectiveLocation;
pub use interface::*;
use intern::string_key::Intern;
use intern::intern;
use lazy_static::lazy_static;

use crate::Schema;
Expand Down Expand Up @@ -626,12 +627,17 @@ impl IntoIterator for ArgumentDefinitions {
}

pub trait TypeWithFields {
fn type_kind(&self) -> StringKey;
fn fields(&self) -> &Vec<FieldID>;
fn interfaces(&self) -> &Vec<InterfaceID>;
fn location(&self) -> &Location;
}

impl TypeWithFields for Interface {
fn type_kind(&self) -> StringKey {
intern!("interface")
}

fn fields(&self) -> &Vec<FieldID> {
&self.fields
}
Expand All @@ -646,6 +652,9 @@ impl TypeWithFields for Interface {
}

impl TypeWithFields for Object {
fn type_kind(&self) -> StringKey {
intern!("type")
}
fn fields(&self) -> &Vec<FieldID> {
&self.fields
}
Expand Down

0 comments on commit 767b0ea

Please sign in to comment.