Skip to content

Commit ea1c404

Browse files
authored
Merge pull request #3932 from dleavitt/transitive-interfaces
Same Interface Shows Up Multiple Times in "Implements"
2 parents 5d6ba58 + fed3cf6 commit ea1c404

File tree

3 files changed

+147
-1
lines changed

3 files changed

+147
-1
lines changed

lib/graphql/schema/member/has_interfaces.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def interfaces(context = GraphQL::Query::NullContext)
8080
visible_interfaces.concat(superclass.interfaces(context))
8181
end
8282

83-
visible_interfaces
83+
visible_interfaces.uniq
8484
end
8585
end
8686
end

spec/graphql/schema/build_from_definition_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,36 @@ def assert_schema_and_compare_output(definition)
556556
assert_schema_and_compare_output(schema)
557557
end
558558

559+
it "only adds the interface to the type once" do
560+
schema = <<-SCHEMA
561+
interface Named implements Node {
562+
id: ID
563+
name: String
564+
}
565+
566+
interface Node {
567+
id: ID
568+
}
569+
570+
type Query {
571+
thing: Thing
572+
}
573+
574+
type Thing implements Named & Node & Timestamped {
575+
id: ID
576+
name: String
577+
timestamp: String
578+
}
579+
580+
interface Timestamped implements Node {
581+
id: ID
582+
timestamp: String
583+
}
584+
SCHEMA
585+
586+
assert_schema_and_compare_output(schema)
587+
end
588+
559589
it 'supports simple output enum' do
560590
schema = <<-SCHEMA
561591
schema {

spec/graphql/schema/interface_spec.rb

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,122 @@ class Query < GraphQL::Schema::Object
224224
end
225225
end
226226

227+
describe "transitive implemention of same interface twice" do
228+
class TransitiveInterfaceSchema < GraphQL::Schema
229+
module Node
230+
include GraphQL::Schema::Interface
231+
field :id, ID
232+
233+
def id; "id"; end
234+
end
235+
236+
module Named
237+
include GraphQL::Schema::Interface
238+
implements Node
239+
field :name, String
240+
241+
def name; "name"; end
242+
end
243+
244+
module Timestamped
245+
include GraphQL::Schema::Interface
246+
implements Node
247+
field :timestamp, String
248+
249+
def timestamp; "ts"; end
250+
end
251+
252+
class BaseObject < GraphQL::Schema::Object
253+
implements Named
254+
implements Timestamped
255+
end
256+
257+
class Thing < BaseObject
258+
implements Named
259+
implements Timestamped
260+
end
261+
262+
class Query < GraphQL::Schema::Object
263+
field :thing, Thing
264+
def thing
265+
{}
266+
end
267+
end
268+
269+
query(Query)
270+
end
271+
272+
it "allows running queries on transitive interfaces" do
273+
result = TransitiveInterfaceSchema.execute("{ thing { id name timestamp } }")
274+
275+
thing = result.dig("data", "thing")
276+
277+
assert_equal "id", thing["id"]
278+
assert_equal "name", thing["name"]
279+
assert_equal "ts", thing["timestamp"]
280+
281+
result2 = TransitiveInterfaceSchema.execute(<<-GRAPHQL)
282+
{
283+
thing {
284+
...on Node { id }
285+
...on Named {
286+
nid: id name
287+
...on Node { nnid: id }
288+
}
289+
... on Timestamped { tid: id timestamp }
290+
}
291+
}
292+
GRAPHQL
293+
294+
thing2 = result2.dig("data", "thing")
295+
296+
assert_equal "id", thing2["id"]
297+
assert_equal "id", thing2["nid"]
298+
assert_equal "id", thing2["tid"]
299+
assert_equal "name", thing2["name"]
300+
assert_equal "ts", thing2["timestamp"]
301+
end
302+
303+
it "has the right structure" do
304+
expected_schema = <<-SCHEMA
305+
interface Named implements Node {
306+
id: ID
307+
name: String
308+
}
309+
310+
interface Node {
311+
id: ID
312+
}
313+
314+
type Query {
315+
thing: Thing
316+
}
317+
318+
type Thing implements Named & Node & Timestamped {
319+
id: ID
320+
name: String
321+
timestamp: String
322+
}
323+
324+
interface Timestamped implements Node {
325+
id: ID
326+
timestamp: String
327+
}
328+
SCHEMA
329+
assert_equal expected_schema, TransitiveInterfaceSchema.to_definition
330+
end
331+
332+
it "only lists each implemented interface once when introspecting" do
333+
introspection = TransitiveInterfaceSchema.as_json
334+
thing_type = introspection.dig("data", "__schema", "types").find do |type|
335+
type["name"] == "Thing"
336+
end
337+
interfaces_names = thing_type["interfaces"].map { |i| i["name"] }.sort
338+
339+
assert_equal interfaces_names, ["Named", "Node", "Timestamped"]
340+
end
341+
end
342+
227343
describe "migrated legacy tests" do
228344
let(:interface) { Dummy::Edible }
229345

0 commit comments

Comments
 (0)