1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
# frozen_string_literal: true
module GraphQL
module Pagination
# A schema-level connection wrapper manager.
#
# Attach as a plugin.
#
# @example Adding a custom wrapper
# class MySchema < GraphQL::Schema
# connections.add(MyApp::SearchResults, MyApp::SearchResultsConnection)
# end
#
# @example Removing default connection support for arrays (they can still be manually wrapped)
# class MySchema < GraphQL::Schema
# connections.delete(Array)
# end
#
# @see {Schema.connections}
class Connections
class ImplementationMissingError < GraphQL::Error
end
def initialize(schema:)
@schema = schema
@wrappers = {}
add_default
end
def add(nodes_class, implementation)
@wrappers[nodes_class] = implementation
end
def delete(nodes_class)
@wrappers.delete(nodes_class)
end
def all_wrappers
all_wrappers = {}
@schema.ancestors.reverse_each do |schema_class|
if schema_class.respond_to?(:connections) && (c = schema_class.connections)
all_wrappers.merge!(c.wrappers)
end
end
all_wrappers
end
def wrapper_for(items, wrappers: all_wrappers)
impl = nil
items.class.ancestors.each { |cls|
impl = wrappers[cls]
break if impl
}
impl
end
# Used by the runtime to wrap values in connection wrappers.
# @api Private
def wrap(field, parent, items, arguments, context)
return items if GraphQL::Execution::Interpreter::RawValue === items
wrappers = context ? context.namespace(:connections)[:all_wrappers] : all_wrappers
impl = wrapper_for(items, wrappers: wrappers)
if impl
impl.new(
items,
context: context,
parent: parent,
field: field,
max_page_size: field.has_max_page_size? ? field.max_page_size : context.schema.default_max_page_size,
default_page_size: field.has_default_page_size? ? field.default_page_size : context.schema.default_page_size,
first: arguments[:first],
after: arguments[:after],
last: arguments[:last],
before: arguments[:before],
arguments: arguments,
edge_class: edge_class_for_field(field),
)
else
raise ImplementationMissingError, "Couldn't find a connection wrapper for #{items.class} during #{field.path} (#{items.inspect})"
end
end
# use an override if there is one
# @api private
def edge_class_for_field(field)
conn_type = field.type.unwrap
conn_type_edge_type = conn_type.respond_to?(:edge_class) && conn_type.edge_class
if conn_type_edge_type && conn_type_edge_type != Pagination::Connection::Edge
conn_type_edge_type
else
nil
end
end
protected
attr_reader :wrappers
private
def add_default
add(Array, Pagination::ArrayConnection)
if defined?(ActiveRecord::Relation)
add(ActiveRecord::Relation, Pagination::ActiveRecordRelationConnection)
end
if defined?(Sequel::Dataset)
add(Sequel::Dataset, Pagination::SequelDatasetConnection)
end
if defined?(Mongoid::Criteria)
add(Mongoid::Criteria, Pagination::MongoidRelationConnection)
end
# Mongoid 5 and 6
if defined?(Mongoid::Relations::Targets::Enumerable)
add(Mongoid::Relations::Targets::Enumerable, Pagination::MongoidRelationConnection)
end
# Mongoid 7
if defined?(Mongoid::Association::Referenced::HasMany::Targets::Enumerable)
add(Mongoid::Association::Referenced::HasMany::Targets::Enumerable, Pagination::MongoidRelationConnection)
end
# Mongoid 7.3+
if defined?(Mongoid::Association::Referenced::HasMany::Enumerable)
add(Mongoid::Association::Referenced::HasMany::Enumerable, Pagination::MongoidRelationConnection)
end
end
end
end
end
|