graphql 2.2.5 → 2.3.2
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/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.rb +9 -0
- 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 +2 -1
- data/lib/graphql/language/parser.rb +25 -11
- 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 +119 -28
- 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
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
|
|
@@ -743,6 +777,7 @@ module GraphQL
|
|
743
777
|
|
744
778
|
def error_bubbling(new_error_bubbling = nil)
|
745
779
|
if !new_error_bubbling.nil?
|
780
|
+
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
781
|
@error_bubbling = new_error_bubbling
|
747
782
|
else
|
748
783
|
@error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
|
@@ -814,9 +849,40 @@ module GraphQL
|
|
814
849
|
end
|
815
850
|
end
|
816
851
|
|
852
|
+
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
853
|
+
# @return [Array<Module>] Type definitions added to this schema
|
854
|
+
def extra_types(*new_extra_types)
|
855
|
+
if new_extra_types.any?
|
856
|
+
new_extra_types = new_extra_types.flatten
|
857
|
+
@own_extra_types ||= []
|
858
|
+
@own_extra_types.concat(new_extra_types)
|
859
|
+
end
|
860
|
+
inherited_et = find_inherited_value(:extra_types, nil)
|
861
|
+
if inherited_et
|
862
|
+
if @own_extra_types
|
863
|
+
inherited_et + @own_extra_types
|
864
|
+
else
|
865
|
+
inherited_et
|
866
|
+
end
|
867
|
+
else
|
868
|
+
@own_extra_types || EMPTY_ARRAY
|
869
|
+
end
|
870
|
+
end
|
871
|
+
|
817
872
|
def orphan_types(*new_orphan_types)
|
818
873
|
if new_orphan_types.any?
|
819
874
|
new_orphan_types = new_orphan_types.flatten
|
875
|
+
non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
|
876
|
+
if non_object_types.any?
|
877
|
+
raise ArgumentError, <<~ERR
|
878
|
+
Only object type classes should be added as `orphan_types(...)`.
|
879
|
+
|
880
|
+
- Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")}
|
881
|
+
- See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
|
882
|
+
|
883
|
+
To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
|
884
|
+
ERR
|
885
|
+
end
|
820
886
|
add_type_and_traverse(new_orphan_types, root: false)
|
821
887
|
own_orphan_types.concat(new_orphan_types.flatten)
|
822
888
|
end
|
@@ -1044,6 +1110,12 @@ module GraphQL
|
|
1044
1110
|
end
|
1045
1111
|
|
1046
1112
|
def instrument(instrument_step, instrumenter, options = {})
|
1113
|
+
warn <<~WARN
|
1114
|
+
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
1115
|
+
(From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
|
1116
|
+
|
1117
|
+
WARN
|
1118
|
+
trace_with(Tracing::LegacyHooksTrace)
|
1047
1119
|
own_instrumenters[instrument_step] << instrumenter
|
1048
1120
|
end
|
1049
1121
|
|
@@ -1079,8 +1151,12 @@ module GraphQL
|
|
1079
1151
|
}.freeze
|
1080
1152
|
end
|
1081
1153
|
|
1082
|
-
def tracer(new_tracer)
|
1083
|
-
|
1154
|
+
def tracer(new_tracer, silence_deprecation_warning: false)
|
1155
|
+
if !silence_deprecation_warning
|
1156
|
+
warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
|
1157
|
+
warn " #{caller(1, 1).first}"
|
1158
|
+
end
|
1159
|
+
default_trace = trace_class_for(:default, build: true)
|
1084
1160
|
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
1085
1161
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
1086
1162
|
end
|
@@ -1106,20 +1182,23 @@ module GraphQL
|
|
1106
1182
|
tc = own_trace_modes[mode] ||= build_trace_mode(mode)
|
1107
1183
|
tc.include(trace_mod)
|
1108
1184
|
own_trace_modules[mode] << trace_mod
|
1109
|
-
|
1185
|
+
add_trace_options_for(mode, options)
|
1110
1186
|
if mode == :default
|
1111
1187
|
# This module is being added as a default tracer. If any other mode classes
|
1112
1188
|
# have already been created, but get their default behavior from a superclass,
|
1113
1189
|
# Then mix this into this schema's subclass.
|
1114
1190
|
# (But don't mix it into mode classes that aren't default-based.)
|
1115
1191
|
own_trace_modes.each do |other_mode_name, other_mode_class|
|
1116
|
-
if other_mode_class < DefaultTraceClass
|
1117
|
-
|
1192
|
+
if other_mode_class < DefaultTraceClass
|
1193
|
+
# Don't add it back to the inheritance tree if it's already there
|
1194
|
+
if !(other_mode_class < trace_mod)
|
1195
|
+
other_mode_class.include(trace_mod)
|
1196
|
+
end
|
1197
|
+
# Add any options so they'll be available
|
1198
|
+
add_trace_options_for(other_mode_name, options)
|
1118
1199
|
end
|
1119
1200
|
end
|
1120
1201
|
end
|
1121
|
-
t_opts = trace_options_for(mode)
|
1122
|
-
t_opts.merge!(options)
|
1123
1202
|
end
|
1124
1203
|
nil
|
1125
1204
|
end
|
@@ -1129,10 +1208,14 @@ module GraphQL
|
|
1129
1208
|
def trace_options_for(mode)
|
1130
1209
|
@trace_options_for_mode ||= {}
|
1131
1210
|
@trace_options_for_mode[mode] ||= begin
|
1211
|
+
# It may be time to create an options hash for a mode that wasn't registered yet.
|
1212
|
+
# Mix in the default options in that case.
|
1213
|
+
default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default)
|
1214
|
+
# Make sure this returns a new object so that other hashes aren't modified later
|
1132
1215
|
if superclass.respond_to?(:trace_options_for)
|
1133
|
-
superclass.trace_options_for(mode).
|
1216
|
+
superclass.trace_options_for(mode).merge(default_options)
|
1134
1217
|
else
|
1135
|
-
|
1218
|
+
default_options.dup
|
1136
1219
|
end
|
1137
1220
|
end
|
1138
1221
|
end
|
@@ -1155,15 +1238,17 @@ module GraphQL
|
|
1155
1238
|
raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
|
1156
1239
|
else
|
1157
1240
|
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
1241
|
+
options_trace_mode = :default
|
1158
1242
|
:default_backtrace
|
1159
1243
|
end
|
1160
1244
|
else
|
1161
1245
|
default_trace_mode
|
1162
1246
|
end
|
1163
1247
|
|
1164
|
-
|
1248
|
+
options_trace_mode ||= trace_mode
|
1249
|
+
base_trace_options = trace_options_for(options_trace_mode)
|
1165
1250
|
trace_options = base_trace_options.merge(options)
|
1166
|
-
trace_class_for_mode = trace_class_for(trace_mode
|
1251
|
+
trace_class_for_mode = trace_class_for(trace_mode, build: true)
|
1167
1252
|
trace_class_for_mode.new(**trace_options)
|
1168
1253
|
end
|
1169
1254
|
|
@@ -1317,6 +1402,12 @@ module GraphQL
|
|
1317
1402
|
|
1318
1403
|
private
|
1319
1404
|
|
1405
|
+
def add_trace_options_for(mode, new_options)
|
1406
|
+
t_opts = trace_options_for(mode)
|
1407
|
+
t_opts.merge!(new_options)
|
1408
|
+
nil
|
1409
|
+
end
|
1410
|
+
|
1320
1411
|
# @param t [Module, Array<Module>]
|
1321
1412
|
# @return [void]
|
1322
1413
|
def add_type_and_traverse(t, root:)
|
@@ -1378,7 +1469,7 @@ module GraphQL
|
|
1378
1469
|
else
|
1379
1470
|
@lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
|
1380
1471
|
@lazy_methods.set(GraphQL::Execution::Lazy, :value)
|
1381
|
-
@lazy_methods.set(GraphQL::Dataloader::Request, :
|
1472
|
+
@lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
|
1382
1473
|
end
|
1383
1474
|
end
|
1384
1475
|
@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)
|
@@ -28,6 +28,7 @@ module GraphQL
|
|
28
28
|
# @return [Array<Hash>]
|
29
29
|
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
30
30
|
query.current_trace.validate(validate: validate, query: query) do
|
31
|
+
begin_t = Time.now
|
31
32
|
errors = if validate == false
|
32
33
|
[]
|
33
34
|
else
|
@@ -52,11 +53,13 @@ module GraphQL
|
|
52
53
|
end
|
53
54
|
|
54
55
|
{
|
56
|
+
remaining_timeout: timeout ? (timeout - (Time.now - begin_t)) : nil,
|
55
57
|
errors: errors,
|
56
58
|
}
|
57
59
|
end
|
58
60
|
rescue GraphQL::ExecutionError => e
|
59
61
|
{
|
62
|
+
remaining_timeout: nil,
|
60
63
|
errors: [e],
|
61
64
|
}
|
62
65
|
end
|
@@ -148,6 +148,8 @@ module GraphQL
|
|
148
148
|
{ TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
|
149
149
|
elsif obj.is_a?(OpenStruct)
|
150
150
|
{ OPEN_STRUCT_KEY => dump_value(obj.to_h) }
|
151
|
+
elsif defined?(ActiveRecord::Relation) && obj.is_a?(ActiveRecord::Relation)
|
152
|
+
dump_value(obj.to_a)
|
151
153
|
else
|
152
154
|
obj
|
153
155
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
require "securerandom"
|
3
3
|
require "graphql/subscriptions/broadcast_analyzer"
|
4
4
|
require "graphql/subscriptions/event"
|
5
|
-
require "graphql/subscriptions/instrumentation"
|
6
5
|
require "graphql/subscriptions/serialize"
|
7
6
|
require "graphql/subscriptions/action_cable_subscriptions"
|
8
7
|
require "graphql/subscriptions/default_subscription_resolve_extension"
|
@@ -30,8 +29,6 @@ module GraphQL
|
|
30
29
|
raise ArgumentError, "Can't reinstall subscriptions. #{schema} is using #{schema.subscriptions}, can't also add #{self}"
|
31
30
|
end
|
32
31
|
|
33
|
-
instrumentation = Subscriptions::Instrumentation.new(schema: schema)
|
34
|
-
defn.instrument(:query, instrumentation)
|
35
32
|
options[:schema] = schema
|
36
33
|
schema.subscriptions = self.new(**options)
|
37
34
|
schema.add_subscription_extension_if_necessary
|
@@ -5,10 +5,7 @@ module GraphQL
|
|
5
5
|
# @param schema_class [Class<GraphQL::Schema>]
|
6
6
|
# @return [Module] A helpers module which always uses the given schema
|
7
7
|
def self.for(schema_class)
|
8
|
-
|
9
|
-
include SchemaHelpers
|
10
|
-
@@schema_class_for_helpers = schema_class
|
11
|
-
end
|
8
|
+
SchemaHelpers.for(schema_class)
|
12
9
|
end
|
13
10
|
|
14
11
|
class Error < GraphQL::Error
|
@@ -42,9 +39,9 @@ module GraphQL
|
|
42
39
|
end
|
43
40
|
end
|
44
41
|
|
45
|
-
def run_graphql_field(schema, field_path, object, arguments: {}, context: {})
|
42
|
+
def run_graphql_field(schema, field_path, object, arguments: {}, context: {}, ast_node: nil, lookahead: nil)
|
46
43
|
type_name, *field_names = field_path.split(".")
|
47
|
-
dummy_query = GraphQL::Query.new(schema, context: context)
|
44
|
+
dummy_query = GraphQL::Query.new(schema, "{ __typename }", context: context)
|
48
45
|
query_context = dummy_query.context
|
49
46
|
object_type = dummy_query.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
|
50
47
|
if object_type
|
@@ -60,6 +57,28 @@ module GraphQL
|
|
60
57
|
dummy_query.context.dataloader.run_isolated {
|
61
58
|
field_args = visible_field.coerce_arguments(graphql_result, arguments, query_context)
|
62
59
|
field_args = schema.sync_lazy(field_args)
|
60
|
+
if visible_field.extras.any?
|
61
|
+
extra_args = {}
|
62
|
+
visible_field.extras.each do |extra|
|
63
|
+
extra_args[extra] = case extra
|
64
|
+
when :ast_node
|
65
|
+
ast_node ||= GraphQL::Language::Nodes::Field.new(name: visible_field.graphql_name)
|
66
|
+
when :lookahead
|
67
|
+
lookahead ||= begin
|
68
|
+
ast_node ||= GraphQL::Language::Nodes::Field.new(name: visible_field.graphql_name)
|
69
|
+
Execution::Lookahead.new(
|
70
|
+
query: dummy_query,
|
71
|
+
ast_nodes: [ast_node],
|
72
|
+
field: visible_field,
|
73
|
+
)
|
74
|
+
end
|
75
|
+
else
|
76
|
+
raise ArgumentError, "This extra isn't supported in `run_graphql_field` yet: `#{extra.inspect}`. Open an issue on GitHub to request it: https://github.com/rmosolgo/graphql-ruby/issues/new"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
field_args = field_args.merge_extras(extra_args)
|
81
|
+
end
|
63
82
|
graphql_result = visible_field.resolve(graphql_result, field_args.keyword_arguments, query_context)
|
64
83
|
graphql_result = schema.sync_lazy(graphql_result)
|
65
84
|
}
|
@@ -119,6 +138,13 @@ module GraphQL
|
|
119
138
|
# schema will be added later
|
120
139
|
super(nil, *args, **kwargs, &block)
|
121
140
|
end
|
141
|
+
|
142
|
+
def self.for(schema_class)
|
143
|
+
Module.new do
|
144
|
+
include SchemaHelpers
|
145
|
+
@@schema_class_for_helpers = schema_class
|
146
|
+
end
|
147
|
+
end
|
122
148
|
end
|
123
149
|
end
|
124
150
|
end
|