graphql 2.0.27 → 2.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +3 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +6 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +2 -0
- data/lib/graphql/analysis/ast/analyzer.rb +7 -0
- data/lib/graphql/analysis/ast/field_usage.rb +32 -7
- data/lib/graphql/analysis/ast/query_complexity.rb +80 -128
- data/lib/graphql/analysis/ast/query_depth.rb +7 -2
- data/lib/graphql/analysis/ast/visitor.rb +2 -2
- data/lib/graphql/analysis/ast.rb +21 -11
- data/lib/graphql/backtrace/trace.rb +12 -15
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +85 -0
- data/lib/graphql/dataloader/source.rb +11 -3
- data/lib/graphql/dataloader.rb +109 -142
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +70 -248
- data/lib/graphql/execution/interpreter.rb +91 -157
- data/lib/graphql/execution/lookahead.rb +88 -21
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +37 -37
- data/lib/graphql/language/lexer.rb +271 -177
- data/lib/graphql/language/nodes.rb +74 -56
- data/lib/graphql/language/parser.rb +697 -1986
- data/lib/graphql/language/printer.rb +299 -146
- data/lib/graphql/language/sanitized_printer.rb +20 -22
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +20 -81
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +3 -3
- data/lib/graphql/pagination/connection.rb +28 -1
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/pagination/relation_connection.rb +3 -3
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +36 -98
- data/lib/graphql/query/null_context.rb +4 -11
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +13 -22
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/schema/argument.rb +6 -1
- data/lib/graphql/schema/build_from_definition.rb +0 -11
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive.rb +1 -1
- data/lib/graphql/schema/enum.rb +3 -3
- data/lib/graphql/schema/field/connection_extension.rb +1 -15
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +8 -5
- data/lib/graphql/schema/has_single_input_argument.rb +156 -0
- data/lib/graphql/schema/input_object.rb +2 -2
- data/lib/graphql/schema/interface.rb +10 -10
- data/lib/graphql/schema/introspection_system.rb +2 -0
- data/lib/graphql/schema/loader.rb +0 -2
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +61 -38
- data/lib/graphql/schema/member/has_fields.rb +8 -5
- data/lib/graphql/schema/member/has_interfaces.rb +23 -9
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +8 -7
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
- data/lib/graphql/schema/resolver.rb +7 -3
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/warden.rb +96 -94
- data/lib/graphql/schema.rb +219 -72
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +1 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +5 -5
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
- data/lib/graphql/subscriptions/event.rb +8 -2
- data/lib/graphql/subscriptions.rb +14 -12
- data/lib/graphql/testing/helpers.rb +125 -0
- data/lib/graphql/testing.rb +2 -0
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/tracing/data_dog_trace.rb +21 -34
- data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +2 -0
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/sentry_trace.rb +94 -0
- data/lib/graphql/tracing/trace.rb +1 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -3
- data/readme.md +12 -2
- metadata +33 -25
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/language/parser.y +0 -560
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
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
|
|