graphql 2.2.5 → 2.3.3
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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/templates/schema.erb +3 -0
- data/lib/graphql/analysis/ast/field_usage.rb +36 -9
- data/lib/graphql/analysis/ast/query_complexity.rb +3 -0
- data/lib/graphql/analysis/ast/visitor.rb +8 -0
- data/lib/graphql/analysis/ast.rb +10 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +6 -4
- data/lib/graphql/execution/interpreter/runtime.rb +93 -106
- data/lib/graphql/execution/interpreter.rb +90 -150
- data/lib/graphql/introspection/entry_points.rb +9 -3
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/document_from_schema_definition.rb +2 -3
- data/lib/graphql/language/lexer.rb +48 -30
- data/lib/graphql/language/nodes.rb +11 -16
- data/lib/graphql/language/parser.rb +94 -45
- data/lib/graphql/language/printer.rb +4 -0
- data/lib/graphql/language.rb +60 -0
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/query/context.rb +30 -33
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +3 -3
- data/lib/graphql/schema/argument.rb +18 -2
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +9 -1
- data/lib/graphql/schema/field.rb +33 -30
- data/lib/graphql/schema/input_object.rb +1 -2
- data/lib/graphql/schema/interface.rb +5 -1
- data/lib/graphql/schema/loader.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/resolver.rb +19 -10
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema.rb +129 -29
- data/lib/graphql/static_validation/literal_validator.rb +1 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +0 -3
- data/lib/graphql/testing/helpers.rb +32 -6
- 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 +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/prometheus_trace.rb +2 -2
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -2
- metadata +38 -23
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -206,8 +206,8 @@ module GraphQL
|
|
206
206
|
# Used by the runtime.
|
207
207
|
# @api private
|
208
208
|
def prepare_value(obj, value, context: nil)
|
209
|
-
if
|
210
|
-
value = value
|
209
|
+
if type.unwrap.kind.input_object?
|
210
|
+
value = recursively_prepare_input_object(value, type)
|
211
211
|
end
|
212
212
|
|
213
213
|
Schema::Validator.validate!(validators, obj, context, value)
|
@@ -290,6 +290,7 @@ module GraphQL
|
|
290
290
|
# TODO code smell to access such a deeply-nested constant in a distant module
|
291
291
|
argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
|
292
292
|
value: resolved_loaded_value,
|
293
|
+
original_value: resolved_coerced_value,
|
293
294
|
definition: self,
|
294
295
|
default_used: default_used,
|
295
296
|
)
|
@@ -372,6 +373,21 @@ module GraphQL
|
|
372
373
|
|
373
374
|
private
|
374
375
|
|
376
|
+
def recursively_prepare_input_object(value, type)
|
377
|
+
if type.non_null?
|
378
|
+
type = type.of_type
|
379
|
+
end
|
380
|
+
|
381
|
+
if type.list? && !value.nil?
|
382
|
+
inner_type = type.of_type
|
383
|
+
value.map { |v| recursively_prepare_input_object(v, inner_type) }
|
384
|
+
elsif value.is_a?(GraphQL::Schema::InputObject)
|
385
|
+
value.prepare
|
386
|
+
else
|
387
|
+
value
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
375
391
|
def validate_input_type(input_type)
|
376
392
|
if input_type.is_a?(String) || input_type.is_a?(GraphQL::Schema::LateBoundType)
|
377
393
|
# Do nothing; assume this will be validated later
|
@@ -1,18 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'graphql/schema/base_64_bp'
|
4
|
-
|
2
|
+
require "base64"
|
5
3
|
module GraphQL
|
6
4
|
class Schema
|
7
5
|
# @api private
|
8
6
|
module Base64Encoder
|
9
7
|
def self.encode(unencoded_text, nonce: false)
|
10
|
-
|
8
|
+
Base64.urlsafe_encode64(unencoded_text, padding: false)
|
11
9
|
end
|
12
10
|
|
13
11
|
def self.decode(encoded_text, nonce: false)
|
14
12
|
# urlsafe_decode64 is for forward compatibility
|
15
|
-
|
13
|
+
Base64.urlsafe_decode64(encoded_text)
|
16
14
|
rescue ArgumentError
|
17
15
|
raise GraphQL::ExecutionError, "Invalid input: #{encoded_text.inspect}"
|
18
16
|
end
|
@@ -7,10 +7,16 @@ module GraphQL
|
|
7
7
|
class << self
|
8
8
|
# @see {Schema.from_definition}
|
9
9
|
def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs)
|
10
|
+
if defined?(parser::SchemaParser)
|
11
|
+
parser = parser::SchemaParser
|
12
|
+
end
|
10
13
|
from_document(schema_superclass, parser.parse(definition_string), **kwargs)
|
11
14
|
end
|
12
15
|
|
13
16
|
def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs)
|
17
|
+
if defined?(parser::SchemaParser)
|
18
|
+
parser = parser::SchemaParser
|
19
|
+
end
|
14
20
|
from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
|
15
21
|
end
|
16
22
|
|
@@ -120,10 +126,12 @@ module GraphQL
|
|
120
126
|
|
121
127
|
builder = self
|
122
128
|
|
129
|
+
found_types = types.values
|
123
130
|
schema_class = Class.new(schema_superclass) do
|
124
131
|
begin
|
125
132
|
# Add these first so that there's some chance of resolving late-bound types
|
126
|
-
|
133
|
+
add_type_and_traverse(found_types, root: false)
|
134
|
+
orphan_types(found_types.select { |t| t.respond_to?(:kind) && t.kind.object? })
|
127
135
|
query query_root_type
|
128
136
|
mutation mutation_root_type
|
129
137
|
subscription subscription_root_type
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -471,6 +471,8 @@ module GraphQL
|
|
471
471
|
if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
|
472
472
|
max_possible_page_size = arguments[:last]
|
473
473
|
end
|
474
|
+
elsif arguments.is_a?(GraphQL::UnauthorizedError)
|
475
|
+
raise arguments
|
474
476
|
end
|
475
477
|
|
476
478
|
if max_possible_page_size.nil?
|
@@ -483,41 +485,24 @@ module GraphQL
|
|
483
485
|
metadata_complexity = 0
|
484
486
|
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
485
487
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
488
|
+
lookahead.selections.each do |next_lookahead|
|
489
|
+
# this includes `pageInfo`, `nodes` and `edges` and any custom fields
|
490
|
+
# TODO this doesn't support procs yet -- unlikely to need it.
|
491
|
+
metadata_complexity += next_lookahead.field.complexity
|
492
|
+
if next_lookahead.name != :nodes && next_lookahead.name != :edges
|
493
|
+
# subfields, eg, for pageInfo -- assumes no subselections
|
494
|
+
metadata_complexity += next_lookahead.selections.size
|
495
|
+
end
|
493
496
|
end
|
494
497
|
|
495
|
-
nodes_edges_complexity = 0
|
496
|
-
nodes_edges_complexity += 1 if lookahead.selects?(:edges)
|
497
|
-
nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
|
498
|
-
|
499
498
|
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
500
|
-
items_complexity = child_complexity - metadata_complexity
|
501
|
-
|
502
|
-
|
499
|
+
items_complexity = child_complexity - metadata_complexity
|
500
|
+
subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity
|
501
|
+
# Apply this field's own complexity
|
502
|
+
apply_own_complexity_to(subfields_complexity, query, nodes)
|
503
503
|
end
|
504
504
|
else
|
505
|
-
|
506
|
-
case defined_complexity
|
507
|
-
when Proc
|
508
|
-
arguments = query.arguments_for(nodes.first, self)
|
509
|
-
if arguments.is_a?(GraphQL::ExecutionError)
|
510
|
-
return child_complexity
|
511
|
-
elsif arguments.respond_to?(:keyword_arguments)
|
512
|
-
arguments = arguments.keyword_arguments
|
513
|
-
end
|
514
|
-
|
515
|
-
defined_complexity.call(query.context, arguments, child_complexity)
|
516
|
-
when Numeric
|
517
|
-
defined_complexity + child_complexity
|
518
|
-
else
|
519
|
-
raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
|
520
|
-
end
|
505
|
+
apply_own_complexity_to(child_complexity, query, nodes)
|
521
506
|
end
|
522
507
|
end
|
523
508
|
|
@@ -882,6 +867,24 @@ ERR
|
|
882
867
|
yield(obj, args)
|
883
868
|
end
|
884
869
|
end
|
870
|
+
|
871
|
+
def apply_own_complexity_to(child_complexity, query, nodes)
|
872
|
+
case (own_complexity = complexity)
|
873
|
+
when Numeric
|
874
|
+
own_complexity + child_complexity
|
875
|
+
when Proc
|
876
|
+
arguments = query.arguments_for(nodes.first, self)
|
877
|
+
if arguments.is_a?(GraphQL::ExecutionError)
|
878
|
+
return child_complexity
|
879
|
+
elsif arguments.respond_to?(:keyword_arguments)
|
880
|
+
arguments = arguments.keyword_arguments
|
881
|
+
end
|
882
|
+
|
883
|
+
own_complexity.call(query.context, arguments, child_complexity)
|
884
|
+
else
|
885
|
+
raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
|
886
|
+
end
|
887
|
+
end
|
885
888
|
end
|
886
889
|
end
|
887
890
|
end
|
@@ -215,8 +215,7 @@ module GraphQL
|
|
215
215
|
if resolved_arguments.is_a?(GraphQL::Error)
|
216
216
|
raise resolved_arguments
|
217
217
|
else
|
218
|
-
|
219
|
-
input_obj_instance.prepare
|
218
|
+
self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
|
220
219
|
end
|
221
220
|
end
|
222
221
|
end
|
@@ -69,7 +69,11 @@ module GraphQL
|
|
69
69
|
end
|
70
70
|
elsif child_class < GraphQL::Schema::Object
|
71
71
|
# This is being included into an object type, make sure it's using `implements(...)`
|
72
|
-
backtrace_line =
|
72
|
+
backtrace_line = caller_locations(0, 10).find do |location|
|
73
|
+
location.base_label == "implements" &&
|
74
|
+
location.path.end_with?("schema/member/has_interfaces.rb")
|
75
|
+
end
|
76
|
+
|
73
77
|
if !backtrace_line
|
74
78
|
raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
|
75
79
|
end
|
@@ -32,7 +32,8 @@ module GraphQL
|
|
32
32
|
end
|
33
33
|
|
34
34
|
Class.new(GraphQL::Schema) do
|
35
|
-
|
35
|
+
add_type_and_traverse(types.values, root: false)
|
36
|
+
orphan_types(types.values.select { |t| t.kind.object? })
|
36
37
|
directives(directives)
|
37
38
|
description(schema["description"])
|
38
39
|
|
@@ -50,7 +50,7 @@ module GraphQL
|
|
50
50
|
if loads && arg_defn.type.list?
|
51
51
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
52
52
|
def #{method_owner}load_#{arg_defn.keyword}(values, context = nil)
|
53
|
-
argument = get_argument("#{arg_defn.graphql_name}")
|
53
|
+
argument = get_argument("#{arg_defn.graphql_name}", context || self.context)
|
54
54
|
(context || self.context).query.after_lazy(values) do |values2|
|
55
55
|
GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, value, context || self.context) })
|
56
56
|
end
|
@@ -59,7 +59,7 @@ module GraphQL
|
|
59
59
|
elsif loads
|
60
60
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
61
61
|
def #{method_owner}load_#{arg_defn.keyword}(value, context = nil)
|
62
|
-
argument = get_argument("#{arg_defn.graphql_name}")
|
62
|
+
argument = get_argument("#{arg_defn.graphql_name}", context || self.context)
|
63
63
|
load_application_object(argument, value, context || self.context)
|
64
64
|
end
|
65
65
|
RUBY
|
@@ -62,6 +62,13 @@ module GraphQL
|
|
62
62
|
extend GraphQL::Schema::Member::HasFields
|
63
63
|
extend GraphQL::Schema::Resolver::HasPayloadType
|
64
64
|
|
65
|
+
# @api private
|
66
|
+
def call_resolve(_args_hash)
|
67
|
+
# Clear any cached values from `loads` or authorization:
|
68
|
+
dataloader.clear_cache
|
69
|
+
super
|
70
|
+
end
|
71
|
+
|
65
72
|
class << self
|
66
73
|
def visible?(context)
|
67
74
|
true
|
@@ -103,11 +103,7 @@ module GraphQL
|
|
103
103
|
end
|
104
104
|
elsif authorized_val
|
105
105
|
# Finally, all the hooks have passed, so resolve it
|
106
|
-
|
107
|
-
public_send(self.class.resolve_method, **loaded_args)
|
108
|
-
else
|
109
|
-
public_send(self.class.resolve_method)
|
110
|
-
end
|
106
|
+
call_resolve(loaded_args)
|
111
107
|
else
|
112
108
|
raise GraphQL::UnauthorizedFieldError.new(context: context, object: object, type: field.owner, field: field)
|
113
109
|
end
|
@@ -117,6 +113,15 @@ module GraphQL
|
|
117
113
|
end
|
118
114
|
end
|
119
115
|
|
116
|
+
# @api private {GraphQL::Schema::Mutation} uses this to clear the dataloader cache
|
117
|
+
def call_resolve(args_hash)
|
118
|
+
if args_hash.any?
|
119
|
+
public_send(self.class.resolve_method, **args_hash)
|
120
|
+
else
|
121
|
+
public_send(self.class.resolve_method)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
120
125
|
# Do the work. Everything happens here.
|
121
126
|
# @return [Object] An object corresponding to the return type
|
122
127
|
def resolve(**args)
|
@@ -166,11 +171,15 @@ module GraphQL
|
|
166
171
|
args.each_value do |argument|
|
167
172
|
arg_keyword = argument.keyword
|
168
173
|
if inputs.key?(arg_keyword) && !(arg_value = inputs[arg_keyword]).nil? && (arg_value != argument.default_value)
|
169
|
-
|
170
|
-
if
|
171
|
-
return
|
172
|
-
|
173
|
-
|
174
|
+
auth_result = argument.authorized?(self, arg_value, context)
|
175
|
+
if auth_result.is_a?(Array)
|
176
|
+
# only return this second value if the application returned a second value
|
177
|
+
arg_auth, err = auth_result
|
178
|
+
if !arg_auth
|
179
|
+
return arg_auth, err
|
180
|
+
end
|
181
|
+
elsif auth_result == false
|
182
|
+
return auth_result
|
174
183
|
end
|
175
184
|
else
|
176
185
|
true
|
data/lib/graphql/schema.rb
CHANGED
@@ -63,10 +63,6 @@ module GraphQL
|
|
63
63
|
# Schemas can restrict large incoming queries with `max_depth` and `max_complexity` configurations.
|
64
64
|
# (These configurations can be overridden by specific calls to {Schema#execute})
|
65
65
|
#
|
66
|
-
# Schemas can specify how queries should be executed against them.
|
67
|
-
# `query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy`
|
68
|
-
# each apply to corresponding root types.
|
69
|
-
#
|
70
66
|
# @example defining a schema
|
71
67
|
# class MySchema < GraphQL::Schema
|
72
68
|
# query QueryType
|
@@ -162,18 +158,29 @@ module GraphQL
|
|
162
158
|
|
163
159
|
def trace_class(new_class = nil)
|
164
160
|
if new_class
|
161
|
+
# If any modules were already added for `:default`,
|
162
|
+
# re-apply them here
|
163
|
+
mods = trace_modules_for(:default)
|
164
|
+
mods.each { |mod| new_class.include(mod) }
|
165
165
|
trace_mode(:default, new_class)
|
166
166
|
backtrace_class = Class.new(new_class)
|
167
167
|
backtrace_class.include(GraphQL::Backtrace::Trace)
|
168
168
|
trace_mode(:default_backtrace, backtrace_class)
|
169
169
|
end
|
170
|
-
trace_class_for(:default)
|
170
|
+
trace_class_for(:default, build: true)
|
171
171
|
end
|
172
172
|
|
173
173
|
# @return [Class] Return the trace class to use for this mode, looking one up on the superclass if this Schema doesn't have one defined.
|
174
|
-
def trace_class_for(mode, build:
|
175
|
-
own_trace_modes[mode]
|
176
|
-
|
174
|
+
def trace_class_for(mode, build: false)
|
175
|
+
if (trace_class = own_trace_modes[mode])
|
176
|
+
trace_class
|
177
|
+
elsif superclass.respond_to?(:trace_class_for) && (trace_class = superclass.trace_class_for(mode, build: false))
|
178
|
+
trace_class
|
179
|
+
elsif build
|
180
|
+
own_trace_modes[mode] = build_trace_mode(mode)
|
181
|
+
else
|
182
|
+
nil
|
183
|
+
end
|
177
184
|
end
|
178
185
|
|
179
186
|
# Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
|
@@ -211,20 +218,24 @@ module GraphQL
|
|
211
218
|
include DefaultTraceClass
|
212
219
|
end
|
213
220
|
when :default_backtrace
|
214
|
-
schema_base_class = trace_class_for(:default)
|
221
|
+
schema_base_class = trace_class_for(:default, build: true)
|
215
222
|
Class.new(schema_base_class) do
|
216
223
|
include(GraphQL::Backtrace::Trace)
|
217
224
|
end
|
218
225
|
else
|
219
226
|
# First, see if the superclass has a custom-defined class for this.
|
220
227
|
# Then, if it doesn't, use this class's default trace
|
221
|
-
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode
|
228
|
+
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default, build: true)
|
222
229
|
# Prepare the default trace class if it hasn't been initialized yet
|
223
230
|
base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
|
224
231
|
mods = trace_modules_for(mode)
|
225
232
|
if base_class < DefaultTraceClass
|
226
233
|
mods = trace_modules_for(:default) + mods
|
227
234
|
end
|
235
|
+
# Copy the existing default options into this mode's options
|
236
|
+
default_options = trace_options_for(:default)
|
237
|
+
add_trace_options_for(mode, default_options)
|
238
|
+
|
228
239
|
Class.new(base_class) do
|
229
240
|
mods.any? && include(*mods)
|
230
241
|
end
|
@@ -632,6 +643,17 @@ module GraphQL
|
|
632
643
|
end
|
633
644
|
end
|
634
645
|
|
646
|
+
# A limit on the number of tokens to accept on incoming query strings.
|
647
|
+
# Use this to prevent parsing maliciously-large query strings.
|
648
|
+
# @return [nil, Integer]
|
649
|
+
def max_query_string_tokens(new_max_tokens = NOT_CONFIGURED)
|
650
|
+
if NOT_CONFIGURED.equal?(new_max_tokens)
|
651
|
+
defined?(@max_query_string_tokens) ? @max_query_string_tokens : find_inherited_value(:max_query_string_tokens)
|
652
|
+
else
|
653
|
+
@max_query_string_tokens = new_max_tokens
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
635
657
|
def default_page_size(new_default_page_size = nil)
|
636
658
|
if new_default_page_size
|
637
659
|
@default_page_size = new_default_page_size
|
@@ -640,27 +662,39 @@ module GraphQL
|
|
640
662
|
end
|
641
663
|
end
|
642
664
|
|
643
|
-
def query_execution_strategy(new_query_execution_strategy = nil)
|
665
|
+
def query_execution_strategy(new_query_execution_strategy = nil, deprecation_warning: true)
|
666
|
+
if deprecation_warning
|
667
|
+
warn "GraphQL::Schema.query_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
668
|
+
warn " #{caller(1, 1).first}"
|
669
|
+
end
|
644
670
|
if new_query_execution_strategy
|
645
671
|
@query_execution_strategy = new_query_execution_strategy
|
646
672
|
else
|
647
|
-
@query_execution_strategy ||
|
673
|
+
@query_execution_strategy || (superclass.respond_to?(:query_execution_strategy) ? superclass.query_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
648
674
|
end
|
649
675
|
end
|
650
676
|
|
651
|
-
def mutation_execution_strategy(new_mutation_execution_strategy = nil)
|
677
|
+
def mutation_execution_strategy(new_mutation_execution_strategy = nil, deprecation_warning: true)
|
678
|
+
if deprecation_warning
|
679
|
+
warn "GraphQL::Schema.mutation_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
680
|
+
warn " #{caller(1, 1).first}"
|
681
|
+
end
|
652
682
|
if new_mutation_execution_strategy
|
653
683
|
@mutation_execution_strategy = new_mutation_execution_strategy
|
654
684
|
else
|
655
|
-
@mutation_execution_strategy ||
|
685
|
+
@mutation_execution_strategy || (superclass.respond_to?(:mutation_execution_strategy) ? superclass.mutation_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
656
686
|
end
|
657
687
|
end
|
658
688
|
|
659
|
-
def subscription_execution_strategy(new_subscription_execution_strategy = nil)
|
689
|
+
def subscription_execution_strategy(new_subscription_execution_strategy = nil, deprecation_warning: true)
|
690
|
+
if deprecation_warning
|
691
|
+
warn "GraphQL::Schema.subscription_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
692
|
+
warn " #{caller(1, 1).first}"
|
693
|
+
end
|
660
694
|
if new_subscription_execution_strategy
|
661
695
|
@subscription_execution_strategy = new_subscription_execution_strategy
|
662
696
|
else
|
663
|
-
@subscription_execution_strategy ||
|
697
|
+
@subscription_execution_strategy || (superclass.respond_to?(:subscription_execution_strategy) ? superclass.subscription_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
664
698
|
end
|
665
699
|
end
|
666
700
|
|
@@ -715,9 +749,10 @@ module GraphQL
|
|
715
749
|
|
716
750
|
attr_writer :max_complexity
|
717
751
|
|
718
|
-
def max_complexity(max_complexity = nil)
|
752
|
+
def max_complexity(max_complexity = nil, count_introspection_fields: true)
|
719
753
|
if max_complexity
|
720
754
|
@max_complexity = max_complexity
|
755
|
+
@max_complexity_count_introspection_fields = count_introspection_fields
|
721
756
|
elsif defined?(@max_complexity)
|
722
757
|
@max_complexity
|
723
758
|
else
|
@@ -725,6 +760,14 @@ module GraphQL
|
|
725
760
|
end
|
726
761
|
end
|
727
762
|
|
763
|
+
def max_complexity_count_introspection_fields
|
764
|
+
if defined?(@max_complexity_count_introspection_fields)
|
765
|
+
@max_complexity_count_introspection_fields
|
766
|
+
else
|
767
|
+
find_inherited_value(:max_complexity_count_introspection_fields, true)
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
728
771
|
attr_writer :analysis_engine
|
729
772
|
|
730
773
|
def analysis_engine
|
@@ -743,6 +786,7 @@ module GraphQL
|
|
743
786
|
|
744
787
|
def error_bubbling(new_error_bubbling = nil)
|
745
788
|
if !new_error_bubbling.nil?
|
789
|
+
warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0")
|
746
790
|
@error_bubbling = new_error_bubbling
|
747
791
|
else
|
748
792
|
@error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
|
@@ -814,9 +858,40 @@ module GraphQL
|
|
814
858
|
end
|
815
859
|
end
|
816
860
|
|
861
|
+
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
862
|
+
# @return [Array<Module>] Type definitions added to this schema
|
863
|
+
def extra_types(*new_extra_types)
|
864
|
+
if new_extra_types.any?
|
865
|
+
new_extra_types = new_extra_types.flatten
|
866
|
+
@own_extra_types ||= []
|
867
|
+
@own_extra_types.concat(new_extra_types)
|
868
|
+
end
|
869
|
+
inherited_et = find_inherited_value(:extra_types, nil)
|
870
|
+
if inherited_et
|
871
|
+
if @own_extra_types
|
872
|
+
inherited_et + @own_extra_types
|
873
|
+
else
|
874
|
+
inherited_et
|
875
|
+
end
|
876
|
+
else
|
877
|
+
@own_extra_types || EMPTY_ARRAY
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
817
881
|
def orphan_types(*new_orphan_types)
|
818
882
|
if new_orphan_types.any?
|
819
883
|
new_orphan_types = new_orphan_types.flatten
|
884
|
+
non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
|
885
|
+
if non_object_types.any?
|
886
|
+
raise ArgumentError, <<~ERR
|
887
|
+
Only object type classes should be added as `orphan_types(...)`.
|
888
|
+
|
889
|
+
- Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")}
|
890
|
+
- See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
|
891
|
+
|
892
|
+
To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
|
893
|
+
ERR
|
894
|
+
end
|
820
895
|
add_type_and_traverse(new_orphan_types, root: false)
|
821
896
|
own_orphan_types.concat(new_orphan_types.flatten)
|
822
897
|
end
|
@@ -1044,6 +1119,12 @@ module GraphQL
|
|
1044
1119
|
end
|
1045
1120
|
|
1046
1121
|
def instrument(instrument_step, instrumenter, options = {})
|
1122
|
+
warn <<~WARN
|
1123
|
+
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
1124
|
+
(From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
|
1125
|
+
|
1126
|
+
WARN
|
1127
|
+
trace_with(Tracing::LegacyHooksTrace)
|
1047
1128
|
own_instrumenters[instrument_step] << instrumenter
|
1048
1129
|
end
|
1049
1130
|
|
@@ -1079,8 +1160,12 @@ module GraphQL
|
|
1079
1160
|
}.freeze
|
1080
1161
|
end
|
1081
1162
|
|
1082
|
-
def tracer(new_tracer)
|
1083
|
-
|
1163
|
+
def tracer(new_tracer, silence_deprecation_warning: false)
|
1164
|
+
if !silence_deprecation_warning
|
1165
|
+
warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
|
1166
|
+
warn " #{caller(1, 1).first}"
|
1167
|
+
end
|
1168
|
+
default_trace = trace_class_for(:default, build: true)
|
1084
1169
|
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
1085
1170
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
1086
1171
|
end
|
@@ -1106,20 +1191,23 @@ module GraphQL
|
|
1106
1191
|
tc = own_trace_modes[mode] ||= build_trace_mode(mode)
|
1107
1192
|
tc.include(trace_mod)
|
1108
1193
|
own_trace_modules[mode] << trace_mod
|
1109
|
-
|
1194
|
+
add_trace_options_for(mode, options)
|
1110
1195
|
if mode == :default
|
1111
1196
|
# This module is being added as a default tracer. If any other mode classes
|
1112
1197
|
# have already been created, but get their default behavior from a superclass,
|
1113
1198
|
# Then mix this into this schema's subclass.
|
1114
1199
|
# (But don't mix it into mode classes that aren't default-based.)
|
1115
1200
|
own_trace_modes.each do |other_mode_name, other_mode_class|
|
1116
|
-
if other_mode_class < DefaultTraceClass
|
1117
|
-
|
1201
|
+
if other_mode_class < DefaultTraceClass
|
1202
|
+
# Don't add it back to the inheritance tree if it's already there
|
1203
|
+
if !(other_mode_class < trace_mod)
|
1204
|
+
other_mode_class.include(trace_mod)
|
1205
|
+
end
|
1206
|
+
# Add any options so they'll be available
|
1207
|
+
add_trace_options_for(other_mode_name, options)
|
1118
1208
|
end
|
1119
1209
|
end
|
1120
1210
|
end
|
1121
|
-
t_opts = trace_options_for(mode)
|
1122
|
-
t_opts.merge!(options)
|
1123
1211
|
end
|
1124
1212
|
nil
|
1125
1213
|
end
|
@@ -1129,10 +1217,14 @@ module GraphQL
|
|
1129
1217
|
def trace_options_for(mode)
|
1130
1218
|
@trace_options_for_mode ||= {}
|
1131
1219
|
@trace_options_for_mode[mode] ||= begin
|
1220
|
+
# It may be time to create an options hash for a mode that wasn't registered yet.
|
1221
|
+
# Mix in the default options in that case.
|
1222
|
+
default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default)
|
1223
|
+
# Make sure this returns a new object so that other hashes aren't modified later
|
1132
1224
|
if superclass.respond_to?(:trace_options_for)
|
1133
|
-
superclass.trace_options_for(mode).
|
1225
|
+
superclass.trace_options_for(mode).merge(default_options)
|
1134
1226
|
else
|
1135
|
-
|
1227
|
+
default_options.dup
|
1136
1228
|
end
|
1137
1229
|
end
|
1138
1230
|
end
|
@@ -1155,15 +1247,17 @@ module GraphQL
|
|
1155
1247
|
raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
|
1156
1248
|
else
|
1157
1249
|
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
1250
|
+
options_trace_mode = :default
|
1158
1251
|
:default_backtrace
|
1159
1252
|
end
|
1160
1253
|
else
|
1161
1254
|
default_trace_mode
|
1162
1255
|
end
|
1163
1256
|
|
1164
|
-
|
1257
|
+
options_trace_mode ||= trace_mode
|
1258
|
+
base_trace_options = trace_options_for(options_trace_mode)
|
1165
1259
|
trace_options = base_trace_options.merge(options)
|
1166
|
-
trace_class_for_mode = trace_class_for(trace_mode
|
1260
|
+
trace_class_for_mode = trace_class_for(trace_mode, build: true)
|
1167
1261
|
trace_class_for_mode.new(**trace_options)
|
1168
1262
|
end
|
1169
1263
|
|
@@ -1317,6 +1411,12 @@ module GraphQL
|
|
1317
1411
|
|
1318
1412
|
private
|
1319
1413
|
|
1414
|
+
def add_trace_options_for(mode, new_options)
|
1415
|
+
t_opts = trace_options_for(mode)
|
1416
|
+
t_opts.merge!(new_options)
|
1417
|
+
nil
|
1418
|
+
end
|
1419
|
+
|
1320
1420
|
# @param t [Module, Array<Module>]
|
1321
1421
|
# @return [void]
|
1322
1422
|
def add_type_and_traverse(t, root:)
|
@@ -1378,7 +1478,7 @@ module GraphQL
|
|
1378
1478
|
else
|
1379
1479
|
@lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
|
1380
1480
|
@lazy_methods.set(GraphQL::Execution::Lazy, :value)
|
1381
|
-
@lazy_methods.set(GraphQL::Dataloader::Request, :
|
1481
|
+
@lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
|
1382
1482
|
end
|
1383
1483
|
end
|
1384
1484
|
@lazy_methods
|
@@ -110,7 +110,7 @@ module GraphQL
|
|
110
110
|
# TODO - would be nice to use these to create an error message so the caller knows
|
111
111
|
# that required fields are missing
|
112
112
|
required_field_names = @warden.arguments(type)
|
113
|
-
.select { |argument| argument.type.kind.non_null? &&
|
113
|
+
.select { |argument| argument.type.kind.non_null? && !argument.default_value? }
|
114
114
|
.map!(&:name)
|
115
115
|
|
116
116
|
present_field_names = ast_node.arguments.map(&:name)
|
@@ -122,7 +122,6 @@ module GraphQL
|
|
122
122
|
arg_type = @warden.get_argument(type, name).type
|
123
123
|
recursively_validate(GraphQL::Language::Nodes::NullValue.new(name: name), arg_type)
|
124
124
|
end
|
125
|
-
|
126
125
|
if type.one_of? && ast_node.arguments.size != 1
|
127
126
|
results << Query::InputValidationResult.from_problem("`#{type.graphql_name}` is a OneOf type, so only one argument may be given (instead of #{ast_node.arguments.size})")
|
128
127
|
end
|
@@ -35,7 +35,7 @@ module GraphQL
|
|
35
35
|
return unless parent_type && parent_type.kind.input_object?
|
36
36
|
|
37
37
|
required_fields = context.warden.arguments(parent_type)
|
38
|
-
.select{|arg| arg.type.kind.non_null?}
|
38
|
+
.select{ |arg| arg.type.kind.non_null? && !arg.default_value? }
|
39
39
|
.map!(&:graphql_name)
|
40
40
|
|
41
41
|
present_fields = ast_node.arguments.map(&:name)
|