graphql 2.0.23 → 2.0.25
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/mutation_delete_generator.rb +1 -1
- data/lib/generators/graphql/mutation_update_generator.rb +1 -1
- data/lib/graphql/dataloader/source.rb +69 -45
- data/lib/graphql/dataloader.rb +4 -4
- data/lib/graphql/execution/interpreter/runtime.rb +13 -19
- data/lib/graphql/introspection/entry_points.rb +1 -1
- data/lib/graphql/query/validation_pipeline.rb +2 -1
- data/lib/graphql/query.rb +16 -1
- data/lib/graphql/schema/addition.rb +32 -12
- data/lib/graphql/schema/field.rb +1 -1
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/member/build_type.rb +10 -2
- data/lib/graphql/schema/object.rb +5 -0
- data/lib/graphql/schema.rb +69 -15
- data/lib/graphql/tracing/appoptics_trace.rb +32 -12
- data/lib/graphql/tracing/data_dog_trace.rb +42 -17
- data/lib/graphql/tracing/platform_trace.rb +22 -13
- data/lib/graphql/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 289b06689b36a3673c053d13d2ce0835a089e92db60e3e24e8af791fdd934d81
|
4
|
+
data.tar.gz: 3c5257c0732d0729b97ac92376427ccba26ef66397f9b9d07c18e9792ea03251
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72ad503ed008aa168a63c1a40ac9e44dbb9b2d6a7871f1cd6191a692d6275506ce17f0df3135ae0a2ef95fe695a457d76239a2623f477cf7cd7f27cc124888c7
|
7
|
+
data.tar.gz: 90976b77a4230bf8a7d3fe5470deae70082e6caac1d3b30c799e958621cd2177283d0f83eb06128081e7e2179e33e718a1ba9353ab388fac83553e0988e7b4bc
|
@@ -6,7 +6,7 @@ module Graphql
|
|
6
6
|
# TODO: What other options should be supported?
|
7
7
|
#
|
8
8
|
# @example Generate a `GraphQL::Schema::RelayClassicMutation` by name
|
9
|
-
# rails g graphql:mutation
|
9
|
+
# rails g graphql:mutation DeletePostMutation
|
10
10
|
class MutationDeleteGenerator < OrmMutationsBase
|
11
11
|
|
12
12
|
desc "Scaffold a Relay Classic ORM delete mutation for the given model class"
|
@@ -6,7 +6,7 @@ module Graphql
|
|
6
6
|
# TODO: What other options should be supported?
|
7
7
|
#
|
8
8
|
# @example Generate a `GraphQL::Schema::RelayClassicMutation` by name
|
9
|
-
# rails g graphql:mutation
|
9
|
+
# rails g graphql:mutation UpdatePostMutation
|
10
10
|
class MutationUpdateGenerator < OrmMutationsBase
|
11
11
|
|
12
12
|
desc "Scaffold a Relay Classic ORM update mutation for the given model class"
|
@@ -7,9 +7,9 @@ module GraphQL
|
|
7
7
|
# @api private
|
8
8
|
def setup(dataloader)
|
9
9
|
# These keys have been requested but haven't been fetched yet
|
10
|
-
@
|
10
|
+
@pending = {}
|
11
11
|
# These keys have been passed to `fetch` but haven't been finished yet
|
12
|
-
@
|
12
|
+
@fetching = {}
|
13
13
|
# { key => result }
|
14
14
|
@results = {}
|
15
15
|
@dataloader = dataloader
|
@@ -18,42 +18,66 @@ module GraphQL
|
|
18
18
|
attr_reader :dataloader
|
19
19
|
|
20
20
|
# @return [Dataloader::Request] a pending request for a value from `key`. Call `.load` on that object to wait for the result.
|
21
|
-
def request(
|
22
|
-
|
23
|
-
|
21
|
+
def request(value)
|
22
|
+
res_key = result_key_for(value)
|
23
|
+
if !@results.key?(res_key)
|
24
|
+
@pending[res_key] ||= value
|
24
25
|
end
|
25
|
-
Dataloader::Request.new(self,
|
26
|
+
Dataloader::Request.new(self, value)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Implement this method to return a stable identifier if different
|
30
|
+
# key objects should load the same data value.
|
31
|
+
#
|
32
|
+
# @param value [Object] A value passed to `.request` or `.load`, for which a value will be loaded
|
33
|
+
# @return [Object] The key for tracking this pending data
|
34
|
+
def result_key_for(value)
|
35
|
+
value
|
26
36
|
end
|
27
37
|
|
28
38
|
# @return [Dataloader::Request] a pending request for a values from `keys`. Call `.load` on that object to wait for the results.
|
29
|
-
def request_all(
|
30
|
-
|
31
|
-
|
32
|
-
|
39
|
+
def request_all(values)
|
40
|
+
values.each do |v|
|
41
|
+
res_key = result_key_for(v)
|
42
|
+
if !@results.key?(res_key)
|
43
|
+
@pending[res_key] ||= v
|
44
|
+
end
|
45
|
+
end
|
46
|
+
Dataloader::RequestAll.new(self, values)
|
33
47
|
end
|
34
48
|
|
35
|
-
# @param
|
49
|
+
# @param value [Object] A loading value which will be passed to {#fetch} if it isn't already in the internal cache.
|
36
50
|
# @return [Object] The result from {#fetch} for `key`. If `key` hasn't been loaded yet, the Fiber will yield until it's loaded.
|
37
|
-
def load(
|
38
|
-
|
39
|
-
|
51
|
+
def load(value)
|
52
|
+
result_key = result_key_for(value)
|
53
|
+
if @results.key?(result_key)
|
54
|
+
result_for(result_key)
|
40
55
|
else
|
41
|
-
@
|
42
|
-
sync
|
43
|
-
result_for(
|
56
|
+
@pending[result_key] ||= value
|
57
|
+
sync([result_key])
|
58
|
+
result_for(result_key)
|
44
59
|
end
|
45
60
|
end
|
46
61
|
|
47
|
-
# @param
|
62
|
+
# @param values [Array<Object>] Loading keys which will be passed to `#fetch` (or read from the internal cache).
|
48
63
|
# @return [Object] The result from {#fetch} for `keys`. If `keys` haven't been loaded yet, the Fiber will yield until they're loaded.
|
49
|
-
def load_all(
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
64
|
+
def load_all(values)
|
65
|
+
result_keys = []
|
66
|
+
pending_keys = []
|
67
|
+
values.each { |v|
|
68
|
+
k = result_key_for(v)
|
69
|
+
result_keys << k
|
70
|
+
if !@results.key?(k)
|
71
|
+
@pending[k] ||= v
|
72
|
+
pending_keys << k
|
73
|
+
end
|
74
|
+
}
|
75
|
+
|
76
|
+
if pending_keys.any?
|
77
|
+
sync(pending_keys)
|
54
78
|
end
|
55
79
|
|
56
|
-
|
80
|
+
result_keys.map { |k| result_for(k) }
|
57
81
|
end
|
58
82
|
|
59
83
|
# Subclasses must implement this method to return a value for each of `keys`
|
@@ -67,14 +91,13 @@ module GraphQL
|
|
67
91
|
# Wait for a batch, if there's anything to batch.
|
68
92
|
# Then run the batch and update the cache.
|
69
93
|
# @return [void]
|
70
|
-
def sync
|
71
|
-
pending_keys = @pending_keys.dup
|
94
|
+
def sync(pending_result_keys)
|
72
95
|
@dataloader.yield
|
73
96
|
iterations = 0
|
74
|
-
while
|
97
|
+
while pending_result_keys.any? { |key| !@results.key?(key) }
|
75
98
|
iterations += 1
|
76
99
|
if iterations > 1000
|
77
|
-
raise "#{self.class}#sync tried 1000 times to load pending keys (#{
|
100
|
+
raise "#{self.class}#sync tried 1000 times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency."
|
78
101
|
end
|
79
102
|
@dataloader.yield
|
80
103
|
end
|
@@ -83,15 +106,18 @@ module GraphQL
|
|
83
106
|
|
84
107
|
# @return [Boolean] True if this source has any pending requests for data.
|
85
108
|
def pending?
|
86
|
-
!@
|
109
|
+
!@pending.empty?
|
87
110
|
end
|
88
111
|
|
89
112
|
# Add these key-value pairs to this source's cache
|
90
113
|
# (future loads will use these merged values).
|
91
|
-
# @param
|
114
|
+
# @param new_results [Hash<Object => Object>] key-value pairs to cache in this source
|
92
115
|
# @return [void]
|
93
|
-
def merge(
|
94
|
-
|
116
|
+
def merge(new_results)
|
117
|
+
new_results.each do |new_k, new_v|
|
118
|
+
key = result_key_for(new_k)
|
119
|
+
@results[key] = new_v
|
120
|
+
end
|
95
121
|
nil
|
96
122
|
end
|
97
123
|
|
@@ -99,24 +125,22 @@ module GraphQL
|
|
99
125
|
# @api private
|
100
126
|
# @return [void]
|
101
127
|
def run_pending_keys
|
102
|
-
if !@
|
103
|
-
@
|
128
|
+
if !@fetching.empty?
|
129
|
+
@fetching.each_key { |k| @pending.delete(k) }
|
104
130
|
end
|
105
|
-
return if @
|
106
|
-
|
107
|
-
@
|
108
|
-
@
|
109
|
-
results = fetch(
|
110
|
-
|
131
|
+
return if @pending.empty?
|
132
|
+
fetch_h = @pending
|
133
|
+
@pending = {}
|
134
|
+
@fetching.merge!(fetch_h)
|
135
|
+
results = fetch(fetch_h.values)
|
136
|
+
fetch_h.each_with_index do |(key, _value), idx|
|
111
137
|
@results[key] = results[idx]
|
112
138
|
end
|
113
139
|
nil
|
114
140
|
rescue StandardError => error
|
115
|
-
|
141
|
+
fetch_h.each_key { |key| @results[key] = error }
|
116
142
|
ensure
|
117
|
-
|
118
|
-
@fetching_keys -= fetch_keys
|
119
|
-
end
|
143
|
+
fetch_h && fetch_h.each_key { |k| @fetching.delete(k) }
|
120
144
|
end
|
121
145
|
|
122
146
|
# These arguments are given to `dataloader.with(source_class, ...)`. The object
|
@@ -137,7 +161,7 @@ module GraphQL
|
|
137
161
|
[*batch_args, **batch_kwargs]
|
138
162
|
end
|
139
163
|
|
140
|
-
attr_reader :
|
164
|
+
attr_reader :pending
|
141
165
|
|
142
166
|
private
|
143
167
|
|
data/lib/graphql/dataloader.rb
CHANGED
@@ -111,8 +111,8 @@ module GraphQL
|
|
111
111
|
@source_cache.each do |source_class, batched_sources|
|
112
112
|
batched_sources.each do |batch_args, batched_source_instance|
|
113
113
|
if batched_source_instance.pending?
|
114
|
-
prev_pending_keys[batched_source_instance] = batched_source_instance.
|
115
|
-
batched_source_instance.
|
114
|
+
prev_pending_keys[batched_source_instance] = batched_source_instance.pending.dup
|
115
|
+
batched_source_instance.pending.clear
|
116
116
|
end
|
117
117
|
end
|
118
118
|
end
|
@@ -127,8 +127,8 @@ module GraphQL
|
|
127
127
|
res
|
128
128
|
ensure
|
129
129
|
@pending_jobs = prev_queue
|
130
|
-
prev_pending_keys.each do |source_instance,
|
131
|
-
source_instance.
|
130
|
+
prev_pending_keys.each do |source_instance, pending|
|
131
|
+
source_instance.pending.merge!(pending)
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
@@ -247,15 +247,15 @@ module GraphQL
|
|
247
247
|
st = get_current_runtime_state
|
248
248
|
st.current_object = query.root_value
|
249
249
|
st.current_result = @response
|
250
|
-
|
251
|
-
|
250
|
+
runtime_object = root_type.wrap(query.root_value, context)
|
251
|
+
runtime_object = schema.sync_lazy(runtime_object)
|
252
252
|
|
253
|
-
if
|
253
|
+
if runtime_object.nil?
|
254
254
|
# Root .authorized? returned false.
|
255
255
|
@response = nil
|
256
256
|
else
|
257
|
-
call_method_on_directives(:resolve,
|
258
|
-
gathered_selections = gather_selections(
|
257
|
+
call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
|
258
|
+
gathered_selections = gather_selections(runtime_object, root_type, root_operation.selections)
|
259
259
|
# This is kind of a hack -- `gathered_selections` is an Array if any of the selections
|
260
260
|
# require isolation during execution (because of runtime directives). In that case,
|
261
261
|
# make a new, isolated result hash for writing the result into. (That isolated response
|
@@ -280,9 +280,9 @@ module GraphQL
|
|
280
280
|
if (directives = selections[:graphql_directives])
|
281
281
|
selections.delete(:graphql_directives)
|
282
282
|
end
|
283
|
-
call_method_on_directives(:resolve,
|
283
|
+
call_method_on_directives(:resolve, runtime_object, directives) do
|
284
284
|
evaluate_selections(
|
285
|
-
|
285
|
+
runtime_object,
|
286
286
|
root_type,
|
287
287
|
root_op_type == "mutation",
|
288
288
|
selections,
|
@@ -438,10 +438,8 @@ module GraphQL
|
|
438
438
|
st.current_result = selections_result
|
439
439
|
st.current_result_name = result_name
|
440
440
|
|
441
|
-
object = owner_object
|
442
|
-
|
443
441
|
if is_introspection
|
444
|
-
|
442
|
+
owner_object = field_defn.owner.wrap(owner_object, context)
|
445
443
|
end
|
446
444
|
|
447
445
|
total_args_count = field_defn.arguments(context).size
|
@@ -449,14 +447,14 @@ module GraphQL
|
|
449
447
|
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
450
448
|
if field_defn.extras.size == 0
|
451
449
|
evaluate_selection_with_resolved_keyword_args(
|
452
|
-
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type,
|
450
|
+
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null
|
453
451
|
)
|
454
452
|
else
|
455
|
-
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type,
|
453
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
|
456
454
|
end
|
457
455
|
else
|
458
|
-
@query.arguments_cache.dataload_for(ast_node, field_defn,
|
459
|
-
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type,
|
456
|
+
@query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
|
457
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
|
460
458
|
end
|
461
459
|
end
|
462
460
|
end
|
@@ -774,7 +772,7 @@ module GraphQL
|
|
774
772
|
end
|
775
773
|
when "OBJECT"
|
776
774
|
object_proxy = begin
|
777
|
-
|
775
|
+
current_type.wrap(value, context)
|
778
776
|
rescue GraphQL::ExecutionError => err
|
779
777
|
err
|
780
778
|
end
|
@@ -1047,10 +1045,6 @@ module GraphQL
|
|
1047
1045
|
end
|
1048
1046
|
end
|
1049
1047
|
|
1050
|
-
def authorized_new(type, value, context)
|
1051
|
-
type.authorized_new(value, context)
|
1052
|
-
end
|
1053
|
-
|
1054
1048
|
def lazy?(object)
|
1055
1049
|
obj_class = object.class
|
1056
1050
|
is_lazy = @lazy_cache[obj_class]
|
@@ -11,7 +11,7 @@ module GraphQL
|
|
11
11
|
# Apply wrapping manually since this field isn't wrapped by instrumentation
|
12
12
|
schema = @context.query.schema
|
13
13
|
schema_type = schema.introspection_system.types["__Schema"]
|
14
|
-
schema_type.
|
14
|
+
schema_type.wrap(schema, @context)
|
15
15
|
end
|
16
16
|
|
17
17
|
def __type(name:)
|
@@ -68,7 +68,8 @@ module GraphQL
|
|
68
68
|
elsif @operation_name_error
|
69
69
|
@validation_errors << @operation_name_error
|
70
70
|
else
|
71
|
-
|
71
|
+
validator = @query.static_validator || @schema.static_validator
|
72
|
+
validation_result = validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
|
72
73
|
@validation_errors.concat(validation_result[:errors])
|
73
74
|
|
74
75
|
if @validation_errors.empty?
|
data/lib/graphql/query.rb
CHANGED
@@ -45,6 +45,20 @@ module GraphQL
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
# @return [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules.
|
49
|
+
attr_reader :static_validator
|
50
|
+
|
51
|
+
# @param new_validate [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules. This can't be reasssigned after validation.
|
52
|
+
def static_validator=(new_validator)
|
53
|
+
if defined?(@validation_pipeline) && @validation_pipeline && @validation_pipeline.has_validated?
|
54
|
+
raise ArgumentError, "Can't reassign Query#static_validator= after validation has run, remove this assignment."
|
55
|
+
elsif !new_validator.is_a?(GraphQL::StaticValidation::Validator)
|
56
|
+
raise ArgumentError, "Expected a `GraphQL::StaticValidation::Validator` instance."
|
57
|
+
else
|
58
|
+
@static_validator = new_validator
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
48
62
|
attr_writer :query_string
|
49
63
|
|
50
64
|
# @return [GraphQL::Language::Nodes::Document]
|
@@ -83,7 +97,7 @@ module GraphQL
|
|
83
97
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
84
98
|
# @param except [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns truthy
|
85
99
|
# @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
|
86
|
-
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil, warden: nil)
|
100
|
+
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil, warden: nil)
|
87
101
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
88
102
|
variables ||= {}
|
89
103
|
@schema = schema
|
@@ -97,6 +111,7 @@ module GraphQL
|
|
97
111
|
@fragments = nil
|
98
112
|
@operations = nil
|
99
113
|
@validate = validate
|
114
|
+
self.static_validator = static_validator if static_validator
|
100
115
|
context_tracers = (context ? context.fetch(:tracers, []) : [])
|
101
116
|
@tracers = schema.tracers + context_tracers
|
102
117
|
|
@@ -40,14 +40,21 @@ module GraphQL
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def add_directives_from(owner)
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
if (dir_instances = owner.directives).any?
|
44
|
+
dirs = dir_instances.map(&:class)
|
45
|
+
@directives.merge(dirs)
|
46
|
+
add_type_and_traverse(dirs)
|
47
|
+
end
|
46
48
|
end
|
47
49
|
|
48
50
|
def add_type_and_traverse(new_types)
|
49
51
|
late_types = []
|
50
|
-
|
52
|
+
path = []
|
53
|
+
new_types.each do |t|
|
54
|
+
path.push(t.graphql_name)
|
55
|
+
add_type(t, owner: nil, late_types: late_types, path: path)
|
56
|
+
path.pop
|
57
|
+
end
|
51
58
|
missed_late_types = 0
|
52
59
|
while (late_type_vals = late_types.shift)
|
53
60
|
type_owner, lt = late_type_vals
|
@@ -158,7 +165,9 @@ module GraphQL
|
|
158
165
|
type.all_argument_definitions.each do |arg|
|
159
166
|
arg_type = arg.type.unwrap
|
160
167
|
references_to(arg_type, from: arg)
|
161
|
-
|
168
|
+
path.push(arg.graphql_name)
|
169
|
+
add_type(arg_type, owner: arg, late_types: late_types, path: path)
|
170
|
+
path.pop
|
162
171
|
if arg.default_value?
|
163
172
|
@arguments_with_default_values << arg
|
164
173
|
end
|
@@ -179,18 +188,21 @@ module GraphQL
|
|
179
188
|
name = field.graphql_name
|
180
189
|
field_type = field.type.unwrap
|
181
190
|
references_to(field_type, from: field)
|
182
|
-
|
183
|
-
add_type(field_type, owner: field, late_types: late_types, path:
|
191
|
+
path.push(name)
|
192
|
+
add_type(field_type, owner: field, late_types: late_types, path: path)
|
184
193
|
add_directives_from(field)
|
185
194
|
field.all_argument_definitions.each do |arg|
|
186
195
|
add_directives_from(arg)
|
187
196
|
arg_type = arg.type.unwrap
|
188
197
|
references_to(arg_type, from: arg)
|
189
|
-
|
198
|
+
path.push(arg.graphql_name)
|
199
|
+
add_type(arg_type, owner: arg, late_types: late_types, path: path)
|
200
|
+
path.pop
|
190
201
|
if arg.default_value?
|
191
202
|
@arguments_with_default_values << arg
|
192
203
|
end
|
193
204
|
end
|
205
|
+
path.pop
|
194
206
|
end
|
195
207
|
end
|
196
208
|
if type.kind.input_object?
|
@@ -198,7 +210,9 @@ module GraphQL
|
|
198
210
|
add_directives_from(arg)
|
199
211
|
arg_type = arg.type.unwrap
|
200
212
|
references_to(arg_type, from: arg)
|
201
|
-
|
213
|
+
path.push(arg.graphql_name)
|
214
|
+
add_type(arg_type, owner: arg, late_types: late_types, path: path)
|
215
|
+
path.pop
|
202
216
|
if arg.default_value?
|
203
217
|
@arguments_with_default_values << arg
|
204
218
|
end
|
@@ -206,14 +220,18 @@ module GraphQL
|
|
206
220
|
end
|
207
221
|
if type.kind.union?
|
208
222
|
@possible_types[type.graphql_name] = type.all_possible_types
|
223
|
+
path.push("possible_types")
|
209
224
|
type.all_possible_types.each do |t|
|
210
|
-
add_type(t, owner: type, late_types: late_types, path: path
|
225
|
+
add_type(t, owner: type, late_types: late_types, path: path)
|
211
226
|
end
|
227
|
+
path.pop
|
212
228
|
end
|
213
229
|
if type.kind.interface?
|
230
|
+
path.push("orphan_types")
|
214
231
|
type.orphan_types.each do |t|
|
215
|
-
add_type(t, owner: type, late_types: late_types, path: path
|
232
|
+
add_type(t, owner: type, late_types: late_types, path: path)
|
216
233
|
end
|
234
|
+
path.pop
|
217
235
|
end
|
218
236
|
if type.kind.object?
|
219
237
|
possible_types_for_this_name = @possible_types[type.graphql_name] ||= []
|
@@ -221,6 +239,7 @@ module GraphQL
|
|
221
239
|
end
|
222
240
|
|
223
241
|
if type.kind.object? || type.kind.interface?
|
242
|
+
path.push("implements")
|
224
243
|
type.interface_type_memberships.each do |interface_type_membership|
|
225
244
|
case interface_type_membership
|
226
245
|
when Schema::TypeMembership
|
@@ -235,8 +254,9 @@ module GraphQL
|
|
235
254
|
else
|
236
255
|
raise ArgumentError, "Invariant: unexpected type membership for #{type.graphql_name}: #{interface_type_membership.class} (#{interface_type_membership.inspect})"
|
237
256
|
end
|
238
|
-
add_type(interface_type, owner: type, late_types: late_types, path: path
|
257
|
+
add_type(interface_type, owner: type, late_types: late_types, path: path)
|
239
258
|
end
|
259
|
+
path.pop
|
240
260
|
end
|
241
261
|
end
|
242
262
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -155,7 +155,7 @@ module GraphQL
|
|
155
155
|
if obj.is_a?(GraphQL::Schema::Object)
|
156
156
|
obj = obj.object
|
157
157
|
end
|
158
|
-
wrapped_object = @object_class.
|
158
|
+
wrapped_object = @object_class.wrap(obj, query_ctx)
|
159
159
|
@inner_resolve.call(wrapped_object, args, ctx)
|
160
160
|
end
|
161
161
|
end
|
@@ -109,7 +109,14 @@ module GraphQL
|
|
109
109
|
to_type_name(something.name)
|
110
110
|
end
|
111
111
|
when String
|
112
|
-
something.
|
112
|
+
if something.include?("]") ||
|
113
|
+
something.include?("[") ||
|
114
|
+
something.include?("!") ||
|
115
|
+
something.include?("::")
|
116
|
+
something.gsub(/\]\[\!/, "").split("::").last
|
117
|
+
else
|
118
|
+
something
|
119
|
+
end
|
113
120
|
when GraphQL::Schema::NonNull, GraphQL::Schema::List
|
114
121
|
to_type_name(something.unwrap)
|
115
122
|
else
|
@@ -122,7 +129,8 @@ module GraphQL
|
|
122
129
|
return string unless string.include?("_")
|
123
130
|
camelized = string.split('_').each(&:capitalize!).join
|
124
131
|
camelized[0] = camelized[0].downcase
|
125
|
-
if
|
132
|
+
if string.start_with?("_")
|
133
|
+
match_data = string.match(/\A(_+)/)
|
126
134
|
camelized = "#{match_data[0]}#{camelized}"
|
127
135
|
end
|
128
136
|
camelized
|
@@ -30,6 +30,11 @@ module GraphQL
|
|
30
30
|
# @see authorized_new to make instances
|
31
31
|
protected :new
|
32
32
|
|
33
|
+
# This is called by the runtime to return an object to call methods on.
|
34
|
+
def wrap(object, context)
|
35
|
+
authorized_new(object, context)
|
36
|
+
end
|
37
|
+
|
33
38
|
# Make a new instance of this type _if_ the auth check passes,
|
34
39
|
# otherwise, raise an error.
|
35
40
|
#
|
data/lib/graphql/schema.rb
CHANGED
@@ -158,16 +158,24 @@ module GraphQL
|
|
158
158
|
def trace_class_for(mode)
|
159
159
|
@trace_modes ||= {}
|
160
160
|
@trace_modes[mode] ||= begin
|
161
|
-
|
161
|
+
case mode
|
162
|
+
when :default
|
163
|
+
superclass_base_class = if superclass.respond_to?(:trace_class_for)
|
164
|
+
superclass.trace_class_for(mode)
|
165
|
+
else
|
166
|
+
GraphQL::Tracing::Trace
|
167
|
+
end
|
168
|
+
Class.new(superclass_base_class)
|
169
|
+
when :default_backtrace
|
162
170
|
schema_base_class = trace_class_for(:default)
|
163
171
|
Class.new(schema_base_class) do
|
164
172
|
include(GraphQL::Backtrace::Trace)
|
165
173
|
end
|
166
|
-
elsif superclass.respond_to?(:trace_class_for)
|
167
|
-
superclass_base_class = superclass.trace_class_for(mode)
|
168
|
-
Class.new(superclass_base_class)
|
169
174
|
else
|
170
|
-
|
175
|
+
mods = trace_modules_for(mode)
|
176
|
+
Class.new(trace_class_for(:default)) do
|
177
|
+
mods.any? && include(*mods)
|
178
|
+
end
|
171
179
|
end
|
172
180
|
end
|
173
181
|
end
|
@@ -183,6 +191,19 @@ module GraphQL
|
|
183
191
|
nil
|
184
192
|
end
|
185
193
|
|
194
|
+
def own_trace_modules
|
195
|
+
@own_trace_modules ||= Hash.new { |h, k| h[k] = [] }
|
196
|
+
end
|
197
|
+
|
198
|
+
# @return [Array<Module>] Modules added for tracing in `trace_mode`, including inherited ones
|
199
|
+
def trace_modules_for(trace_mode)
|
200
|
+
modules = own_trace_modules[trace_mode]
|
201
|
+
if superclass.respond_to?(:trace_modules_for)
|
202
|
+
modules += superclass.trace_modules_for(trace_mode)
|
203
|
+
end
|
204
|
+
modules
|
205
|
+
end
|
206
|
+
|
186
207
|
|
187
208
|
# Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
|
188
209
|
# @see {#as_json}
|
@@ -992,26 +1013,58 @@ module GraphQL
|
|
992
1013
|
# will be called at runtime.
|
993
1014
|
#
|
994
1015
|
# @param trace_mod [Module] A module that implements tracing methods
|
1016
|
+
# @param mode [Symbol] Trace module will only be used for this trade mode
|
995
1017
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
996
1018
|
# @return [void]
|
997
|
-
def trace_with(trace_mod, **options)
|
998
|
-
|
999
|
-
|
1019
|
+
def trace_with(trace_mod, mode: :default, **options)
|
1020
|
+
if mode.is_a?(Array)
|
1021
|
+
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
1022
|
+
else
|
1023
|
+
tc = trace_class_for(mode)
|
1024
|
+
tc.include(trace_mod)
|
1025
|
+
if mode != :default
|
1026
|
+
own_trace_modules[mode] << trace_mod
|
1027
|
+
end
|
1028
|
+
t_opts = trace_options_for(mode)
|
1029
|
+
t_opts.merge!(options)
|
1030
|
+
end
|
1031
|
+
nil
|
1000
1032
|
end
|
1001
1033
|
|
1002
|
-
|
1003
|
-
|
1034
|
+
# The options hash for this trace mode
|
1035
|
+
# @return [Hash]
|
1036
|
+
def trace_options_for(mode)
|
1037
|
+
@trace_options_for_mode ||= {}
|
1038
|
+
@trace_options_for_mode[mode] ||= begin
|
1039
|
+
if superclass.respond_to?(:trace_options_for)
|
1040
|
+
superclass.trace_options_for(mode).dup
|
1041
|
+
else
|
1042
|
+
{}
|
1043
|
+
end
|
1044
|
+
end
|
1004
1045
|
end
|
1005
1046
|
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1047
|
+
# Create a trace instance which will include the trace modules specified for the optional mode.
|
1048
|
+
#
|
1049
|
+
# @param mode [Symbol] Trace modules for this trade mode will be included
|
1050
|
+
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1051
|
+
# @return [Tracing::Trace]
|
1052
|
+
def new_trace(mode: nil, **options)
|
1053
|
+
target = options[:query] || options[:multiplex]
|
1054
|
+
mode ||= target && target.context[:trace_mode]
|
1055
|
+
|
1056
|
+
trace_mode = if mode
|
1057
|
+
mode
|
1058
|
+
elsif target && target.context[:backtrace]
|
1009
1059
|
:default_backtrace
|
1010
1060
|
else
|
1011
1061
|
:default
|
1012
1062
|
end
|
1013
|
-
|
1014
|
-
|
1063
|
+
|
1064
|
+
base_trace_options = trace_options_for(trace_mode)
|
1065
|
+
trace_options = base_trace_options.merge(options)
|
1066
|
+
trace_class_for_mode = trace_class_for(trace_mode)
|
1067
|
+
trace_class_for_mode.new(**trace_options)
|
1015
1068
|
end
|
1016
1069
|
|
1017
1070
|
def query_analyzer(new_analyzer)
|
@@ -1052,6 +1105,7 @@ module GraphQL
|
|
1052
1105
|
tracers: ctx[:tracers],
|
1053
1106
|
trace: ctx[:trace],
|
1054
1107
|
dataloader: ctx[:dataloader],
|
1108
|
+
trace_mode: ctx[:trace_mode],
|
1055
1109
|
}
|
1056
1110
|
else
|
1057
1111
|
{}
|
@@ -55,20 +55,39 @@ module GraphQL
|
|
55
55
|
RUBY
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
58
|
+
def execute_field(query:, field:, ast_node:, arguments:, object:)
|
59
|
+
return_type = field.type.unwrap
|
60
|
+
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
61
|
+
(field.trace.nil? && @trace_scalars) || field.trace
|
62
|
+
else
|
63
|
+
true
|
64
|
+
end
|
65
|
+
platform_key = if trace_field
|
66
|
+
@platform_key_cache[AppOpticsTrace].platform_field_key_cache[field]
|
67
|
+
else
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
if platform_key && trace_field
|
71
|
+
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
72
|
+
layer = platform_key
|
73
|
+
kvs = metadata({query: query, field: field, ast_node: ast_node, arguments: arguments, object: object}, layer)
|
74
|
+
|
75
|
+
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
76
|
+
kvs.clear # we don't have to send them twice
|
77
|
+
super
|
78
|
+
end
|
79
|
+
else
|
80
|
+
super
|
66
81
|
end
|
67
82
|
end
|
68
83
|
|
84
|
+
def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
|
85
|
+
execute_field(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
86
|
+
end
|
87
|
+
|
69
88
|
def authorized(**data)
|
70
89
|
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
71
|
-
layer = @platform_authorized_key_cache[data[:type]]
|
90
|
+
layer = @platform_key_cache[AppOpticsTrace].platform_authorized_key_cache[data[:type]]
|
72
91
|
kvs = metadata(data, layer)
|
73
92
|
|
74
93
|
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
@@ -79,7 +98,7 @@ module GraphQL
|
|
79
98
|
|
80
99
|
def authorized_lazy(**data)
|
81
100
|
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
82
|
-
layer = @platform_authorized_key_cache[data[:type]]
|
101
|
+
layer = @platform_key_cache[AppOpticsTrace].platform_authorized_key_cache[data[:type]]
|
83
102
|
kvs = metadata(data, layer)
|
84
103
|
|
85
104
|
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
@@ -90,7 +109,8 @@ module GraphQL
|
|
90
109
|
|
91
110
|
def resolve_type(**data)
|
92
111
|
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
93
|
-
layer = @platform_resolve_type_key_cache[data[:type]]
|
112
|
+
layer = @platform_key_cache[AppOpticsTrace].platform_resolve_type_key_cache[data[:type]]
|
113
|
+
|
94
114
|
kvs = metadata(data, layer)
|
95
115
|
|
96
116
|
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
@@ -101,7 +121,7 @@ module GraphQL
|
|
101
121
|
|
102
122
|
def resolve_type_lazy(**data)
|
103
123
|
return super if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
|
104
|
-
layer = @platform_resolve_type_key_cache[data[:type]]
|
124
|
+
layer = @platform_key_cache[AppOpticsTrace].platform_resolve_type_key_cache[data[:type]]
|
105
125
|
kvs = metadata(data, layer)
|
106
126
|
|
107
127
|
::AppOpticsAPM::SDK.trace(layer, kvs) do
|
@@ -18,6 +18,7 @@ module GraphQL
|
|
18
18
|
@analytics_enabled = analytics_available && Datadog::Contrib::Analytics.enabled?(analytics_enabled)
|
19
19
|
@analytics_sample_rate = analytics_sample_rate
|
20
20
|
@service_name = service
|
21
|
+
@has_prepare_span = respond_to?(:prepare_span)
|
21
22
|
super
|
22
23
|
end
|
23
24
|
|
@@ -66,38 +67,60 @@ module GraphQL
|
|
66
67
|
RUBY
|
67
68
|
end
|
68
69
|
}
|
69
|
-
|
70
|
+
if @has_prepare_span
|
71
|
+
prepare_span("#{trace_method.sub("platform_", "")}", data, span)
|
72
|
+
end
|
70
73
|
super
|
71
74
|
end
|
72
75
|
end
|
73
76
|
RUBY
|
74
77
|
end
|
75
78
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
def execute_field(span_key = "execute_field", query:, field:, ast_node:, arguments:, object:)
|
80
|
+
return_type = field.type.unwrap
|
81
|
+
trace_field = if return_type.kind.scalar? || return_type.kind.enum?
|
82
|
+
(field.trace.nil? && @trace_scalars) || field.trace
|
83
|
+
else
|
84
|
+
true
|
85
|
+
end
|
86
|
+
platform_key = if trace_field
|
87
|
+
@platform_key_cache[DataDogTrace].platform_field_key_cache[field]
|
88
|
+
else
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
if platform_key && trace_field
|
92
|
+
@tracer.trace(platform_key, service: @service_name) do |span|
|
93
|
+
span.span_type = 'custom'
|
94
|
+
if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
|
95
|
+
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
|
96
|
+
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, span_key)
|
97
|
+
end
|
98
|
+
if @has_prepare_span
|
99
|
+
prepare_span_data = { query: query, field: field, ast_node: ast_node, arguments: arguments, object: object }
|
100
|
+
prepare_span(span_key, prepare_span_data, span)
|
101
|
+
end
|
102
|
+
super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
82
103
|
end
|
83
|
-
|
84
|
-
|
104
|
+
else
|
105
|
+
super(query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
85
106
|
end
|
86
107
|
end
|
87
108
|
|
88
|
-
def
|
89
|
-
|
109
|
+
def execute_field_lazy(query:, field:, ast_node:, arguments:, object:)
|
110
|
+
execute_field("execute_field_lazy", query: query, field: field, ast_node: ast_node, arguments: arguments, object: object)
|
90
111
|
end
|
91
112
|
|
92
113
|
def authorized(object:, type:, query:, span_key: "authorized")
|
93
|
-
platform_key = @platform_authorized_key_cache[type]
|
114
|
+
platform_key = @platform_key_cache[DataDogTrace].platform_authorized_key_cache[type]
|
94
115
|
@tracer.trace(platform_key, service: @service_name) do |span|
|
95
116
|
span.span_type = 'custom'
|
96
117
|
if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
|
97
118
|
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
|
98
119
|
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, span_key)
|
99
120
|
end
|
100
|
-
|
121
|
+
if @has_prepare_span
|
122
|
+
prepare_span(span_key, {object: object, type: type, query: query}, span)
|
123
|
+
end
|
101
124
|
super(query: query, type: type, object: object)
|
102
125
|
end
|
103
126
|
end
|
@@ -107,14 +130,16 @@ module GraphQL
|
|
107
130
|
end
|
108
131
|
|
109
132
|
def resolve_type(object:, type:, query:, span_key: "resolve_type")
|
110
|
-
platform_key = @platform_resolve_type_key_cache[type]
|
133
|
+
platform_key = @platform_key_cache[DataDogTrace].platform_resolve_type_key_cache[type]
|
111
134
|
@tracer.trace(platform_key, service: @service_name) do |span|
|
112
135
|
span.span_type = 'custom'
|
113
136
|
if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
|
114
137
|
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
|
115
138
|
span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, span_key)
|
116
139
|
end
|
117
|
-
|
140
|
+
if @has_prepare_span
|
141
|
+
prepare_span(span_key, {object: object, type: type, query: query}, span)
|
142
|
+
end
|
118
143
|
super(query: query, type: type, object: object)
|
119
144
|
end
|
120
145
|
end
|
@@ -129,8 +154,8 @@ module GraphQL
|
|
129
154
|
# @param key [String] The event being traced
|
130
155
|
# @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
|
131
156
|
# @param span [Datadog::Tracing::SpanOperation] The datadog span for this event
|
132
|
-
def prepare_span(key, data, span)
|
133
|
-
end
|
157
|
+
# def prepare_span(key, data, span)
|
158
|
+
# end
|
134
159
|
|
135
160
|
def platform_field_key(field)
|
136
161
|
field.path
|
@@ -5,12 +5,22 @@ module GraphQL
|
|
5
5
|
module PlatformTrace
|
6
6
|
def initialize(trace_scalars: false, **_options)
|
7
7
|
@trace_scalars = trace_scalars
|
8
|
-
|
9
|
-
@
|
10
|
-
@platform_resolve_type_key_cache = Hash.new { |h, k| h[k] = platform_resolve_type_key(k) }
|
8
|
+
|
9
|
+
@platform_key_cache = Hash.new { |h, mod| h[mod] = mod::KeyCache.new }
|
11
10
|
super
|
12
11
|
end
|
13
12
|
|
13
|
+
module BaseKeyCache
|
14
|
+
def initialize
|
15
|
+
@platform_field_key_cache = Hash.new { |h, k| h[k] = platform_field_key(k) }
|
16
|
+
@platform_authorized_key_cache = Hash.new { |h, k| h[k] = platform_authorized_key(k) }
|
17
|
+
@platform_resolve_type_key_cache = Hash.new { |h, k| h[k] = platform_resolve_type_key(k) }
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :platform_field_key_cache, :platform_authorized_key_cache, :platform_resolve_type_key_cache
|
21
|
+
end
|
22
|
+
|
23
|
+
|
14
24
|
def platform_execute_field_lazy(*args, &block)
|
15
25
|
platform_execute_field(*args, &block)
|
16
26
|
end
|
@@ -24,10 +34,11 @@ module GraphQL
|
|
24
34
|
end
|
25
35
|
|
26
36
|
def self.included(child_class)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
37
|
+
key_methods_class = Class.new {
|
38
|
+
include(child_class)
|
39
|
+
include(BaseKeyCache)
|
40
|
+
}
|
41
|
+
child_class.const_set(:KeyCache, key_methods_class)
|
31
42
|
[:execute_field, :execute_field_lazy].each do |field_trace_method|
|
32
43
|
if !child_class.method_defined?(field_trace_method)
|
33
44
|
child_class.module_eval <<-RUBY, __FILE__, __LINE__
|
@@ -39,12 +50,12 @@ module GraphQL
|
|
39
50
|
true
|
40
51
|
end
|
41
52
|
platform_key = if trace_field
|
42
|
-
@platform_field_key_cache[field]
|
53
|
+
@platform_key_cache[#{child_class}].platform_field_key_cache[field]
|
43
54
|
else
|
44
55
|
nil
|
45
56
|
end
|
46
57
|
if platform_key && trace_field
|
47
|
-
platform_#{field_trace_method}(platform_key
|
58
|
+
platform_#{field_trace_method}(platform_key) do
|
48
59
|
super
|
49
60
|
end
|
50
61
|
else
|
@@ -60,7 +71,7 @@ module GraphQL
|
|
60
71
|
if !child_class.method_defined?(auth_trace_method)
|
61
72
|
child_class.module_eval <<-RUBY, __FILE__, __LINE__
|
62
73
|
def #{auth_trace_method}(type:, query:, object:)
|
63
|
-
platform_key = @platform_authorized_key_cache[type]
|
74
|
+
platform_key = @platform_key_cache[#{child_class}].platform_authorized_key_cache[type]
|
64
75
|
platform_#{auth_trace_method}(platform_key) do
|
65
76
|
super
|
66
77
|
end
|
@@ -73,7 +84,7 @@ module GraphQL
|
|
73
84
|
if !child_class.method_defined?(rt_trace_method)
|
74
85
|
child_class.module_eval <<-RUBY, __FILE__, __LINE__
|
75
86
|
def #{rt_trace_method}(query:, type:, object:)
|
76
|
-
platform_key = @platform_resolve_type_key_cache[type]
|
87
|
+
platform_key = @platform_key_cache[#{child_class}].platform_resolve_type_key_cache[type]
|
77
88
|
platform_#{rt_trace_method}(platform_key) do
|
78
89
|
super
|
79
90
|
end
|
@@ -83,8 +94,6 @@ module GraphQL
|
|
83
94
|
end
|
84
95
|
end
|
85
96
|
|
86
|
-
|
87
|
-
|
88
97
|
private
|
89
98
|
|
90
99
|
# Get the transaction name based on the operation type and name if possible, or fall back to a user provided
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.25
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -623,7 +623,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
623
623
|
- !ruby/object:Gem::Version
|
624
624
|
version: '0'
|
625
625
|
requirements: []
|
626
|
-
rubygems_version: 3.4.
|
626
|
+
rubygems_version: 3.4.10
|
627
627
|
signing_key:
|
628
628
|
specification_version: 4
|
629
629
|
summary: A GraphQL language and runtime for Ruby
|