From 238f19326b18372e5bf5e29dddd6326d38d7e2b9 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Fri, 17 Apr 2026 10:03:41 -0400 Subject: [PATCH 1/2] Require transitive interface names when loading a schema from SDL --- lib/graphql/schema/build_from_definition.rb | 10 +++++++ .../schema/build_from_definition_spec.rb | 27 +++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/graphql/schema/build_from_definition.rb b/lib/graphql/schema/build_from_definition.rb index 264647da9f5..f5fb32ad5e7 100644 --- a/lib/graphql/schema/build_from_definition.rb +++ b/lib/graphql/schema/build_from_definition.rb @@ -99,6 +99,16 @@ def build(schema_superclass, document, default_resolve:, using: {}, base_types: # It's possible that this was already loaded by the directives prev_type = types[definition.name] if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType) + if definition.is_a?(GraphQL::Language::Nodes::ObjectTypeDefinition) || definition.is_a?(Language::Nodes::InterfaceTypeDefinition) + interface_names = definition.interfaces.map(&:name) + transitive_names = interface_names.map { |n| document.definitions.find { |d| d.respond_to?(:name) && d.name == n }&.interfaces&.map(&:name) } + transitive_names.flatten! + transitive_names.compact! + if (missing_transitive_interfaces = transitive_names - interface_names).any? + raise GraphQL::Schema::InvalidDocumentError, "type #{definition.name} is missing one or more transitive interface names: #{missing_transitive_interfaces.join(", ")}. Add them to the type's `implements` list and try again." + end + end + types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve, base_types) end end diff --git a/spec/graphql/schema/build_from_definition_spec.rb b/spec/graphql/schema/build_from_definition_spec.rb index 66d15d3d688..4c3c02f4d21 100644 --- a/spec/graphql/schema/build_from_definition_spec.rb +++ b/spec/graphql/schema/build_from_definition_spec.rb @@ -539,6 +539,29 @@ def assert_schema_and_compare_output(definition) assert_schema_and_compare_output(schema) end + it "requires transitive dependencies to be named" do + schema_str = <<~GRAPHQL + interface A implements B { + s: String + } + + interface B { + s: String + } + + type Query implements A & B { + s: String + } + GRAPHQL + + assert_schema_and_compare_output(schema_str) + + invalid_schema_str = schema_str.sub("A & B", "A") + assert_raises GraphQL::Schema::InvalidDocumentError do + GraphQL::Schema.from_definition(invalid_schema_str) + end + end + it "supports interfaces that implement interfaces" do schema = <<-SCHEMA interface Named implements Node { @@ -1542,13 +1565,13 @@ def self.parse(string) person: Person } - type Person implements NamedEntity { + type Person implements NamedEntity & Entity { id: ID! name: String nationality: String } - type Product implements NamedEntity { + type Product implements NamedEntity & Entity { id: ID! name: String amount: Int From 00276ab65fd3d302cfb31ef81ffd9a4375d55372 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Fri, 17 Apr 2026 10:08:22 -0400 Subject: [PATCH 2/2] Fix lint error --- lib/graphql/schema/build_from_definition.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/graphql/schema/build_from_definition.rb b/lib/graphql/schema/build_from_definition.rb index f5fb32ad5e7..1295af1bbeb 100644 --- a/lib/graphql/schema/build_from_definition.rb +++ b/lib/graphql/schema/build_from_definition.rb @@ -104,7 +104,7 @@ def build(schema_superclass, document, default_resolve:, using: {}, base_types: transitive_names = interface_names.map { |n| document.definitions.find { |d| d.respond_to?(:name) && d.name == n }&.interfaces&.map(&:name) } transitive_names.flatten! transitive_names.compact! - if (missing_transitive_interfaces = transitive_names - interface_names).any? + if !(missing_transitive_interfaces = transitive_names - interface_names).empty? raise GraphQL::Schema::InvalidDocumentError, "type #{definition.name} is missing one or more transitive interface names: #{missing_transitive_interfaces.join(", ")}. Add them to the type's `implements` list and try again." end end