graphql 2.0.30 → 2.3.6
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/install/mutation_root_generator.rb +2 -2
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +3 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +6 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +5 -0
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/query_depth.rb +58 -0
- data/lib/graphql/analysis/visitor.rb +282 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/backtrace/trace.rb +12 -15
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +88 -0
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +11 -3
- data/lib/graphql/dataloader.rb +112 -142
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
- data/lib/graphql/execution/interpreter/runtime.rb +163 -365
- data/lib/graphql/execution/interpreter.rb +92 -158
- data/lib/graphql/execution/lookahead.rb +88 -21
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +38 -38
- data/lib/graphql/language/lexer.rb +305 -193
- data/lib/graphql/language/nodes.rb +113 -66
- data/lib/graphql/language/parser.rb +787 -1986
- data/lib/graphql/language/printer.rb +303 -146
- data/lib/graphql/language/sanitized_printer.rb +20 -22
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +20 -81
- data/lib/graphql/language.rb +61 -0
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +28 -1
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +66 -131
- data/lib/graphql/query/null_context.rb +4 -11
- data/lib/graphql/query/validation_pipeline.rb +4 -4
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +17 -26
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/schema/addition.rb +21 -11
- data/lib/graphql/schema/argument.rb +43 -8
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +9 -12
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive.rb +3 -1
- data/lib/graphql/schema/enum.rb +3 -3
- data/lib/graphql/schema/field/connection_extension.rb +1 -15
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +49 -35
- data/lib/graphql/schema/has_single_input_argument.rb +157 -0
- data/lib/graphql/schema/input_object.rb +4 -4
- data/lib/graphql/schema/interface.rb +10 -10
- data/lib/graphql/schema/introspection_system.rb +4 -2
- data/lib/graphql/schema/late_bound_type.rb +4 -0
- data/lib/graphql/schema/list.rb +2 -2
- data/lib/graphql/schema/loader.rb +2 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +63 -73
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +8 -5
- data/lib/graphql/schema/member/has_interfaces.rb +23 -9
- data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -2
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +8 -7
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
- data/lib/graphql/schema/resolver.rb +27 -13
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema/warden.rb +96 -95
- data/lib/graphql/schema.rb +323 -102
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +1 -1
- data/lib/graphql/static_validation/literal_validator.rb +2 -3
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +2 -2
- data/lib/graphql/static_validation/validation_context.rb +5 -5
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +4 -3
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +1 -1
- data/lib/graphql/subscriptions/event.rb +8 -2
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +15 -13
- data/lib/graphql/testing/helpers.rb +151 -0
- data/lib/graphql/testing.rb +2 -0
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/prometheus_trace.rb +9 -9
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing/trace.rb +1 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/type_kinds.rb +1 -1
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +13 -13
- data/readme.md +12 -2
- metadata +33 -26
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -276
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/language/parser.y +0 -560
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Query
|
4
|
+
class Context
|
5
|
+
class ScopedContext
|
6
|
+
def initialize(query_context)
|
7
|
+
@query_context = query_context
|
8
|
+
@scoped_contexts = nil
|
9
|
+
@all_keys = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def merged_context
|
13
|
+
if @scoped_contexts.nil?
|
14
|
+
GraphQL::EmptyObjects::EMPTY_HASH
|
15
|
+
else
|
16
|
+
merged_ctx = {}
|
17
|
+
each_present_path_ctx do |path_ctx|
|
18
|
+
merged_ctx = path_ctx.merge(merged_ctx)
|
19
|
+
end
|
20
|
+
merged_ctx
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge!(hash, at: current_path)
|
25
|
+
@all_keys ||= Set.new
|
26
|
+
@all_keys.merge(hash.keys)
|
27
|
+
ctx = @scoped_contexts ||= {}
|
28
|
+
at.each do |path_part|
|
29
|
+
ctx = ctx[path_part] ||= { parent: ctx }
|
30
|
+
end
|
31
|
+
this_scoped_ctx = ctx[:scoped_context] ||= {}
|
32
|
+
this_scoped_ctx.merge!(hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
def key?(key)
|
36
|
+
if @all_keys && @all_keys.include?(key)
|
37
|
+
each_present_path_ctx do |path_ctx|
|
38
|
+
if path_ctx.key?(key)
|
39
|
+
return true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def [](key)
|
47
|
+
each_present_path_ctx do |path_ctx|
|
48
|
+
if path_ctx.key?(key)
|
49
|
+
return path_ctx[key]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def current_path
|
56
|
+
@query_context.current_path || GraphQL::EmptyObjects::EMPTY_ARRAY
|
57
|
+
end
|
58
|
+
|
59
|
+
def dig(key, *other_keys)
|
60
|
+
each_present_path_ctx do |path_ctx|
|
61
|
+
if path_ctx.key?(key)
|
62
|
+
found_value = path_ctx[key]
|
63
|
+
if other_keys.any?
|
64
|
+
return found_value.dig(*other_keys)
|
65
|
+
else
|
66
|
+
return found_value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
# Start at the current location,
|
76
|
+
# but look up the tree for previously-assigned scoped values
|
77
|
+
def each_present_path_ctx
|
78
|
+
ctx = @scoped_contexts
|
79
|
+
if ctx.nil?
|
80
|
+
# no-op
|
81
|
+
else
|
82
|
+
current_path.each do |path_part|
|
83
|
+
if ctx.key?(path_part)
|
84
|
+
ctx = ctx[path_part]
|
85
|
+
else
|
86
|
+
break
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
while ctx
|
91
|
+
if (scoped_ctx = ctx[:scoped_context])
|
92
|
+
yield(scoped_ctx)
|
93
|
+
end
|
94
|
+
ctx = ctx[:parent]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -1,39 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "graphql/query/context/scoped_context"
|
3
|
+
|
2
4
|
module GraphQL
|
3
5
|
class Query
|
4
6
|
# Expose some query-specific info to field resolve functions.
|
5
7
|
# It delegates `[]` to the hash that's passed to `GraphQL::Query#initialize`.
|
6
8
|
class Context
|
7
|
-
module SharedMethods
|
8
|
-
# Return this value to tell the runtime
|
9
|
-
# to exclude this field from the response altogether
|
10
|
-
def skip
|
11
|
-
GraphQL::Execution::SKIP
|
12
|
-
end
|
13
|
-
|
14
|
-
# Add error at query-level.
|
15
|
-
# @param error [GraphQL::ExecutionError] an execution error
|
16
|
-
# @return [void]
|
17
|
-
def add_error(error)
|
18
|
-
if !error.is_a?(ExecutionError)
|
19
|
-
raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
|
20
|
-
end
|
21
|
-
errors << error
|
22
|
-
nil
|
23
|
-
end
|
24
|
-
|
25
|
-
# @example Print the GraphQL backtrace during field resolution
|
26
|
-
# puts ctx.backtrace
|
27
|
-
#
|
28
|
-
# @return [GraphQL::Backtrace] The backtrace for this point in query execution
|
29
|
-
def backtrace
|
30
|
-
GraphQL::Backtrace.new(self)
|
31
|
-
end
|
32
|
-
|
33
|
-
def execution_errors
|
34
|
-
@execution_errors ||= ExecutionErrors.new(self)
|
35
|
-
end
|
36
|
-
end
|
37
9
|
|
38
10
|
class ExecutionErrors
|
39
11
|
def initialize(ctx)
|
@@ -57,7 +29,6 @@ module GraphQL
|
|
57
29
|
alias :push :add
|
58
30
|
end
|
59
31
|
|
60
|
-
include SharedMethods
|
61
32
|
extend Forwardable
|
62
33
|
|
63
34
|
# @return [Array<GraphQL::ExecutionError>] errors returned during execution
|
@@ -75,11 +46,10 @@ module GraphQL
|
|
75
46
|
# Make a new context which delegates key lookup to `values`
|
76
47
|
# @param query [GraphQL::Query] the query who owns this context
|
77
48
|
# @param values [Hash] A hash of arbitrary values which will be accessible at query-time
|
78
|
-
def initialize(query:, schema: query.schema, values
|
49
|
+
def initialize(query:, schema: query.schema, values:)
|
79
50
|
@query = query
|
80
51
|
@schema = schema
|
81
52
|
@provided_values = values || {}
|
82
|
-
@object = object
|
83
53
|
# Namespaced storage, where user-provided values are in `nil` namespace:
|
84
54
|
@storage = Hash.new { |h, k| h[k] = {} }
|
85
55
|
@storage[nil] = @provided_values
|
@@ -90,104 +60,6 @@ module GraphQL
|
|
90
60
|
@scoped_context = ScopedContext.new(self)
|
91
61
|
end
|
92
62
|
|
93
|
-
class ScopedContext
|
94
|
-
NO_PATH = GraphQL::EmptyObjects::EMPTY_ARRAY
|
95
|
-
NO_CONTEXT = GraphQL::EmptyObjects::EMPTY_HASH
|
96
|
-
|
97
|
-
def initialize(query_context)
|
98
|
-
@query_context = query_context
|
99
|
-
@scoped_contexts = nil
|
100
|
-
@all_keys = nil
|
101
|
-
end
|
102
|
-
|
103
|
-
def merged_context
|
104
|
-
if @scoped_contexts.nil?
|
105
|
-
NO_CONTEXT
|
106
|
-
else
|
107
|
-
merged_ctx = {}
|
108
|
-
each_present_path_ctx do |path_ctx|
|
109
|
-
merged_ctx = path_ctx.merge(merged_ctx)
|
110
|
-
end
|
111
|
-
merged_ctx
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def merge!(hash)
|
116
|
-
@all_keys ||= Set.new
|
117
|
-
@all_keys.merge(hash.keys)
|
118
|
-
ctx = @scoped_contexts ||= {}
|
119
|
-
current_path.each do |path_part|
|
120
|
-
ctx = ctx[path_part] ||= { parent: ctx }
|
121
|
-
end
|
122
|
-
this_scoped_ctx = ctx[:scoped_context] ||= {}
|
123
|
-
this_scoped_ctx.merge!(hash)
|
124
|
-
end
|
125
|
-
|
126
|
-
def key?(key)
|
127
|
-
if @all_keys && @all_keys.include?(key)
|
128
|
-
each_present_path_ctx do |path_ctx|
|
129
|
-
if path_ctx.key?(key)
|
130
|
-
return true
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
false
|
135
|
-
end
|
136
|
-
|
137
|
-
def [](key)
|
138
|
-
each_present_path_ctx do |path_ctx|
|
139
|
-
if path_ctx.key?(key)
|
140
|
-
return path_ctx[key]
|
141
|
-
end
|
142
|
-
end
|
143
|
-
nil
|
144
|
-
end
|
145
|
-
|
146
|
-
def current_path
|
147
|
-
@query_context.current_path || NO_PATH
|
148
|
-
end
|
149
|
-
|
150
|
-
def dig(key, *other_keys)
|
151
|
-
each_present_path_ctx do |path_ctx|
|
152
|
-
if path_ctx.key?(key)
|
153
|
-
found_value = path_ctx[key]
|
154
|
-
if other_keys.any?
|
155
|
-
return found_value.dig(*other_keys)
|
156
|
-
else
|
157
|
-
return found_value
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
nil
|
162
|
-
end
|
163
|
-
|
164
|
-
private
|
165
|
-
|
166
|
-
# Start at the current location,
|
167
|
-
# but look up the tree for previously-assigned scoped values
|
168
|
-
def each_present_path_ctx
|
169
|
-
ctx = @scoped_contexts
|
170
|
-
if ctx.nil?
|
171
|
-
# no-op
|
172
|
-
else
|
173
|
-
current_path.each do |path_part|
|
174
|
-
if ctx.key?(path_part)
|
175
|
-
ctx = ctx[path_part]
|
176
|
-
else
|
177
|
-
break
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
while ctx
|
182
|
-
if (scoped_ctx = ctx[:scoped_context])
|
183
|
-
yield(scoped_ctx)
|
184
|
-
end
|
185
|
-
ctx = ctx[:parent]
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
63
|
# @return [Hash] A hash that will be added verbatim to the result hash, as `"extensions" => { ... }`
|
192
64
|
def response_extensions
|
193
65
|
namespace(:__query_result_extensions__)
|
@@ -236,6 +108,35 @@ module GraphQL
|
|
236
108
|
end
|
237
109
|
end
|
238
110
|
|
111
|
+
# Return this value to tell the runtime
|
112
|
+
# to exclude this field from the response altogether
|
113
|
+
def skip
|
114
|
+
GraphQL::Execution::SKIP
|
115
|
+
end
|
116
|
+
|
117
|
+
# Add error at query-level.
|
118
|
+
# @param error [GraphQL::ExecutionError] an execution error
|
119
|
+
# @return [void]
|
120
|
+
def add_error(error)
|
121
|
+
if !error.is_a?(ExecutionError)
|
122
|
+
raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
|
123
|
+
end
|
124
|
+
errors << error
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
|
128
|
+
# @example Print the GraphQL backtrace during field resolution
|
129
|
+
# puts ctx.backtrace
|
130
|
+
#
|
131
|
+
# @return [GraphQL::Backtrace] The backtrace for this point in query execution
|
132
|
+
def backtrace
|
133
|
+
GraphQL::Backtrace.new(self)
|
134
|
+
end
|
135
|
+
|
136
|
+
def execution_errors
|
137
|
+
@execution_errors ||= ExecutionErrors.new(self)
|
138
|
+
end
|
139
|
+
|
239
140
|
def current_path
|
240
141
|
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
241
142
|
query_runtime_state = current_runtime_state && current_runtime_state[@query]
|
@@ -333,6 +234,10 @@ module GraphQL
|
|
333
234
|
@storage.key?(ns)
|
334
235
|
end
|
335
236
|
|
237
|
+
def logger
|
238
|
+
@query && @query.logger
|
239
|
+
end
|
240
|
+
|
336
241
|
def inspect
|
337
242
|
"#<Query::Context ...>"
|
338
243
|
end
|
@@ -345,6 +250,36 @@ module GraphQL
|
|
345
250
|
scoped_merge!(key => value)
|
346
251
|
nil
|
347
252
|
end
|
253
|
+
|
254
|
+
# Use this when you need to do a scoped set _inside_ a lazy-loaded (or batch-loaded)
|
255
|
+
# block of code.
|
256
|
+
#
|
257
|
+
# @example using scoped context inside a promise
|
258
|
+
# scoped_ctx = context.scoped
|
259
|
+
# SomeBatchLoader.load(...).then do |thing|
|
260
|
+
# # use a scoped_ctx which was created _before_ dataloading:
|
261
|
+
# scoped_ctx.set!(:thing, thing)
|
262
|
+
# end
|
263
|
+
# @return [Context::Scoped]
|
264
|
+
def scoped
|
265
|
+
Scoped.new(@scoped_context, current_path)
|
266
|
+
end
|
267
|
+
|
268
|
+
class Scoped
|
269
|
+
def initialize(scoped_context, path)
|
270
|
+
@path = path
|
271
|
+
@scoped_context = scoped_context
|
272
|
+
end
|
273
|
+
|
274
|
+
def merge!(hash)
|
275
|
+
@scoped_context.merge!(hash, at: @path)
|
276
|
+
end
|
277
|
+
|
278
|
+
def set!(key, value)
|
279
|
+
@scoped_context.merge!({ key => value }, at: @path)
|
280
|
+
nil
|
281
|
+
end
|
282
|
+
end
|
348
283
|
end
|
349
284
|
end
|
350
285
|
end
|
@@ -1,8 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "graphql/query/context"
|
2
3
|
module GraphQL
|
3
4
|
class Query
|
4
5
|
# This object can be `ctx` in places where there is no query
|
5
|
-
class NullContext
|
6
|
+
class NullContext < Context
|
7
|
+
include Singleton
|
8
|
+
|
6
9
|
class NullQuery
|
7
10
|
def after_lazy(value)
|
8
11
|
yield(value)
|
@@ -27,16 +30,6 @@ module GraphQL
|
|
27
30
|
def interpreter?
|
28
31
|
true
|
29
32
|
end
|
30
|
-
|
31
|
-
class << self
|
32
|
-
extend Forwardable
|
33
|
-
|
34
|
-
def instance
|
35
|
-
@instance ||= self.new
|
36
|
-
end
|
37
|
-
|
38
|
-
def_delegators :instance, :query, :warden, :schema, :interpreter?, :dataloader, :[], :fetch, :dig, :key?
|
39
|
-
end
|
40
33
|
end
|
41
34
|
end
|
42
35
|
end
|
@@ -14,7 +14,7 @@ module GraphQL
|
|
14
14
|
#
|
15
15
|
# @api private
|
16
16
|
class ValidationPipeline
|
17
|
-
attr_reader :max_depth, :max_complexity
|
17
|
+
attr_reader :max_depth, :max_complexity, :validate_timeout_remaining
|
18
18
|
|
19
19
|
def initialize(query:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
|
20
20
|
@validation_errors = []
|
@@ -71,7 +71,7 @@ module GraphQL
|
|
71
71
|
validator = @query.static_validator || @schema.static_validator
|
72
72
|
validation_result = validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
|
73
73
|
@validation_errors.concat(validation_result[:errors])
|
74
|
-
|
74
|
+
@validate_timeout_remaining = validation_result[:remaining_timeout]
|
75
75
|
if @validation_errors.empty?
|
76
76
|
@validation_errors.concat(@query.variables.errors)
|
77
77
|
end
|
@@ -100,10 +100,10 @@ module GraphQL
|
|
100
100
|
# Depending on the analysis engine, we must use different analyzers
|
101
101
|
# remove this once everything has switched over to AST analyzers
|
102
102
|
if max_depth
|
103
|
-
qa << GraphQL::Analysis::
|
103
|
+
qa << GraphQL::Analysis::MaxQueryDepth
|
104
104
|
end
|
105
105
|
if max_complexity
|
106
|
-
qa << GraphQL::Analysis::
|
106
|
+
qa << GraphQL::Analysis::MaxQueryComplexity
|
107
107
|
end
|
108
108
|
qa
|
109
109
|
else
|
@@ -26,7 +26,7 @@ module GraphQL
|
|
26
26
|
# - Then, fall back to the default value from the query string
|
27
27
|
# If it's still nil, raise an error if it's required.
|
28
28
|
variable_type = schema.type_from_ast(ast_variable.type, context: ctx)
|
29
|
-
if variable_type.nil?
|
29
|
+
if variable_type.nil? || !variable_type.unwrap.kind.input?
|
30
30
|
# Pass -- it will get handled by a validator
|
31
31
|
else
|
32
32
|
variable_name = ast_variable.name
|
@@ -80,12 +80,12 @@ module GraphQL
|
|
80
80
|
else
|
81
81
|
val
|
82
82
|
end
|
83
|
-
end
|
83
|
+
end
|
84
84
|
|
85
85
|
def add_max_errors_reached_message
|
86
86
|
message = "Too many errors processing variables, max validation error limit reached. Execution aborted"
|
87
87
|
validation_result = GraphQL::Query::InputValidationResult.from_problem(message)
|
88
|
-
errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
|
88
|
+
errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
data/lib/graphql/query.rb
CHANGED
@@ -95,16 +95,11 @@ module GraphQL
|
|
95
95
|
# @param root_value [Object] the object used to resolve fields on the root type
|
96
96
|
# @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
|
97
97
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
98
|
-
|
99
|
-
# @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
|
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)
|
98
|
+
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, warden: nil)
|
101
99
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
102
100
|
variables ||= {}
|
103
101
|
@schema = schema
|
104
|
-
|
105
|
-
merge_filters(except: except, only: only)
|
106
|
-
end
|
107
|
-
@context = schema.context_class.new(query: self, object: root_value, values: context)
|
102
|
+
@context = schema.context_class.new(query: self, values: context)
|
108
103
|
@warden = warden
|
109
104
|
@subscription_topic = subscription_topic
|
110
105
|
@root_value = root_value
|
@@ -120,8 +115,6 @@ module GraphQL
|
|
120
115
|
if schema.trace_class <= GraphQL::Tracing::CallLegacyTracers
|
121
116
|
context_tracers += [GraphQL::Backtrace::Tracer]
|
122
117
|
@tracers << GraphQL::Backtrace::Tracer
|
123
|
-
elsif !(current_trace.class <= GraphQL::Backtrace::Trace)
|
124
|
-
raise "Invariant: `backtrace: true` should have provided a trace class with Backtrace mixed in, but it didnt. (Found: #{current_trace.class.ancestors}). This is a bug in GraphQL-Ruby, please report it on GitHub."
|
125
118
|
end
|
126
119
|
end
|
127
120
|
|
@@ -129,7 +122,6 @@ module GraphQL
|
|
129
122
|
raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
|
130
123
|
end
|
131
124
|
|
132
|
-
|
133
125
|
@analysis_errors = []
|
134
126
|
if variables.is_a?(String)
|
135
127
|
raise ArgumentError, "Query variables should be a Hash, not a String. Try JSON.parse to prepare variables."
|
@@ -168,6 +160,14 @@ module GraphQL
|
|
168
160
|
|
169
161
|
@result_values = nil
|
170
162
|
@executed = false
|
163
|
+
|
164
|
+
@logger = if context && context[:logger] == false
|
165
|
+
Logger.new(IO::NULL)
|
166
|
+
elsif context && (l = context[:logger])
|
167
|
+
l
|
168
|
+
else
|
169
|
+
schema.default_logger
|
170
|
+
end
|
171
171
|
end
|
172
172
|
|
173
173
|
# If a document was provided to `GraphQL::Schema#execute` instead of the raw query string, we will need to get it from the document
|
@@ -222,7 +222,7 @@ module GraphQL
|
|
222
222
|
end
|
223
223
|
|
224
224
|
# Get the result for this query, executing it once
|
225
|
-
# @return [
|
225
|
+
# @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys
|
226
226
|
def result
|
227
227
|
if !@executed
|
228
228
|
Execution::Interpreter.run_all(@schema, [self], context: @context)
|
@@ -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)
|
@@ -317,7 +317,7 @@ module GraphQL
|
|
317
317
|
end
|
318
318
|
|
319
319
|
def_delegators :validation_pipeline, :validation_errors,
|
320
|
-
:analyzers, :ast_analyzers, :max_depth, :max_complexity
|
320
|
+
:analyzers, :ast_analyzers, :max_depth, :max_complexity, :validate_timeout_remaining
|
321
321
|
|
322
322
|
attr_accessor :analysis_errors
|
323
323
|
def valid?
|
@@ -354,17 +354,6 @@ module GraphQL
|
|
354
354
|
with_prepared_ast { @query }
|
355
355
|
end
|
356
356
|
|
357
|
-
# @return [void]
|
358
|
-
def merge_filters(only: nil, except: nil)
|
359
|
-
if @prepared_ast
|
360
|
-
raise "Can't add filters after preparing the query"
|
361
|
-
else
|
362
|
-
@filter ||= @schema.default_filter
|
363
|
-
@filter = @filter.merge(only: only, except: except)
|
364
|
-
end
|
365
|
-
nil
|
366
|
-
end
|
367
|
-
|
368
357
|
def subscription?
|
369
358
|
with_prepared_ast { @subscription }
|
370
359
|
end
|
@@ -386,6 +375,8 @@ module GraphQL
|
|
386
375
|
end
|
387
376
|
end
|
388
377
|
|
378
|
+
attr_reader :logger
|
379
|
+
|
389
380
|
private
|
390
381
|
|
391
382
|
def find_operation(operations, operation_name)
|
@@ -400,11 +391,11 @@ module GraphQL
|
|
400
391
|
|
401
392
|
def prepare_ast
|
402
393
|
@prepared_ast = true
|
403
|
-
@warden ||= @schema.warden_class.new(
|
394
|
+
@warden ||= @schema.warden_class.new(schema: @schema, context: @context)
|
404
395
|
parse_error = nil
|
405
396
|
@document ||= begin
|
406
397
|
if query_string
|
407
|
-
GraphQL.parse(query_string, trace: self.current_trace)
|
398
|
+
GraphQL.parse(query_string, trace: self.current_trace, max_tokens: @schema.max_query_string_tokens)
|
408
399
|
end
|
409
400
|
rescue GraphQL::ParseError => err
|
410
401
|
parse_error = err
|
data/lib/graphql/railtie.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module GraphQL
|
3
4
|
class Railtie < Rails::Railtie
|
4
|
-
config.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
if
|
9
|
-
Language::Parser.cache ||= Language::Cache.new(
|
5
|
+
config.graphql = ActiveSupport::OrderedOptions.new
|
6
|
+
config.graphql.parser_cache = false
|
7
|
+
|
8
|
+
initializer("graphql.cache") do |app|
|
9
|
+
if config.graphql.parser_cache
|
10
|
+
Language::Parser.cache ||= Language::Cache.new(
|
11
|
+
app.root.join("tmp/cache/graphql")
|
12
|
+
)
|
10
13
|
end
|
11
14
|
end
|
12
15
|
end
|
data/lib/graphql/rake_task.rb
CHANGED
@@ -9,8 +9,7 @@ module GraphQL
|
|
9
9
|
# By default, schemas are looked up by name as constants using `schema_name:`.
|
10
10
|
# You can provide a `load_schema` function to return your schema another way.
|
11
11
|
#
|
12
|
-
# `load_context
|
13
|
-
# you can keep an eye on how filters affect your schema.
|
12
|
+
# Use `load_context:` and `visible?` to dump schemas under certain visibility constraints.
|
14
13
|
#
|
15
14
|
# @example Dump a Schema to .graphql + .json files
|
16
15
|
# require "graphql/rake_task"
|
@@ -36,8 +35,6 @@ module GraphQL
|
|
36
35
|
schema_name: nil,
|
37
36
|
load_schema: ->(task) { Object.const_get(task.schema_name) },
|
38
37
|
load_context: ->(task) { {} },
|
39
|
-
only: nil,
|
40
|
-
except: nil,
|
41
38
|
directory: ".",
|
42
39
|
idl_outfile: "schema.graphql",
|
43
40
|
json_outfile: "schema.json",
|
@@ -68,12 +65,6 @@ module GraphQL
|
|
68
65
|
# @return [<#call(task)>] A callable for loading the query context
|
69
66
|
attr_accessor :load_context
|
70
67
|
|
71
|
-
# @return [<#call(member, ctx)>, nil] A filter for this task
|
72
|
-
attr_accessor :only
|
73
|
-
|
74
|
-
# @return [<#call(member, ctx)>, nil] A filter for this task
|
75
|
-
attr_accessor :except
|
76
|
-
|
77
68
|
# @return [String] target for IDL task
|
78
69
|
attr_accessor :idl_outfile
|
79
70
|
|
@@ -117,10 +108,10 @@ module GraphQL
|
|
117
108
|
include_is_repeatable: include_is_repeatable,
|
118
109
|
include_specified_by_url: include_specified_by_url,
|
119
110
|
include_schema_description: include_schema_description,
|
120
|
-
|
111
|
+
context: context
|
121
112
|
)
|
122
113
|
when :to_definition
|
123
|
-
schema.to_definition(
|
114
|
+
schema.to_definition(context: context)
|
124
115
|
else
|
125
116
|
raise ArgumentError, "Unexpected schema dump method: #{method_name.inspect}"
|
126
117
|
end
|
@@ -9,7 +9,7 @@ module GraphQL
|
|
9
9
|
|
10
10
|
# Return the source of `send_node`, but without the keyword argument represented by `pair_node`
|
11
11
|
def source_without_keyword_argument(send_node, pair_node)
|
12
|
-
# work back to the
|
12
|
+
# work back to the preceding comma
|
13
13
|
first_pos = pair_node.location.expression.begin_pos
|
14
14
|
end_pos = pair_node.location.expression.end_pos
|
15
15
|
node_source = send_node.source_range.source
|