Repository: apollographql/apollo-rs (rust)

Issue:
add interface definitions to compiler's context
- add interface definition to source database
     - add a way to link implements interface in both interface definition and object definition to the originally defined interface definition
- add validation for interface definitions
    - interface definitions must be unique
    - all fields in an interface definition must be unique
    - implements interface must refer to an interface that exists (no undefined interface definition)
    - transitively implemented interface definitions must be implemented on the implementing type
    - When defining an interface that implements another interface, the implementing interface must define each field that is specified by the implemented interface.  

Pull request:
add interface definition to compiler's context
- adds interface definitions to compiler's source database
- changes the signature for ImplementsInterfaces - it now holds the name of implements interfaces. Its parent (object type definition & interface definition) stores it in an Arc<Vec<ImplementsInterfaces>> instead.
- Implements Interfaces gets two getters - interface_definition(), which finds the defined interface definition type and interface() which returns a reference to implements interface name
- adds validations for interfaces:
    - interface definitions must have unique names. error: UniqueDefinition
    - interface definition must not implement itself. error: RecursiveDefinition
    - fields in an interface definition must be unique. error: UniqueValue
    - implements interface in an interface definition must be defined. error: UndefinedDefinition
    - transitively implemented interfaces must be defined on the implementing interface. error: TransitiveImplementedInterfaces
    - from the spec: "When defining an interface that implements another interface, the implementing interface must define each field that is specified by the implemented interface." error: Missing Field.

closes #240 

