graphql 1.10.14 → 1.11.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +8 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +13 -9
- data/lib/generators/graphql/templates/interface.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/object.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/scalar.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +2 -0
- data/lib/generators/graphql/templates/union.erb +2 -0
- data/lib/graphql.rb +3 -3
- data/lib/graphql/execution/interpreter.rb +1 -1
- data/lib/graphql/execution/interpreter/runtime.rb +26 -26
- data/lib/graphql/execution/multiplex.rb +1 -2
- data/lib/graphql/introspection/schema_type.rb +3 -3
- data/lib/graphql/invalid_null_error.rb +18 -0
- data/lib/graphql/language/nodes.rb +1 -0
- data/lib/graphql/language/visitor.rb +2 -2
- data/lib/graphql/pagination/connection.rb +18 -13
- data/lib/graphql/pagination/connections.rb +17 -4
- data/lib/graphql/query.rb +1 -2
- data/lib/graphql/query/context.rb +20 -1
- data/lib/graphql/query/fingerprint.rb +2 -0
- data/lib/graphql/query/validation_pipeline.rb +3 -0
- data/lib/graphql/schema.rb +26 -16
- data/lib/graphql/schema/build_from_definition.rb +7 -12
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
- data/lib/graphql/schema/enum_value.rb +1 -0
- data/lib/graphql/schema/field.rb +63 -77
- data/lib/graphql/schema/field/connection_extension.rb +42 -32
- data/lib/graphql/schema/loader.rb +19 -1
- data/lib/graphql/schema/member/has_fields.rb +15 -5
- data/lib/graphql/schema/mutation.rb +4 -0
- data/lib/graphql/schema/object.rb +1 -1
- data/lib/graphql/schema/resolver.rb +20 -0
- data/lib/graphql/schema/resolver/has_payload_type.rb +2 -1
- data/lib/graphql/schema/subscription.rb +3 -13
- data/lib/graphql/schema/union.rb +29 -0
- data/lib/graphql/schema/warden.rb +2 -4
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +4 -2
- data/lib/graphql/subscriptions.rb +69 -24
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +66 -11
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +84 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
- data/lib/graphql/subscriptions/event.rb +16 -1
- data/lib/graphql/subscriptions/serialize.rb +22 -4
- data/lib/graphql/subscriptions/subscription_root.rb +3 -1
- data/lib/graphql/tracing.rb +1 -27
- data/lib/graphql/tracing/appoptics_tracing.rb +10 -2
- data/lib/graphql/tracing/platform_tracing.rb +25 -15
- data/lib/graphql/tracing/statsd_tracing.rb +42 -0
- data/lib/graphql/types/iso_8601_date_time.rb +2 -1
- data/lib/graphql/types/relay/base_connection.rb +6 -5
- data/lib/graphql/version.rb +1 -1
- metadata +5 -2
@@ -28,5 +28,23 @@ module GraphQL
|
|
28
28
|
def parent_error?
|
29
29
|
false
|
30
30
|
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
attr_accessor :parent_class
|
34
|
+
|
35
|
+
def subclass_for(parent_class)
|
36
|
+
subclass = Class.new(self)
|
37
|
+
subclass.parent_class = parent_class
|
38
|
+
subclass
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
if name.nil? && parent_class.respond_to?(:mutation) && (mutation = parent_class.mutation)
|
43
|
+
"#{mutation.inspect}::#{parent_class.graphql_name}::InvalidNullError"
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
31
49
|
end
|
32
50
|
end
|
@@ -89,7 +89,7 @@ module GraphQL
|
|
89
89
|
# @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
|
90
90
|
# @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
|
91
91
|
def on_abstract_node(node, parent)
|
92
|
-
if node
|
92
|
+
if node.equal?(DELETE_NODE)
|
93
93
|
# This might be passed to `super(DELETE_NODE, ...)`
|
94
94
|
# by a user hook, don't want to keep visiting in that case.
|
95
95
|
nil
|
@@ -179,7 +179,7 @@ module GraphQL
|
|
179
179
|
# The user-provided hook returned a new node.
|
180
180
|
new_parent = new_parent && new_parent.replace_child(node, new_node)
|
181
181
|
return new_node, new_parent
|
182
|
-
elsif new_node
|
182
|
+
elsif new_node.equal?(DELETE_NODE)
|
183
183
|
# The user-provided hook requested to remove this node
|
184
184
|
new_parent = new_parent && new_parent.delete_child(node)
|
185
185
|
return nil, new_parent
|
@@ -15,17 +15,15 @@ module GraphQL
|
|
15
15
|
class PaginationImplementationMissingError < GraphQL::Error
|
16
16
|
end
|
17
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
18
|
# @return [Object] A list object, from the application. This is the unpaginated value passed into the connection.
|
24
19
|
attr_reader :items
|
25
20
|
|
26
21
|
# @return [GraphQL::Query::Context]
|
27
22
|
attr_accessor :context
|
28
23
|
|
24
|
+
# @return [Object] the object this collection belongs to
|
25
|
+
attr_accessor :parent
|
26
|
+
|
29
27
|
# Raw access to client-provided values. (`max_page_size` not applied to first or last.)
|
30
28
|
attr_accessor :before_value, :after_value, :first_value, :last_value
|
31
29
|
|
@@ -49,19 +47,21 @@ module GraphQL
|
|
49
47
|
|
50
48
|
# @param items [Object] some unpaginated collection item, like an `Array` or `ActiveRecord::Relation`
|
51
49
|
# @param context [Query::Context]
|
50
|
+
# @param parent [Object] The object this collection belongs to
|
52
51
|
# @param first [Integer, nil] The limit parameter from the client, if it provided one
|
53
52
|
# @param after [String, nil] A cursor for pagination, if the client provided one
|
54
53
|
# @param last [Integer, nil] Limit parameter from the client, if provided
|
55
54
|
# @param before [String, nil] A cursor for pagination, if the client provided one.
|
56
55
|
# @param max_page_size [Integer, nil] A configured value to cap the result size. Applied as `first` if neither first or last are given.
|
57
|
-
def initialize(items, context: nil, first: nil, after: nil, max_page_size: :not_given, last: nil, before: nil)
|
56
|
+
def initialize(items, parent: nil, context: nil, first: nil, after: nil, max_page_size: :not_given, last: nil, before: nil, edge_class: nil)
|
58
57
|
@items = items
|
58
|
+
@parent = parent
|
59
59
|
@context = context
|
60
60
|
@first_value = first
|
61
61
|
@after_value = after
|
62
62
|
@last_value = last
|
63
63
|
@before_value = before
|
64
|
-
|
64
|
+
@edge_class = edge_class || self.class::Edge
|
65
65
|
# This is only true if the object was _initialized_ with an override
|
66
66
|
# or if one is assigned later.
|
67
67
|
@has_max_page_size_override = max_page_size != :not_given
|
@@ -112,9 +112,12 @@ module GraphQL
|
|
112
112
|
|
113
113
|
# @return [Array<Edge>] {nodes}, but wrapped with Edge instances
|
114
114
|
def edges
|
115
|
-
@edges ||= nodes.map { |n|
|
115
|
+
@edges ||= nodes.map { |n| @edge_class.new(n, self) }
|
116
116
|
end
|
117
117
|
|
118
|
+
# @return [Class] A wrapper class for edges of this connection
|
119
|
+
attr_accessor :edge_class
|
120
|
+
|
118
121
|
# @return [Array<Object>] A slice of {items}, constrained by {@first_value}/{@after_value}/{@last_value}/{@before_value}
|
119
122
|
def nodes
|
120
123
|
raise PaginationImplementationMissingError, "Implement #{self.class}#nodes to paginate `@items`"
|
@@ -185,17 +188,19 @@ module GraphQL
|
|
185
188
|
# A wrapper around paginated items. It includes a {cursor} for pagination
|
186
189
|
# and could be extended with custom relationship-level data.
|
187
190
|
class Edge
|
188
|
-
|
191
|
+
attr_reader :node
|
192
|
+
|
193
|
+
def initialize(node, connection)
|
189
194
|
@connection = connection
|
190
|
-
@
|
195
|
+
@node = node
|
191
196
|
end
|
192
197
|
|
193
|
-
def
|
194
|
-
@
|
198
|
+
def parent
|
199
|
+
@connection.parent
|
195
200
|
end
|
196
201
|
|
197
202
|
def cursor
|
198
|
-
@connection.cursor_for(@
|
203
|
+
@cursor ||= @connection.cursor_for(@node)
|
199
204
|
end
|
200
205
|
end
|
201
206
|
end
|
@@ -65,29 +65,42 @@ module GraphQL
|
|
65
65
|
|
66
66
|
# Used by the runtime to wrap values in connection wrappers.
|
67
67
|
# @api Private
|
68
|
-
def wrap(field,
|
68
|
+
def wrap(field, parent, items, arguments, context, wrappers: all_wrappers)
|
69
69
|
impl = nil
|
70
70
|
|
71
|
-
|
71
|
+
items.class.ancestors.each { |cls|
|
72
72
|
impl = wrappers[cls]
|
73
73
|
break if impl
|
74
74
|
}
|
75
75
|
|
76
76
|
if impl.nil?
|
77
|
-
raise ImplementationMissingError, "Couldn't find a connection wrapper for #{
|
77
|
+
raise ImplementationMissingError, "Couldn't find a connection wrapper for #{items.class} during #{field.path} (#{items.inspect})"
|
78
78
|
end
|
79
79
|
|
80
80
|
impl.new(
|
81
|
-
|
81
|
+
items,
|
82
82
|
context: context,
|
83
|
+
parent: parent,
|
83
84
|
max_page_size: field.max_page_size || context.schema.default_max_page_size,
|
84
85
|
first: arguments[:first],
|
85
86
|
after: arguments[:after],
|
86
87
|
last: arguments[:last],
|
87
88
|
before: arguments[:before],
|
89
|
+
edge_class: edge_class_for_field(field),
|
88
90
|
)
|
89
91
|
end
|
90
92
|
|
93
|
+
# use an override if there is one
|
94
|
+
# @api private
|
95
|
+
def edge_class_for_field(field)
|
96
|
+
conn_type = field.type.unwrap
|
97
|
+
conn_type_edge_type = conn_type.respond_to?(:edge_class) && conn_type.edge_class
|
98
|
+
if conn_type_edge_type && conn_type_edge_type != Relay::Edge
|
99
|
+
conn_type_edge_type
|
100
|
+
else
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
end
|
91
104
|
protected
|
92
105
|
|
93
106
|
attr_reader :wrappers
|
data/lib/graphql/query.rb
CHANGED
@@ -96,8 +96,7 @@ module GraphQL
|
|
96
96
|
@fragments = nil
|
97
97
|
@operations = nil
|
98
98
|
@validate = validate
|
99
|
-
|
100
|
-
@tracers = schema.tracers + GraphQL::Tracing.tracers + (context ? context.fetch(:tracers, []) : [])
|
99
|
+
@tracers = schema.tracers + (context ? context.fetch(:tracers, []) : [])
|
101
100
|
# Support `ctx[:backtrace] = true` for wrapping backtraces
|
102
101
|
if context && context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
|
103
102
|
@tracers << GraphQL::Backtrace::Tracer
|
@@ -168,7 +168,6 @@ module GraphQL
|
|
168
168
|
attr_accessor :scoped_context
|
169
169
|
|
170
170
|
def_delegators :@provided_values, :[]=
|
171
|
-
def_delegators :to_h, :fetch, :dig
|
172
171
|
def_delegators :@query, :trace, :interpreter?
|
173
172
|
|
174
173
|
# @!method []=(key, value)
|
@@ -180,6 +179,26 @@ module GraphQL
|
|
180
179
|
@provided_values[key]
|
181
180
|
end
|
182
181
|
|
182
|
+
UNSPECIFIED_FETCH_DEFAULT = Object.new
|
183
|
+
|
184
|
+
def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
|
185
|
+
if @scoped_context.key?(key)
|
186
|
+
@scoped_context[key]
|
187
|
+
elsif @provided_values.key?(key)
|
188
|
+
@provided_values[key]
|
189
|
+
elsif default != UNSPECIFIED_FETCH_DEFAULT
|
190
|
+
default
|
191
|
+
elsif block_given?
|
192
|
+
yield(self, key)
|
193
|
+
else
|
194
|
+
raise KeyError.new(key: key)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def dig(key, *other_keys)
|
199
|
+
@scoped_context.key?(key) ? @scoped_context.dig(key, *other_keys) : @provided_values.dig(key, *other_keys)
|
200
|
+
end
|
201
|
+
|
183
202
|
def to_h
|
184
203
|
@provided_values.merge(@scoped_context)
|
185
204
|
end
|
data/lib/graphql/schema.rb
CHANGED
@@ -127,7 +127,7 @@ module GraphQL
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
-
# @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered
|
130
|
+
# @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {#lazy_resolve}.
|
131
131
|
def lazy_method_name(obj)
|
132
132
|
lazy_methods.get(obj)
|
133
133
|
end
|
@@ -1075,6 +1075,7 @@ module GraphQL
|
|
1075
1075
|
raise GraphQL::Error, "Second definition of `subscription(...)` (#{new_subscription_object.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
|
1076
1076
|
else
|
1077
1077
|
@subscription_object = new_subscription_object
|
1078
|
+
add_subscription_extension_if_necessary
|
1078
1079
|
add_type_and_traverse(new_subscription_object, root: true)
|
1079
1080
|
nil
|
1080
1081
|
end
|
@@ -1533,9 +1534,9 @@ module GraphQL
|
|
1533
1534
|
|
1534
1535
|
# Add several directives at once
|
1535
1536
|
# @param new_directives [Class]
|
1536
|
-
def directives(new_directives
|
1537
|
-
if new_directives
|
1538
|
-
new_directives.each { |d| directive(d) }
|
1537
|
+
def directives(*new_directives)
|
1538
|
+
if new_directives.any?
|
1539
|
+
new_directives.flatten.each { |d| directive(d) }
|
1539
1540
|
end
|
1540
1541
|
|
1541
1542
|
find_inherited_value(:directives, default_directives).merge(own_directives)
|
@@ -1549,11 +1550,11 @@ module GraphQL
|
|
1549
1550
|
end
|
1550
1551
|
|
1551
1552
|
def default_directives
|
1552
|
-
{
|
1553
|
+
@default_directives ||= {
|
1553
1554
|
"include" => GraphQL::Schema::Directive::Include,
|
1554
1555
|
"skip" => GraphQL::Schema::Directive::Skip,
|
1555
1556
|
"deprecated" => GraphQL::Schema::Directive::Deprecated,
|
1556
|
-
}
|
1557
|
+
}.freeze
|
1557
1558
|
end
|
1558
1559
|
|
1559
1560
|
def tracer(new_tracer)
|
@@ -1648,6 +1649,24 @@ module GraphQL
|
|
1648
1649
|
end
|
1649
1650
|
end
|
1650
1651
|
|
1652
|
+
# @api private
|
1653
|
+
def add_subscription_extension_if_necessary
|
1654
|
+
if interpreter? && !defined?(@subscription_extension_added) && subscription && self.subscriptions
|
1655
|
+
@subscription_extension_added = true
|
1656
|
+
if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot)
|
1657
|
+
warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).")
|
1658
|
+
else
|
1659
|
+
subscription.fields.each do |name, field|
|
1660
|
+
field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
|
1661
|
+
end
|
1662
|
+
end
|
1663
|
+
end
|
1664
|
+
end
|
1665
|
+
|
1666
|
+
def query_stack_error(query, err)
|
1667
|
+
query.analysis_errors.push(GraphQL::AnalysisError.new("This query is too large to execute."))
|
1668
|
+
end
|
1669
|
+
|
1651
1670
|
private
|
1652
1671
|
|
1653
1672
|
def lazy_methods
|
@@ -1768,16 +1787,7 @@ module GraphQL
|
|
1768
1787
|
if owner.kind.union?
|
1769
1788
|
# It's a union with possible_types
|
1770
1789
|
# Replace the item by class name
|
1771
|
-
owner.
|
1772
|
-
possible_type = tm.object_type
|
1773
|
-
if possible_type.is_a?(String) && (possible_type == type.name)
|
1774
|
-
# This is a match of Ruby class names, not graphql names,
|
1775
|
-
# since strings are used to refer to constants.
|
1776
|
-
tm.object_type = type
|
1777
|
-
elsif possible_type.is_a?(LateBoundType) && possible_type.graphql_name == type.graphql_name
|
1778
|
-
tm.object_type = type
|
1779
|
-
end
|
1780
|
-
}
|
1790
|
+
owner.assign_type_membership_object_type(type)
|
1781
1791
|
own_possible_types[owner.graphql_name] = owner.possible_types
|
1782
1792
|
elsif type.kind.interface? && owner.kind.object?
|
1783
1793
|
new_interfaces = []
|
@@ -43,9 +43,7 @@ module GraphQL
|
|
43
43
|
when GraphQL::Language::Nodes::EnumTypeDefinition
|
44
44
|
types[definition.name] = build_enum_type(definition, type_resolver)
|
45
45
|
when GraphQL::Language::Nodes::ObjectTypeDefinition
|
46
|
-
|
47
|
-
should_extend_subscription_root = is_subscription_root && interpreter
|
48
|
-
types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve, extend_subscription_root: should_extend_subscription_root)
|
46
|
+
types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve)
|
49
47
|
when GraphQL::Language::Nodes::InterfaceTypeDefinition
|
50
48
|
types[definition.name] = build_interface_type(definition, type_resolver)
|
51
49
|
when GraphQL::Language::Nodes::UnionTypeDefinition
|
@@ -204,7 +202,7 @@ module GraphQL
|
|
204
202
|
end
|
205
203
|
end
|
206
204
|
|
207
|
-
def build_object_type(object_type_definition, type_resolver, default_resolve
|
205
|
+
def build_object_type(object_type_definition, type_resolver, default_resolve:)
|
208
206
|
builder = self
|
209
207
|
type_def = nil
|
210
208
|
typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def, field, obj, args, ctx) }
|
@@ -214,10 +212,6 @@ module GraphQL
|
|
214
212
|
graphql_name(object_type_definition.name)
|
215
213
|
description(object_type_definition.description)
|
216
214
|
ast_node(object_type_definition)
|
217
|
-
if extend_subscription_root
|
218
|
-
# This has to come before `field ...` configurations since it modifies them
|
219
|
-
extend Subscriptions::SubscriptionRoot
|
220
|
-
end
|
221
215
|
|
222
216
|
object_type_definition.interfaces.each do |interface_name|
|
223
217
|
interface_defn = type_resolver.call(interface_name)
|
@@ -303,7 +297,7 @@ module GraphQL
|
|
303
297
|
|
304
298
|
field_definitions.map do |field_definition|
|
305
299
|
type_name = resolve_type_name(field_definition.type)
|
306
|
-
|
300
|
+
resolve_method_name = "resolve_field_#{field_definition.name}"
|
307
301
|
owner.field(
|
308
302
|
field_definition.name,
|
309
303
|
description: field_definition.description,
|
@@ -315,14 +309,15 @@ module GraphQL
|
|
315
309
|
ast_node: field_definition,
|
316
310
|
method_conflict_warning: false,
|
317
311
|
camelize: false,
|
312
|
+
resolver_method: resolve_method_name,
|
318
313
|
) do
|
319
314
|
builder.build_arguments(self, field_definition.arguments, type_resolver)
|
320
315
|
|
321
316
|
# Don't do this for interfaces
|
322
317
|
if default_resolve
|
323
|
-
|
324
|
-
|
325
|
-
default_resolve.call(
|
318
|
+
owner.send(:define_method, resolve_method_name) do |**args|
|
319
|
+
field_instance = self.class.get_field(field_definition.name)
|
320
|
+
default_resolve.call(field_instance, object, args, context)
|
326
321
|
end
|
327
322
|
end
|
328
323
|
end
|
@@ -45,8 +45,10 @@ module GraphQL
|
|
45
45
|
@resolve_hash[type_name_s][field_name.to_s] = resolve_fn
|
46
46
|
end
|
47
47
|
when Proc
|
48
|
-
# for example,
|
48
|
+
# for example, "resolve_type"
|
49
49
|
@resolve_hash[type_name_s] = fields
|
50
|
+
else
|
51
|
+
raise ArgumentError, "Unexpected resolve hash value for #{type_name.inspect}: #{fields.inspect} (#{fields.class})"
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
@@ -41,6 +41,7 @@ module GraphQL
|
|
41
41
|
|
42
42
|
def initialize(graphql_name, desc = nil, owner:, ast_node: nil, description: nil, value: nil, deprecation_reason: nil, &block)
|
43
43
|
@graphql_name = graphql_name.to_s
|
44
|
+
GraphQL::NameValidator.validate!(@graphql_name)
|
44
45
|
@description = desc || description
|
45
46
|
@value = value.nil? ? @graphql_name : value
|
46
47
|
@deprecation_reason = deprecation_reason
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -36,9 +36,18 @@ module GraphQL
|
|
36
36
|
# @return [Symbol] The method on the type to look up
|
37
37
|
attr_reader :resolver_method
|
38
38
|
|
39
|
-
# @return [Class] The
|
39
|
+
# @return [Class] The thing this field was defined on (type, mutation, resolver)
|
40
40
|
attr_accessor :owner
|
41
41
|
|
42
|
+
# @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type)
|
43
|
+
def owner_type
|
44
|
+
@owner_type ||= if owner < GraphQL::Schema::Mutation
|
45
|
+
owner.payload_type
|
46
|
+
else
|
47
|
+
owner
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
42
51
|
# @return [Symbol] the original name of the field, passed in by the user
|
43
52
|
attr_reader :original_name
|
44
53
|
|
@@ -191,9 +200,10 @@ module GraphQL
|
|
191
200
|
# @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
|
192
201
|
# @param extensions [Array<Class, Hash<Class => Object>>] Named extensions to apply to this field (see also {#extension})
|
193
202
|
# @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
|
203
|
+
# @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
|
194
204
|
# @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
|
195
205
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
196
|
-
def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: [], extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, arguments: EMPTY_HASH, &definition_block)
|
206
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: [], extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, &definition_block)
|
197
207
|
if name.nil?
|
198
208
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
199
209
|
end
|
@@ -237,8 +247,8 @@ module GraphQL
|
|
237
247
|
end
|
238
248
|
|
239
249
|
# TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
|
240
|
-
method_name = method || hash_key ||
|
241
|
-
resolver_method ||=
|
250
|
+
method_name = method || hash_key || name_s
|
251
|
+
resolver_method ||= name_s.to_sym
|
242
252
|
|
243
253
|
@method_str = method_name.to_s
|
244
254
|
@method_sym = method_name.to_sym
|
@@ -251,6 +261,7 @@ module GraphQL
|
|
251
261
|
@max_page_size = max_page_size == :not_given ? nil : max_page_size
|
252
262
|
@introspection = introspection
|
253
263
|
@extras = extras
|
264
|
+
@broadcastable = broadcastable
|
254
265
|
@resolver_class = resolver_class
|
255
266
|
@scope = scope
|
256
267
|
@trace = trace
|
@@ -295,6 +306,13 @@ module GraphQL
|
|
295
306
|
end
|
296
307
|
end
|
297
308
|
|
309
|
+
# If true, subscription updates with this field can be shared between viewers
|
310
|
+
# @return [Boolean, nil]
|
311
|
+
# @see GraphQL::Subscriptions::BroadcastAnalyzer
|
312
|
+
def broadcastable?
|
313
|
+
@broadcastable
|
314
|
+
end
|
315
|
+
|
298
316
|
# @param text [String]
|
299
317
|
# @return [String]
|
300
318
|
def description(text = nil)
|
@@ -534,7 +552,7 @@ module GraphQL
|
|
534
552
|
@resolve_proc.call(extended_obj, args, ctx)
|
535
553
|
end
|
536
554
|
else
|
537
|
-
public_send_field(after_obj, ruby_args,
|
555
|
+
public_send_field(after_obj, ruby_args, query_ctx)
|
538
556
|
end
|
539
557
|
else
|
540
558
|
err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
|
@@ -558,30 +576,7 @@ module GraphQL
|
|
558
576
|
application_object = object.object
|
559
577
|
ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
|
560
578
|
if is_authorized
|
561
|
-
|
562
|
-
with_extensions(object, args, ctx) do |extended_obj, extended_args|
|
563
|
-
field_receiver = if @resolver_class
|
564
|
-
resolver_obj = if extended_obj.is_a?(GraphQL::Schema::Object)
|
565
|
-
extended_obj.object
|
566
|
-
else
|
567
|
-
extended_obj
|
568
|
-
end
|
569
|
-
@resolver_class.new(object: resolver_obj, context: ctx, field: self)
|
570
|
-
else
|
571
|
-
extended_obj
|
572
|
-
end
|
573
|
-
|
574
|
-
if field_receiver.respond_to?(@resolver_method)
|
575
|
-
# Call the method with kwargs, if there are any
|
576
|
-
if extended_args.any?
|
577
|
-
field_receiver.public_send(@resolver_method, **extended_args)
|
578
|
-
else
|
579
|
-
field_receiver.public_send(@resolver_method)
|
580
|
-
end
|
581
|
-
else
|
582
|
-
resolve_field_method(field_receiver, extended_args, ctx)
|
583
|
-
end
|
584
|
-
end
|
579
|
+
public_send_field(object, args, ctx)
|
585
580
|
else
|
586
581
|
err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
|
587
582
|
ctx.schema.unauthorized_field(err)
|
@@ -597,43 +592,6 @@ module GraphQL
|
|
597
592
|
err
|
598
593
|
end
|
599
594
|
|
600
|
-
# Find a way to resolve this field, checking:
|
601
|
-
#
|
602
|
-
# - Hash keys, if the wrapped object is a hash;
|
603
|
-
# - A method on the wrapped object;
|
604
|
-
# - Or, raise not implemented.
|
605
|
-
#
|
606
|
-
# This can be overridden by defining a method on the object type.
|
607
|
-
# @param obj [GraphQL::Schema::Object]
|
608
|
-
# @param ruby_kwargs [Hash<Symbol => Object>]
|
609
|
-
# @param ctx [GraphQL::Query::Context]
|
610
|
-
def resolve_field_method(obj, ruby_kwargs, ctx)
|
611
|
-
if obj.object.is_a?(Hash)
|
612
|
-
inner_object = obj.object
|
613
|
-
if inner_object.key?(@method_sym)
|
614
|
-
inner_object[@method_sym]
|
615
|
-
else
|
616
|
-
inner_object[@method_str]
|
617
|
-
end
|
618
|
-
elsif obj.object.respond_to?(@method_sym)
|
619
|
-
if ruby_kwargs.any?
|
620
|
-
obj.object.public_send(@method_sym, **ruby_kwargs)
|
621
|
-
else
|
622
|
-
obj.object.public_send(@method_sym)
|
623
|
-
end
|
624
|
-
else
|
625
|
-
raise <<-ERR
|
626
|
-
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
627
|
-
|
628
|
-
- `#{obj.class}##{@resolver_method}`, which did not exist
|
629
|
-
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
630
|
-
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
631
|
-
|
632
|
-
To implement this field, define one of the methods above (and check for typos)
|
633
|
-
ERR
|
634
|
-
end
|
635
|
-
end
|
636
|
-
|
637
595
|
# @param ctx [GraphQL::Query::Context::FieldResolutionContext]
|
638
596
|
def fetch_extra(extra_name, ctx)
|
639
597
|
if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
|
@@ -704,24 +662,52 @@ module GraphQL
|
|
704
662
|
end
|
705
663
|
end
|
706
664
|
|
707
|
-
def public_send_field(
|
708
|
-
query_ctx
|
709
|
-
with_extensions(obj, ruby_kwargs, query_ctx) do |extended_obj, extended_args|
|
665
|
+
def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
|
666
|
+
with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
|
710
667
|
if @resolver_class
|
711
|
-
if
|
712
|
-
|
668
|
+
if obj.is_a?(GraphQL::Schema::Object)
|
669
|
+
obj = obj.object
|
713
670
|
end
|
714
|
-
|
671
|
+
obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
|
715
672
|
end
|
716
673
|
|
717
|
-
|
718
|
-
|
719
|
-
|
674
|
+
# Find a way to resolve this field, checking:
|
675
|
+
#
|
676
|
+
# - A method on the type instance;
|
677
|
+
# - Hash keys, if the wrapped object is a hash;
|
678
|
+
# - A method on the wrapped object;
|
679
|
+
# - Or, raise not implemented.
|
680
|
+
#
|
681
|
+
if obj.respond_to?(@resolver_method)
|
682
|
+
# Call the method with kwargs, if there are any
|
683
|
+
if ruby_kwargs.any?
|
684
|
+
obj.public_send(@resolver_method, **ruby_kwargs)
|
685
|
+
else
|
686
|
+
obj.public_send(@resolver_method)
|
687
|
+
end
|
688
|
+
elsif obj.object.is_a?(Hash)
|
689
|
+
inner_object = obj.object
|
690
|
+
if inner_object.key?(@method_sym)
|
691
|
+
inner_object[@method_sym]
|
720
692
|
else
|
721
|
-
|
693
|
+
inner_object[@method_str]
|
694
|
+
end
|
695
|
+
elsif obj.object.respond_to?(@method_sym)
|
696
|
+
if ruby_kwargs.any?
|
697
|
+
obj.object.public_send(@method_sym, **ruby_kwargs)
|
698
|
+
else
|
699
|
+
obj.object.public_send(@method_sym)
|
722
700
|
end
|
723
701
|
else
|
724
|
-
|
702
|
+
raise <<-ERR
|
703
|
+
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
704
|
+
|
705
|
+
- `#{obj.class}##{@resolver_method}`, which did not exist
|
706
|
+
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
707
|
+
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
708
|
+
|
709
|
+
To implement this field, define one of the methods above (and check for typos)
|
710
|
+
ERR
|
725
711
|
end
|
726
712
|
end
|
727
713
|
end
|