graphql 1.10.14 → 1.11.4
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 +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
|