graphql 2.0.30 → 2.3.6
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/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
|