graphql 2.3.8 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +46 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_resolver.erb +2 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis.rb +1 -1
- data/lib/graphql/current.rb +52 -0
- data/lib/graphql/dataloader/async_dataloader.rb +3 -2
- data/lib/graphql/dataloader/source.rb +6 -3
- data/lib/graphql/dataloader.rb +35 -11
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +10 -6
- data/lib/graphql/execution/interpreter/runtime.rb +23 -19
- data/lib/graphql/execution/interpreter.rb +2 -0
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/schema_type.rb +4 -19
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/document_from_schema_definition.rb +38 -4
- data/lib/graphql/language/lexer.rb +15 -12
- data/lib/graphql/language/nodes.rb +24 -16
- data/lib/graphql/language/parser.rb +14 -1
- data/lib/graphql/language/printer.rb +23 -7
- data/lib/graphql/language.rb +6 -5
- data/lib/graphql/query/context.rb +4 -2
- data/lib/graphql/query/null_context.rb +1 -5
- data/lib/graphql/query.rb +49 -20
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
- data/lib/graphql/rubocop.rb +2 -0
- data/lib/graphql/schema/addition.rb +1 -0
- data/lib/graphql/schema/always_visible.rb +6 -3
- data/lib/graphql/schema/argument.rb +14 -1
- data/lib/graphql/schema/build_from_definition.rb +9 -1
- data/lib/graphql/schema/directive/flagged.rb +1 -1
- data/lib/graphql/schema/enum.rb +49 -15
- data/lib/graphql/schema/enum_value.rb +9 -1
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +91 -36
- data/lib/graphql/schema/input_object.rb +20 -7
- data/lib/graphql/schema/interface.rb +21 -4
- data/lib/graphql/schema/introspection_system.rb +3 -2
- data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
- data/lib/graphql/schema/member/has_arguments.rb +9 -5
- data/lib/graphql/schema/member/has_fields.rb +5 -5
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/printer.rb +1 -0
- data/lib/graphql/schema/resolver.rb +3 -4
- data/lib/graphql/schema/validator/all_validator.rb +2 -0
- data/lib/graphql/schema/validator/required_validator.rb +28 -4
- data/lib/graphql/schema/visibility/migration.rb +186 -0
- data/lib/graphql/schema/visibility/profile.rb +523 -0
- data/lib/graphql/schema/visibility.rb +75 -0
- data/lib/graphql/schema/warden.rb +80 -21
- data/lib/graphql/schema.rb +254 -85
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +2 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +2 -1
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +2 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -0
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +11 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +10 -1
- data/lib/graphql/static_validation/validation_context.rb +15 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
- data/lib/graphql/subscriptions.rb +3 -1
- data/lib/graphql/testing/helpers.rb +7 -3
- data/lib/graphql/tracing/notifications_trace.rb +2 -2
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -0
- metadata +28 -7
- data/lib/graphql/schema/subset.rb +0 -397
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 26e43b0bc48317698ed17f8a11498c19a7a4a0df9d33fdc9785edc47ae1147b6
|
|
4
|
+
data.tar.gz: 74402e930ebe03a451bc2bdb82285642aeeba7222ab491dd9329400fc852eb41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f1d97c4397ca8410f6b62c3ea2de9a0a4b18ca610adcb92de9676c5377b00a071cbff4fbd0396760eea9ab359e4b0b8cdfff1630c9fa83de51cfd31b0b96307f
|
|
7
|
+
data.tar.gz: d9250a9ad1d57f40e7b0151484f77a66e11a361f8b0f50938f42219eb8322a9b4dc59c59102f4b49f62a28e90f03b8f9e0cf5b2799b5fd5080dbcf443cebac15
|
|
@@ -45,6 +45,13 @@ module Graphql
|
|
|
45
45
|
# post "/graphql", to: "graphql#execute"
|
|
46
46
|
# ```
|
|
47
47
|
#
|
|
48
|
+
# Add ActiveRecord::QueryLogs metadata:
|
|
49
|
+
# ```ruby
|
|
50
|
+
# current_graphql_operation: -> { GraphQL::Current.operation_name },
|
|
51
|
+
# current_graphql_field: -> { GraphQL::Current.field&.path },
|
|
52
|
+
# current_dataloader_source: -> { GraphQL::Current.dataloader_source_class },
|
|
53
|
+
# ```
|
|
54
|
+
#
|
|
48
55
|
# Accept a `--batch` option which adds `GraphQL::Batch` setup.
|
|
49
56
|
#
|
|
50
57
|
# Use `--skip-graphiql` to skip `graphiql-rails` installation.
|
|
@@ -92,6 +99,11 @@ module Graphql
|
|
|
92
99
|
default: false,
|
|
93
100
|
desc: "Use GraphQL Playground over Graphiql as IDE"
|
|
94
101
|
|
|
102
|
+
class_option :skip_query_logs,
|
|
103
|
+
type: :boolean,
|
|
104
|
+
default: false,
|
|
105
|
+
desc: "Skip ActiveRecord::QueryLogs hooks in config/application.rb"
|
|
106
|
+
|
|
95
107
|
# These two options are taken from Rails' own generators'
|
|
96
108
|
class_option :api,
|
|
97
109
|
type: :boolean,
|
|
@@ -180,6 +192,40 @@ RUBY
|
|
|
180
192
|
install_relay
|
|
181
193
|
end
|
|
182
194
|
|
|
195
|
+
if !options[:skip_query_logs]
|
|
196
|
+
config_file = "config/application.rb"
|
|
197
|
+
current_app_rb = File.read(Rails.root.join(config_file))
|
|
198
|
+
existing_log_tags_pattern = /config.active_record.query_log_tags = \[\n?(\s*:[a-z_]+,?\s*\n?|\s*#[^\]]*\n)*/m
|
|
199
|
+
existing_log_tags = existing_log_tags_pattern.match(current_app_rb)
|
|
200
|
+
if existing_log_tags && behavior == :invoke
|
|
201
|
+
code = <<-RUBY
|
|
202
|
+
# GraphQL-Ruby query log tags:
|
|
203
|
+
current_graphql_operation: -> { GraphQL::Current.operation_name },
|
|
204
|
+
current_graphql_field: -> { GraphQL::Current.field&.path },
|
|
205
|
+
current_dataloader_source: -> { GraphQL::Current.dataloader_source_class },
|
|
206
|
+
RUBY
|
|
207
|
+
if !existing_log_tags.to_s.end_with?(",")
|
|
208
|
+
code = ",\n#{code} "
|
|
209
|
+
end
|
|
210
|
+
# Try to insert this code _after_ any plain symbol entries in the array of query log tags:
|
|
211
|
+
after_code = existing_log_tags_pattern
|
|
212
|
+
else
|
|
213
|
+
code = <<-RUBY
|
|
214
|
+
config.active_record.query_log_tags_enabled = true
|
|
215
|
+
config.active_record.query_log_tags = [
|
|
216
|
+
# Rails query log tags:
|
|
217
|
+
:application, :controller, :action, :job,
|
|
218
|
+
# GraphQL-Ruby query log tags:
|
|
219
|
+
current_graphql_operation: -> { GraphQL::Current.operation_name },
|
|
220
|
+
current_graphql_field: -> { GraphQL::Current.field&.path },
|
|
221
|
+
current_dataloader_source: -> { GraphQL::Current.dataloader_source_class },
|
|
222
|
+
]
|
|
223
|
+
RUBY
|
|
224
|
+
after_code = "class Application < Rails::Application\n"
|
|
225
|
+
end
|
|
226
|
+
insert_into_file(config_file, code, after: after_code)
|
|
227
|
+
end
|
|
228
|
+
|
|
183
229
|
if gemfile_modified?
|
|
184
230
|
say "Gemfile has been modified, make sure you `bundle install`"
|
|
185
231
|
end
|
|
@@ -18,7 +18,7 @@ module Graphql
|
|
|
18
18
|
class_option :orm, banner: "NAME", type: :string, required: true,
|
|
19
19
|
desc: "ORM to generate the controller for"
|
|
20
20
|
|
|
21
|
-
class_option
|
|
21
|
+
class_option :namespaced_types,
|
|
22
22
|
type: :boolean,
|
|
23
23
|
required: false,
|
|
24
24
|
default: false,
|
data/lib/graphql/analysis.rb
CHANGED
|
@@ -81,7 +81,7 @@ module GraphQL
|
|
|
81
81
|
end
|
|
82
82
|
rescue Timeout::Error
|
|
83
83
|
[GraphQL::AnalysisError.new("Timeout on validation of query")]
|
|
84
|
-
rescue GraphQL::UnauthorizedError
|
|
84
|
+
rescue GraphQL::UnauthorizedError, GraphQL::ExecutionError
|
|
85
85
|
# This error was raised during analysis and will be returned the client before execution
|
|
86
86
|
[]
|
|
87
87
|
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
# This module exposes Fiber-level runtime information.
|
|
5
|
+
#
|
|
6
|
+
# It won't work across unrelated fibers, although it will work in child Fibers.
|
|
7
|
+
#
|
|
8
|
+
# @example Setting Up ActiveRecord::QueryLogs
|
|
9
|
+
#
|
|
10
|
+
# config.active_record.query_log_tags = [
|
|
11
|
+
# :namespaced_controller,
|
|
12
|
+
# :action,
|
|
13
|
+
# :job,
|
|
14
|
+
# # ...
|
|
15
|
+
# {
|
|
16
|
+
# # GraphQL runtime info:
|
|
17
|
+
# current_graphql_operation: -> { GraphQL::Current.operation_name },
|
|
18
|
+
# current_graphql_field: -> { GraphQL::Current.field&.path },
|
|
19
|
+
# current_dataloader_source: -> { GraphQL::Current.dataloader_source_class },
|
|
20
|
+
# # ...
|
|
21
|
+
# },
|
|
22
|
+
# ]
|
|
23
|
+
#
|
|
24
|
+
module Current
|
|
25
|
+
# @return [String, nil] Comma-joined operation names for the currently-running {Multiplex}. `nil` if all operations are anonymous.
|
|
26
|
+
def self.operation_name
|
|
27
|
+
if (m = Fiber[:__graphql_current_multiplex])
|
|
28
|
+
m.context[:__graphql_current_operation_name] ||= begin
|
|
29
|
+
names = m.queries.map { |q| q.selected_operation_name }
|
|
30
|
+
if names.all?(&:nil?)
|
|
31
|
+
nil
|
|
32
|
+
else
|
|
33
|
+
names.join(",")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @see GraphQL::Field#path for a string identifying this field
|
|
42
|
+
# @return [GraphQL::Field, nil] The currently-running field, if there is one.
|
|
43
|
+
def self.field
|
|
44
|
+
Thread.current[:__graphql_runtime_info]&.values&.first&.current_field
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [Class, nil] The currently-running {Dataloader::Source} class, if there is one.
|
|
48
|
+
def self.dataloader_source_class
|
|
49
|
+
Fiber[:__graphql_current_dataloader_source]&.class
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -12,6 +12,7 @@ module GraphQL
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def run
|
|
15
|
+
jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
|
|
15
16
|
job_fibers = []
|
|
16
17
|
next_job_fibers = []
|
|
17
18
|
source_tasks = []
|
|
@@ -23,7 +24,7 @@ module GraphQL
|
|
|
23
24
|
first_pass = false
|
|
24
25
|
fiber_vars = get_fiber_variables
|
|
25
26
|
|
|
26
|
-
while (f = (job_fibers.shift || spawn_job_fiber))
|
|
27
|
+
while (f = (job_fibers.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size) < jobs_fiber_limit) && spawn_job_fiber)))
|
|
27
28
|
if f.alive?
|
|
28
29
|
finished = run_fiber(f)
|
|
29
30
|
if !finished
|
|
@@ -37,7 +38,7 @@ module GraphQL
|
|
|
37
38
|
Sync do |root_task|
|
|
38
39
|
set_fiber_variables(fiber_vars)
|
|
39
40
|
while source_tasks.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) }
|
|
40
|
-
while (task = source_tasks.shift || spawn_source_task(root_task, sources_condition))
|
|
41
|
+
while (task = (source_tasks.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size + next_source_tasks.size) < total_fiber_limit) && spawn_source_task(root_task, sources_condition))))
|
|
41
42
|
if task.alive?
|
|
42
43
|
root_task.yield # give the source task a chance to run
|
|
43
44
|
next_source_tasks << task
|
|
@@ -98,7 +98,7 @@ module GraphQL
|
|
|
98
98
|
while pending_result_keys.any? { |key| !@results.key?(key) }
|
|
99
99
|
iterations += 1
|
|
100
100
|
if iterations > MAX_ITERATIONS
|
|
101
|
-
raise "#{self.class}#sync tried #{MAX_ITERATIONS} times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency."
|
|
101
|
+
raise "#{self.class}#sync tried #{MAX_ITERATIONS} times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency#{@dataloader.fiber_limit ? " or `fiber_limit: #{@dataloader.fiber_limit}` is set too low" : ""}."
|
|
102
102
|
end
|
|
103
103
|
@dataloader.yield
|
|
104
104
|
end
|
|
@@ -186,8 +186,11 @@ This key should have been loaded already. This is a bug in GraphQL::Dataloader,
|
|
|
186
186
|
ERR
|
|
187
187
|
end
|
|
188
188
|
result = @results[key]
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
if result.is_a?(StandardError)
|
|
190
|
+
# Dup it because the rescuer may modify it.
|
|
191
|
+
# (This happens for GraphQL::ExecutionErrors, at least)
|
|
192
|
+
raise result.dup
|
|
193
|
+
end
|
|
191
194
|
|
|
192
195
|
result
|
|
193
196
|
end
|
data/lib/graphql/dataloader.rb
CHANGED
|
@@ -24,18 +24,23 @@ module GraphQL
|
|
|
24
24
|
#
|
|
25
25
|
class Dataloader
|
|
26
26
|
class << self
|
|
27
|
-
attr_accessor :default_nonblocking
|
|
27
|
+
attr_accessor :default_nonblocking, :default_fiber_limit
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def self.use(schema, nonblocking: nil)
|
|
33
|
-
schema.dataloader_class = if nonblocking
|
|
30
|
+
def self.use(schema, nonblocking: nil, fiber_limit: nil)
|
|
31
|
+
dataloader_class = if nonblocking
|
|
34
32
|
warn("`nonblocking: true` is deprecated from `GraphQL::Dataloader`, please use `GraphQL::Dataloader::AsyncDataloader` instead. Docs: https://graphql-ruby.org/dataloader/async_dataloader.")
|
|
35
|
-
|
|
33
|
+
Class.new(self) { self.default_nonblocking = true }
|
|
36
34
|
else
|
|
37
35
|
self
|
|
38
36
|
end
|
|
37
|
+
|
|
38
|
+
if fiber_limit
|
|
39
|
+
dataloader_class = Class.new(dataloader_class)
|
|
40
|
+
dataloader_class.default_fiber_limit = fiber_limit
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
schema.dataloader_class = dataloader_class
|
|
39
44
|
end
|
|
40
45
|
|
|
41
46
|
# Call the block with a Dataloader instance,
|
|
@@ -50,14 +55,18 @@ module GraphQL
|
|
|
50
55
|
result
|
|
51
56
|
end
|
|
52
57
|
|
|
53
|
-
def initialize(nonblocking: self.class.default_nonblocking)
|
|
58
|
+
def initialize(nonblocking: self.class.default_nonblocking, fiber_limit: self.class.default_fiber_limit)
|
|
54
59
|
@source_cache = Hash.new { |h, k| h[k] = {} }
|
|
55
60
|
@pending_jobs = []
|
|
56
61
|
if !nonblocking.nil?
|
|
57
62
|
@nonblocking = nonblocking
|
|
58
63
|
end
|
|
64
|
+
@fiber_limit = fiber_limit
|
|
59
65
|
end
|
|
60
66
|
|
|
67
|
+
# @return [Integer, nil]
|
|
68
|
+
attr_reader :fiber_limit
|
|
69
|
+
|
|
61
70
|
def nonblocking?
|
|
62
71
|
@nonblocking
|
|
63
72
|
end
|
|
@@ -178,6 +187,7 @@ module GraphQL
|
|
|
178
187
|
end
|
|
179
188
|
|
|
180
189
|
def run
|
|
190
|
+
jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
|
|
181
191
|
job_fibers = []
|
|
182
192
|
next_job_fibers = []
|
|
183
193
|
source_fibers = []
|
|
@@ -187,7 +197,7 @@ module GraphQL
|
|
|
187
197
|
while first_pass || job_fibers.any?
|
|
188
198
|
first_pass = false
|
|
189
199
|
|
|
190
|
-
while (f = (job_fibers.shift || spawn_job_fiber))
|
|
200
|
+
while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber)))
|
|
191
201
|
if f.alive?
|
|
192
202
|
finished = run_fiber(f)
|
|
193
203
|
if !finished
|
|
@@ -197,8 +207,8 @@ module GraphQL
|
|
|
197
207
|
end
|
|
198
208
|
join_queues(job_fibers, next_job_fibers)
|
|
199
209
|
|
|
200
|
-
while source_fibers.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) }
|
|
201
|
-
while (f = source_fibers.shift || spawn_source_fiber)
|
|
210
|
+
while (source_fibers.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
|
|
211
|
+
while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber))
|
|
202
212
|
if f.alive?
|
|
203
213
|
finished = run_fiber(f)
|
|
204
214
|
if !finished
|
|
@@ -242,6 +252,17 @@ module GraphQL
|
|
|
242
252
|
|
|
243
253
|
private
|
|
244
254
|
|
|
255
|
+
def calculate_fiber_limit
|
|
256
|
+
total_fiber_limit = @fiber_limit || Float::INFINITY
|
|
257
|
+
if total_fiber_limit < 4
|
|
258
|
+
raise ArgumentError, "Dataloader fiber limit is too low (#{total_fiber_limit}), it must be at least 4"
|
|
259
|
+
end
|
|
260
|
+
total_fiber_limit -= 1 # deduct one fiber for `manager`
|
|
261
|
+
# Deduct at least one fiber for sources
|
|
262
|
+
jobs_fiber_limit = total_fiber_limit - 2
|
|
263
|
+
return jobs_fiber_limit, total_fiber_limit
|
|
264
|
+
end
|
|
265
|
+
|
|
245
266
|
def join_queues(prev_queue, new_queue)
|
|
246
267
|
@nonblocking && Fiber.scheduler.run
|
|
247
268
|
prev_queue.concat(new_queue)
|
|
@@ -271,7 +292,10 @@ module GraphQL
|
|
|
271
292
|
|
|
272
293
|
if pending_sources
|
|
273
294
|
spawn_fiber do
|
|
274
|
-
pending_sources.each
|
|
295
|
+
pending_sources.each do |source|
|
|
296
|
+
Fiber[:__graphql_current_dataloader_source] = source
|
|
297
|
+
source.run_pending_keys
|
|
298
|
+
end
|
|
275
299
|
end
|
|
276
300
|
end
|
|
277
301
|
end
|
|
@@ -8,22 +8,17 @@ module GraphQL
|
|
|
8
8
|
@query = query
|
|
9
9
|
@dataloader = query.context.dataloader
|
|
10
10
|
@storage = Hash.new do |h, argument_owner|
|
|
11
|
-
|
|
11
|
+
h[argument_owner] = if argument_owner.arguments_statically_coercible?
|
|
12
12
|
shared_values_cache = {}
|
|
13
13
|
Hash.new do |h2, ignored_parent_object|
|
|
14
14
|
h2[ignored_parent_object] = shared_values_cache
|
|
15
|
-
end
|
|
15
|
+
end.compare_by_identity
|
|
16
16
|
else
|
|
17
17
|
Hash.new do |h2, parent_object|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
h2[parent_object] = args_by_node
|
|
21
|
-
end
|
|
18
|
+
h2[parent_object] = {}.compare_by_identity
|
|
19
|
+
end.compare_by_identity
|
|
22
20
|
end
|
|
23
|
-
|
|
24
|
-
h[argument_owner] = args_by_parent
|
|
25
|
-
end
|
|
26
|
-
@storage.compare_by_identity
|
|
21
|
+
end.compare_by_identity
|
|
27
22
|
end
|
|
28
23
|
|
|
29
24
|
def fetch(ast_node, argument_owner, parent_object)
|
|
@@ -12,12 +12,16 @@ module GraphQL
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def self.resolve_each_depth(lazies_at_depth, dataloader)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
smallest_depth = nil
|
|
16
|
+
lazies_at_depth.each_key do |depth_key|
|
|
17
|
+
smallest_depth ||= depth_key
|
|
18
|
+
if depth_key < smallest_depth
|
|
19
|
+
smallest_depth = depth_key
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if smallest_depth
|
|
24
|
+
lazies = lazies_at_depth.delete(smallest_depth)
|
|
21
25
|
if lazies.any?
|
|
22
26
|
dataloader.append_job {
|
|
23
27
|
lazies.each(&:value) # resolve these Lazy instances
|
|
@@ -53,8 +53,7 @@ module GraphQL
|
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
# { Class => Boolean }
|
|
56
|
-
@lazy_cache = {}
|
|
57
|
-
@lazy_cache.compare_by_identity
|
|
56
|
+
@lazy_cache = {}.compare_by_identity
|
|
58
57
|
end
|
|
59
58
|
|
|
60
59
|
def final_result
|
|
@@ -208,22 +207,32 @@ module GraphQL
|
|
|
208
207
|
finished_jobs = 0
|
|
209
208
|
enqueued_jobs = gathered_selections.size
|
|
210
209
|
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
|
211
|
-
|
|
212
|
-
evaluate_selection(
|
|
213
|
-
result_name, field_ast_nodes_or_ast_node, selections_result
|
|
214
|
-
)
|
|
215
|
-
finished_jobs += 1
|
|
216
|
-
if target_result && finished_jobs == enqueued_jobs
|
|
217
|
-
selections_result.merge_into(target_result)
|
|
218
|
-
end
|
|
219
|
-
}
|
|
210
|
+
|
|
220
211
|
# Field resolution may pause the fiber,
|
|
221
212
|
# so it wouldn't get to the `Resolve` call that happens below.
|
|
222
213
|
# So instead trigger a run from this outer context.
|
|
223
214
|
if selections_result.graphql_is_eager
|
|
224
215
|
@dataloader.clear_cache
|
|
225
|
-
@dataloader.
|
|
226
|
-
|
|
216
|
+
@dataloader.run_isolated {
|
|
217
|
+
evaluate_selection(
|
|
218
|
+
result_name, field_ast_nodes_or_ast_node, selections_result
|
|
219
|
+
)
|
|
220
|
+
finished_jobs += 1
|
|
221
|
+
if target_result && finished_jobs == enqueued_jobs
|
|
222
|
+
selections_result.merge_into(target_result)
|
|
223
|
+
end
|
|
224
|
+
@dataloader.clear_cache
|
|
225
|
+
}
|
|
226
|
+
else
|
|
227
|
+
@dataloader.append_job {
|
|
228
|
+
evaluate_selection(
|
|
229
|
+
result_name, field_ast_nodes_or_ast_node, selections_result
|
|
230
|
+
)
|
|
231
|
+
finished_jobs += 1
|
|
232
|
+
if target_result && finished_jobs == enqueued_jobs
|
|
233
|
+
selections_result.merge_into(target_result)
|
|
234
|
+
end
|
|
235
|
+
}
|
|
227
236
|
end
|
|
228
237
|
end
|
|
229
238
|
selections_result
|
|
@@ -727,12 +736,7 @@ module GraphQL
|
|
|
727
736
|
end
|
|
728
737
|
|
|
729
738
|
def get_current_runtime_state
|
|
730
|
-
current_state = Thread.current[:__graphql_runtime_info] ||=
|
|
731
|
-
per_query_state = {}
|
|
732
|
-
per_query_state.compare_by_identity
|
|
733
|
-
per_query_state
|
|
734
|
-
end
|
|
735
|
-
|
|
739
|
+
current_state = Thread.current[:__graphql_runtime_info] ||= {}.compare_by_identity
|
|
736
740
|
current_state[@query] ||= CurrentState.new
|
|
737
741
|
end
|
|
738
742
|
|
|
@@ -34,6 +34,7 @@ module GraphQL
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
|
|
37
|
+
Fiber[:__graphql_current_multiplex] = multiplex
|
|
37
38
|
multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
|
|
38
39
|
schema = multiplex.schema
|
|
39
40
|
queries = multiplex.queries
|
|
@@ -136,6 +137,7 @@ module GraphQL
|
|
|
136
137
|
queries.map { |q| q.result_values ||= {} }
|
|
137
138
|
raise
|
|
138
139
|
ensure
|
|
140
|
+
Fiber[:__graphql_current_multiplex] = nil
|
|
139
141
|
queries.map { |query|
|
|
140
142
|
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
|
141
143
|
if runtime
|
|
@@ -15,8 +15,8 @@ module GraphQL
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def __type(name:)
|
|
18
|
-
if context.types.reachable_type?(name)
|
|
19
|
-
|
|
18
|
+
if context.types.reachable_type?(name) && (type = context.types.type(name))
|
|
19
|
+
type
|
|
20
20
|
elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
|
|
21
21
|
type
|
|
22
22
|
else
|
|
@@ -27,34 +27,19 @@ module GraphQL
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def query_type
|
|
30
|
-
|
|
30
|
+
@context.types.query_root
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def mutation_type
|
|
34
|
-
|
|
34
|
+
@context.types.mutation_root
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def subscription_type
|
|
38
|
-
|
|
38
|
+
@context.types.subscription_root
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def directives
|
|
42
|
-
@context.types.directives
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
private
|
|
46
|
-
|
|
47
|
-
def permitted_root_type(op_type)
|
|
48
|
-
case op_type
|
|
49
|
-
when "query"
|
|
50
|
-
@context.types.query_root
|
|
51
|
-
when "mutation"
|
|
52
|
-
@context.types.mutation_root
|
|
53
|
-
when "subcription"
|
|
54
|
-
@context.types.subscription_root
|
|
55
|
-
else
|
|
56
|
-
nil
|
|
57
|
-
end
|
|
42
|
+
@context.types.directives.sort_by(&:graphql_name)
|
|
58
43
|
end
|
|
59
44
|
end
|
|
60
45
|
end
|
|
@@ -39,7 +39,7 @@ module GraphQL
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def inspect
|
|
42
|
-
if (name.nil? || parent_class
|
|
42
|
+
if (name.nil? || parent_class&.name.nil?) && parent_class.respond_to?(:mutation) && (mutation = parent_class.mutation)
|
|
43
43
|
"#{mutation.inspect}::#{parent_class.graphql_name}::InvalidNullError"
|
|
44
44
|
else
|
|
45
45
|
super
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
module Language
|
|
4
|
+
module Comment
|
|
5
|
+
def self.print(str, indent: '')
|
|
6
|
+
lines = str.split("\n").map do |line|
|
|
7
|
+
comment_str = "".dup
|
|
8
|
+
comment_str << indent
|
|
9
|
+
comment_str << "# "
|
|
10
|
+
comment_str << line
|
|
11
|
+
comment_str.rstrip
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
lines.join("\n") + "\n"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -18,6 +18,7 @@ module GraphQL
|
|
|
18
18
|
include_built_in_directives: false, include_built_in_scalars: false, always_include_schema: false
|
|
19
19
|
)
|
|
20
20
|
@schema = schema
|
|
21
|
+
@context = context
|
|
21
22
|
@always_include_schema = always_include_schema
|
|
22
23
|
@include_introspection_types = include_introspection_types
|
|
23
24
|
@include_built_in_scalars = include_built_in_scalars
|
|
@@ -58,6 +59,7 @@ module GraphQL
|
|
|
58
59
|
|
|
59
60
|
GraphQL::Language::Nodes::ObjectTypeDefinition.new(
|
|
60
61
|
name: object_type.graphql_name,
|
|
62
|
+
comment: object_type.comment,
|
|
61
63
|
interfaces: ints,
|
|
62
64
|
fields: build_field_nodes(@types.fields(object_type)),
|
|
63
65
|
description: object_type.description,
|
|
@@ -68,6 +70,7 @@ module GraphQL
|
|
|
68
70
|
def build_field_node(field)
|
|
69
71
|
GraphQL::Language::Nodes::FieldDefinition.new(
|
|
70
72
|
name: field.graphql_name,
|
|
73
|
+
comment: field.comment,
|
|
71
74
|
arguments: build_argument_nodes(@types.arguments(field)),
|
|
72
75
|
type: build_type_name_node(field.type),
|
|
73
76
|
description: field.description,
|
|
@@ -78,6 +81,7 @@ module GraphQL
|
|
|
78
81
|
def build_union_type_node(union_type)
|
|
79
82
|
GraphQL::Language::Nodes::UnionTypeDefinition.new(
|
|
80
83
|
name: union_type.graphql_name,
|
|
84
|
+
comment: union_type.comment,
|
|
81
85
|
description: union_type.description,
|
|
82
86
|
types: @types.possible_types(union_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) },
|
|
83
87
|
directives: directives(union_type),
|
|
@@ -87,6 +91,7 @@ module GraphQL
|
|
|
87
91
|
def build_interface_type_node(interface_type)
|
|
88
92
|
GraphQL::Language::Nodes::InterfaceTypeDefinition.new(
|
|
89
93
|
name: interface_type.graphql_name,
|
|
94
|
+
comment: interface_type.comment,
|
|
90
95
|
interfaces: @types.interfaces(interface_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) },
|
|
91
96
|
description: interface_type.description,
|
|
92
97
|
fields: build_field_nodes(@types.fields(interface_type)),
|
|
@@ -97,6 +102,7 @@ module GraphQL
|
|
|
97
102
|
def build_enum_type_node(enum_type)
|
|
98
103
|
GraphQL::Language::Nodes::EnumTypeDefinition.new(
|
|
99
104
|
name: enum_type.graphql_name,
|
|
105
|
+
comment: enum_type.comment,
|
|
100
106
|
values: @types.enum_values(enum_type).sort_by(&:graphql_name).map do |enum_value|
|
|
101
107
|
build_enum_value_node(enum_value)
|
|
102
108
|
end,
|
|
@@ -108,6 +114,7 @@ module GraphQL
|
|
|
108
114
|
def build_enum_value_node(enum_value)
|
|
109
115
|
GraphQL::Language::Nodes::EnumValueDefinition.new(
|
|
110
116
|
name: enum_value.graphql_name,
|
|
117
|
+
comment: enum_value.comment,
|
|
111
118
|
description: enum_value.description,
|
|
112
119
|
directives: directives(enum_value),
|
|
113
120
|
)
|
|
@@ -116,6 +123,7 @@ module GraphQL
|
|
|
116
123
|
def build_scalar_type_node(scalar_type)
|
|
117
124
|
GraphQL::Language::Nodes::ScalarTypeDefinition.new(
|
|
118
125
|
name: scalar_type.graphql_name,
|
|
126
|
+
comment: scalar_type.comment,
|
|
119
127
|
description: scalar_type.description,
|
|
120
128
|
directives: directives(scalar_type),
|
|
121
129
|
)
|
|
@@ -130,6 +138,7 @@ module GraphQL
|
|
|
130
138
|
|
|
131
139
|
argument_node = GraphQL::Language::Nodes::InputValueDefinition.new(
|
|
132
140
|
name: argument.graphql_name,
|
|
141
|
+
comment: argument.comment,
|
|
133
142
|
description: argument.description,
|
|
134
143
|
type: build_type_name_node(argument.type),
|
|
135
144
|
default_value: default_value,
|
|
@@ -142,6 +151,7 @@ module GraphQL
|
|
|
142
151
|
def build_input_object_node(input_object)
|
|
143
152
|
GraphQL::Language::Nodes::InputObjectTypeDefinition.new(
|
|
144
153
|
name: input_object.graphql_name,
|
|
154
|
+
comment: input_object.comment,
|
|
145
155
|
fields: build_argument_nodes(@types.arguments(input_object)),
|
|
146
156
|
description: input_object.description,
|
|
147
157
|
directives: directives(input_object),
|
|
@@ -259,7 +269,33 @@ module GraphQL
|
|
|
259
269
|
end
|
|
260
270
|
definitions = build_directive_nodes(dirs_to_build)
|
|
261
271
|
all_types = @types.all_types
|
|
262
|
-
type_nodes = build_type_definition_nodes(all_types
|
|
272
|
+
type_nodes = build_type_definition_nodes(all_types)
|
|
273
|
+
|
|
274
|
+
if (ex_t = schema.extra_types).any?
|
|
275
|
+
dummy_query = Class.new(GraphQL::Schema::Object) do
|
|
276
|
+
graphql_name "DummyQuery"
|
|
277
|
+
(all_types + ex_t).each_with_index do |type, idx|
|
|
278
|
+
if !type.kind.input_object? && !type.introspection?
|
|
279
|
+
field "f#{idx}", type
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
extra_types_schema = Class.new(GraphQL::Schema) do
|
|
285
|
+
query(dummy_query)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
extra_types_types = GraphQL::Query.new(extra_types_schema, "{ __typename }", context: @context).types # rubocop:disable Development/ContextIsPassedCop
|
|
289
|
+
# Temporarily replace `@types` with something from this example schema.
|
|
290
|
+
# It'd be much nicer to pass this in, but that would be a big refactor :S
|
|
291
|
+
prev_types = @types
|
|
292
|
+
@types = extra_types_types
|
|
293
|
+
type_nodes += build_type_definition_nodes(ex_t)
|
|
294
|
+
@types = prev_types
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
type_nodes.sort_by!(&:name)
|
|
298
|
+
|
|
263
299
|
if @include_one_of
|
|
264
300
|
# This may have been set to true when iterating over all types
|
|
265
301
|
definitions.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))
|
|
@@ -282,9 +318,7 @@ module GraphQL
|
|
|
282
318
|
types = types.reject { |type| type.kind.scalar? && type.default_scalar? }
|
|
283
319
|
end
|
|
284
320
|
|
|
285
|
-
types
|
|
286
|
-
.map { |type| build_type_definition_node(type) }
|
|
287
|
-
.sort_by(&:name)
|
|
321
|
+
types.map { |type| build_type_definition_node(type) }
|
|
288
322
|
end
|
|
289
323
|
|
|
290
324
|
def build_field_nodes(fields)
|