Patch:
diff --git a/crates/apollo-compiler/src/diagnostics.rs b/crates/apollo-compiler/src/diagnostics.rs
index 7a51942..6d2f581 100644
--- a/crates/apollo-compiler/src/diagnostics.rs
+++ b/crates/apollo-compiler/src/diagnostics.rs
@@ -13,14 +13,45 @@ pub enum ApolloDiagnostic {
 
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 pub enum ErrorDiagnostic {
+    BuiltInScalarDefinition {
+        message: String,
+        scalar: String,
+    },
     MissingIdent(String),
+    MissingField {
+        message: String,
+        field: String,
+        current_definition: String,
+        super_definition: String,
+    },
     QueryRootOperationType(String),
+    RecursiveDefinition {
+        message: String,
+        definition: String,
+    },
     SingleRootField(String),
+    ScalarSpecificationURL {
+        message: String,
+        scalar: String,
+    },
     SyntaxError {
         message: String,
         data: String,
         index: usize,
     },
+    TransitiveImplementedInterfaces {
+        message: String,
+        interface: String,
+        missing_implemented_interface: String,
+    },
+    UniqueDefinition {
+        message: String,
+        definition: String,
+    },
+    UnsupportedOperation {
+        message: String,
+        operation: Option<String>,
+    },
     UniqueOperationDefinition {
         message: String,
         operation: String,
@@ -34,17 +65,9 @@ pub enum ErrorDiagnostic {
         message: String,
         value: String,
     },
-    UnsupportedOperation {
+    UndefinedDefinition {
         message: String,
-        operation: Option<String>,
-    },
-    BuiltInScalarDefinition {
-        message: String,
-        scalar: String,
-    },
-    ScalarSpecificationURL {
-        message: String,
-        scalar: String,
+        missing_definition: String,
     },
     UndefinedVariable {
         message: String,
diff --git a/crates/apollo-compiler/src/queries/database.rs b/crates/apollo-compiler/src/queries/database.rs
index 810755a..a1f3919 100644
--- a/crates/apollo-compiler/src/queries/database.rs
+++ b/crates/apollo-compiler/src/queries/database.rs
@@ -50,6 +50,8 @@ pub trait SourceDatabase {
 
     fn unions(&self) -> Arc<Vec<UnionDefinition>>;
 
+    fn interfaces(&self) -> Arc<Vec<InterfaceDefinition>>;
+
     fn query_operations(&self) -> Operations;
 
     fn mutation_operations(&self) -> Operations;
@@ -66,6 +68,10 @@ pub trait SourceDatabase {
 
     fn find_object_type_by_name(&self, name: String) -> Option<Arc<ObjectTypeDefinition>>;
 
+    fn find_interface(&self, id: Uuid) -> Option<Arc<InterfaceDefinition>>;
+
+    fn find_interface_by_name(&self, name: String) -> Option<Arc<InterfaceDefinition>>;
+
     fn operation_definition_variables(&self, id: Uuid) -> Arc<HashSet<String>>;
 
     fn selection_variables(&self, id: Uuid) -> Arc<HashSet<String>>;
@@ -344,6 +350,27 @@ fn object_types(db: &dyn SourceDatabase) -> Arc<Vec<ObjectTypeDefinition>> {
     Arc::new(objects)
 }
 
+fn find_object_type(db: &dyn SourceDatabase, id: Uuid) -> Option<Arc<ObjectTypeDefinition>> {
+    db.object_types().iter().find_map(|object_type| {
+        if &id == object_type.id() {
+            return Some(Arc::new(object_type.clone()));
+        }
+        None
+    })
+}
+
+fn find_object_type_by_name(
+    db: &dyn SourceDatabase,
+    name: String,
+) -> Option<Arc<ObjectTypeDefinition>> {
+    db.object_types().iter().find_map(|object_type| {
+        if name == object_type.name() {
+            return Some(Arc::new(object_type.clone()));
+        }
+        None
+    })
+}
+
 fn scalars(db: &dyn SourceDatabase) -> Arc<Vec<ScalarDefinition>> {
     let scalars = db
         .definitions()
@@ -386,22 +413,36 @@ fn unions(db: &dyn SourceDatabase) -> Arc<Vec<UnionDefinition>> {
     Arc::new(unions)
 }
 
-fn find_object_type(db: &dyn SourceDatabase, id: Uuid) -> Option<Arc<ObjectTypeDefinition>> {
-    db.object_types().iter().find_map(|object_type| {
-        if &id == object_type.id() {
-            return Some(Arc::new(object_type.clone()));
+fn interfaces(db: &dyn SourceDatabase) -> Arc<Vec<InterfaceDefinition>> {
+    let interfaces = db
+        .definitions()
+        .iter()
+        .filter_map(|definition| match definition {
+            ast::Definition::InterfaceTypeDefinition(interface_def) => {
+                Some(interface_definition(interface_def.clone()))
+            }
+            _ => None,
+        })
+        .collect();
+    Arc::new(interfaces)
+}
+
+fn find_interface(db: &dyn SourceDatabase, id: Uuid) -> Option<Arc<InterfaceDefinition>> {
+    db.interfaces().iter().find_map(|interface| {
+        if &id == interface.id() {
+            return Some(Arc::new(interface.clone()));
         }
         None
     })
 }
 
-fn find_object_type_by_name(
+fn find_interface_by_name(
     db: &dyn SourceDatabase,
     name: String,
-) -> Option<Arc<ObjectTypeDefinition>> {
-    db.object_types().iter().find_map(|object_type| {
-        if name == object_type.name() {
-            return Some(Arc::new(object_type.clone()));
+) -> Option<Arc<InterfaceDefinition>> {
+    db.interfaces().iter().find_map(|interface| {
+        if name == interface.name() {
+            return Some(Arc::new(interface.clone()));
         }
         None
     })
@@ -592,6 +633,24 @@ fn union_member(db: &dyn SourceDatabase, member: ast::NamedType) -> UnionMember
     UnionMember { name, object_id }
 }
 
+fn interface_definition(interface_def: ast::InterfaceTypeDefinition) -> InterfaceDefinition {
+    let id = Uuid::new_v4();
+    let description = description(interface_def.description());
+    let name = name(interface_def.name());
+    let implements_interfaces = implements_interfaces(interface_def.implements_interfaces());
+    let directives = directives(interface_def.directives());
+    let fields_definition = fields_definition(interface_def.fields_definition());
+
+    InterfaceDefinition {
+        id,
+        description,
+        name,
+        implements_interfaces,
+        directives,
+        fields_definition,
+    }
+}
+
 fn add_object_type_id_to_schema(db: &dyn SourceDatabase) -> Arc<Vec<RootOperationTypeDefinition>> {
     // Schema Definition does not have to be present in the SDL if ObjectType name is
     // - Query
@@ -625,23 +684,21 @@ fn add_object_type_id_to_schema(db: &dyn SourceDatabase) -> Arc<Vec<RootOperatio
 
 fn implements_interfaces(
     implements_interfaces: Option<ast::ImplementsInterfaces>,
-) -> ImplementsInterfaces {
-    let interfaces: Vec<Type> = implements_interfaces
+) -> Arc<Vec<ImplementsInterface>> {
+    let interfaces: Vec<ImplementsInterface> = implements_interfaces
         .iter()
         .flat_map(|interfaces| {
-            let types: Vec<Type> = interfaces
+            let types: Vec<ImplementsInterface> = interfaces
                 .named_types()
-                .map(|n| Type::Named {
-                    name: n.name().expect("Name must have text").text().to_string(),
+                .map(|n| ImplementsInterface {
+                    interface: n.name().expect("Name must have text").text().to_string(),
                 })
                 .collect();
             types
         })
         .collect();
 
-    ImplementsInterfaces {
-        interfaces: Arc::new(interfaces),
-    }
+    Arc::new(interfaces)
 }
 
 fn fields_definition(
diff --git a/crates/apollo-compiler/src/queries/values.rs b/crates/apollo-compiler/src/queries/values.rs
index 0f29455..29c8d63 100644
--- a/crates/apollo-compiler/src/queries/values.rs
+++ b/crates/apollo-compiler/src/queries/values.rs
@@ -659,7 +659,7 @@ pub struct ObjectTypeDefinition {
     pub(crate) id: Uuid,
     pub(crate) description: Option<String>,
     pub(crate) name: String,
-    pub(crate) implements_interfaces: ImplementsInterfaces,
+    pub(crate) implements_interfaces: Arc<Vec<ImplementsInterface>>,
     pub(crate) directives: Arc<Vec<Directive>>,
     pub(crate) fields_definition: Arc<Vec<FieldDefinition>>,
 }
@@ -679,11 +679,29 @@ impl ObjectTypeDefinition {
     pub fn fields_definition(&self) -> &[FieldDefinition] {
         self.fields_definition.as_ref()
     }
+
+    /// Get a reference to object type definition's implements interfaces vector.
+    pub fn implements_interfaces(&self) -> &[ImplementsInterface] {
+        self.implements_interfaces.as_ref()
+    }
 }
 
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
-pub struct ImplementsInterfaces {
-    pub(crate) interfaces: Arc<Vec<Type>>,
+pub struct ImplementsInterface {
+    pub(crate) interface: String,
+}
+
+impl ImplementsInterface {
+    pub fn interface_definition(
+        &self,
+        db: &dyn SourceDatabase,
+    ) -> Option<Arc<InterfaceDefinition>> {
+        db.find_interface_by_name(self.interface.clone())
+    }
+
+    pub fn interface(&self) -> &str {
+        self.interface.as_ref()
+    }
 }
 
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
@@ -814,3 +832,35 @@ impl UnionMember {
         db.find_object_type(self.object_id?)
     }
 }
+
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub struct InterfaceDefinition {
+    pub(crate) id: Uuid,
+    pub(crate) description: Option<String>,
+    pub(crate) name: String,
+    pub(crate) implements_interfaces: Arc<Vec<ImplementsInterface>>,
+    pub(crate) directives: Arc<Vec<Directive>>,
+    pub(crate) fields_definition: Arc<Vec<FieldDefinition>>,
+}
+
+impl InterfaceDefinition {
+    /// Get the interface definition's id.
+    pub fn id(&self) -> &Uuid {
+        &self.id
+    }
+
+    /// Get a reference to the interface definition's name.
+    pub fn name(&self) -> &str {
+        self.name.as_ref()
+    }
+
+    /// Get a reference to interface definition's implements interfaces vector.
+    pub fn implements_interfaces(&self) -> &[ImplementsInterface] {
+        self.implements_interfaces.as_ref()
+    }
+
+    /// Get a reference to interface definition's fields.
+    pub fn fields_definition(&self) -> &[FieldDefinition] {
+        self.fields_definition.as_ref()
+    }
+}
diff --git a/crates/apollo-compiler/src/validation/interfaces.rs b/crates/apollo-compiler/src/validation/interfaces.rs
new file mode 100644
index 0000000..0c7843c
--- /dev/null
+++ b/crates/apollo-compiler/src/validation/interfaces.rs
@@ -0,0 +1,164 @@
+use std::collections::HashSet;
+
+use crate::{diagnostics::ErrorDiagnostic, ApolloDiagnostic, SourceDatabase};
+
+pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
+    let mut errors = Vec::new();
+
+    // Interface definitions must have unique names.
+    //
+    // Return a Unique Definition error in case of a duplicate name.
+    let mut seen = HashSet::new();
+    for interface in db.interfaces().iter() {
+        let name = interface.name();
+        if seen.contains(name) {
+            errors.push(ApolloDiagnostic::Error(ErrorDiagnostic::UniqueDefinition {
+                message: "Operation Definitions must have unique names".into(),
+                definition: name.to_string(),
+            }));
+        } else {
+            seen.insert(name);
+        }
+    }
+
+    // Interface must not implement itself.
+    //
+    // Return Recursive Definition error.
+    //
+    // NOTE(@lrlna): we should also check for more sneaky cyclic references for interfaces like this, for example:
+    //
+    // interface Node implements Named & Node {
+    //   id: ID!
+    //   name: String
+    // }
+    //
+    // interface Named implements Node & Named {
+    //   id: ID!
+    //   name: String
+    // }
+    for interface_def in db.interfaces().iter() {
+        let name = interface_def.name();
+        for implements_interface in interface_def.implements_interfaces() {
+            if let Some(interface) = implements_interface.interface_definition(db) {
+                if name == &(*interface.name()) {
+                    errors.push(ApolloDiagnostic::Error(
+                        ErrorDiagnostic::RecursiveDefinition {
+                            message: "Interface cannot implement itself".into(),
+                            definition: (*interface.name()).to_string(),
+                        },
+                    ));
+                }
+            }
+        }
+    }
+
+    // Fields in an Interface definition must be unique
+    for interface_def in db.interfaces().iter() {
+        let mut seen = HashSet::new();
+
+        let fields = interface_def.fields_definition();
+
+        for field in fields {
+            let field_name = field.name();
+            if seen.contains(&field_name) {
+                errors.push(ApolloDiagnostic::Error(ErrorDiagnostic::UniqueValue {
+                    message: "Fields must be unique".into(),
+                    value: field_name.into(),
+                }));
+            } else {
+                seen.insert(field_name);
+            }
+        }
+    }
+
+    let interfaces = db.interfaces();
+    let defined_interfaces: HashSet<String> = interfaces
+        .iter()
+        .map(|interface| interface.name().into())
+        .collect();
+    for interface_def in interfaces.iter() {
+        // Implements Interfaces must be defined.
+        //
+        // Returns Undefined Definition error.
+        let implements_interfaces: HashSet<String> = interface_def
+            .implements_interfaces()
+            .iter()
+            .map(|interface| interface.interface().into())
+            .collect();
+        let diff = implements_interfaces.difference(&defined_interfaces);
+        for undefined_interface in diff {
+            errors.push(ApolloDiagnostic::Error(
+                ErrorDiagnostic::UndefinedDefinition {
+                    message: "Implements Interface must be defined".into(),
+                    missing_definition: undefined_interface.into(),
+                },
+            ))
+        }
+
+        // Transitively implemented interfaces must be defined on an implementing
+        // type or interface.
+        //
+        // Returns Transitive Implemented Interfaces error.
+        let transitive_interfaces: HashSet<String> = interface_def
+            .implements_interfaces()
+            .iter()
+            .filter_map(|implements_interface| {
+                if let Some(interface) = implements_interface.interface_definition(db) {
+                    let child_interfaces: HashSet<String> = interface
+                        .implements_interfaces()
+                        .iter()
+                        .map(|interface| interface.interface().into())
+                        .collect();
+                    Some(child_interfaces)
+                } else {
+                    None
+                }
+            })
+            .flatten()
+            .collect();
+        let transitive_diff = transitive_interfaces.difference(&implements_interfaces);
+        for undefined_interface in transitive_diff {
+            errors.push(ApolloDiagnostic::Error(
+                ErrorDiagnostic::TransitiveImplementedInterfaces {
+                    message: "Transitively implemented interfaces must also be defined on an implementing interface".into(),
+                    interface: interface_def.name().into(),
+                    missing_implemented_interface: undefined_interface.into(),
+                },
+            ))
+        }
+
+        // When defining an interface that implements another interface, the
+        // implementing interface must define each field that is specified by
+        // the implemented interface.
+        //
+        // Returns a Missing Field error.
+        let fields: HashSet<String> = interface_def
+            .fields_definition()
+            .iter()
+            .map(|field| field.name().into())
+            .collect();
+        for implements_interface in interface_def.implements_interfaces().iter() {
+            if let Some(interface) = implements_interface.interface_definition(db) {
+                let implements_interface_fields: HashSet<String> = interface
+                    .fields_definition()
+                    .iter()
+                    .map(|field| field.name().into())
+                    .collect();
+
+                let field_diff = implements_interface_fields.difference(&fields);
+
+                for missing_field in field_diff {
+                    errors.push(ApolloDiagnostic::Error(ErrorDiagnostic::MissingField {
+                        message: "An interface must be a super-set of all interfaces it implements"
+                            .into(),
+                        field: missing_field.into(),
+                        current_definition: interface_def.name().into(),
+                        super_definition: interface.name().into(),
+                    }))
+                }
+            }
+        }
+    }
+
+    errors
+}
diff --git a/crates/apollo-compiler/src/validation/mod.rs b/crates/apollo-compiler/src/validation/mod.rs
index 0a4d66e..992d431 100644
--- a/crates/apollo-compiler/src/validation/mod.rs
+++ b/crates/apollo-compiler/src/validation/mod.rs
@@ -8,6 +8,9 @@ pub mod enums;
 pub mod scalars;
 pub mod unions;
 
+// composite nodes
+pub mod interfaces;
+
 // executable definitions
 pub mod operations;
 
@@ -36,6 +39,8 @@ impl<'a> Validator<'a> {
         self.errors.extend(enums::check(self.db));
         self.errors.extend(unions::check(self.db));
 
+        self.errors.extend(interfaces::check(self.db));
+
         self.errors.extend(operations::check(self.db));
         self.errors.extend(unused_variables::check(self.db));
 


Test patch:
diff --git a/crates/apollo-compiler/test_data/err/0022_duplicate_interface_definitions.graphql b/crates/apollo-compiler/test_data/err/0022_duplicate_interface_definitions.graphql
new file mode 100644
index 0000000..16793c9
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0022_duplicate_interface_definitions.graphql
@@ -0,0 +1,11 @@
+type Query implements NamedEntity {
+  name: String
+}
+
+interface NamedEntity {
+  name: String
+}
+
+interface NamedEntity {
+  firstName: String
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/err/0022_duplicate_interface_definitions.txt b/crates/apollo-compiler/test_data/err/0022_duplicate_interface_definitions.txt
new file mode 100644
index 0000000..7d59c57
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0022_duplicate_interface_definitions.txt
@@ -0,0 +1,1 @@
+[Error(UniqueDefinition { message: "Operation Definitions must have unique names", definition: "NamedEntity" })]
diff --git a/crates/apollo-compiler/test_data/err/0023_interface_definition_with_cyclic_implements_interfaces.graphql b/crates/apollo-compiler/test_data/err/0023_interface_definition_with_cyclic_implements_interfaces.graphql
new file mode 100644
index 0000000..ae7836d
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0023_interface_definition_with_cyclic_implements_interfaces.graphql
@@ -0,0 +1,7 @@
+type Query implements NamedEntity {
+  name: String
+}
+
+interface NamedEntity implements NamedEntity {
+  name: String
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/err/0023_interface_definition_with_cyclic_implements_interfaces.txt b/crates/apollo-compiler/test_data/err/0023_interface_definition_with_cyclic_implements_interfaces.txt
new file mode 100644
index 0000000..93f7249
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0023_interface_definition_with_cyclic_implements_interfaces.txt
@@ -0,0 +1,1 @@
+[Error(RecursiveDefinition { message: "Interface cannot implement itself", definition: "NamedEntity" })]
diff --git a/crates/apollo-compiler/test_data/err/0024_interface_definition_with_duplicate_fields.graphql b/crates/apollo-compiler/test_data/err/0024_interface_definition_with_duplicate_fields.graphql
new file mode 100644
index 0000000..4f54928
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0024_interface_definition_with_duplicate_fields.graphql
@@ -0,0 +1,8 @@
+type Query implements NamedEntity {
+  name: String
+}
+
+interface NamedEntity {
+  name: String
+  name: String
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/err/0024_interface_definition_with_duplicate_fields.txt b/crates/apollo-compiler/test_data/err/0024_interface_definition_with_duplicate_fields.txt
new file mode 100644
index 0000000..4fe52a4
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0024_interface_definition_with_duplicate_fields.txt
@@ -0,0 +1,1 @@
+[Error(UniqueValue { message: "Fields must be unique", value: "name" })]
diff --git a/crates/apollo-compiler/test_data/err/0025_interface_definition_with_missing_transitive_fields.graphql b/crates/apollo-compiler/test_data/err/0025_interface_definition_with_missing_transitive_fields.graphql
new file mode 100644
index 0000000..5645185
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0025_interface_definition_with_missing_transitive_fields.graphql
@@ -0,0 +1,17 @@
+type Query implements Node {
+    ud: ID!
+}
+
+interface Node {
+  id: ID!
+}
+
+interface Resource implements Node {
+  id: ID!
+  width: Int
+}
+
+interface Image implements Resource & Node {
+  id: ID!
+  thumbnail: String
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/err/0025_interface_definition_with_missing_transitive_fields.txt b/crates/apollo-compiler/test_data/err/0025_interface_definition_with_missing_transitive_fields.txt
new file mode 100644
index 0000000..6f79d26
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0025_interface_definition_with_missing_transitive_fields.txt
@@ -0,0 +1,1 @@
+[Error(MissingField { message: "An interface must be a super-set of all interfaces it implements", field: "width", current_definition: "Image", super_definition: "Resource" })]
diff --git a/crates/apollo-compiler/test_data/err/0026_interface_definition_with_missing_implemetns_interface.graphql b/crates/apollo-compiler/test_data/err/0026_interface_definition_with_missing_implemetns_interface.graphql
new file mode 100644
index 0000000..fdbf949
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0026_interface_definition_with_missing_implemetns_interface.graphql
@@ -0,0 +1,20 @@
+type Query implements Node {
+    ud: ID!
+}
+
+interface Node {
+  id: ID!
+}
+
+interface Resource implements Node {
+  id: ID!
+  url: String
+  width: Int
+}
+
+interface Image implements Resource {
+  id: ID!
+  url: String
+  width: Int
+  thumbnail: String
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/err/0026_interface_definition_with_missing_implemetns_interface.txt b/crates/apollo-compiler/test_data/err/0026_interface_definition_with_missing_implemetns_interface.txt
new file mode 100644
index 0000000..41455cc
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0026_interface_definition_with_missing_implemetns_interface.txt
@@ -0,0 +1,1 @@
+[Error(TransitiveImplementedInterfaces { message: "Transitively implemented interfaces must also be defined on an implementing interface", interface: "Image", missing_implemented_interface: "Node" })]
diff --git a/crates/apollo-compiler/test_data/err/0027_interface_definition_with_undefined_implements_interface.graphql b/crates/apollo-compiler/test_data/err/0027_interface_definition_with_undefined_implements_interface.graphql
new file mode 100644
index 0000000..af8c318
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0027_interface_definition_with_undefined_implements_interface.graphql
@@ -0,0 +1,15 @@
+type Query implements Node {
+    ud: ID!
+}
+
+interface Resource {
+  id: ID!
+  width: Int
+}
+
+interface Image implements Resource & Url {
+  id: ID!
+  url: String
+  width: Int
+  thumbnail: String
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/err/0027_interface_definition_with_undefined_implements_interface.txt b/crates/apollo-compiler/test_data/err/0027_interface_definition_with_undefined_implements_interface.txt
new file mode 100644
index 0000000..e1c1388
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0027_interface_definition_with_undefined_implements_interface.txt
@@ -0,0 +1,1 @@
+[Error(UndefinedDefinition { message: "Implements Interface must be defined", missing_definition: "Url" })]
diff --git a/crates/apollo-compiler/test_data/err/0028_interface_definition_with_missing_fields_implements_intrefaces_undefined_interfaces.graphql b/crates/apollo-compiler/test_data/err/0028_interface_definition_with_missing_fields_implements_intrefaces_undefined_interfaces.graphql
new file mode 100644
index 0000000..a3715a0
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0028_interface_definition_with_missing_fields_implements_intrefaces_undefined_interfaces.graphql
@@ -0,0 +1,17 @@
+type Query implements Node {
+    ud: ID!
+}
+
+interface Node {
+  id: ID!
+}
+
+interface Resource implements Node {
+  id: ID!
+  width: Int
+}
+
+interface Image implements Resource & Url{
+  id: ID!
+  thumbnail: String
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/err/0028_interface_definition_with_missing_fields_implements_intrefaces_undefined_interfaces.txt b/crates/apollo-compiler/test_data/err/0028_interface_definition_with_missing_fields_implements_intrefaces_undefined_interfaces.txt
new file mode 100644
index 0000000..f68a9d9
--- /dev/null
+++ b/crates/apollo-compiler/test_data/err/0028_interface_definition_with_missing_fields_implements_intrefaces_undefined_interfaces.txt
@@ -0,0 +1,1 @@
+[Error(UndefinedDefinition { message: "Implements Interface must be defined", missing_definition: "Url" }), Error(TransitiveImplementedInterfaces { message: "Transitively implemented interfaces must also be defined on an implementing interface", interface: "Image", missing_implemented_interface: "Node" }), Error(MissingField { message: "An interface must be a super-set of all interfaces it implements", field: "width", current_definition: "Image", super_definition: "Resource" })]
diff --git a/crates/apollo-compiler/test_data/ok/0007_schema_with_interface_definition.graphql b/crates/apollo-compiler/test_data/ok/0007_schema_with_interface_definition.graphql
new file mode 100644
index 0000000..cacd023
--- /dev/null
+++ b/crates/apollo-compiler/test_data/ok/0007_schema_with_interface_definition.graphql
@@ -0,0 +1,20 @@
+type Query implements Node {
+    id: ID!
+}
+
+interface Node {
+  id: ID!
+}
+
+interface Resource implements Node {
+  id: ID!
+  width: Int
+  height: Int
+}
+
+interface Image implements Resource & Node {
+  id: ID!
+  width: Int
+  height: Int
+  thumbnail: String
+}
\ No newline at end of file
diff --git a/crates/apollo-compiler/test_data/ok/0007_schema_with_interface_definition.txt b/crates/apollo-compiler/test_data/ok/0007_schema_with_interface_definition.txt
new file mode 100644
index 0000000..52c86e1
--- /dev/null
+++ b/crates/apollo-compiler/test_data/ok/0007_schema_with_interface_definition.txt
@@ -0,0 +1,161 @@
+- DOCUMENT@0..254
+    - OBJECT_TYPE_DEFINITION@0..44
+        - type_KW@0..4 "type"
+        - WHITESPACE@4..5 " "
+        - NAME@5..11
+            - IDENT@5..10 "Query"
+            - WHITESPACE@10..11 " "
+        - IMPLEMENTS_INTERFACES@11..27
+            - implements_KW@11..21 "implements"
+            - WHITESPACE@21..22 " "
+            - NAMED_TYPE@22..27
+                - NAME@22..27
+                    - IDENT@22..26 "Node"
+                    - WHITESPACE@26..27 " "
+        - FIELDS_DEFINITION@27..44
+            - L_CURLY@27..28 "{"
+            - WHITESPACE@28..33 "\n    "
+            - FIELD_DEFINITION@33..41
+                - NAME@33..35
+                    - IDENT@33..35 "id"
+                - COLON@35..36 ":"
+                - WHITESPACE@36..37 " "
+                - NON_NULL_TYPE@37..41
+                    - NAMED_TYPE@37..39
+                        - NAME@37..39
+                            - IDENT@37..39 "ID"
+                    - BANG@39..40 "!"
+                    - WHITESPACE@40..41 "\n"
+            - R_CURLY@41..42 "}"
+            - WHITESPACE@42..44 "\n\n"
+    - INTERFACE_TYPE_DEFINITION@44..74
+        - interface_KW@44..53 "interface"
+        - WHITESPACE@53..54 " "
+        - NAME@54..59
+            - IDENT@54..58 "Node"
+            - WHITESPACE@58..59 " "
+        - FIELDS_DEFINITION@59..74
+            - L_CURLY@59..60 "{"
+            - WHITESPACE@60..63 "\n  "
+            - FIELD_DEFINITION@63..71
+                - NAME@63..65
+                    - IDENT@63..65 "id"
+                - COLON@65..66 ":"
+                - WHITESPACE@66..67 " "
+                - NON_NULL_TYPE@67..71
+                    - NAMED_TYPE@67..69
+                        - NAME@67..69
+                            - IDENT@67..69 "ID"
+                    - BANG@69..70 "!"
+                    - WHITESPACE@70..71 "\n"
+            - R_CURLY@71..72 "}"
+            - WHITESPACE@72..74 "\n\n"
+    - INTERFACE_TYPE_DEFINITION@74..151
+        - interface_KW@74..83 "interface"
+        - WHITESPACE@83..84 " "
+        - NAME@84..93
+            - IDENT@84..92 "Resource"
+            - WHITESPACE@92..93 " "
+        - IMPLEMENTS_INTERFACES@93..109
+            - implements_KW@93..103 "implements"
+            - WHITESPACE@103..104 " "
+            - NAMED_TYPE@104..109
+                - NAME@104..109
+                    - IDENT@104..108 "Node"
+                    - WHITESPACE@108..109 " "
+        - FIELDS_DEFINITION@109..151
+            - L_CURLY@109..110 "{"
+            - WHITESPACE@110..113 "\n  "
+            - FIELD_DEFINITION@113..123
+                - NAME@113..115
+                    - IDENT@113..115 "id"
+                - COLON@115..116 ":"
+                - WHITESPACE@116..117 " "
+                - NON_NULL_TYPE@117..123
+                    - NAMED_TYPE@117..119
+                        - NAME@117..119
+                            - IDENT@117..119 "ID"
+                    - BANG@119..120 "!"
+                    - WHITESPACE@120..123 "\n  "
+            - FIELD_DEFINITION@123..136
+                - NAME@123..128
+                    - IDENT@123..128 "width"
+                - COLON@128..129 ":"
+                - WHITESPACE@129..130 " "
+                - NAMED_TYPE@130..133
+                    - NAME@130..133
+                        - IDENT@130..133 "Int"
+                - WHITESPACE@133..136 "\n  "
+            - FIELD_DEFINITION@136..148
+                - NAME@136..142
+                    - IDENT@136..142 "height"
+                - COLON@142..143 ":"
+                - WHITESPACE@143..144 " "
+                - NAMED_TYPE@144..147
+                    - NAME@144..147
+                        - IDENT@144..147 "Int"
+                - WHITESPACE@147..148 "\n"
+            - R_CURLY@148..149 "}"
+            - WHITESPACE@149..151 "\n\n"
+    - INTERFACE_TYPE_DEFINITION@151..254
+        - interface_KW@151..160 "interface"
+        - WHITESPACE@160..161 " "
+        - NAME@161..167
+            - IDENT@161..166 "Image"
+            - WHITESPACE@166..167 " "
+        - IMPLEMENTS_INTERFACES@167..194
+            - implements_KW@167..177 "implements"
+            - WHITESPACE@177..178 " "
+            - NAMED_TYPE@178..187
+                - NAME@178..187
+                    - IDENT@178..186 "Resource"
+                    - WHITESPACE@186..187 " "
+            - AMP@187..188 "&"
+            - WHITESPACE@188..189 " "
+            - NAMED_TYPE@189..194
+                - NAME@189..194
+                    - IDENT@189..193 "Node"
+                    - WHITESPACE@193..194 " "
+        - FIELDS_DEFINITION@194..254
+            - L_CURLY@194..195 "{"
+            - WHITESPACE@195..198 "\n  "
+            - FIELD_DEFINITION@198..208
+                - NAME@198..200
+                    - IDENT@198..200 "id"
+                - COLON@200..201 ":"
+                - WHITESPACE@201..202 " "
+                - NON_NULL_TYPE@202..208
+                    - NAMED_TYPE@202..204
+                        - NAME@202..204
+                            - IDENT@202..204 "ID"
+                    - BANG@204..205 "!"
+                    - WHITESPACE@205..208 "\n  "
+            - FIELD_DEFINITION@208..221
+                - NAME@208..213
+                    - IDENT@208..213 "width"
+                - COLON@213..214 ":"
+                - WHITESPACE@214..215 " "
+                - NAMED_TYPE@215..218
+                    - NAME@215..218
+                        - IDENT@215..218 "Int"
+                - WHITESPACE@218..221 "\n  "
+            - FIELD_DEFINITION@221..235
+                - NAME@221..227
+                    - IDENT@221..227 "height"
+                - COLON@227..228 ":"
+                - WHITESPACE@228..229 " "
+                - NAMED_TYPE@229..232
+                    - NAME@229..232
+                        - IDENT@229..232 "Int"
+                - WHITESPACE@232..235 "\n  "
+            - FIELD_DEFINITION@235..253
+                - NAME@235..244
+                    - IDENT@235..244 "thumbnail"
+                - COLON@244..245 ":"
+                - WHITESPACE@245..246 " "
+                - NAMED_TYPE@246..252
+                    - NAME@246..252
+                        - IDENT@246..252 "String"
+                - WHITESPACE@252..253 "\n"
+            - R_CURLY@253..254 "}"
+