graphql 2.0.27 → 2.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install/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
data/lib/graphql/schema.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "logger"
|
2
3
|
require "graphql/schema/addition"
|
3
4
|
require "graphql/schema/always_visible"
|
4
5
|
require "graphql/schema/base_64_encoder"
|
@@ -37,10 +38,12 @@ require "graphql/schema/directive/skip"
|
|
37
38
|
require "graphql/schema/directive/feature"
|
38
39
|
require "graphql/schema/directive/flagged"
|
39
40
|
require "graphql/schema/directive/transform"
|
41
|
+
require "graphql/schema/directive/specified_by"
|
40
42
|
require "graphql/schema/type_membership"
|
41
43
|
|
42
44
|
require "graphql/schema/resolver"
|
43
45
|
require "graphql/schema/mutation"
|
46
|
+
require "graphql/schema/has_single_input_argument"
|
44
47
|
require "graphql/schema/relay_classic_mutation"
|
45
48
|
require "graphql/schema/subscription"
|
46
49
|
|
@@ -144,6 +147,19 @@ module GraphQL
|
|
144
147
|
@subscriptions = new_implementation
|
145
148
|
end
|
146
149
|
|
150
|
+
# @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
|
151
|
+
def default_trace_mode(new_mode = nil)
|
152
|
+
if new_mode
|
153
|
+
@default_trace_mode = new_mode
|
154
|
+
elsif defined?(@default_trace_mode)
|
155
|
+
@default_trace_mode
|
156
|
+
elsif superclass.respond_to?(:default_trace_mode)
|
157
|
+
superclass.default_trace_mode
|
158
|
+
else
|
159
|
+
:default
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
147
163
|
def trace_class(new_class = nil)
|
148
164
|
if new_class
|
149
165
|
trace_mode(:default, new_class)
|
@@ -155,42 +171,66 @@ module GraphQL
|
|
155
171
|
end
|
156
172
|
|
157
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.
|
158
|
-
def trace_class_for(mode)
|
159
|
-
|
160
|
-
|
161
|
-
case mode
|
162
|
-
when :default
|
163
|
-
superclass_base_class = if superclass.respond_to?(:trace_class_for)
|
164
|
-
superclass.trace_class_for(mode)
|
165
|
-
else
|
166
|
-
GraphQL::Tracing::Trace
|
167
|
-
end
|
168
|
-
Class.new(superclass_base_class)
|
169
|
-
when :default_backtrace
|
170
|
-
schema_base_class = trace_class_for(:default)
|
171
|
-
Class.new(schema_base_class) do
|
172
|
-
include(GraphQL::Backtrace::Trace)
|
173
|
-
end
|
174
|
-
else
|
175
|
-
mods = trace_modules_for(mode)
|
176
|
-
Class.new(trace_class_for(:default)) do
|
177
|
-
mods.any? && include(*mods)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
174
|
+
def trace_class_for(mode, build: true)
|
175
|
+
own_trace_modes[mode] ||
|
176
|
+
(superclass.respond_to?(:trace_class_for) ? superclass.trace_class_for(mode, build: build) : (build ? (own_trace_modes[mode] = build_trace_mode(mode)) : nil))
|
181
177
|
end
|
182
178
|
|
183
179
|
# Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
|
184
|
-
#
|
180
|
+
# {default_trace_mode} is used when no `trace_mode: ...` is requested.
|
181
|
+
#
|
182
|
+
# When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
|
183
|
+
# unless `trace_mode` is explicitly given. (This class will not recieve any default trace modules.)
|
184
|
+
#
|
185
|
+
# Subclasses of the schema will use `trace_class` as a base class for this mode and those
|
186
|
+
# subclass also will _not_ receive default tracing modules.
|
187
|
+
#
|
185
188
|
# @param mode_name [Symbol]
|
186
189
|
# @param trace_class [Class] subclass of GraphQL::Tracing::Trace
|
187
190
|
# @return void
|
188
191
|
def trace_mode(mode_name, trace_class)
|
189
|
-
|
190
|
-
@trace_modes[mode_name] = trace_class
|
192
|
+
own_trace_modes[mode_name] = trace_class
|
191
193
|
nil
|
192
194
|
end
|
193
195
|
|
196
|
+
def own_trace_modes
|
197
|
+
@own_trace_modes ||= {}
|
198
|
+
end
|
199
|
+
|
200
|
+
module DefaultTraceClass
|
201
|
+
end
|
202
|
+
|
203
|
+
private_constant :DefaultTraceClass
|
204
|
+
|
205
|
+
def build_trace_mode(mode)
|
206
|
+
case mode
|
207
|
+
when :default
|
208
|
+
# Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class.
|
209
|
+
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || GraphQL::Tracing::Trace
|
210
|
+
Class.new(base_class) do
|
211
|
+
include DefaultTraceClass
|
212
|
+
end
|
213
|
+
when :default_backtrace
|
214
|
+
schema_base_class = trace_class_for(:default)
|
215
|
+
Class.new(schema_base_class) do
|
216
|
+
include(GraphQL::Backtrace::Trace)
|
217
|
+
end
|
218
|
+
else
|
219
|
+
# First, see if the superclass has a custom-defined class for this.
|
220
|
+
# 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, build: false)) || trace_class_for(:default)
|
222
|
+
# Prepare the default trace class if it hasn't been initialized yet
|
223
|
+
base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
|
224
|
+
mods = trace_modules_for(mode)
|
225
|
+
if base_class < DefaultTraceClass
|
226
|
+
mods = trace_modules_for(:default) + mods
|
227
|
+
end
|
228
|
+
Class.new(base_class) do
|
229
|
+
mods.any? && include(*mods)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
194
234
|
def own_trace_modules
|
195
235
|
@own_trace_modules ||= Hash.new { |h, k| h[k] = [] }
|
196
236
|
end
|
@@ -222,7 +262,7 @@ module GraphQL
|
|
222
262
|
# @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response
|
223
263
|
# @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects
|
224
264
|
# @return [Hash] GraphQL result
|
225
|
-
def as_json(
|
265
|
+
def as_json(context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
|
226
266
|
introspection_query = Introspection.query(
|
227
267
|
include_deprecated_args: include_deprecated_args,
|
228
268
|
include_schema_description: include_schema_description,
|
@@ -231,16 +271,14 @@ module GraphQL
|
|
231
271
|
include_specified_by_url: include_specified_by_url,
|
232
272
|
)
|
233
273
|
|
234
|
-
execute(introspection_query,
|
274
|
+
execute(introspection_query, context: context).to_h
|
235
275
|
end
|
236
276
|
|
237
277
|
# Return the GraphQL IDL for the schema
|
238
278
|
# @param context [Hash]
|
239
|
-
# @param only [<#call(member, ctx)>]
|
240
|
-
# @param except [<#call(member, ctx)>]
|
241
279
|
# @return [String]
|
242
|
-
def to_definition(
|
243
|
-
GraphQL::Schema::Printer.print_schema(self,
|
280
|
+
def to_definition(context: {})
|
281
|
+
GraphQL::Schema::Printer.print_schema(self, context: context)
|
244
282
|
end
|
245
283
|
|
246
284
|
# Return the GraphQL::Language::Document IDL AST for the schema
|
@@ -268,20 +306,6 @@ module GraphQL
|
|
268
306
|
@find_cache[path] ||= @finder.find(path)
|
269
307
|
end
|
270
308
|
|
271
|
-
def default_filter
|
272
|
-
GraphQL::Filter.new(except: default_mask)
|
273
|
-
end
|
274
|
-
|
275
|
-
def default_mask(new_mask = nil)
|
276
|
-
if new_mask
|
277
|
-
line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
|
278
|
-
GraphQL::Deprecation.warn("GraphQL::Filter and Schema.mask are deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).\n #{line}")
|
279
|
-
@own_default_mask = new_mask
|
280
|
-
else
|
281
|
-
@own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
309
|
def static_validator
|
286
310
|
GraphQL::StaticValidation::Validator.new(schema: self)
|
287
311
|
end
|
@@ -302,7 +326,7 @@ module GraphQL
|
|
302
326
|
# Build a map of `{ name => type }` and return it
|
303
327
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
304
328
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
305
|
-
def types(context = GraphQL::Query::NullContext)
|
329
|
+
def types(context = GraphQL::Query::NullContext.instance)
|
306
330
|
all_types = non_introspection_types.merge(introspection_system.types)
|
307
331
|
visible_types = {}
|
308
332
|
all_types.each do |k, v|
|
@@ -329,7 +353,7 @@ module GraphQL
|
|
329
353
|
|
330
354
|
# @param type_name [String]
|
331
355
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
332
|
-
def get_type(type_name, context = GraphQL::Query::NullContext)
|
356
|
+
def get_type(type_name, context = GraphQL::Query::NullContext.instance)
|
333
357
|
local_entry = own_types[type_name]
|
334
358
|
type_defn = case local_entry
|
335
359
|
when nil
|
@@ -360,6 +384,11 @@ module GraphQL
|
|
360
384
|
(superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
|
361
385
|
end
|
362
386
|
|
387
|
+
# @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
|
388
|
+
def has_defined_type?(type_name)
|
389
|
+
own_types.key?(type_name) || introspection_system.types.key?(type_name) || (superclass.respond_to?(:has_defined_type?) ? superclass.has_defined_type?(type_name) : false)
|
390
|
+
end
|
391
|
+
|
363
392
|
# @api private
|
364
393
|
attr_writer :connections
|
365
394
|
|
@@ -460,7 +489,7 @@ module GraphQL
|
|
460
489
|
# @param type [Module] The type definition whose possible types you want to see
|
461
490
|
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
462
491
|
# @return [Array<Module>] Possible types for `type`, if it's given.
|
463
|
-
def possible_types(type = nil, context = GraphQL::Query::NullContext)
|
492
|
+
def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
|
464
493
|
if type
|
465
494
|
# TODO duck-typing `.possible_types` would probably be nicer here
|
466
495
|
if type.kind.union?
|
@@ -513,18 +542,17 @@ module GraphQL
|
|
513
542
|
attr_writer :dataloader_class
|
514
543
|
|
515
544
|
def references_to(to_type = nil, from: nil)
|
516
|
-
@own_references_to ||=
|
545
|
+
@own_references_to ||= {}
|
517
546
|
if to_type
|
518
547
|
if !to_type.is_a?(String)
|
519
548
|
to_type = to_type.graphql_name
|
520
549
|
end
|
521
550
|
|
522
551
|
if from
|
523
|
-
@own_references_to[to_type]
|
552
|
+
refs = @own_references_to[to_type] ||= []
|
553
|
+
refs << from
|
524
554
|
else
|
525
|
-
|
526
|
-
inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY
|
527
|
-
own_refs + inherited_refs
|
555
|
+
get_references_to(to_type) || EMPTY_ARRAY
|
528
556
|
end
|
529
557
|
else
|
530
558
|
# `@own_references_to` can be quite large for big schemas,
|
@@ -544,7 +572,7 @@ module GraphQL
|
|
544
572
|
GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
|
545
573
|
end
|
546
574
|
|
547
|
-
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
|
575
|
+
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
|
548
576
|
parent_type = case type_or_name
|
549
577
|
when LateBoundType
|
550
578
|
get_type(type_or_name.name, context)
|
@@ -567,7 +595,7 @@ module GraphQL
|
|
567
595
|
end
|
568
596
|
end
|
569
597
|
|
570
|
-
def get_fields(type, context = GraphQL::Query::NullContext)
|
598
|
+
def get_fields(type, context = GraphQL::Query::NullContext.instance)
|
571
599
|
type.fields(context)
|
572
600
|
end
|
573
601
|
|
@@ -657,7 +685,7 @@ module GraphQL
|
|
657
685
|
else
|
658
686
|
string_or_document
|
659
687
|
end
|
660
|
-
query =
|
688
|
+
query = query_class.new(self, document: doc, context: context)
|
661
689
|
validator_opts = { schema: self }
|
662
690
|
rules && (validator_opts[:rules] = rules)
|
663
691
|
validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
|
@@ -665,6 +693,14 @@ module GraphQL
|
|
665
693
|
res[:errors]
|
666
694
|
end
|
667
695
|
|
696
|
+
def query_class(new_query_class = NOT_CONFIGURED)
|
697
|
+
if NOT_CONFIGURED.equal?(new_query_class)
|
698
|
+
@query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
|
699
|
+
else
|
700
|
+
@query_class = new_query_class
|
701
|
+
end
|
702
|
+
end
|
703
|
+
|
668
704
|
attr_writer :validate_max_errors
|
669
705
|
|
670
706
|
def validate_max_errors(new_validate_max_errors = nil)
|
@@ -717,9 +753,10 @@ module GraphQL
|
|
717
753
|
|
718
754
|
attr_writer :max_depth
|
719
755
|
|
720
|
-
def max_depth(new_max_depth = nil)
|
756
|
+
def max_depth(new_max_depth = nil, count_introspection_fields: true)
|
721
757
|
if new_max_depth
|
722
758
|
@max_depth = new_max_depth
|
759
|
+
@count_introspection_fields = count_introspection_fields
|
723
760
|
elsif defined?(@max_depth)
|
724
761
|
@max_depth
|
725
762
|
else
|
@@ -727,6 +764,14 @@ module GraphQL
|
|
727
764
|
end
|
728
765
|
end
|
729
766
|
|
767
|
+
def count_introspection_fields
|
768
|
+
if defined?(@count_introspection_fields)
|
769
|
+
@count_introspection_fields
|
770
|
+
else
|
771
|
+
find_inherited_value(:count_introspection_fields, true)
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
730
775
|
def disable_introspection_entry_points
|
731
776
|
@disable_introspection_entry_points = true
|
732
777
|
# TODO: this clears the cache made in `def types`. But this is not a great solution.
|
@@ -769,6 +814,26 @@ module GraphQL
|
|
769
814
|
end
|
770
815
|
end
|
771
816
|
|
817
|
+
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
818
|
+
# @return [Array<Module>] Type definitions added to this schema
|
819
|
+
def extra_types(*new_extra_types)
|
820
|
+
if new_extra_types.any?
|
821
|
+
new_extra_types = new_extra_types.flatten
|
822
|
+
@own_extra_types ||= []
|
823
|
+
@own_extra_types.concat(new_extra_types)
|
824
|
+
end
|
825
|
+
inherited_et = find_inherited_value(:extra_types, nil)
|
826
|
+
if inherited_et
|
827
|
+
if @own_extra_types
|
828
|
+
inherited_et + @own_extra_types
|
829
|
+
else
|
830
|
+
inherited_et
|
831
|
+
end
|
832
|
+
else
|
833
|
+
@own_extra_types || EMPTY_ARRAY
|
834
|
+
end
|
835
|
+
end
|
836
|
+
|
772
837
|
def orphan_types(*new_orphan_types)
|
773
838
|
if new_orphan_types.any?
|
774
839
|
new_orphan_types = new_orphan_types.flatten
|
@@ -776,7 +841,16 @@ module GraphQL
|
|
776
841
|
own_orphan_types.concat(new_orphan_types.flatten)
|
777
842
|
end
|
778
843
|
|
779
|
-
find_inherited_value(:orphan_types,
|
844
|
+
inherited_ot = find_inherited_value(:orphan_types, nil)
|
845
|
+
if inherited_ot
|
846
|
+
if own_orphan_types.any?
|
847
|
+
inherited_ot + own_orphan_types
|
848
|
+
else
|
849
|
+
inherited_ot
|
850
|
+
end
|
851
|
+
else
|
852
|
+
own_orphan_types
|
853
|
+
end
|
780
854
|
end
|
781
855
|
|
782
856
|
def default_execution_strategy
|
@@ -795,6 +869,26 @@ module GraphQL
|
|
795
869
|
end
|
796
870
|
end
|
797
871
|
|
872
|
+
def default_logger(new_default_logger = NOT_CONFIGURED)
|
873
|
+
if NOT_CONFIGURED.equal?(new_default_logger)
|
874
|
+
if defined?(@default_logger)
|
875
|
+
@default_logger
|
876
|
+
elsif superclass.respond_to?(:default_logger)
|
877
|
+
superclass.default_logger
|
878
|
+
elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger)
|
879
|
+
rails_logger
|
880
|
+
else
|
881
|
+
def_logger = Logger.new($stdout)
|
882
|
+
def_logger.info! # It doesn't output debug info by default
|
883
|
+
def_logger
|
884
|
+
end
|
885
|
+
elsif new_default_logger == nil
|
886
|
+
@default_logger = Logger.new(IO::NULL)
|
887
|
+
else
|
888
|
+
@default_logger = new_default_logger
|
889
|
+
end
|
890
|
+
end
|
891
|
+
|
798
892
|
def context_class(new_context_class = nil)
|
799
893
|
if new_context_class
|
800
894
|
@context_class = new_context_class
|
@@ -870,17 +964,19 @@ module GraphQL
|
|
870
964
|
end
|
871
965
|
|
872
966
|
def resolve_type(type, obj, ctx)
|
873
|
-
|
874
|
-
type
|
875
|
-
else
|
876
|
-
raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
|
877
|
-
end
|
967
|
+
raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types, Interface types, or `loads:` (tried to resolve: #{type.name})"
|
878
968
|
end
|
879
969
|
# rubocop:enable Lint/DuplicateMethods
|
880
970
|
|
881
971
|
def inherited(child_class)
|
882
972
|
if self == GraphQL::Schema
|
883
973
|
child_class.directives(default_directives.values)
|
974
|
+
child_class.extend(SubclassGetReferencesTo)
|
975
|
+
end
|
976
|
+
# Make sure the child class has these built out, so that
|
977
|
+
# subclasses can be modified by later calls to `trace_with`
|
978
|
+
own_trace_modes.each do |name, _class|
|
979
|
+
child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
|
884
980
|
end
|
885
981
|
child_class.singleton_class.prepend(ResolveTypeWithType)
|
886
982
|
super
|
@@ -968,6 +1064,12 @@ module GraphQL
|
|
968
1064
|
end
|
969
1065
|
|
970
1066
|
def instrument(instrument_step, instrumenter, options = {})
|
1067
|
+
warn <<~WARN
|
1068
|
+
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
1069
|
+
(From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
|
1070
|
+
|
1071
|
+
WARN
|
1072
|
+
trace_with(Tracing::LegacyHooksTrace)
|
971
1073
|
own_instrumenters[instrument_step] << instrumenter
|
972
1074
|
end
|
973
1075
|
|
@@ -978,7 +1080,12 @@ module GraphQL
|
|
978
1080
|
new_directives.flatten.each { |d| directive(d) }
|
979
1081
|
end
|
980
1082
|
|
981
|
-
find_inherited_value(:directives, default_directives)
|
1083
|
+
inherited_dirs = find_inherited_value(:directives, default_directives)
|
1084
|
+
if own_directives.any?
|
1085
|
+
inherited_dirs.merge(own_directives)
|
1086
|
+
else
|
1087
|
+
inherited_dirs
|
1088
|
+
end
|
982
1089
|
end
|
983
1090
|
|
984
1091
|
# Attach a single directive to this schema
|
@@ -994,11 +1101,13 @@ module GraphQL
|
|
994
1101
|
"skip" => GraphQL::Schema::Directive::Skip,
|
995
1102
|
"deprecated" => GraphQL::Schema::Directive::Deprecated,
|
996
1103
|
"oneOf" => GraphQL::Schema::Directive::OneOf,
|
1104
|
+
"specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
|
997
1105
|
}.freeze
|
998
1106
|
end
|
999
1107
|
|
1000
1108
|
def tracer(new_tracer)
|
1001
|
-
|
1109
|
+
default_trace = trace_class_for(:default)
|
1110
|
+
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
1002
1111
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
1003
1112
|
end
|
1004
1113
|
|
@@ -1020,10 +1129,20 @@ module GraphQL
|
|
1020
1129
|
if mode.is_a?(Array)
|
1021
1130
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
1022
1131
|
else
|
1023
|
-
tc =
|
1132
|
+
tc = own_trace_modes[mode] ||= build_trace_mode(mode)
|
1024
1133
|
tc.include(trace_mod)
|
1025
|
-
|
1026
|
-
|
1134
|
+
own_trace_modules[mode] << trace_mod
|
1135
|
+
|
1136
|
+
if mode == :default
|
1137
|
+
# This module is being added as a default tracer. If any other mode classes
|
1138
|
+
# have already been created, but get their default behavior from a superclass,
|
1139
|
+
# Then mix this into this schema's subclass.
|
1140
|
+
# (But don't mix it into mode classes that aren't default-based.)
|
1141
|
+
own_trace_modes.each do |other_mode_name, other_mode_class|
|
1142
|
+
if other_mode_class < DefaultTraceClass && !(other_mode_class < trace_mod)
|
1143
|
+
other_mode_class.include(trace_mod)
|
1144
|
+
end
|
1145
|
+
end
|
1027
1146
|
end
|
1028
1147
|
t_opts = trace_options_for(mode)
|
1029
1148
|
t_opts.merge!(options)
|
@@ -1046,6 +1165,8 @@ module GraphQL
|
|
1046
1165
|
|
1047
1166
|
# Create a trace instance which will include the trace modules specified for the optional mode.
|
1048
1167
|
#
|
1168
|
+
# If no `mode:` is given, then {default_trace_mode} will be used.
|
1169
|
+
#
|
1049
1170
|
# @param mode [Symbol] Trace modules for this trade mode will be included
|
1050
1171
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
1051
1172
|
# @return [Tracing::Trace]
|
@@ -1056,14 +1177,19 @@ module GraphQL
|
|
1056
1177
|
trace_mode = if mode
|
1057
1178
|
mode
|
1058
1179
|
elsif target && target.context[:backtrace]
|
1059
|
-
:
|
1180
|
+
if default_trace_mode != :default
|
1181
|
+
raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
|
1182
|
+
else
|
1183
|
+
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
1184
|
+
:default_backtrace
|
1185
|
+
end
|
1060
1186
|
else
|
1061
|
-
|
1187
|
+
default_trace_mode
|
1062
1188
|
end
|
1063
1189
|
|
1064
1190
|
base_trace_options = trace_options_for(trace_mode)
|
1065
1191
|
trace_options = base_trace_options.merge(options)
|
1066
|
-
trace_class_for_mode = trace_class_for(trace_mode)
|
1192
|
+
trace_class_for_mode = trace_class_for(trace_mode) || raise(ArgumentError, "#{self} has no trace class for mode: #{trace_mode.inspect}")
|
1067
1193
|
trace_class_for_mode.new(**trace_options)
|
1068
1194
|
end
|
1069
1195
|
|
@@ -1327,6 +1453,27 @@ module GraphQL
|
|
1327
1453
|
def own_multiplex_analyzers
|
1328
1454
|
@own_multiplex_analyzers ||= []
|
1329
1455
|
end
|
1456
|
+
|
1457
|
+
# This is overridden in subclasses to check the inheritance chain
|
1458
|
+
def get_references_to(type_name)
|
1459
|
+
@own_references_to[type_name]
|
1460
|
+
end
|
1461
|
+
end
|
1462
|
+
|
1463
|
+
module SubclassGetReferencesTo
|
1464
|
+
def get_references_to(type_name)
|
1465
|
+
own_refs = @own_references_to[type_name]
|
1466
|
+
inherited_refs = superclass.references_to(type_name)
|
1467
|
+
if inherited_refs&.any?
|
1468
|
+
if own_refs&.any?
|
1469
|
+
own_refs + inherited_refs
|
1470
|
+
else
|
1471
|
+
inherited_refs
|
1472
|
+
end
|
1473
|
+
else
|
1474
|
+
own_refs
|
1475
|
+
end
|
1476
|
+
end
|
1330
1477
|
end
|
1331
1478
|
|
1332
1479
|
# Install these here so that subclasses will also install it.
|
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
module StaticValidation
|
4
4
|
# Default rules for {GraphQL::StaticValidation::Validator}
|
5
5
|
#
|
6
|
-
# Order is important here. Some validators
|
6
|
+
# Order is important here. Some validators skip later hooks.
|
7
7
|
# which stops the visit on that node. That way it doesn't try to find fields on types that
|
8
8
|
# don't exist, etc.
|
9
9
|
ALL_RULES = [
|
@@ -111,7 +111,7 @@ module GraphQL
|
|
111
111
|
# that required fields are missing
|
112
112
|
required_field_names = @warden.arguments(type)
|
113
113
|
.select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
|
114
|
-
.map(&:name)
|
114
|
+
.map!(&:name)
|
115
115
|
|
116
116
|
present_field_names = ast_node.arguments.map(&:name)
|
117
117
|
missing_required_field_names = required_field_names - present_field_names
|
@@ -340,7 +340,7 @@ module GraphQL
|
|
340
340
|
selections.each do |node|
|
341
341
|
case node
|
342
342
|
when GraphQL::Language::Nodes::Field
|
343
|
-
definition = context.
|
343
|
+
definition = context.warden.get_field(owner_type, node.name)
|
344
344
|
fields << Field.new(node, definition, owner_type, parents)
|
345
345
|
when GraphQL::Language::Nodes::InlineFragment
|
346
346
|
fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
present_argument_names = ast_node.arguments.map(&:name)
|
22
22
|
required_argument_names = context.warden.arguments(defn)
|
23
23
|
.select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
|
24
|
-
.map(&:name)
|
24
|
+
.map!(&:name)
|
25
25
|
|
26
26
|
missing_names = required_argument_names - present_argument_names
|
27
27
|
if missing_names.any?
|
@@ -36,7 +36,7 @@ module GraphQL
|
|
36
36
|
|
37
37
|
required_fields = context.warden.arguments(parent_type)
|
38
38
|
.select{|arg| arg.type.kind.non_null?}
|
39
|
-
.map(&:graphql_name)
|
39
|
+
.map!(&:graphql_name)
|
40
40
|
|
41
41
|
present_fields = ast_node.arguments.map(&:name)
|
42
42
|
missing_fields = required_fields - present_fields
|
@@ -8,20 +8,20 @@ module GraphQL
|
|
8
8
|
# It provides access to the schema & fragments which validators may read from.
|
9
9
|
#
|
10
10
|
# It holds a list of errors which each validator may add to.
|
11
|
-
#
|
12
|
-
# It also provides limited access to the {TypeStack} instance,
|
13
|
-
# which tracks state as you climb in and out of different fields.
|
14
11
|
class ValidationContext
|
15
12
|
extend Forwardable
|
16
13
|
|
17
14
|
attr_reader :query, :errors, :visitor,
|
18
15
|
:on_dependency_resolve_handlers,
|
19
|
-
:max_errors
|
16
|
+
:max_errors, :warden, :schema
|
17
|
+
|
20
18
|
|
21
|
-
def_delegators :@query, :
|
19
|
+
def_delegators :@query, :document, :fragments, :operations
|
22
20
|
|
23
21
|
def initialize(query, visitor_class, max_errors)
|
24
22
|
@query = query
|
23
|
+
@warden = query.warden
|
24
|
+
@schema = query.schema
|
25
25
|
@literal_validator = LiteralValidator.new(context: query.context)
|
26
26
|
@errors = []
|
27
27
|
@max_errors = max_errors || Float::INFINITY
|
@@ -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
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "graphql/static_validation/error"
|
3
3
|
require "graphql/static_validation/definition_dependencies"
|
4
|
-
require "graphql/static_validation/type_stack"
|
5
4
|
require "graphql/static_validation/validator"
|
6
5
|
require "graphql/static_validation/validation_context"
|
7
6
|
require "graphql/static_validation/validation_timeout_error"
|
@@ -35,7 +35,7 @@ module GraphQL
|
|
35
35
|
# }
|
36
36
|
#
|
37
37
|
# result = MySchema.execute(
|
38
|
-
# query
|
38
|
+
# query,
|
39
39
|
# context: context,
|
40
40
|
# variables: variables,
|
41
41
|
# operation_name: operation_name
|
@@ -124,7 +124,8 @@ module GraphQL
|
|
124
124
|
# This subscription was re-evaluated.
|
125
125
|
# Send it to the specific stream where this client was waiting.
|
126
126
|
def deliver(subscription_id, result)
|
127
|
-
|
127
|
+
has_more = !result.context.namespace(:subscriptions)[:final_update]
|
128
|
+
payload = { result: result.to_h, more: has_more }
|
128
129
|
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
129
130
|
end
|
130
131
|
|