graphql 2.0.27 → 2.2.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/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 +2 -0
- data/lib/graphql/analysis/ast/analyzer.rb +7 -0
- data/lib/graphql/analysis/ast/field_usage.rb +32 -7
- data/lib/graphql/analysis/ast/query_complexity.rb +80 -128
- data/lib/graphql/analysis/ast/query_depth.rb +7 -2
- data/lib/graphql/analysis/ast/visitor.rb +2 -2
- data/lib/graphql/analysis/ast.rb +21 -11
- data/lib/graphql/backtrace/trace.rb +12 -15
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +85 -0
- data/lib/graphql/dataloader/source.rb +11 -3
- data/lib/graphql/dataloader.rb +109 -142
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +70 -248
- data/lib/graphql/execution/interpreter.rb +91 -157
- 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 +37 -37
- data/lib/graphql/language/lexer.rb +271 -177
- data/lib/graphql/language/nodes.rb +74 -56
- data/lib/graphql/language/parser.rb +697 -1986
- data/lib/graphql/language/printer.rb +299 -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 +1 -0
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +3 -3
- data/lib/graphql/pagination/connection.rb +28 -1
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/pagination/relation_connection.rb +3 -3
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +36 -98
- data/lib/graphql/query/null_context.rb +4 -11
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +13 -22
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/schema/argument.rb +6 -1
- data/lib/graphql/schema/build_from_definition.rb +0 -11
- 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 +1 -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 +8 -5
- data/lib/graphql/schema/has_single_input_argument.rb +156 -0
- data/lib/graphql/schema/input_object.rb +2 -2
- data/lib/graphql/schema/interface.rb +10 -10
- data/lib/graphql/schema/introspection_system.rb +2 -0
- data/lib/graphql/schema/loader.rb +0 -2
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +61 -38
- 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/scoped.rb +19 -0
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- 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 +7 -3
- 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/warden.rb +96 -94
- data/lib/graphql/schema.rb +219 -72
- 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 +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- 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 +1 -1
- 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 +3 -2
- data/lib/graphql/subscriptions/event.rb +8 -2
- data/lib/graphql/subscriptions.rb +14 -12
- data/lib/graphql/testing/helpers.rb +125 -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/data_dog_trace.rb +21 -34
- data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +2 -0
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/sentry_trace.rb +94 -0
- data/lib/graphql/tracing/trace.rb +1 -0
- data/lib/graphql/tracing.rb +3 -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 +3 -3
- data/readme.md +12 -2
- metadata +33 -25
- 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/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
|
@@ -1,4 +1,6 @@
|
|
|
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.
|
|
@@ -90,104 +92,6 @@ module GraphQL
|
|
|
90
92
|
@scoped_context = ScopedContext.new(self)
|
|
91
93
|
end
|
|
92
94
|
|
|
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
95
|
# @return [Hash] A hash that will be added verbatim to the result hash, as `"extensions" => { ... }`
|
|
192
96
|
def response_extensions
|
|
193
97
|
namespace(:__query_result_extensions__)
|
|
@@ -333,6 +237,10 @@ module GraphQL
|
|
|
333
237
|
@storage.key?(ns)
|
|
334
238
|
end
|
|
335
239
|
|
|
240
|
+
def logger
|
|
241
|
+
@query && @query.logger
|
|
242
|
+
end
|
|
243
|
+
|
|
336
244
|
def inspect
|
|
337
245
|
"#<Query::Context ...>"
|
|
338
246
|
end
|
|
@@ -345,6 +253,36 @@ module GraphQL
|
|
|
345
253
|
scoped_merge!(key => value)
|
|
346
254
|
nil
|
|
347
255
|
end
|
|
256
|
+
|
|
257
|
+
# Use this when you need to do a scoped set _inside_ a lazy-loaded (or batch-loaded)
|
|
258
|
+
# block of code.
|
|
259
|
+
#
|
|
260
|
+
# @example using scoped context inside a promise
|
|
261
|
+
# scoped_ctx = context.scoped
|
|
262
|
+
# SomeBatchLoader.load(...).then do |thing|
|
|
263
|
+
# # use a scoped_ctx which was created _before_ dataloading:
|
|
264
|
+
# scoped_ctx.set!(:thing, thing)
|
|
265
|
+
# end
|
|
266
|
+
# @return [Context::Scoped]
|
|
267
|
+
def scoped
|
|
268
|
+
Scoped.new(@scoped_context, current_path)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
class Scoped
|
|
272
|
+
def initialize(scoped_context, path)
|
|
273
|
+
@path = path
|
|
274
|
+
@scoped_context = scoped_context
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def merge!(hash)
|
|
278
|
+
@scoped_context.merge!(hash, at: @path)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def set!(key, value)
|
|
282
|
+
@scoped_context.merge!({ key => value }, at: @path)
|
|
283
|
+
nil
|
|
284
|
+
end
|
|
285
|
+
end
|
|
348
286
|
end
|
|
349
287
|
end
|
|
350
288
|
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
|
|
@@ -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,15 +95,10 @@ 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
|
-
if only || except
|
|
105
|
-
merge_filters(except: except, only: only)
|
|
106
|
-
end
|
|
107
102
|
@context = schema.context_class.new(query: self, object: root_value, values: context)
|
|
108
103
|
@warden = warden
|
|
109
104
|
@subscription_topic = subscription_topic
|
|
@@ -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
|
|
@@ -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,7 +391,7 @@ 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
|
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
|
|
@@ -221,8 +221,13 @@ module GraphQL
|
|
|
221
221
|
#
|
|
222
222
|
# This will have to be called later, when the runtime object _is_ available.
|
|
223
223
|
value
|
|
224
|
-
|
|
224
|
+
elsif obj.respond_to?(@prepare)
|
|
225
225
|
obj.public_send(@prepare, value)
|
|
226
|
+
elsif owner.respond_to?(@prepare)
|
|
227
|
+
owner.public_send(@prepare, value, context || obj.context)
|
|
228
|
+
else
|
|
229
|
+
raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}. "\
|
|
230
|
+
"Could not find prepare method #{@prepare} on #{obj.class} or #{owner}."
|
|
226
231
|
end
|
|
227
232
|
elsif @prepare.respond_to?(:call)
|
|
228
233
|
@prepare.call(value, context || obj.context)
|
|
@@ -432,14 +432,12 @@ module GraphQL
|
|
|
432
432
|
builder = self
|
|
433
433
|
|
|
434
434
|
field_definitions.each do |field_definition|
|
|
435
|
-
type_name = resolve_type_name(field_definition.type)
|
|
436
435
|
resolve_method_name = -"resolve_field_#{field_definition.name}"
|
|
437
436
|
schema_field_defn = owner.field(
|
|
438
437
|
field_definition.name,
|
|
439
438
|
description: field_definition.description,
|
|
440
439
|
type: type_resolver.call(field_definition.type),
|
|
441
440
|
null: true,
|
|
442
|
-
connection: type_name.end_with?("Connection"),
|
|
443
441
|
connection_extension: nil,
|
|
444
442
|
deprecation_reason: build_deprecation_reason(field_definition.directives),
|
|
445
443
|
ast_node: field_definition,
|
|
@@ -487,15 +485,6 @@ module GraphQL
|
|
|
487
485
|
}
|
|
488
486
|
resolve_type_proc
|
|
489
487
|
end
|
|
490
|
-
|
|
491
|
-
def resolve_type_name(type)
|
|
492
|
-
case type
|
|
493
|
-
when GraphQL::Language::Nodes::TypeName
|
|
494
|
-
return type.name
|
|
495
|
-
else
|
|
496
|
-
resolve_type_name(type.of_type)
|
|
497
|
-
end
|
|
498
|
-
end
|
|
499
488
|
end
|
|
500
489
|
|
|
501
490
|
private_constant :Builder
|
|
@@ -6,6 +6,18 @@ module GraphQL
|
|
|
6
6
|
description "Requires that exactly one field must be supplied and that field must not be `null`."
|
|
7
7
|
locations(GraphQL::Schema::Directive::INPUT_OBJECT)
|
|
8
8
|
default_directive true
|
|
9
|
+
|
|
10
|
+
def initialize(...)
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
owner.extend(IsOneOf)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module IsOneOf
|
|
17
|
+
def one_of?
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
9
21
|
end
|
|
10
22
|
end
|
|
11
23
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
class Schema
|
|
4
|
+
class Directive < GraphQL::Schema::Member
|
|
5
|
+
class SpecifiedBy < GraphQL::Schema::Directive
|
|
6
|
+
description "Exposes a URL that specifies the behavior of this scalar."
|
|
7
|
+
locations(GraphQL::Schema::Directive::SCALAR)
|
|
8
|
+
default_directive true
|
|
9
|
+
|
|
10
|
+
argument :url, String, description: "The URL that specifies the behavior of this scalar."
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -119,7 +119,7 @@ module GraphQL
|
|
|
119
119
|
# - lazy resolution
|
|
120
120
|
# Probably, those won't be needed here, since these are configuration arguments,
|
|
121
121
|
# not runtime arguments.
|
|
122
|
-
@arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext)
|
|
122
|
+
@arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
def graphql_name
|
data/lib/graphql/schema/enum.rb
CHANGED
|
@@ -14,7 +14,7 @@ module GraphQL
|
|
|
14
14
|
# # ONIONS
|
|
15
15
|
# # PEPPERS
|
|
16
16
|
# # }
|
|
17
|
-
# class PizzaTopping < GraphQL::Enum
|
|
17
|
+
# class PizzaTopping < GraphQL::Schema::Enum
|
|
18
18
|
# value :MUSHROOMS
|
|
19
19
|
# value :ONIONS
|
|
20
20
|
# value :PEPPERS
|
|
@@ -68,7 +68,7 @@ module GraphQL
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
# @return [Array<GraphQL::Schema::EnumValue>] Possible values of this enum
|
|
71
|
-
def enum_values(context = GraphQL::Query::NullContext)
|
|
71
|
+
def enum_values(context = GraphQL::Query::NullContext.instance)
|
|
72
72
|
inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
|
|
73
73
|
visible_values = []
|
|
74
74
|
warden = Warden.from_context(context)
|
|
@@ -110,7 +110,7 @@ module GraphQL
|
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
# @return [Hash<String => GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name.
|
|
113
|
-
def values(context = GraphQL::Query::NullContext)
|
|
113
|
+
def values(context = GraphQL::Query::NullContext.instance)
|
|
114
114
|
enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
|
|
115
115
|
end
|
|
116
116
|
|
|
@@ -54,23 +54,9 @@ module GraphQL
|
|
|
54
54
|
value.edge_class = custom_t
|
|
55
55
|
end
|
|
56
56
|
value
|
|
57
|
-
|
|
57
|
+
else
|
|
58
58
|
context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
|
|
59
59
|
context.schema.connections.wrap(field, object.object, value, original_arguments, context)
|
|
60
|
-
else
|
|
61
|
-
if object.is_a?(GraphQL::Schema::Object)
|
|
62
|
-
object = object.object
|
|
63
|
-
end
|
|
64
|
-
connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(value)
|
|
65
|
-
connection_class.new(
|
|
66
|
-
value,
|
|
67
|
-
original_arguments,
|
|
68
|
-
field: field,
|
|
69
|
-
max_page_size: field.max_page_size,
|
|
70
|
-
default_page_size: field.default_page_size,
|
|
71
|
-
parent: object,
|
|
72
|
-
context: context,
|
|
73
|
-
)
|
|
74
60
|
end
|
|
75
61
|
end
|
|
76
62
|
end
|
|
@@ -10,7 +10,14 @@ module GraphQL
|
|
|
10
10
|
else
|
|
11
11
|
ret_type = @field.type.unwrap
|
|
12
12
|
if ret_type.respond_to?(:scope_items)
|
|
13
|
-
ret_type.scope_items(value, context)
|
|
13
|
+
scoped_items = ret_type.scope_items(value, context)
|
|
14
|
+
if !scoped_items.equal?(value) && !ret_type.reauthorize_scoped_objects
|
|
15
|
+
if (current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
|
|
16
|
+
(query_runtime_state = current_runtime_state[context.query])
|
|
17
|
+
query_runtime_state.was_authorized_by_scope_items = true
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
scoped_items
|
|
14
21
|
else
|
|
15
22
|
value
|
|
16
23
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
|
@@ -138,7 +138,7 @@ module GraphQL
|
|
|
138
138
|
# As a last ditch, try to force loading the return type:
|
|
139
139
|
type.unwrap.name
|
|
140
140
|
end
|
|
141
|
-
@connection = return_type_name.end_with?("Connection")
|
|
141
|
+
@connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
|
|
142
142
|
else
|
|
143
143
|
@connection
|
|
144
144
|
end
|
|
@@ -218,8 +218,8 @@ module GraphQL
|
|
|
218
218
|
# @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
|
|
219
219
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
|
220
220
|
# @param validates [Array<Hash>] Configurations for validating this field
|
|
221
|
-
# @fallback_value [Object] A fallback value if the method is not defined
|
|
222
|
-
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, &definition_block)
|
|
221
|
+
# @param fallback_value [Object] A fallback value if the method is not defined
|
|
222
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
|
|
223
223
|
if name.nil?
|
|
224
224
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
|
225
225
|
end
|
|
@@ -267,6 +267,7 @@ module GraphQL
|
|
|
267
267
|
@method_sym = method_name.to_sym
|
|
268
268
|
@resolver_method = (resolver_method || name_s).to_sym
|
|
269
269
|
@complexity = complexity
|
|
270
|
+
@dynamic_introspection = dynamic_introspection
|
|
270
271
|
@return_type_expr = type
|
|
271
272
|
@return_type_null = if !null.nil?
|
|
272
273
|
null
|
|
@@ -351,6 +352,8 @@ module GraphQL
|
|
|
351
352
|
@call_after_define = true
|
|
352
353
|
end
|
|
353
354
|
|
|
355
|
+
attr_accessor :dynamic_introspection
|
|
356
|
+
|
|
354
357
|
# If true, subscription updates with this field can be shared between viewers
|
|
355
358
|
# @return [Boolean, nil]
|
|
356
359
|
# @see GraphQL::Subscriptions::BroadcastAnalyzer
|
|
@@ -659,7 +662,7 @@ module GraphQL
|
|
|
659
662
|
method_to_call = nil
|
|
660
663
|
method_args = nil
|
|
661
664
|
|
|
662
|
-
Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
|
665
|
+
@own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
|
663
666
|
|
|
664
667
|
query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
|
|
665
668
|
if is_authorized
|
|
@@ -701,7 +704,7 @@ module GraphQL
|
|
|
701
704
|
inner_object.dig(*@dig_keys)
|
|
702
705
|
elsif inner_object.key?(@method_sym)
|
|
703
706
|
inner_object[@method_sym]
|
|
704
|
-
elsif inner_object.key?(@method_str)
|
|
707
|
+
elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
|
|
705
708
|
inner_object[@method_str]
|
|
706
709
|
elsif @fallback_value != NOT_CONFIGURED
|
|
707
710
|
@fallback_value
|