Skip to content

Commit 4a5c69b

Browse files
committed
Fix connection wrapper inheritance
1 parent d8cd341 commit 4a5c69b

File tree

5 files changed

+101
-8
lines changed

5 files changed

+101
-8
lines changed

lib/graphql/pagination/connections.rb

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,17 @@ class ImplementationMissingError < GraphQL::Error
3030

3131
def self.use(schema_defn)
3232
if schema_defn.is_a?(Class)
33-
schema_defn.connections = self.new
33+
schema_defn.connections = self.new(schema: schema_defn)
3434
else
3535
# Unwrap a `.define` object
3636
schema_defn = schema_defn.target
37-
schema_defn.connections = self.new
37+
schema_defn.connections = self.new(schema: schema_defn)
3838
schema_defn.class.connections = schema_defn.connections
3939
end
4040
end
4141

42-
def initialize
42+
def initialize(schema:)
43+
@schema = schema
4344
@wrappers = {}
4445
add_default
4546
end
@@ -52,12 +53,23 @@ def delete(nodes_class)
5253
@wrappers.delete(nodes_class)
5354
end
5455

56+
def all_wrappers
57+
all_wrappers = {}
58+
@schema.ancestors.reverse_each do |schema_class|
59+
if schema_class.respond_to?(:connections) && (c = schema_class.connections)
60+
all_wrappers.merge!(c.wrappers)
61+
end
62+
end
63+
all_wrappers
64+
end
65+
5566
# Used by the runtime to wrap values in connection wrappers.
5667
# @api Private
57-
def wrap(field, object, arguments, context)
68+
def wrap(field, object, arguments, context, wrappers: all_wrappers)
5869
impl = nil
70+
5971
object.class.ancestors.each { |cls|
60-
impl = @wrappers[cls]
72+
impl = wrappers[cls]
6173
break if impl
6274
}
6375

@@ -76,6 +88,10 @@ def wrap(field, object, arguments, context)
7688
)
7789
end
7890

91+
protected
92+
93+
attr_reader :wrappers
94+
7995
private
8096

8197
def add_default

lib/graphql/schema.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -942,8 +942,24 @@ def get_type(type_name)
942942
find_inherited_value(:types, EMPTY_HASH)[type_name]
943943
end
944944

945+
# @api private
946+
attr_writer :connections
947+
945948
# @return [GraphQL::Pagination::Connections] if installed
946-
attr_accessor :connections
949+
def connections
950+
if defined?(@connections)
951+
@connections
952+
else
953+
inherited_connections = find_inherited_value(:connections, nil)
954+
# This schema is part of an inheritance chain which is using new connections,
955+
# make a new instance, so we don't pollute the upstream one.
956+
if inherited_connections
957+
@connections = Pagination::Connections.new(schema: self)
958+
else
959+
nil
960+
end
961+
end
962+
end
947963

948964
def new_connections?
949965
!!connections

lib/graphql/schema/field/connection_extension.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ def after_resolve(value:, object:, arguments:, context:, memo:)
3838
value.max_page_size ||= field.max_page_size
3939
value
4040
elsif context.schema.new_connections?
41-
context.schema.connections.wrap(field, value, arguments, context)
41+
wrappers = context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
42+
context.schema.connections.wrap(field, value, arguments, context, wrappers: wrappers)
4243
else
4344
if object.is_a?(GraphQL::Schema::Object)
4445
object = object.object
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# frozen_string_literal: true
2+
require "spec_helper"
3+
4+
describe GraphQL::Pagination::Connections do
5+
ITEMS = ConnectionAssertions::NAMES.map { |n| { name: n } }
6+
7+
class ArrayConnectionWithTotalCount < GraphQL::Pagination::ArrayConnection
8+
def total_count
9+
items.size
10+
end
11+
end
12+
13+
let(:base_schema) {
14+
ConnectionAssertions.build_schema(
15+
connection_class: GraphQL::Pagination::ArrayConnection,
16+
total_count_connection_class: ArrayConnectionWithTotalCount,
17+
get_items: -> { ITEMS }
18+
)
19+
}
20+
21+
# These wouldn't _work_, I just need to test `.wrap`
22+
class SetConnection < GraphQL::Pagination::ArrayConnection; end
23+
class HashConnection < GraphQL::Pagination::ArrayConnection; end
24+
class OtherArrayConnection < GraphQL::Pagination::ArrayConnection; end
25+
26+
let(:schema) do
27+
other_base_schema = Class.new(base_schema) do
28+
connections.add(Set, SetConnection)
29+
end
30+
31+
Class.new(other_base_schema) do
32+
connections.add(Hash, HashConnection)
33+
connections.add(Array, OtherArrayConnection)
34+
end
35+
end
36+
37+
it "returns connections by class, using inherited mappings and local overrides" do
38+
field_defn = OpenStruct.new(max_page_size: 10)
39+
40+
set_wrapper = schema.connections.wrap(field_defn, Set.new([1,2,3]), {}, nil)
41+
assert_instance_of SetConnection, set_wrapper
42+
43+
hash_wrapper = schema.connections.wrap(field_defn, {1 => :a, 2 => :b}, {}, nil)
44+
assert_instance_of HashConnection, hash_wrapper
45+
46+
array_wrapper = schema.connections.wrap(field_defn, [1,2,3], {}, nil)
47+
assert_instance_of OtherArrayConnection, array_wrapper
48+
end
49+
50+
it "uses passed-in wrappers" do
51+
field_defn = OpenStruct.new(max_page_size: 10)
52+
53+
assert_raises GraphQL::Pagination::Connections::ImplementationMissingError do
54+
schema.connections.wrap(field_defn, Set.new([1,2,3]), {}, nil, wrappers: {})
55+
end
56+
end
57+
end

spec/support/connection_assertions.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ module ConnectionAssertions
2020
]
2121

2222
def self.build_schema(get_items:, connection_class:, total_count_connection_class:)
23-
Class.new(GraphQL::Schema) do
23+
base_schema = Class.new(GraphQL::Schema) do
2424
use GraphQL::Pagination::Connections
25+
end
26+
27+
Class.new(base_schema) do
2528
use GraphQL::Execution::Interpreter
2629

2730
default_max_page_size ConnectionAssertions::MAX_PAGE_SIZE

0 commit comments

Comments
 (0)