graphql 1.9.21 → 1.10.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +0 -1
- data/lib/generators/graphql/install_generator.rb +0 -1
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/templates/base_field.erb +4 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +0 -5
- data/lib/generators/graphql/templates/mutation.erb +1 -1
- data/lib/generators/graphql/templates/schema.erb +1 -1
- data/lib/graphql.rb +1 -11
- data/lib/graphql/analysis/ast.rb +2 -2
- data/lib/graphql/analysis/ast/analyzer.rb +4 -23
- data/lib/graphql/analysis/ast/max_query_complexity.rb +3 -3
- data/lib/graphql/analysis/ast/max_query_depth.rb +3 -7
- data/lib/graphql/analysis/ast/query_complexity.rb +2 -2
- data/lib/graphql/argument.rb +6 -2
- data/lib/graphql/backtrace/table.rb +10 -2
- data/lib/graphql/base_type.rb +5 -1
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
- data/lib/graphql/define/assign_object_field.rb +2 -2
- data/lib/graphql/define/defined_object_proxy.rb +0 -3
- data/lib/graphql/define/instance_definable.rb +3 -14
- data/lib/graphql/enum_type.rb +4 -0
- data/lib/graphql/execution/directive_checks.rb +2 -2
- data/lib/graphql/execution/errors.rb +14 -15
- data/lib/graphql/execution/execute.rb +1 -1
- data/lib/graphql/execution/interpreter/runtime.rb +17 -39
- data/lib/graphql/execution/multiplex.rb +3 -3
- data/lib/graphql/field.rb +8 -0
- data/lib/graphql/filter.rb +1 -1
- data/lib/graphql/function.rb +1 -1
- data/lib/graphql/input_object_type.rb +1 -2
- data/lib/graphql/introspection/entry_points.rb +1 -2
- data/lib/graphql/introspection/input_value_type.rb +27 -9
- data/lib/graphql/introspection/schema_type.rb +1 -2
- data/lib/graphql/language/block_string.rb +2 -2
- data/lib/graphql/language/document_from_schema_definition.rb +5 -11
- data/lib/graphql/language/lexer.rb +48 -49
- data/lib/graphql/language/lexer.rl +48 -49
- data/lib/graphql/language/nodes.rb +11 -14
- data/lib/graphql/language/parser.rb +645 -650
- data/lib/graphql/language/parser.y +7 -8
- data/lib/graphql/language/token.rb +1 -1
- data/lib/graphql/non_null_type.rb +0 -10
- data/lib/graphql/pagination.rb +6 -0
- data/lib/graphql/pagination/active_record_relation_connection.rb +35 -0
- data/lib/graphql/pagination/array_connection.rb +78 -0
- data/lib/graphql/pagination/connection.rb +150 -0
- data/lib/graphql/pagination/connections.rb +103 -0
- data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
- data/lib/graphql/pagination/relation_connection.rb +157 -0
- data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
- data/lib/graphql/query.rb +1 -7
- data/lib/graphql/query/arguments.rb +3 -9
- data/lib/graphql/query/context.rb +9 -31
- data/lib/graphql/query/literal_input.rb +29 -10
- data/lib/graphql/query/null_context.rb +0 -4
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query/variables.rb +2 -4
- data/lib/graphql/relay/base_connection.rb +7 -3
- data/lib/graphql/relay/edges_instrumentation.rb +1 -1
- data/lib/graphql/relay/node.rb +2 -2
- data/lib/graphql/relay/relation_connection.rb +5 -9
- data/lib/graphql/schema.rb +27 -68
- data/lib/graphql/schema/argument.rb +31 -5
- data/lib/graphql/schema/base_64_bp.rb +2 -3
- data/lib/graphql/schema/build_from_definition.rb +113 -179
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +10 -4
- data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
- data/lib/graphql/schema/directive.rb +6 -7
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/enum.rb +1 -0
- data/lib/graphql/schema/enum_value.rb +4 -1
- data/lib/graphql/schema/field.rb +37 -39
- data/lib/graphql/schema/field/connection_extension.rb +11 -1
- data/lib/graphql/schema/input_object.rb +2 -5
- data/lib/graphql/schema/interface.rb +2 -0
- data/lib/graphql/schema/introspection_system.rb +1 -4
- data/lib/graphql/schema/loader.rb +6 -12
- data/lib/graphql/schema/member.rb +2 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -2
- data/lib/graphql/schema/member/build_type.rb +4 -0
- data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
- data/lib/graphql/schema/member/has_ast_node.rb +17 -0
- data/lib/graphql/schema/member/has_fields.rb +10 -16
- data/lib/graphql/schema/member/instrumentation.rb +1 -6
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/mutation.rb +1 -1
- data/lib/graphql/schema/object.rb +5 -6
- data/lib/graphql/schema/possible_types.rb +3 -3
- data/lib/graphql/schema/printer.rb +1 -3
- data/lib/graphql/schema/relay_classic_mutation.rb +2 -6
- data/lib/graphql/schema/resolver.rb +5 -35
- data/lib/graphql/schema/scalar.rb +1 -0
- data/lib/graphql/schema/subscription.rb +6 -6
- data/lib/graphql/schema/timeout_middleware.rb +2 -3
- data/lib/graphql/schema/type_expression.rb +27 -17
- data/lib/graphql/schema/union.rb +7 -26
- data/lib/graphql/schema/validation.rb +1 -17
- data/lib/graphql/schema/warden.rb +3 -77
- data/lib/graphql/schema/wrapper.rb +1 -1
- data/lib/graphql/static_validation/definition_dependencies.rb +12 -21
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +9 -4
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +10 -7
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +4 -4
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -3
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +1 -1
- data/lib/graphql/subscriptions.rb +7 -7
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -2
- data/lib/graphql/subscriptions/event.rb +5 -19
- data/lib/graphql/subscriptions/instrumentation.rb +9 -4
- data/lib/graphql/subscriptions/subscription_root.rb +2 -10
- data/lib/graphql/tracing/skylight_tracing.rb +0 -1
- data/lib/graphql/types/int.rb +1 -1
- data/lib/graphql/types/relay/base_connection.rb +3 -1
- data/lib/graphql/union_type.rb +23 -58
- data/lib/graphql/upgrader/member.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +20 -13
- data/lib/generators/graphql/templates/base_mutation.erb +0 -8
- data/lib/graphql/schema/type_membership.rb +0 -34
@@ -77,8 +77,11 @@ rule
|
|
77
77
|
}
|
78
78
|
|
79
79
|
type:
|
80
|
+
nullable_type { return val[0] }
|
81
|
+
| nullable_type BANG { return make_node(:NonNullType, of_type: val[0]) }
|
82
|
+
|
83
|
+
nullable_type:
|
80
84
|
name { return make_node(:TypeName, name: val[0])}
|
81
|
-
| type BANG { return make_node(:NonNullType, of_type: val[0]) }
|
82
85
|
| LBRACKET type RBRACKET { return make_node(:ListType, of_type: val[1]) }
|
83
86
|
|
84
87
|
default_value_opt:
|
@@ -165,7 +168,6 @@ rule
|
|
165
168
|
|
166
169
|
arguments_opt:
|
167
170
|
/* none */ { return EMPTY_ARRAY }
|
168
|
-
| LPAREN RPAREN { return EMPTY_ARRAY }
|
169
171
|
| LPAREN arguments_list RPAREN { return val[1] }
|
170
172
|
|
171
173
|
arguments_list:
|
@@ -187,7 +189,7 @@ rule
|
|
187
189
|
| object_literal_value
|
188
190
|
|
189
191
|
input_value:
|
190
|
-
|
192
|
+
literal_value
|
191
193
|
| variable
|
192
194
|
| object_value
|
193
195
|
|
@@ -440,7 +442,6 @@ def initialize(query_string, filename:, tracer: Tracing::NullTracer)
|
|
440
442
|
@query_string = query_string
|
441
443
|
@filename = filename
|
442
444
|
@tracer = tracer
|
443
|
-
@reused_next_token = [nil, nil]
|
444
445
|
end
|
445
446
|
|
446
447
|
def parse_document
|
@@ -452,7 +453,7 @@ def parse_document
|
|
452
453
|
# From the tokens, build an AST
|
453
454
|
@tracer.trace("parse", {query_string: @query_string}) do
|
454
455
|
if @tokens.empty?
|
455
|
-
|
456
|
+
raise GraphQL::ParseError.new("Unexpected end of document", nil, nil, @query_string)
|
456
457
|
else
|
457
458
|
do_parse
|
458
459
|
end
|
@@ -471,9 +472,7 @@ def next_token
|
|
471
472
|
if lexer_token.nil?
|
472
473
|
nil
|
473
474
|
else
|
474
|
-
|
475
|
-
@reused_next_token[1] = lexer_token
|
476
|
-
@reused_next_token
|
475
|
+
[lexer_token.name, lexer_token]
|
477
476
|
end
|
478
477
|
end
|
479
478
|
|
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
|
-
class DoubleNonNullTypeError < GraphQL::Error
|
4
|
-
end
|
5
|
-
|
6
3
|
# A non-null type modifies another type.
|
7
4
|
#
|
8
5
|
# Non-null types can be created with `!` (`InnerType!`)
|
@@ -37,13 +34,6 @@ module GraphQL
|
|
37
34
|
|
38
35
|
attr_reader :of_type
|
39
36
|
def initialize(of_type:)
|
40
|
-
if of_type.is_a?(GraphQL::NonNullType)
|
41
|
-
raise(
|
42
|
-
DoubleNonNullTypeError,
|
43
|
-
"You tried to add a non-null constraint twice (!! instead of !)"
|
44
|
-
)
|
45
|
-
end
|
46
|
-
|
47
37
|
super()
|
48
38
|
@of_type = of_type
|
49
39
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "graphql/pagination/array_connection"
|
3
|
+
require "graphql/pagination/active_record_relation_connection"
|
4
|
+
require "graphql/pagination/connections"
|
5
|
+
require "graphql/pagination/mongoid_relation_connection"
|
6
|
+
require "graphql/pagination/sequel_dataset_connection"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "graphql/pagination/relation_connection"
|
3
|
+
|
4
|
+
module GraphQL
|
5
|
+
module Pagination
|
6
|
+
# Customizes `RelationConnection` to work with `ActiveRecord::Relation`s.
|
7
|
+
class ActiveRecordRelationConnection < Pagination::RelationConnection
|
8
|
+
def relation_count(relation)
|
9
|
+
if relation.respond_to?(:unscope)
|
10
|
+
relation.unscope(:order).count(:all)
|
11
|
+
else
|
12
|
+
# Rails 3
|
13
|
+
relation.count
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def relation_limit(relation)
|
18
|
+
relation.limit_value
|
19
|
+
end
|
20
|
+
|
21
|
+
def relation_offset(relation)
|
22
|
+
relation.offset_value
|
23
|
+
end
|
24
|
+
|
25
|
+
def null_relation(relation)
|
26
|
+
if relation.respond_to?(:none)
|
27
|
+
relation.none
|
28
|
+
else
|
29
|
+
# Rails 3
|
30
|
+
relation.where("1=2")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "graphql/pagination/connection"
|
3
|
+
|
4
|
+
module GraphQL
|
5
|
+
module Pagination
|
6
|
+
class ArrayConnection < Pagination::Connection
|
7
|
+
def nodes
|
8
|
+
load_nodes
|
9
|
+
@nodes
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_previous_page
|
13
|
+
load_nodes
|
14
|
+
@has_previous_page
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_next_page
|
18
|
+
load_nodes
|
19
|
+
@has_next_page
|
20
|
+
end
|
21
|
+
|
22
|
+
def cursor_for(item)
|
23
|
+
idx = items.find_index(item) + 1
|
24
|
+
context.schema.cursor_encoder.encode(idx.to_s)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def index_from_cursor(cursor)
|
30
|
+
decode(cursor).to_i
|
31
|
+
end
|
32
|
+
|
33
|
+
# Populate all the pagination info _once_,
|
34
|
+
# It doesn't do anything on subsequent calls.
|
35
|
+
def load_nodes
|
36
|
+
@nodes ||= begin
|
37
|
+
sliced_nodes = if before && after
|
38
|
+
items[index_from_cursor(after)..index_from_cursor(before)-1] || []
|
39
|
+
elsif before
|
40
|
+
items[0..index_from_cursor(before)-2] || []
|
41
|
+
elsif after
|
42
|
+
items[index_from_cursor(after)..-1] || []
|
43
|
+
else
|
44
|
+
items
|
45
|
+
end
|
46
|
+
|
47
|
+
@has_previous_page = if last
|
48
|
+
# There are items preceding the ones in this result
|
49
|
+
sliced_nodes.count > last
|
50
|
+
elsif after
|
51
|
+
# We've paginated into the Array a bit, there are some behind us
|
52
|
+
index_from_cursor(after) > 0
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
@has_next_page = if first
|
58
|
+
# There are more items after these items
|
59
|
+
sliced_nodes.count > first
|
60
|
+
elsif before
|
61
|
+
# The original array is longer than the `before` index
|
62
|
+
index_from_cursor(before) < items.length
|
63
|
+
else
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
limited_nodes = sliced_nodes
|
68
|
+
|
69
|
+
limited_nodes = limited_nodes.first(first) if first
|
70
|
+
limited_nodes = limited_nodes.last(last) if last
|
71
|
+
limited_nodes = limited_nodes.first(max_page_size) if max_page_size && !first && !last
|
72
|
+
|
73
|
+
limited_nodes
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Pagination
|
5
|
+
# A Connection wraps a list of items and provides cursor-based pagination over it.
|
6
|
+
#
|
7
|
+
# Connections were introduced by Facebook's `Relay` front-end framework, but
|
8
|
+
# proved to be generally useful for GraphQL APIs. When in doubt, use connections
|
9
|
+
# to serve lists (like Arrays, ActiveRecord::Relations) via GraphQL.
|
10
|
+
#
|
11
|
+
# Unlike the previous connection implementation, these default to bidirectional pagination.
|
12
|
+
#
|
13
|
+
# Pagination arguments and context may be provided at initialization or assigned later (see {Schema::Field::ConnectionExtension}).
|
14
|
+
class Connection
|
15
|
+
class PaginationImplementationMissingError < GraphQL::Error
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Class] The class to use for wrapping items as `edges { ... }`. Defaults to `Connection::Edge`
|
19
|
+
def self.edge_class
|
20
|
+
self::Edge
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Object] A list object, from the application. This is the unpaginated value passed into the connection.
|
24
|
+
attr_reader :items
|
25
|
+
|
26
|
+
# @return [GraphQL::Query::Context]
|
27
|
+
attr_accessor :context
|
28
|
+
|
29
|
+
attr_accessor :before, :after
|
30
|
+
|
31
|
+
# @param items [Object] some unpaginated collection item, like an `Array` or `ActiveRecord::Relation`
|
32
|
+
# @param context [Query::Context]
|
33
|
+
# @param first [Integer, nil] The limit parameter from the client, if it provided one
|
34
|
+
# @param after [String, nil] A cursor for pagination, if the client provided one
|
35
|
+
# @param last [Integer, nil] Limit parameter from the client, if provided
|
36
|
+
# @param before [String, nil] A cursor for pagination, if the client provided one.
|
37
|
+
def initialize(items, context: nil, first: nil, after: nil, max_page_size: nil, last: nil, before: nil)
|
38
|
+
@items = items
|
39
|
+
@context = context
|
40
|
+
@first = first
|
41
|
+
@after = after
|
42
|
+
@last = last
|
43
|
+
@before = before
|
44
|
+
@max_page_size = max_page_size
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_writer :max_page_size
|
48
|
+
def max_page_size
|
49
|
+
@max_page_size ||= context.schema.default_max_page_size
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_writer :first
|
53
|
+
# @return [Integer, nil] a clamped `first` value. (The underlying instance variable doesn't have limits on it)
|
54
|
+
def first
|
55
|
+
limit_pagination_argument(@first, max_page_size)
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_writer :last
|
59
|
+
# @return [Integer, nil] a clamped `last` value. (The underlying instance variable doesn't have limits on it)
|
60
|
+
def last
|
61
|
+
limit_pagination_argument(@last, max_page_size)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Array<Edge>] {nodes}, but wrapped with Edge instances
|
65
|
+
def edges
|
66
|
+
@edges ||= nodes.map { |n| self.class.edge_class.new(n, self) }
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [Array<Object>] A slice of {items}, constrained by {@first}/{@after}/{@last}/{@before}
|
70
|
+
def nodes
|
71
|
+
raise PaginationImplementationMissingError, "Implement #{self.class}#nodes to paginate `@items`"
|
72
|
+
end
|
73
|
+
|
74
|
+
# A dynamic alias for compatibility with {Relay::BaseConnection}.
|
75
|
+
# @deprecated use {#nodes} instead
|
76
|
+
def edge_nodes
|
77
|
+
nodes
|
78
|
+
end
|
79
|
+
|
80
|
+
# The connection object itself implements `PageInfo` fields
|
81
|
+
def page_info
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [Boolean] True if there are more items after this page
|
86
|
+
def has_next_page
|
87
|
+
raise PaginationImplementationMissingError, "Implement #{self.class}#has_next_page to return the next-page check"
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [Boolean] True if there were items before these items
|
91
|
+
def has_previous_page
|
92
|
+
raise PaginationImplementationMissingError, "Implement #{self.class}#has_previous_page to return the previous-page check"
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [String] The cursor of the first item in {nodes}
|
96
|
+
def start_cursor
|
97
|
+
nodes.first && cursor_for(nodes.first)
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [String] The cursor of the last item in {nodes}
|
101
|
+
def end_cursor
|
102
|
+
nodes.last && cursor_for(nodes.last)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Return a cursor for this item.
|
106
|
+
# @param item [Object] one of the passed in {items}, taken from {nodes}
|
107
|
+
# @return [String]
|
108
|
+
def cursor_for(item)
|
109
|
+
raise PaginationImplementationMissingError, "Implement #{self.class}#cursor_for(item) to return the cursor for #{item.inspect}"
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
# @param argument [nil, Integer] `first` or `last`, as provided by the client
|
115
|
+
# @param max_page_size [nil, Integer]
|
116
|
+
# @return [nil, Integer] `nil` if the input was `nil`, otherwise a value between `0` and `max_page_size`
|
117
|
+
def limit_pagination_argument(argument, max_page_size)
|
118
|
+
if argument
|
119
|
+
if argument < 0
|
120
|
+
argument = 0
|
121
|
+
elsif max_page_size && argument > max_page_size
|
122
|
+
argument = max_page_size
|
123
|
+
end
|
124
|
+
end
|
125
|
+
argument
|
126
|
+
end
|
127
|
+
|
128
|
+
def decode(cursor)
|
129
|
+
context.schema.cursor_encoder.decode(cursor)
|
130
|
+
end
|
131
|
+
|
132
|
+
# A wrapper around paginated items. It includes a {cursor} for pagination
|
133
|
+
# and could be extended with custom relationship-level data.
|
134
|
+
class Edge
|
135
|
+
def initialize(item, connection)
|
136
|
+
@connection = connection
|
137
|
+
@item = item
|
138
|
+
end
|
139
|
+
|
140
|
+
def node
|
141
|
+
@item
|
142
|
+
end
|
143
|
+
|
144
|
+
def cursor
|
145
|
+
@connection.cursor_for(@item)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Pagination
|
5
|
+
# A schema-level connection wrapper manager.
|
6
|
+
#
|
7
|
+
# Attach as a plugin.
|
8
|
+
#
|
9
|
+
# @example Using new default connections
|
10
|
+
# class MySchema < GraphQL::Schema
|
11
|
+
# use GraphQL::Pagination::Connections
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# @example Adding a custom wrapper
|
15
|
+
# class MySchema < GraphQL::Schema
|
16
|
+
# use GraphQL::Pagination::Connections
|
17
|
+
# connections.add(MyApp::SearchResults, MyApp::SearchResultsConnection)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# @example Removing default connection support for arrays (they can still be manually wrapped)
|
21
|
+
# class MySchema < GraphQL::Schema
|
22
|
+
# use GraphQL::Pagination::Connections
|
23
|
+
# connections.delete(Array)
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# @see {Schema.connections}
|
27
|
+
class Connections
|
28
|
+
class ImplementationMissingError < GraphQL::Error
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.use(schema_defn)
|
32
|
+
[schema_defn.target, schema_defn.target.class].each do |schema|
|
33
|
+
schema.connections = self.new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
@wrappers = {}
|
39
|
+
add_default
|
40
|
+
end
|
41
|
+
|
42
|
+
def add(nodes_class, implementation)
|
43
|
+
@wrappers[nodes_class] = implementation
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete(nodes_class)
|
47
|
+
@wrappers.delete(nodes_class)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Used by the runtime to wrap values in connection wrappers.
|
51
|
+
# @api Private
|
52
|
+
def wrap(field, object, arguments, context)
|
53
|
+
impl = nil
|
54
|
+
object.class.ancestors.each { |cls|
|
55
|
+
impl = @wrappers[cls]
|
56
|
+
break if impl
|
57
|
+
}
|
58
|
+
|
59
|
+
if impl.nil?
|
60
|
+
raise ImplementationMissingError, "Couldn't find a connection wrapper for #{object.class} during #{field.path} (#{object.inspect})"
|
61
|
+
end
|
62
|
+
|
63
|
+
impl.new(
|
64
|
+
object,
|
65
|
+
context: context,
|
66
|
+
max_page_size: field.max_page_size || context.schema.default_max_page_size,
|
67
|
+
first: arguments[:first],
|
68
|
+
after: arguments[:after],
|
69
|
+
last: arguments[:last],
|
70
|
+
before: arguments[:before],
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def add_default
|
77
|
+
add(Array, Pagination::ArrayConnection)
|
78
|
+
|
79
|
+
if defined?(ActiveRecord::Relation)
|
80
|
+
add(ActiveRecord::Relation, Pagination::ActiveRecordRelationConnection)
|
81
|
+
end
|
82
|
+
|
83
|
+
if defined?(Sequel::Dataset)
|
84
|
+
add(Sequel::Dataset, Pagination::SequelDatasetConnection)
|
85
|
+
end
|
86
|
+
|
87
|
+
if defined?(Mongoid::Criteria)
|
88
|
+
add(Mongoid::Criteria, Pagination::MongoidRelationConnection)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Mongoid 5 and 6
|
92
|
+
if defined?(Mongoid::Relations::Targets::Enumerable)
|
93
|
+
add(Mongoid::Relations::Targets::Enumerable, Pagination::MongoidRelationConnection)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Mongoid 7
|
97
|
+
if defined?(Mongoid::Association::Referenced::HasMany::Targets::Enumerable)
|
98
|
+
add(Mongoid::Association::Referenced::HasMany::Targets::Enumerable, Pagination::MongoidRelationConnection)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|