graphql 2.3.2 → 2.3.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/graphql/analysis/ast/query_complexity.rb +3 -0
- data/lib/graphql/dataloader/async_dataloader.rb +1 -0
- data/lib/graphql/dataloader.rb +6 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +6 -4
- data/lib/graphql/execution/interpreter/runtime.rb +97 -111
- data/lib/graphql/language/nodes.rb +53 -20
- data/lib/graphql/language/parser.rb +72 -37
- data/lib/graphql/query.rb +1 -1
- data/lib/graphql/schema.rb +10 -1
- data/lib/graphql/tracing/prometheus_trace.rb +8 -8
- data/lib/graphql/tracing/sentry_trace.rb +10 -10
- data/lib/graphql/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8f2d38dd0c349521f7f22050bb70cee1f06a7832de74d5afe523a35310ed318
|
|
4
|
+
data.tar.gz: da047a5e11a1441060411c47468daad16300db1dc7064ab1b1a76d2e519e76fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 73bd380530a4183241587083beb2ef6c96709657fb18e702a294a37c22f26e2141b2111b96c79570b9588d330e9c8d851fd532cdaf6d6fbd55f9dd5b30c6fe4e
|
|
7
|
+
data.tar.gz: 917e88d0cec3cbc760ec89714cf31325088f94f0a93094b3fcbf1cc3c07f52a99e30f0e418e561518c0a7deef0358dfa0ae457b8bad88fb50883aacb9d7dae7f
|
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
|
8
8
|
# - `complexities_on_type` holds complexity scores for each type
|
|
9
9
|
def initialize(query)
|
|
10
10
|
super
|
|
11
|
+
@skip_introspection_fields = !query.schema.max_complexity_count_introspection_fields
|
|
11
12
|
@complexities_on_type_by_query = {}
|
|
12
13
|
end
|
|
13
14
|
|
|
@@ -51,6 +52,7 @@ module GraphQL
|
|
|
51
52
|
# we'll visit them when we hit the spreads instead
|
|
52
53
|
return if visitor.visiting_fragment_definition?
|
|
53
54
|
return if visitor.skipping?
|
|
55
|
+
return if @skip_introspection_fields && visitor.field_definition.introspection?
|
|
54
56
|
parent_type = visitor.parent_type_definition
|
|
55
57
|
field_key = node.alias || node.name
|
|
56
58
|
|
|
@@ -68,6 +70,7 @@ module GraphQL
|
|
|
68
70
|
# we'll visit them when we hit the spreads instead
|
|
69
71
|
return if visitor.visiting_fragment_definition?
|
|
70
72
|
return if visitor.skipping?
|
|
73
|
+
return if @skip_introspection_fields && visitor.field_definition.introspection?
|
|
71
74
|
scopes_stack = @complexities_on_type_by_query[visitor.query]
|
|
72
75
|
scopes_stack.pop
|
|
73
76
|
end
|
data/lib/graphql/dataloader.rb
CHANGED
|
@@ -88,6 +88,11 @@ module GraphQL
|
|
|
88
88
|
nil
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
+
# This method is called when Dataloader is finished using a fiber.
|
|
92
|
+
# Use it to perform any cleanup, such as releasing database connections (if required manually)
|
|
93
|
+
def cleanup_fiber
|
|
94
|
+
end
|
|
95
|
+
|
|
91
96
|
# Get a Source instance from this dataloader, for calling `.load(...)` or `.request(...)` on.
|
|
92
97
|
#
|
|
93
98
|
# @param source_class [Class<GraphQL::Dataloader::Source]
|
|
@@ -231,9 +236,7 @@ module GraphQL
|
|
|
231
236
|
Fiber.new(blocking: !@nonblocking) {
|
|
232
237
|
set_fiber_variables(fiber_vars)
|
|
233
238
|
yield
|
|
234
|
-
|
|
235
|
-
# if the fiber is allowed to terminate normally, control is passed to the main fiber instead.
|
|
236
|
-
true
|
|
239
|
+
cleanup_fiber
|
|
237
240
|
}
|
|
238
241
|
end
|
|
239
242
|
|
|
@@ -5,8 +5,10 @@ module GraphQL
|
|
|
5
5
|
class Interpreter
|
|
6
6
|
class Runtime
|
|
7
7
|
module GraphQLResult
|
|
8
|
-
def initialize(result_name, parent_result, is_non_null_in_parent)
|
|
8
|
+
def initialize(result_name, result_type, application_value, parent_result, is_non_null_in_parent)
|
|
9
9
|
@graphql_parent = parent_result
|
|
10
|
+
@graphql_application_value = application_value
|
|
11
|
+
@graphql_result_type = result_type
|
|
10
12
|
if parent_result && parent_result.graphql_dead
|
|
11
13
|
@graphql_dead = true
|
|
12
14
|
end
|
|
@@ -26,14 +28,14 @@ module GraphQL
|
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
attr_accessor :graphql_dead
|
|
29
|
-
attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent
|
|
31
|
+
attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent, :graphql_application_value, :graphql_result_type
|
|
30
32
|
|
|
31
33
|
# @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
|
|
32
34
|
attr_accessor :graphql_result_data
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
class GraphQLResultHash
|
|
36
|
-
def initialize(_result_name, _parent_result, _is_non_null_in_parent)
|
|
38
|
+
def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent)
|
|
37
39
|
super
|
|
38
40
|
@graphql_result_data = {}
|
|
39
41
|
end
|
|
@@ -121,7 +123,7 @@ module GraphQL
|
|
|
121
123
|
class GraphQLResultArray
|
|
122
124
|
include GraphQLResult
|
|
123
125
|
|
|
124
|
-
def initialize(_result_name, _parent_result, _is_non_null_in_parent)
|
|
126
|
+
def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent)
|
|
125
127
|
super
|
|
126
128
|
@graphql_result_data = []
|
|
127
129
|
end
|
|
@@ -11,7 +11,6 @@ module GraphQL
|
|
|
11
11
|
class Runtime
|
|
12
12
|
class CurrentState
|
|
13
13
|
def initialize
|
|
14
|
-
@current_object = nil
|
|
15
14
|
@current_field = nil
|
|
16
15
|
@current_arguments = nil
|
|
17
16
|
@current_result_name = nil
|
|
@@ -19,8 +18,12 @@ module GraphQL
|
|
|
19
18
|
@was_authorized_by_scope_items = nil
|
|
20
19
|
end
|
|
21
20
|
|
|
21
|
+
def current_object
|
|
22
|
+
@current_result.graphql_application_value
|
|
23
|
+
end
|
|
24
|
+
|
|
22
25
|
attr_accessor :current_result, :current_result_name,
|
|
23
|
-
:current_arguments, :current_field, :
|
|
26
|
+
:current_arguments, :current_field, :was_authorized_by_scope_items
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
# @return [GraphQL::Query]
|
|
@@ -39,7 +42,7 @@ module GraphQL
|
|
|
39
42
|
@lazies_at_depth = lazies_at_depth
|
|
40
43
|
@schema = query.schema
|
|
41
44
|
@context = query.context
|
|
42
|
-
@response =
|
|
45
|
+
@response = nil
|
|
43
46
|
# Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
|
|
44
47
|
@runtime_directive_names = []
|
|
45
48
|
noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
|
|
@@ -79,12 +82,11 @@ module GraphQL
|
|
|
79
82
|
root_operation = query.selected_operation
|
|
80
83
|
root_op_type = root_operation.operation_type || "query"
|
|
81
84
|
root_type = schema.root_type_for_operation(root_op_type)
|
|
82
|
-
|
|
83
|
-
st = get_current_runtime_state
|
|
84
|
-
st.current_object = query.root_value
|
|
85
|
-
st.current_result = @response
|
|
86
85
|
runtime_object = root_type.wrap(query.root_value, context)
|
|
87
86
|
runtime_object = schema.sync_lazy(runtime_object)
|
|
87
|
+
@response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false)
|
|
88
|
+
st = get_current_runtime_state
|
|
89
|
+
st.current_result = @response
|
|
88
90
|
|
|
89
91
|
if runtime_object.nil?
|
|
90
92
|
# Root .authorized? returned false.
|
|
@@ -101,7 +103,7 @@ module GraphQL
|
|
|
101
103
|
# directly evaluated and the results can be written right into the main response hash.
|
|
102
104
|
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
|
103
105
|
if is_selection_array
|
|
104
|
-
selection_response = GraphQLResultHash.new(nil, nil, false)
|
|
106
|
+
selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false)
|
|
105
107
|
final_response = @response
|
|
106
108
|
else
|
|
107
109
|
selection_response = @response
|
|
@@ -109,26 +111,14 @@ module GraphQL
|
|
|
109
111
|
end
|
|
110
112
|
|
|
111
113
|
@dataloader.append_job {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
call_method_on_directives(:resolve, runtime_object, directives) do
|
|
121
|
-
evaluate_selections(
|
|
122
|
-
runtime_object,
|
|
123
|
-
root_type,
|
|
124
|
-
root_op_type == "mutation",
|
|
125
|
-
selections,
|
|
126
|
-
selection_response,
|
|
127
|
-
final_response,
|
|
128
|
-
nil,
|
|
129
|
-
st,
|
|
130
|
-
)
|
|
131
|
-
end
|
|
114
|
+
evaluate_selections(
|
|
115
|
+
root_op_type == "mutation",
|
|
116
|
+
selections,
|
|
117
|
+
selection_response,
|
|
118
|
+
final_response,
|
|
119
|
+
nil,
|
|
120
|
+
nil,
|
|
121
|
+
)
|
|
132
122
|
}
|
|
133
123
|
end
|
|
134
124
|
end
|
|
@@ -137,7 +127,6 @@ module GraphQL
|
|
|
137
127
|
end
|
|
138
128
|
|
|
139
129
|
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
|
|
140
|
-
|
|
141
130
|
selections.each do |node|
|
|
142
131
|
# Skip gathering this if the directive says so
|
|
143
132
|
if !directives_include?(node, owner_object, owner_type)
|
|
@@ -182,17 +171,26 @@ module GraphQL
|
|
|
182
171
|
type_defn = schema.get_type(node.type.name, context)
|
|
183
172
|
|
|
184
173
|
if query.warden.possible_types(type_defn).include?(owner_type)
|
|
185
|
-
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
|
174
|
+
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
|
175
|
+
if !result.equal?(next_selections)
|
|
176
|
+
selections_to_run = result
|
|
177
|
+
end
|
|
186
178
|
end
|
|
187
179
|
else
|
|
188
180
|
# it's an untyped fragment, definitely continue
|
|
189
|
-
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
|
181
|
+
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
|
182
|
+
if !result.equal?(next_selections)
|
|
183
|
+
selections_to_run = result
|
|
184
|
+
end
|
|
190
185
|
end
|
|
191
186
|
when GraphQL::Language::Nodes::FragmentSpread
|
|
192
187
|
fragment_def = query.fragments[node.name]
|
|
193
188
|
type_defn = query.get_type(fragment_def.type.name)
|
|
194
189
|
if query.warden.possible_types(type_defn).include?(owner_type)
|
|
195
|
-
gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
|
190
|
+
result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
|
191
|
+
if !result.equal?(next_selections)
|
|
192
|
+
selections_to_run = result
|
|
193
|
+
end
|
|
196
194
|
end
|
|
197
195
|
else
|
|
198
196
|
raise "Invariant: unexpected selection class: #{node.class}"
|
|
@@ -205,36 +203,44 @@ module GraphQL
|
|
|
205
203
|
NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
|
|
206
204
|
|
|
207
205
|
# @return [void]
|
|
208
|
-
def evaluate_selections(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
206
|
+
def evaluate_selections(is_eager_selection, gathered_selections, selections_result, target_result, parent_object, runtime_state) # rubocop:disable Metrics/ParameterLists
|
|
207
|
+
runtime_state ||= get_current_runtime_state
|
|
208
|
+
runtime_state.current_result_name = nil
|
|
209
|
+
runtime_state.current_result = selections_result
|
|
210
|
+
# This is a less-frequent case; use a fast check since it's often not there.
|
|
211
|
+
if (directives = gathered_selections[:graphql_directives])
|
|
212
|
+
gathered_selections.delete(:graphql_directives)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
call_method_on_directives(:resolve, selections_result.graphql_application_value, directives) do
|
|
216
|
+
finished_jobs = 0
|
|
217
|
+
enqueued_jobs = gathered_selections.size
|
|
218
|
+
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
|
219
|
+
@dataloader.append_job {
|
|
220
|
+
evaluate_selection(
|
|
221
|
+
result_name, field_ast_nodes_or_ast_node, is_eager_selection, selections_result, parent_object
|
|
222
|
+
)
|
|
223
|
+
finished_jobs += 1
|
|
224
|
+
if target_result && finished_jobs == enqueued_jobs
|
|
225
|
+
selections_result.merge_into(target_result)
|
|
226
|
+
end
|
|
227
|
+
}
|
|
228
|
+
# Field resolution may pause the fiber,
|
|
229
|
+
# so it wouldn't get to the `Resolve` call that happens below.
|
|
230
|
+
# So instead trigger a run from this outer context.
|
|
231
|
+
if is_eager_selection
|
|
232
|
+
@dataloader.clear_cache
|
|
233
|
+
@dataloader.run
|
|
234
|
+
@dataloader.clear_cache
|
|
220
235
|
end
|
|
221
|
-
}
|
|
222
|
-
# Field resolution may pause the fiber,
|
|
223
|
-
# so it wouldn't get to the `Resolve` call that happens below.
|
|
224
|
-
# So instead trigger a run from this outer context.
|
|
225
|
-
if is_eager_selection
|
|
226
|
-
@dataloader.clear_cache
|
|
227
|
-
@dataloader.run
|
|
228
|
-
@dataloader.clear_cache
|
|
229
236
|
end
|
|
237
|
+
selections_result
|
|
230
238
|
end
|
|
231
|
-
|
|
232
|
-
selections_result
|
|
233
239
|
end
|
|
234
240
|
|
|
235
241
|
# @return [void]
|
|
236
|
-
def evaluate_selection(result_name, field_ast_nodes_or_ast_node,
|
|
237
|
-
return if
|
|
242
|
+
def evaluate_selection(result_name, field_ast_nodes_or_ast_node, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
|
|
243
|
+
return if selections_result.graphql_dead
|
|
238
244
|
# As a performance optimization, the hash key will be a `Node` if
|
|
239
245
|
# there's only one selection of the field. But if there are multiple
|
|
240
246
|
# selections of the field, it will be an Array of nodes
|
|
@@ -246,13 +252,16 @@ module GraphQL
|
|
|
246
252
|
ast_node = field_ast_nodes_or_ast_node
|
|
247
253
|
end
|
|
248
254
|
field_name = ast_node.name
|
|
255
|
+
owner_type = selections_result.graphql_result_type
|
|
249
256
|
field_defn = query.warden.get_field(owner_type, field_name)
|
|
250
257
|
|
|
251
258
|
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
|
259
|
+
runtime_state = get_current_runtime_state
|
|
252
260
|
runtime_state.current_field = field_defn
|
|
253
261
|
runtime_state.current_result = selections_result
|
|
254
262
|
runtime_state.current_result_name = result_name
|
|
255
263
|
|
|
264
|
+
owner_object = selections_result.graphql_application_value
|
|
256
265
|
if field_defn.dynamic_introspection
|
|
257
266
|
owner_object = field_defn.owner.wrap(owner_object, context)
|
|
258
267
|
end
|
|
@@ -262,24 +271,24 @@ module GraphQL
|
|
|
262
271
|
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
|
263
272
|
if field_defn.extras.size == 0
|
|
264
273
|
evaluate_selection_with_resolved_keyword_args(
|
|
265
|
-
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes,
|
|
274
|
+
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type.non_null?, runtime_state
|
|
266
275
|
)
|
|
267
276
|
else
|
|
268
|
-
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes,
|
|
277
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, runtime_state)
|
|
269
278
|
end
|
|
270
279
|
else
|
|
271
280
|
@query.arguments_cache.dataload_for(ast_node, field_defn, owner_object) do |resolved_arguments|
|
|
272
281
|
runtime_state = get_current_runtime_state # This might be in a different fiber
|
|
273
|
-
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes,
|
|
282
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_object, is_eager_field, result_name, selections_result, parent_object, return_type, runtime_state)
|
|
274
283
|
end
|
|
275
284
|
end
|
|
276
285
|
end
|
|
277
286
|
|
|
278
|
-
def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes,
|
|
287
|
+
def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, object, is_eager_field, result_name, selection_result, parent_object, return_type, runtime_state) # rubocop:disable Metrics/ParameterLists
|
|
279
288
|
after_lazy(arguments, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_arguments, runtime_state|
|
|
280
289
|
return_type_non_null = return_type.non_null?
|
|
281
290
|
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
|
282
|
-
continue_value(resolved_arguments,
|
|
291
|
+
continue_value(resolved_arguments, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
|
283
292
|
next
|
|
284
293
|
end
|
|
285
294
|
|
|
@@ -328,13 +337,12 @@ module GraphQL
|
|
|
328
337
|
resolved_arguments.keyword_arguments
|
|
329
338
|
end
|
|
330
339
|
|
|
331
|
-
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes,
|
|
340
|
+
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null, runtime_state)
|
|
332
341
|
end
|
|
333
342
|
end
|
|
334
343
|
|
|
335
|
-
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes,
|
|
344
|
+
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null, runtime_state) # rubocop:disable Metrics/ParameterLists
|
|
336
345
|
runtime_state.current_field = field_defn
|
|
337
|
-
runtime_state.current_object = object
|
|
338
346
|
runtime_state.current_arguments = resolved_arguments
|
|
339
347
|
runtime_state.current_result_name = result_name
|
|
340
348
|
runtime_state.current_result = selection_result
|
|
@@ -356,7 +364,6 @@ module GraphQL
|
|
|
356
364
|
# This might be executed in a different context; reset this info
|
|
357
365
|
runtime_state = get_current_runtime_state
|
|
358
366
|
runtime_state.current_field = field_defn
|
|
359
|
-
runtime_state.current_object = object
|
|
360
367
|
runtime_state.current_arguments = resolved_arguments
|
|
361
368
|
runtime_state.current_result_name = result_name
|
|
362
369
|
runtime_state.current_result = selection_result
|
|
@@ -376,7 +383,8 @@ module GraphQL
|
|
|
376
383
|
end
|
|
377
384
|
end
|
|
378
385
|
after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_result, runtime_state|
|
|
379
|
-
|
|
386
|
+
owner_type = selection_result.graphql_result_type
|
|
387
|
+
continue_value = continue_value(inner_result, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
|
380
388
|
if HALT != continue_value
|
|
381
389
|
was_scoped = runtime_state.was_authorized_by_scope_items
|
|
382
390
|
runtime_state.was_authorized_by_scope_items = nil
|
|
@@ -384,25 +392,16 @@ module GraphQL
|
|
|
384
392
|
end
|
|
385
393
|
end
|
|
386
394
|
end
|
|
387
|
-
|
|
388
395
|
# If this field is a root mutation field, immediately resolve
|
|
389
396
|
# all of its child fields before moving on to the next root mutation field.
|
|
390
397
|
# (Subselections of this mutation will still be resolved level-by-level.)
|
|
391
398
|
if is_eager_field
|
|
392
399
|
Interpreter::Resolve.resolve_all([field_result], @dataloader)
|
|
393
|
-
else
|
|
394
|
-
# Return this from `after_lazy` because it might be another lazy that needs to be resolved
|
|
395
|
-
field_result
|
|
396
400
|
end
|
|
397
401
|
end
|
|
398
402
|
|
|
399
|
-
|
|
400
|
-
def dead_result?(selection_result)
|
|
401
|
-
selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
|
|
402
|
-
end
|
|
403
|
-
|
|
404
403
|
def set_result(selection_result, result_name, value, is_child_result, is_non_null)
|
|
405
|
-
if !
|
|
404
|
+
if !selection_result.graphql_dead
|
|
406
405
|
if value.nil? && is_non_null
|
|
407
406
|
# This is an invalid nil that should be propagated
|
|
408
407
|
# One caller of this method passes a block,
|
|
@@ -456,11 +455,13 @@ module GraphQL
|
|
|
456
455
|
end
|
|
457
456
|
|
|
458
457
|
HALT = Object.new
|
|
459
|
-
def continue_value(value,
|
|
458
|
+
def continue_value(value, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
|
460
459
|
case value
|
|
461
460
|
when nil
|
|
462
461
|
if is_non_null
|
|
463
462
|
set_result(selection_result, result_name, nil, false, is_non_null) do
|
|
463
|
+
# When this comes from a list item, use the parent object:
|
|
464
|
+
parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
|
|
464
465
|
# This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
|
|
465
466
|
err = parent_type::InvalidNullError.new(parent_type, field, value)
|
|
466
467
|
schema.type_error(err, context)
|
|
@@ -474,7 +475,7 @@ module GraphQL
|
|
|
474
475
|
# to avoid the overhead of checking three different classes
|
|
475
476
|
# every time.
|
|
476
477
|
if value.is_a?(GraphQL::ExecutionError)
|
|
477
|
-
if selection_result.nil? || !
|
|
478
|
+
if selection_result.nil? || !selection_result.graphql_dead
|
|
478
479
|
value.path ||= current_path
|
|
479
480
|
value.ast_node ||= ast_node
|
|
480
481
|
context.errors << value
|
|
@@ -492,7 +493,7 @@ module GraphQL
|
|
|
492
493
|
rescue GraphQL::ExecutionError => err
|
|
493
494
|
err
|
|
494
495
|
end
|
|
495
|
-
continue_value(next_value,
|
|
496
|
+
continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
|
|
496
497
|
elsif value.is_a?(GraphQL::UnauthorizedError)
|
|
497
498
|
# this hook might raise & crash, or it might return
|
|
498
499
|
# a replacement value
|
|
@@ -501,7 +502,7 @@ module GraphQL
|
|
|
501
502
|
rescue GraphQL::ExecutionError => err
|
|
502
503
|
err
|
|
503
504
|
end
|
|
504
|
-
continue_value(next_value,
|
|
505
|
+
continue_value(next_value, field, is_non_null, ast_node, result_name, selection_result)
|
|
505
506
|
elsif GraphQL::Execution::SKIP == value
|
|
506
507
|
# It's possible a lazy was already written here
|
|
507
508
|
case selection_result
|
|
@@ -524,7 +525,7 @@ module GraphQL
|
|
|
524
525
|
# It's an array full of execution errors; add them all.
|
|
525
526
|
if value.any? && value.all?(GraphQL::ExecutionError)
|
|
526
527
|
list_type_at_all = (field && (field.type.list?))
|
|
527
|
-
if selection_result.nil? || !
|
|
528
|
+
if selection_result.nil? || !selection_result.graphql_dead
|
|
528
529
|
value.each_with_index do |error, index|
|
|
529
530
|
error.ast_node ||= ast_node
|
|
530
531
|
error.path ||= current_path + (list_type_at_all ? [index] : [])
|
|
@@ -604,9 +605,9 @@ module GraphQL
|
|
|
604
605
|
err
|
|
605
606
|
end
|
|
606
607
|
after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_object, runtime_state|
|
|
607
|
-
continue_value = continue_value(inner_object,
|
|
608
|
+
continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
|
|
608
609
|
if HALT != continue_value
|
|
609
|
-
response_hash = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
|
610
|
+
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null)
|
|
610
611
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
|
611
612
|
|
|
612
613
|
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
|
@@ -620,34 +621,21 @@ module GraphQL
|
|
|
620
621
|
# (Technically, it's possible that one of those entries _doesn't_ require isolation.)
|
|
621
622
|
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
|
622
623
|
if is_selection_array
|
|
623
|
-
this_result = GraphQLResultHash.new(result_name, selection_result, is_non_null)
|
|
624
|
+
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null)
|
|
624
625
|
final_result = response_hash
|
|
625
626
|
else
|
|
626
627
|
this_result = response_hash
|
|
627
628
|
final_result = nil
|
|
628
629
|
end
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
call_method_on_directives(:resolve, continue_value, directives) do
|
|
639
|
-
evaluate_selections(
|
|
640
|
-
continue_value,
|
|
641
|
-
current_type,
|
|
642
|
-
false,
|
|
643
|
-
selections,
|
|
644
|
-
this_result,
|
|
645
|
-
final_result,
|
|
646
|
-
owner_object.object,
|
|
647
|
-
runtime_state,
|
|
648
|
-
)
|
|
649
|
-
this_result
|
|
650
|
-
end
|
|
630
|
+
|
|
631
|
+
evaluate_selections(
|
|
632
|
+
false,
|
|
633
|
+
selections,
|
|
634
|
+
this_result,
|
|
635
|
+
final_result,
|
|
636
|
+
owner_object.object,
|
|
637
|
+
runtime_state,
|
|
638
|
+
)
|
|
651
639
|
end
|
|
652
640
|
end
|
|
653
641
|
end
|
|
@@ -656,7 +644,7 @@ module GraphQL
|
|
|
656
644
|
# This is true for objects, unions, and interfaces
|
|
657
645
|
use_dataloader_job = !inner_type.unwrap.kind.input?
|
|
658
646
|
inner_type_non_null = inner_type.non_null?
|
|
659
|
-
response_list = GraphQLResultArray.new(result_name, selection_result, is_non_null)
|
|
647
|
+
response_list = GraphQLResultArray.new(result_name, current_type, response_list, selection_result, is_non_null)
|
|
660
648
|
set_result(selection_result, result_name, response_list, true, is_non_null)
|
|
661
649
|
idx = nil
|
|
662
650
|
list_value = begin
|
|
@@ -694,7 +682,7 @@ module GraphQL
|
|
|
694
682
|
end
|
|
695
683
|
# Detect whether this error came while calling `.each` (before `idx` is set) or while running list *items* (after `idx` is set)
|
|
696
684
|
error_is_non_null = idx.nil? ? is_non_null : inner_type.non_null?
|
|
697
|
-
continue_value(list_value,
|
|
685
|
+
continue_value(list_value, field, error_is_non_null, ast_node, result_name, selection_result)
|
|
698
686
|
else
|
|
699
687
|
raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
|
|
700
688
|
end
|
|
@@ -706,7 +694,7 @@ module GraphQL
|
|
|
706
694
|
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
|
707
695
|
# This will update `response_list` with the lazy
|
|
708
696
|
after_lazy(inner_value, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list, runtime_state: runtime_state) do |inner_inner_value, runtime_state|
|
|
709
|
-
continue_value = continue_value(inner_inner_value,
|
|
697
|
+
continue_value = continue_value(inner_inner_value, field, inner_type_non_null, ast_node, this_idx, response_list)
|
|
710
698
|
if HALT != continue_value
|
|
711
699
|
continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list, was_scoped, runtime_state)
|
|
712
700
|
end
|
|
@@ -728,7 +716,6 @@ module GraphQL
|
|
|
728
716
|
raw_dir_args = arguments(nil, dir_defn, dir_node)
|
|
729
717
|
dir_args = continue_value(
|
|
730
718
|
raw_dir_args, # value
|
|
731
|
-
dir_defn, # parent_type
|
|
732
719
|
nil, # field
|
|
733
720
|
false, # is_non_null
|
|
734
721
|
dir_node, # ast_node
|
|
@@ -794,7 +781,6 @@ module GraphQL
|
|
|
794
781
|
# In that case, this will initialize a new state
|
|
795
782
|
# to avoid conflicting with the parent fiber.
|
|
796
783
|
runtime_state = get_current_runtime_state
|
|
797
|
-
runtime_state.current_object = owner_object
|
|
798
784
|
runtime_state.current_field = field
|
|
799
785
|
runtime_state.current_arguments = arguments
|
|
800
786
|
runtime_state.current_result_name = result_name
|
|
@@ -20,26 +20,29 @@ module GraphQL
|
|
|
20
20
|
@definition_line = definition_line
|
|
21
21
|
super(**_rest)
|
|
22
22
|
end
|
|
23
|
+
|
|
24
|
+
def marshal_dump
|
|
25
|
+
super << @definition_line
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def marshal_load(values)
|
|
29
|
+
@definition_line = values.pop
|
|
30
|
+
super
|
|
31
|
+
end
|
|
23
32
|
end
|
|
24
33
|
|
|
25
34
|
attr_reader :filename
|
|
26
35
|
|
|
27
36
|
def line
|
|
28
|
-
@line ||= (@
|
|
37
|
+
@line ||= @source.line_at(@pos)
|
|
29
38
|
end
|
|
30
39
|
|
|
31
40
|
def col
|
|
32
|
-
@col ||=
|
|
33
|
-
if @pos == 0
|
|
34
|
-
1
|
|
35
|
-
else
|
|
36
|
-
@source_string[0..@pos].split("\n").last.length
|
|
37
|
-
end
|
|
38
|
-
end
|
|
41
|
+
@col ||= @source.column_at(@pos)
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
def definition_line
|
|
42
|
-
@definition_line ||= (@
|
|
45
|
+
@definition_line ||= (@source && @definition_pos) ? @source.line_at(@definition_pos) : nil
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
# Value equality
|
|
@@ -267,7 +270,7 @@ module GraphQL
|
|
|
267
270
|
"col: nil",
|
|
268
271
|
"pos: nil",
|
|
269
272
|
"filename: nil",
|
|
270
|
-
"
|
|
273
|
+
"source: nil",
|
|
271
274
|
]
|
|
272
275
|
|
|
273
276
|
def generate_initialize
|
|
@@ -279,18 +282,20 @@ module GraphQL
|
|
|
279
282
|
end
|
|
280
283
|
end
|
|
281
284
|
|
|
282
|
-
|
|
285
|
+
children_method_names = @children_methods.keys
|
|
286
|
+
|
|
287
|
+
all_method_names = scalar_method_names + children_method_names
|
|
283
288
|
if all_method_names.include?(:alias)
|
|
284
289
|
# Rather than complicating this special case,
|
|
285
290
|
# let it be overridden (in field)
|
|
286
291
|
return
|
|
287
292
|
else
|
|
288
293
|
arguments = scalar_method_names.map { |m| "#{m}: nil"} +
|
|
289
|
-
|
|
294
|
+
children_method_names.map { |m| "#{m}: NO_CHILDREN" } +
|
|
290
295
|
DEFAULT_INITIALIZE_OPTIONS
|
|
291
296
|
|
|
292
297
|
assignments = scalar_method_names.map { |m| "@#{m} = #{m}"} +
|
|
293
|
-
|
|
298
|
+
children_method_names.map { |m| "@#{m} = #{m}.freeze" }
|
|
294
299
|
|
|
295
300
|
if name.end_with?("Definition") && name != "FragmentDefinition"
|
|
296
301
|
arguments << "definition_pos: nil"
|
|
@@ -298,7 +303,7 @@ module GraphQL
|
|
|
298
303
|
end
|
|
299
304
|
|
|
300
305
|
keywords = scalar_method_names.map { |m| "#{m}: #{m}"} +
|
|
301
|
-
|
|
306
|
+
children_method_names.map { |m| "#{m}: #{m}" }
|
|
302
307
|
|
|
303
308
|
module_eval <<-RUBY, __FILE__, __LINE__
|
|
304
309
|
def initialize(#{arguments.join(", ")})
|
|
@@ -306,13 +311,25 @@ module GraphQL
|
|
|
306
311
|
@col = col
|
|
307
312
|
@pos = pos
|
|
308
313
|
@filename = filename
|
|
309
|
-
@
|
|
314
|
+
@source = source
|
|
310
315
|
#{assignments.join("\n")}
|
|
311
316
|
end
|
|
312
317
|
|
|
313
|
-
def self.from_a(filename, line, col, #{
|
|
318
|
+
def self.from_a(filename, line, col, #{all_method_names.join(", ")})
|
|
314
319
|
self.new(filename: filename, line: line, col: col, #{keywords.join(", ")})
|
|
315
320
|
end
|
|
321
|
+
|
|
322
|
+
def marshal_dump
|
|
323
|
+
[
|
|
324
|
+
line, col, # use methods here to force them to be calculated
|
|
325
|
+
@filename,
|
|
326
|
+
#{all_method_names.map { |n| "@#{n}," }.join}
|
|
327
|
+
]
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def marshal_load(values)
|
|
331
|
+
@line, @col, @filename #{all_method_names.map { |n| ", @#{n}"}.join} = values
|
|
332
|
+
end
|
|
316
333
|
RUBY
|
|
317
334
|
end
|
|
318
335
|
end
|
|
@@ -385,7 +402,7 @@ module GraphQL
|
|
|
385
402
|
# @!attribute selections
|
|
386
403
|
# @return [Array<Nodes::Field>] Selections on this object (or empty array if this is a scalar field)
|
|
387
404
|
|
|
388
|
-
def initialize(name: nil, arguments: NONE, directives: NONE, selections: NONE, field_alias: nil, line: nil, col: nil, pos: nil, filename: nil,
|
|
405
|
+
def initialize(name: nil, arguments: NONE, directives: NONE, selections: NONE, field_alias: nil, line: nil, col: nil, pos: nil, filename: nil, source: nil)
|
|
389
406
|
@name = name
|
|
390
407
|
@arguments = arguments || NONE
|
|
391
408
|
@directives = directives || NONE
|
|
@@ -396,13 +413,21 @@ module GraphQL
|
|
|
396
413
|
@col = col
|
|
397
414
|
@pos = pos
|
|
398
415
|
@filename = filename
|
|
399
|
-
@
|
|
416
|
+
@source = source
|
|
400
417
|
end
|
|
401
418
|
|
|
402
419
|
def self.from_a(filename, line, col, field_alias, name, arguments, directives, selections) # rubocop:disable Metrics/ParameterLists
|
|
403
420
|
self.new(filename: filename, line: line, col: col, field_alias: field_alias, name: name, arguments: arguments, directives: directives, selections: selections)
|
|
404
421
|
end
|
|
405
422
|
|
|
423
|
+
def marshal_dump
|
|
424
|
+
[line, col, @filename, @name, @arguments, @directives, @selections, @alias]
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def marshal_load(values)
|
|
428
|
+
@line, @col, @filename, @name, @arguments, @directives, @selections, @alias = values
|
|
429
|
+
end
|
|
430
|
+
|
|
406
431
|
# Override this because default is `:fields`
|
|
407
432
|
self.children_method_name = :selections
|
|
408
433
|
end
|
|
@@ -421,14 +446,14 @@ module GraphQL
|
|
|
421
446
|
|
|
422
447
|
# @!attribute type
|
|
423
448
|
# @return [String] the type condition for this fragment (name of type which it may apply to)
|
|
424
|
-
def initialize(name: nil, type: nil, directives: NONE, selections: NONE, filename: nil, pos: nil,
|
|
449
|
+
def initialize(name: nil, type: nil, directives: NONE, selections: NONE, filename: nil, pos: nil, source: nil, line: nil, col: nil)
|
|
425
450
|
@name = name
|
|
426
451
|
@type = type
|
|
427
452
|
@directives = directives
|
|
428
453
|
@selections = selections
|
|
429
454
|
@filename = filename
|
|
430
455
|
@pos = pos
|
|
431
|
-
@
|
|
456
|
+
@source = source
|
|
432
457
|
@line = line
|
|
433
458
|
@col = col
|
|
434
459
|
end
|
|
@@ -436,6 +461,14 @@ module GraphQL
|
|
|
436
461
|
def self.from_a(filename, line, col, name, type, directives, selections)
|
|
437
462
|
self.new(filename: filename, line: line, col: col, name: name, type: type, directives: directives, selections: selections)
|
|
438
463
|
end
|
|
464
|
+
|
|
465
|
+
def marshal_dump
|
|
466
|
+
[line, col, @filename, @name, @type, @directives, @selections]
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def marshal_load(values)
|
|
470
|
+
@line, @col, @filename, @name, @type, @directives, @selections = values
|
|
471
|
+
end
|
|
439
472
|
end
|
|
440
473
|
|
|
441
474
|
# Application of a named fragment in a selection
|
|
@@ -36,6 +36,7 @@ module GraphQL
|
|
|
36
36
|
@filename = filename
|
|
37
37
|
@trace = trace
|
|
38
38
|
@dedup_identifiers = false
|
|
39
|
+
@lines_at = nil
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def parse
|
|
@@ -48,9 +49,43 @@ module GraphQL
|
|
|
48
49
|
end
|
|
49
50
|
end
|
|
50
51
|
|
|
52
|
+
def line_at(pos)
|
|
53
|
+
line = lines_at.bsearch_index { |l| l >= pos }
|
|
54
|
+
if line.nil?
|
|
55
|
+
@lines_at.size + 1
|
|
56
|
+
else
|
|
57
|
+
line + 1
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def column_at(pos)
|
|
62
|
+
next_line_idx = lines_at.bsearch_index { |l| l >= pos } || 0
|
|
63
|
+
if next_line_idx > 0
|
|
64
|
+
line_pos = @lines_at[next_line_idx - 1]
|
|
65
|
+
pos - line_pos
|
|
66
|
+
else
|
|
67
|
+
pos + 1
|
|
68
|
+
end
|
|
69
|
+
end
|
|
51
70
|
|
|
52
71
|
private
|
|
53
72
|
|
|
73
|
+
# @return [Array<Integer>] Positions of each line break in the original string
|
|
74
|
+
def lines_at
|
|
75
|
+
@lines_at ||= begin
|
|
76
|
+
la = []
|
|
77
|
+
idx = 0
|
|
78
|
+
while idx
|
|
79
|
+
idx = @graphql_str.index("\n", idx)
|
|
80
|
+
if idx
|
|
81
|
+
la << idx
|
|
82
|
+
idx += 1
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
la
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
54
89
|
attr_reader :token_name
|
|
55
90
|
|
|
56
91
|
def advance_token
|
|
@@ -73,7 +108,7 @@ module GraphQL
|
|
|
73
108
|
while !@lexer.eos?
|
|
74
109
|
defns << definition
|
|
75
110
|
end
|
|
76
|
-
Document.new(pos: 0, definitions: defns, filename: @filename,
|
|
111
|
+
Document.new(pos: 0, definitions: defns, filename: @filename, source: self)
|
|
77
112
|
end
|
|
78
113
|
|
|
79
114
|
def definition
|
|
@@ -95,7 +130,7 @@ module GraphQL
|
|
|
95
130
|
directives: directives,
|
|
96
131
|
selections: selections,
|
|
97
132
|
filename: @filename,
|
|
98
|
-
|
|
133
|
+
source: self
|
|
99
134
|
)
|
|
100
135
|
when :QUERY, :MUTATION, :SUBSCRIPTION, :LCURLY
|
|
101
136
|
op_loc = pos
|
|
@@ -131,7 +166,7 @@ module GraphQL
|
|
|
131
166
|
default_value: default_value,
|
|
132
167
|
directives: directives,
|
|
133
168
|
filename: @filename,
|
|
134
|
-
|
|
169
|
+
source: self
|
|
135
170
|
)
|
|
136
171
|
end
|
|
137
172
|
expect_token(:RPAREN)
|
|
@@ -150,7 +185,7 @@ module GraphQL
|
|
|
150
185
|
directives: directives,
|
|
151
186
|
selections: selection_set,
|
|
152
187
|
filename: @filename,
|
|
153
|
-
|
|
188
|
+
source: self
|
|
154
189
|
)
|
|
155
190
|
when :EXTEND
|
|
156
191
|
loc = pos
|
|
@@ -160,7 +195,7 @@ module GraphQL
|
|
|
160
195
|
advance_token
|
|
161
196
|
name = parse_name
|
|
162
197
|
directives = parse_directives
|
|
163
|
-
ScalarTypeExtension.new(pos: loc, name: name, directives: directives, filename: @filename,
|
|
198
|
+
ScalarTypeExtension.new(pos: loc, name: name, directives: directives, filename: @filename, source: self)
|
|
164
199
|
when :TYPE
|
|
165
200
|
advance_token
|
|
166
201
|
name = parse_name
|
|
@@ -168,32 +203,32 @@ module GraphQL
|
|
|
168
203
|
directives = parse_directives
|
|
169
204
|
field_defns = at?(:LCURLY) ? parse_field_definitions : EMPTY_ARRAY
|
|
170
205
|
|
|
171
|
-
ObjectTypeExtension.new(pos: loc, name: name, interfaces: implements_interfaces, directives: directives, fields: field_defns, filename: @filename,
|
|
206
|
+
ObjectTypeExtension.new(pos: loc, name: name, interfaces: implements_interfaces, directives: directives, fields: field_defns, filename: @filename, source: self)
|
|
172
207
|
when :INTERFACE
|
|
173
208
|
advance_token
|
|
174
209
|
name = parse_name
|
|
175
210
|
directives = parse_directives
|
|
176
211
|
interfaces = parse_implements
|
|
177
212
|
fields_definition = at?(:LCURLY) ? parse_field_definitions : EMPTY_ARRAY
|
|
178
|
-
InterfaceTypeExtension.new(pos: loc, name: name, directives: directives, fields: fields_definition, interfaces: interfaces, filename: @filename,
|
|
213
|
+
InterfaceTypeExtension.new(pos: loc, name: name, directives: directives, fields: fields_definition, interfaces: interfaces, filename: @filename, source: self)
|
|
179
214
|
when :UNION
|
|
180
215
|
advance_token
|
|
181
216
|
name = parse_name
|
|
182
217
|
directives = parse_directives
|
|
183
218
|
union_member_types = parse_union_members
|
|
184
|
-
UnionTypeExtension.new(pos: loc, name: name, directives: directives, types: union_member_types, filename: @filename,
|
|
219
|
+
UnionTypeExtension.new(pos: loc, name: name, directives: directives, types: union_member_types, filename: @filename, source: self)
|
|
185
220
|
when :ENUM
|
|
186
221
|
advance_token
|
|
187
222
|
name = parse_name
|
|
188
223
|
directives = parse_directives
|
|
189
224
|
enum_values_definition = parse_enum_value_definitions
|
|
190
|
-
Nodes::EnumTypeExtension.new(pos: loc, name: name, directives: directives, values: enum_values_definition, filename: @filename,
|
|
225
|
+
Nodes::EnumTypeExtension.new(pos: loc, name: name, directives: directives, values: enum_values_definition, filename: @filename, source: self)
|
|
191
226
|
when :INPUT
|
|
192
227
|
advance_token
|
|
193
228
|
name = parse_name
|
|
194
229
|
directives = parse_directives
|
|
195
230
|
input_fields_definition = parse_input_object_field_definitions
|
|
196
|
-
InputObjectTypeExtension.new(pos: loc, name: name, directives: directives, fields: input_fields_definition, filename: @filename,
|
|
231
|
+
InputObjectTypeExtension.new(pos: loc, name: name, directives: directives, fields: input_fields_definition, filename: @filename, source: self)
|
|
197
232
|
when :SCHEMA
|
|
198
233
|
advance_token
|
|
199
234
|
directives = parse_directives
|
|
@@ -226,7 +261,7 @@ module GraphQL
|
|
|
226
261
|
directives: directives,
|
|
227
262
|
pos: loc,
|
|
228
263
|
filename: @filename,
|
|
229
|
-
|
|
264
|
+
source: self,
|
|
230
265
|
)
|
|
231
266
|
else
|
|
232
267
|
expect_one_of([:SCHEMA, :SCALAR, :TYPE, :ENUM, :INPUT, :UNION, :INTERFACE])
|
|
@@ -259,7 +294,7 @@ module GraphQL
|
|
|
259
294
|
end
|
|
260
295
|
end
|
|
261
296
|
expect_token :RCURLY
|
|
262
|
-
SchemaDefinition.new(pos: loc, definition_pos: defn_loc, query: query, mutation: mutation, subscription: subscription, directives: directives, filename: @filename,
|
|
297
|
+
SchemaDefinition.new(pos: loc, definition_pos: defn_loc, query: query, mutation: mutation, subscription: subscription, directives: directives, filename: @filename, source: self)
|
|
263
298
|
when :DIRECTIVE
|
|
264
299
|
advance_token
|
|
265
300
|
expect_token :DIR_SIGN
|
|
@@ -272,12 +307,12 @@ module GraphQL
|
|
|
272
307
|
false
|
|
273
308
|
end
|
|
274
309
|
expect_token :ON
|
|
275
|
-
directive_locations = [DirectiveLocation.new(pos: pos, name: parse_name, filename: @filename,
|
|
310
|
+
directive_locations = [DirectiveLocation.new(pos: pos, name: parse_name, filename: @filename, source: self)]
|
|
276
311
|
while at?(:PIPE)
|
|
277
312
|
advance_token
|
|
278
|
-
directive_locations << DirectiveLocation.new(pos: pos, name: parse_name, filename: @filename,
|
|
313
|
+
directive_locations << DirectiveLocation.new(pos: pos, name: parse_name, filename: @filename, source: self)
|
|
279
314
|
end
|
|
280
|
-
DirectiveDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, arguments: arguments_definition, locations: directive_locations, repeatable: repeatable, filename: @filename,
|
|
315
|
+
DirectiveDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, arguments: arguments_definition, locations: directive_locations, repeatable: repeatable, filename: @filename, source: self)
|
|
281
316
|
when :TYPE
|
|
282
317
|
advance_token
|
|
283
318
|
name = parse_name
|
|
@@ -285,37 +320,37 @@ module GraphQL
|
|
|
285
320
|
directives = parse_directives
|
|
286
321
|
field_defns = at?(:LCURLY) ? parse_field_definitions : EMPTY_ARRAY
|
|
287
322
|
|
|
288
|
-
ObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, interfaces: implements_interfaces, directives: directives, fields: field_defns, filename: @filename,
|
|
323
|
+
ObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, interfaces: implements_interfaces, directives: directives, fields: field_defns, filename: @filename, source: self)
|
|
289
324
|
when :INTERFACE
|
|
290
325
|
advance_token
|
|
291
326
|
name = parse_name
|
|
292
327
|
interfaces = parse_implements
|
|
293
328
|
directives = parse_directives
|
|
294
329
|
fields_definition = parse_field_definitions
|
|
295
|
-
InterfaceTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: fields_definition, interfaces: interfaces, filename: @filename,
|
|
330
|
+
InterfaceTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: fields_definition, interfaces: interfaces, filename: @filename, source: self)
|
|
296
331
|
when :UNION
|
|
297
332
|
advance_token
|
|
298
333
|
name = parse_name
|
|
299
334
|
directives = parse_directives
|
|
300
335
|
union_member_types = parse_union_members
|
|
301
|
-
UnionTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, types: union_member_types, filename: @filename,
|
|
336
|
+
UnionTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, types: union_member_types, filename: @filename, source: self)
|
|
302
337
|
when :SCALAR
|
|
303
338
|
advance_token
|
|
304
339
|
name = parse_name
|
|
305
340
|
directives = parse_directives
|
|
306
|
-
ScalarTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, filename: @filename,
|
|
341
|
+
ScalarTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, filename: @filename, source: self)
|
|
307
342
|
when :ENUM
|
|
308
343
|
advance_token
|
|
309
344
|
name = parse_name
|
|
310
345
|
directives = parse_directives
|
|
311
346
|
enum_values_definition = parse_enum_value_definitions
|
|
312
|
-
Nodes::EnumTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, values: enum_values_definition, filename: @filename,
|
|
347
|
+
Nodes::EnumTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, values: enum_values_definition, filename: @filename, source: self)
|
|
313
348
|
when :INPUT
|
|
314
349
|
advance_token
|
|
315
350
|
name = parse_name
|
|
316
351
|
directives = parse_directives
|
|
317
352
|
input_fields_definition = parse_input_object_field_definitions
|
|
318
|
-
InputObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: input_fields_definition, filename: @filename,
|
|
353
|
+
InputObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: input_fields_definition, filename: @filename, source: self)
|
|
319
354
|
else
|
|
320
355
|
expect_one_of([:SCHEMA, :SCALAR, :TYPE, :ENUM, :INPUT, :UNION, :INTERFACE])
|
|
321
356
|
end
|
|
@@ -346,7 +381,7 @@ module GraphQL
|
|
|
346
381
|
defn_loc = pos
|
|
347
382
|
enum_value = parse_enum_name
|
|
348
383
|
v_directives = parse_directives
|
|
349
|
-
list << EnumValueDefinition.new(pos: v_loc, definition_pos: defn_loc, description: description, name: enum_value, directives: v_directives, filename: @filename,
|
|
384
|
+
list << EnumValueDefinition.new(pos: v_loc, definition_pos: defn_loc, description: description, name: enum_value, directives: v_directives, filename: @filename, source: self)
|
|
350
385
|
end
|
|
351
386
|
expect_token :RCURLY
|
|
352
387
|
list
|
|
@@ -397,7 +432,7 @@ module GraphQL
|
|
|
397
432
|
type = self.type
|
|
398
433
|
directives = parse_directives
|
|
399
434
|
|
|
400
|
-
list << FieldDefinition.new(pos: loc, definition_pos: defn_loc, description: description, name: name, arguments: arguments_definition, type: type, directives: directives, filename: @filename,
|
|
435
|
+
list << FieldDefinition.new(pos: loc, definition_pos: defn_loc, description: description, name: name, arguments: arguments_definition, type: type, directives: directives, filename: @filename, source: self)
|
|
401
436
|
end
|
|
402
437
|
expect_token :RCURLY
|
|
403
438
|
list
|
|
@@ -431,7 +466,7 @@ module GraphQL
|
|
|
431
466
|
nil
|
|
432
467
|
end
|
|
433
468
|
directives = parse_directives
|
|
434
|
-
InputValueDefinition.new(pos: loc, definition_pos: defn_loc, description: description, name: name, type: type, default_value: default_value, directives: directives, filename: @filename,
|
|
469
|
+
InputValueDefinition.new(pos: loc, definition_pos: defn_loc, description: description, name: name, type: type, default_value: default_value, directives: directives, filename: @filename, source: self)
|
|
435
470
|
end
|
|
436
471
|
|
|
437
472
|
def type
|
|
@@ -443,7 +478,7 @@ module GraphQL
|
|
|
443
478
|
end
|
|
444
479
|
|
|
445
480
|
if at?(:BANG)
|
|
446
|
-
type = Nodes::NonNullType.new(pos: pos, of_type: type)
|
|
481
|
+
type = Nodes::NonNullType.new(pos: pos, of_type: type, source: self)
|
|
447
482
|
expect_token(:BANG)
|
|
448
483
|
end
|
|
449
484
|
type
|
|
@@ -452,7 +487,7 @@ module GraphQL
|
|
|
452
487
|
def list_type
|
|
453
488
|
loc = pos
|
|
454
489
|
expect_token(:LBRACKET)
|
|
455
|
-
type = Nodes::ListType.new(pos: loc, of_type: self.type)
|
|
490
|
+
type = Nodes::ListType.new(pos: loc, of_type: self.type, source: self)
|
|
456
491
|
expect_token(:RBRACKET)
|
|
457
492
|
type
|
|
458
493
|
end
|
|
@@ -489,7 +524,7 @@ module GraphQL
|
|
|
489
524
|
|
|
490
525
|
directives = parse_directives
|
|
491
526
|
|
|
492
|
-
Nodes::InlineFragment.new(pos: loc, type: if_type, directives: directives, selections: selection_set, filename: @filename,
|
|
527
|
+
Nodes::InlineFragment.new(pos: loc, type: if_type, directives: directives, selections: selection_set, filename: @filename, source: self)
|
|
493
528
|
else
|
|
494
529
|
name = parse_name_without_on
|
|
495
530
|
directives = parse_directives
|
|
@@ -497,7 +532,7 @@ module GraphQL
|
|
|
497
532
|
# Can this ever happen?
|
|
498
533
|
# expect_token(:IDENTIFIER) if at?(:ON)
|
|
499
534
|
|
|
500
|
-
FragmentSpread.new(pos: loc, name: name, directives: directives, filename: @filename,
|
|
535
|
+
FragmentSpread.new(pos: loc, name: name, directives: directives, filename: @filename, source: self)
|
|
501
536
|
end
|
|
502
537
|
else
|
|
503
538
|
loc = pos
|
|
@@ -515,7 +550,7 @@ module GraphQL
|
|
|
515
550
|
directives = at?(:DIR_SIGN) ? parse_directives : nil
|
|
516
551
|
selection_set = at?(:LCURLY) ? self.selection_set : nil
|
|
517
552
|
|
|
518
|
-
Nodes::Field.new(pos: loc, field_alias: field_alias, name: name, arguments: arguments, directives: directives, selections: selection_set, filename: @filename,
|
|
553
|
+
Nodes::Field.new(pos: loc, field_alias: field_alias, name: name, arguments: arguments, directives: directives, selections: selection_set, filename: @filename, source: self)
|
|
519
554
|
end
|
|
520
555
|
end
|
|
521
556
|
expect_token(:RCURLY)
|
|
@@ -609,7 +644,7 @@ module GraphQL
|
|
|
609
644
|
end
|
|
610
645
|
|
|
611
646
|
def parse_type_name
|
|
612
|
-
TypeName.new(pos: pos, name: parse_name, filename: @filename,
|
|
647
|
+
TypeName.new(pos: pos, name: parse_name, filename: @filename, source: self)
|
|
613
648
|
end
|
|
614
649
|
|
|
615
650
|
def parse_directives
|
|
@@ -621,7 +656,7 @@ module GraphQL
|
|
|
621
656
|
name = parse_name
|
|
622
657
|
arguments = parse_arguments
|
|
623
658
|
|
|
624
|
-
dirs << Nodes::Directive.new(pos: loc, name: name, arguments: arguments, filename: @filename,
|
|
659
|
+
dirs << Nodes::Directive.new(pos: loc, name: name, arguments: arguments, filename: @filename, source: self)
|
|
625
660
|
end
|
|
626
661
|
dirs
|
|
627
662
|
else
|
|
@@ -637,7 +672,7 @@ module GraphQL
|
|
|
637
672
|
loc = pos
|
|
638
673
|
name = parse_name
|
|
639
674
|
expect_token(:COLON)
|
|
640
|
-
args << Nodes::Argument.new(pos: loc, name: name, value: value, filename: @filename,
|
|
675
|
+
args << Nodes::Argument.new(pos: loc, name: name, value: value, filename: @filename, source: self)
|
|
641
676
|
end
|
|
642
677
|
if args.empty?
|
|
643
678
|
expect_token(:ARGUMENT_NAME) # At least one argument is required
|
|
@@ -671,9 +706,9 @@ module GraphQL
|
|
|
671
706
|
false
|
|
672
707
|
when :NULL
|
|
673
708
|
advance_token
|
|
674
|
-
NullValue.new(pos: pos, name: "null", filename: @filename,
|
|
709
|
+
NullValue.new(pos: pos, name: "null", filename: @filename, source: self)
|
|
675
710
|
when :IDENTIFIER
|
|
676
|
-
Nodes::Enum.new(pos: pos, name: expect_token_value(:IDENTIFIER), filename: @filename,
|
|
711
|
+
Nodes::Enum.new(pos: pos, name: expect_token_value(:IDENTIFIER), filename: @filename, source: self)
|
|
677
712
|
when :LBRACKET
|
|
678
713
|
advance_token
|
|
679
714
|
list = []
|
|
@@ -690,14 +725,14 @@ module GraphQL
|
|
|
690
725
|
loc = pos
|
|
691
726
|
n = parse_name
|
|
692
727
|
expect_token(:COLON)
|
|
693
|
-
args << Argument.new(pos: loc, name: n, value: value, filename: @filename,
|
|
728
|
+
args << Argument.new(pos: loc, name: n, value: value, filename: @filename, source: self)
|
|
694
729
|
end
|
|
695
730
|
expect_token(:RCURLY)
|
|
696
|
-
InputObject.new(pos: start, arguments: args, filename: @filename,
|
|
731
|
+
InputObject.new(pos: start, arguments: args, filename: @filename, source: self)
|
|
697
732
|
when :VAR_SIGN
|
|
698
733
|
loc = pos
|
|
699
734
|
advance_token
|
|
700
|
-
VariableIdentifier.new(pos: loc, name: parse_name, filename: @filename,
|
|
735
|
+
VariableIdentifier.new(pos: loc, name: parse_name, filename: @filename, source: self)
|
|
701
736
|
else
|
|
702
737
|
expect_token(:VALUE)
|
|
703
738
|
end
|
data/lib/graphql/query.rb
CHANGED
|
@@ -304,7 +304,7 @@ module GraphQL
|
|
|
304
304
|
|
|
305
305
|
# @return [String] An opaque hash for identifying this query's given query string and selected operation
|
|
306
306
|
def operation_fingerprint
|
|
307
|
-
@operation_fingerprint ||= "#{selected_operation_name || "anonymous"}/#{Fingerprint.generate(query_string)}"
|
|
307
|
+
@operation_fingerprint ||= "#{selected_operation_name || "anonymous"}/#{Fingerprint.generate(query_string || "")}"
|
|
308
308
|
end
|
|
309
309
|
|
|
310
310
|
# @return [String] An opaque hash for identifying this query's given a variable values (not including defaults)
|
data/lib/graphql/schema.rb
CHANGED
|
@@ -749,9 +749,10 @@ module GraphQL
|
|
|
749
749
|
|
|
750
750
|
attr_writer :max_complexity
|
|
751
751
|
|
|
752
|
-
def max_complexity(max_complexity = nil)
|
|
752
|
+
def max_complexity(max_complexity = nil, count_introspection_fields: true)
|
|
753
753
|
if max_complexity
|
|
754
754
|
@max_complexity = max_complexity
|
|
755
|
+
@max_complexity_count_introspection_fields = count_introspection_fields
|
|
755
756
|
elsif defined?(@max_complexity)
|
|
756
757
|
@max_complexity
|
|
757
758
|
else
|
|
@@ -759,6 +760,14 @@ module GraphQL
|
|
|
759
760
|
end
|
|
760
761
|
end
|
|
761
762
|
|
|
763
|
+
def max_complexity_count_introspection_fields
|
|
764
|
+
if defined?(@max_complexity_count_introspection_fields)
|
|
765
|
+
@max_complexity_count_introspection_fields
|
|
766
|
+
else
|
|
767
|
+
find_inherited_value(:max_complexity_count_introspection_fields, true)
|
|
768
|
+
end
|
|
769
|
+
end
|
|
770
|
+
|
|
762
771
|
attr_writer :analysis_engine
|
|
763
772
|
|
|
764
773
|
def analysis_engine
|
|
@@ -25,33 +25,33 @@ module GraphQL
|
|
|
25
25
|
}.each do |trace_method, platform_key|
|
|
26
26
|
module_eval <<-RUBY, __FILE__, __LINE__
|
|
27
27
|
def #{trace_method}(**data)
|
|
28
|
-
|
|
28
|
+
instrument_prometheus_execution("#{platform_key}", "#{trace_method}") { super }
|
|
29
29
|
end
|
|
30
30
|
RUBY
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def platform_execute_field(platform_key, &block)
|
|
34
|
-
|
|
34
|
+
instrument_prometheus_execution(platform_key, "execute_field", &block)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def platform_execute_field_lazy(platform_key, &block)
|
|
38
|
-
|
|
38
|
+
instrument_prometheus_execution(platform_key, "execute_field_lazy", &block)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def platform_authorized(platform_key, &block)
|
|
42
|
-
|
|
42
|
+
instrument_prometheus_execution(platform_key, "authorized", &block)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def platform_authorized_lazy(platform_key, &block)
|
|
46
|
-
|
|
46
|
+
instrument_prometheus_execution(platform_key, "authorized_lazy", &block)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def platform_resolve_type(platform_key, &block)
|
|
50
|
-
|
|
50
|
+
instrument_prometheus_execution(platform_key, "resolve_type", &block)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def platform_resolve_type_lazy(platform_key, &block)
|
|
54
|
-
|
|
54
|
+
instrument_prometheus_execution(platform_key, "resolve_type_lazy", &block)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def platform_field_key(field)
|
|
@@ -68,7 +68,7 @@ module GraphQL
|
|
|
68
68
|
|
|
69
69
|
private
|
|
70
70
|
|
|
71
|
-
def
|
|
71
|
+
def instrument_prometheus_execution(platform_key, key, &block)
|
|
72
72
|
if @keys_whitelist.include?(key)
|
|
73
73
|
start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC
|
|
74
74
|
result = block.call
|
|
@@ -12,7 +12,7 @@ module GraphQL
|
|
|
12
12
|
@set_transaction_name = set_transaction_name
|
|
13
13
|
super
|
|
14
14
|
end
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
def execute_query(**data)
|
|
17
17
|
set_this_txn_name = data[:query].context[:set_sentry_transaction_name]
|
|
18
18
|
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
|
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
|
20
20
|
scope.set_transaction_name(transaction_name(data[:query]))
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
|
-
|
|
23
|
+
instrument_sentry_execution("graphql.execute", "execute_query", data) { super }
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
{
|
|
@@ -34,33 +34,33 @@ module GraphQL
|
|
|
34
34
|
}.each do |trace_method, platform_key|
|
|
35
35
|
module_eval <<-RUBY, __FILE__, __LINE__
|
|
36
36
|
def #{trace_method}(**data)
|
|
37
|
-
|
|
37
|
+
instrument_sentry_execution("#{platform_key}", "#{trace_method}", data) { super }
|
|
38
38
|
end
|
|
39
39
|
RUBY
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def platform_execute_field(platform_key, &block)
|
|
43
|
-
|
|
43
|
+
instrument_sentry_execution(platform_key, "execute_field", &block)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def platform_execute_field_lazy(platform_key, &block)
|
|
47
|
-
|
|
47
|
+
instrument_sentry_execution(platform_key, "execute_field_lazy", &block)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def platform_authorized(platform_key, &block)
|
|
51
|
-
|
|
51
|
+
instrument_sentry_execution(platform_key, "authorized", &block)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def platform_authorized_lazy(platform_key, &block)
|
|
55
|
-
|
|
55
|
+
instrument_sentry_execution(platform_key, "authorized_lazy", &block)
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def platform_resolve_type(platform_key, &block)
|
|
59
|
-
|
|
59
|
+
instrument_sentry_execution(platform_key, "resolve_type", &block)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def platform_resolve_type_lazy(platform_key, &block)
|
|
63
|
-
|
|
63
|
+
instrument_sentry_execution(platform_key, "resolve_type_lazy", &block)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def platform_field_key(field)
|
|
@@ -77,7 +77,7 @@ module GraphQL
|
|
|
77
77
|
|
|
78
78
|
private
|
|
79
79
|
|
|
80
|
-
def
|
|
80
|
+
def instrument_sentry_execution(platform_key, trace_method, data=nil, &block)
|
|
81
81
|
return yield unless Sentry.initialized?
|
|
82
82
|
|
|
83
83
|
Sentry.with_child_span(op: platform_key, start_timestamp: Sentry.utc_now.to_f) do |span|
|
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.3.
|
|
4
|
+
version: 2.3.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Mosolgo
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-
|
|
11
|
+
date: 2024-05-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|