graphql 2.0.27 → 2.2.5
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/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 +15 -11
- data/lib/graphql/backtrace/trace.rb +12 -15
- 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 +1 -7
- data/lib/graphql/execution/lookahead.rb +88 -21
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- 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 +36 -35
- 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/connection.rb +28 -1
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- 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.rb +12 -21
- 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 +193 -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.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 -9
- 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/trace.rb +1 -0
- 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 +29 -22
- 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/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.
|
|
@@ -776,7 +821,16 @@ module GraphQL
|
|
|
776
821
|
own_orphan_types.concat(new_orphan_types.flatten)
|
|
777
822
|
end
|
|
778
823
|
|
|
779
|
-
find_inherited_value(:orphan_types,
|
|
824
|
+
inherited_ot = find_inherited_value(:orphan_types, nil)
|
|
825
|
+
if inherited_ot
|
|
826
|
+
if own_orphan_types.any?
|
|
827
|
+
inherited_ot + own_orphan_types
|
|
828
|
+
else
|
|
829
|
+
inherited_ot
|
|
830
|
+
end
|
|
831
|
+
else
|
|
832
|
+
own_orphan_types
|
|
833
|
+
end
|
|
780
834
|
end
|
|
781
835
|
|
|
782
836
|
def default_execution_strategy
|
|
@@ -795,6 +849,26 @@ module GraphQL
|
|
|
795
849
|
end
|
|
796
850
|
end
|
|
797
851
|
|
|
852
|
+
def default_logger(new_default_logger = NOT_CONFIGURED)
|
|
853
|
+
if NOT_CONFIGURED.equal?(new_default_logger)
|
|
854
|
+
if defined?(@default_logger)
|
|
855
|
+
@default_logger
|
|
856
|
+
elsif superclass.respond_to?(:default_logger)
|
|
857
|
+
superclass.default_logger
|
|
858
|
+
elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger)
|
|
859
|
+
rails_logger
|
|
860
|
+
else
|
|
861
|
+
def_logger = Logger.new($stdout)
|
|
862
|
+
def_logger.info! # It doesn't output debug info by default
|
|
863
|
+
def_logger
|
|
864
|
+
end
|
|
865
|
+
elsif new_default_logger == nil
|
|
866
|
+
@default_logger = Logger.new(IO::NULL)
|
|
867
|
+
else
|
|
868
|
+
@default_logger = new_default_logger
|
|
869
|
+
end
|
|
870
|
+
end
|
|
871
|
+
|
|
798
872
|
def context_class(new_context_class = nil)
|
|
799
873
|
if new_context_class
|
|
800
874
|
@context_class = new_context_class
|
|
@@ -870,17 +944,19 @@ module GraphQL
|
|
|
870
944
|
end
|
|
871
945
|
|
|
872
946
|
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
|
|
947
|
+
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
948
|
end
|
|
879
949
|
# rubocop:enable Lint/DuplicateMethods
|
|
880
950
|
|
|
881
951
|
def inherited(child_class)
|
|
882
952
|
if self == GraphQL::Schema
|
|
883
953
|
child_class.directives(default_directives.values)
|
|
954
|
+
child_class.extend(SubclassGetReferencesTo)
|
|
955
|
+
end
|
|
956
|
+
# Make sure the child class has these built out, so that
|
|
957
|
+
# subclasses can be modified by later calls to `trace_with`
|
|
958
|
+
own_trace_modes.each do |name, _class|
|
|
959
|
+
child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
|
|
884
960
|
end
|
|
885
961
|
child_class.singleton_class.prepend(ResolveTypeWithType)
|
|
886
962
|
super
|
|
@@ -978,7 +1054,12 @@ module GraphQL
|
|
|
978
1054
|
new_directives.flatten.each { |d| directive(d) }
|
|
979
1055
|
end
|
|
980
1056
|
|
|
981
|
-
find_inherited_value(:directives, default_directives)
|
|
1057
|
+
inherited_dirs = find_inherited_value(:directives, default_directives)
|
|
1058
|
+
if own_directives.any?
|
|
1059
|
+
inherited_dirs.merge(own_directives)
|
|
1060
|
+
else
|
|
1061
|
+
inherited_dirs
|
|
1062
|
+
end
|
|
982
1063
|
end
|
|
983
1064
|
|
|
984
1065
|
# Attach a single directive to this schema
|
|
@@ -994,11 +1075,13 @@ module GraphQL
|
|
|
994
1075
|
"skip" => GraphQL::Schema::Directive::Skip,
|
|
995
1076
|
"deprecated" => GraphQL::Schema::Directive::Deprecated,
|
|
996
1077
|
"oneOf" => GraphQL::Schema::Directive::OneOf,
|
|
1078
|
+
"specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
|
|
997
1079
|
}.freeze
|
|
998
1080
|
end
|
|
999
1081
|
|
|
1000
1082
|
def tracer(new_tracer)
|
|
1001
|
-
|
|
1083
|
+
default_trace = trace_class_for(:default)
|
|
1084
|
+
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
|
1002
1085
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
|
1003
1086
|
end
|
|
1004
1087
|
|
|
@@ -1020,10 +1103,20 @@ module GraphQL
|
|
|
1020
1103
|
if mode.is_a?(Array)
|
|
1021
1104
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
|
1022
1105
|
else
|
|
1023
|
-
tc =
|
|
1106
|
+
tc = own_trace_modes[mode] ||= build_trace_mode(mode)
|
|
1024
1107
|
tc.include(trace_mod)
|
|
1025
|
-
|
|
1026
|
-
|
|
1108
|
+
own_trace_modules[mode] << trace_mod
|
|
1109
|
+
|
|
1110
|
+
if mode == :default
|
|
1111
|
+
# This module is being added as a default tracer. If any other mode classes
|
|
1112
|
+
# have already been created, but get their default behavior from a superclass,
|
|
1113
|
+
# Then mix this into this schema's subclass.
|
|
1114
|
+
# (But don't mix it into mode classes that aren't default-based.)
|
|
1115
|
+
own_trace_modes.each do |other_mode_name, other_mode_class|
|
|
1116
|
+
if other_mode_class < DefaultTraceClass && !(other_mode_class < trace_mod)
|
|
1117
|
+
other_mode_class.include(trace_mod)
|
|
1118
|
+
end
|
|
1119
|
+
end
|
|
1027
1120
|
end
|
|
1028
1121
|
t_opts = trace_options_for(mode)
|
|
1029
1122
|
t_opts.merge!(options)
|
|
@@ -1046,6 +1139,8 @@ module GraphQL
|
|
|
1046
1139
|
|
|
1047
1140
|
# Create a trace instance which will include the trace modules specified for the optional mode.
|
|
1048
1141
|
#
|
|
1142
|
+
# If no `mode:` is given, then {default_trace_mode} will be used.
|
|
1143
|
+
#
|
|
1049
1144
|
# @param mode [Symbol] Trace modules for this trade mode will be included
|
|
1050
1145
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
|
1051
1146
|
# @return [Tracing::Trace]
|
|
@@ -1056,14 +1151,19 @@ module GraphQL
|
|
|
1056
1151
|
trace_mode = if mode
|
|
1057
1152
|
mode
|
|
1058
1153
|
elsif target && target.context[:backtrace]
|
|
1059
|
-
:
|
|
1154
|
+
if default_trace_mode != :default
|
|
1155
|
+
raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
|
|
1156
|
+
else
|
|
1157
|
+
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
|
1158
|
+
:default_backtrace
|
|
1159
|
+
end
|
|
1060
1160
|
else
|
|
1061
|
-
|
|
1161
|
+
default_trace_mode
|
|
1062
1162
|
end
|
|
1063
1163
|
|
|
1064
1164
|
base_trace_options = trace_options_for(trace_mode)
|
|
1065
1165
|
trace_options = base_trace_options.merge(options)
|
|
1066
|
-
trace_class_for_mode = trace_class_for(trace_mode)
|
|
1166
|
+
trace_class_for_mode = trace_class_for(trace_mode) || raise(ArgumentError, "#{self} has no trace class for mode: #{trace_mode.inspect}")
|
|
1067
1167
|
trace_class_for_mode.new(**trace_options)
|
|
1068
1168
|
end
|
|
1069
1169
|
|
|
@@ -1327,6 +1427,27 @@ module GraphQL
|
|
|
1327
1427
|
def own_multiplex_analyzers
|
|
1328
1428
|
@own_multiplex_analyzers ||= []
|
|
1329
1429
|
end
|
|
1430
|
+
|
|
1431
|
+
# This is overridden in subclasses to check the inheritance chain
|
|
1432
|
+
def get_references_to(type_name)
|
|
1433
|
+
@own_references_to[type_name]
|
|
1434
|
+
end
|
|
1435
|
+
end
|
|
1436
|
+
|
|
1437
|
+
module SubclassGetReferencesTo
|
|
1438
|
+
def get_references_to(type_name)
|
|
1439
|
+
own_refs = @own_references_to[type_name]
|
|
1440
|
+
inherited_refs = superclass.references_to(type_name)
|
|
1441
|
+
if inherited_refs&.any?
|
|
1442
|
+
if own_refs&.any?
|
|
1443
|
+
own_refs + inherited_refs
|
|
1444
|
+
else
|
|
1445
|
+
inherited_refs
|
|
1446
|
+
end
|
|
1447
|
+
else
|
|
1448
|
+
own_refs
|
|
1449
|
+
end
|
|
1450
|
+
end
|
|
1330
1451
|
end
|
|
1331
1452
|
|
|
1332
1453
|
# 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
|
|
@@ -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
|
|
|
@@ -37,7 +37,7 @@ module GraphQL
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
# @return [String] an identifier for this unit of subscription
|
|
40
|
-
def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext)
|
|
40
|
+
def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext.instance)
|
|
41
41
|
subscription = field.resolver || GraphQL::Schema::Subscription
|
|
42
42
|
normalized_args = stringify_args(field, arguments.to_h, context)
|
|
43
43
|
subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
|
|
@@ -126,7 +126,13 @@ module GraphQL
|
|
|
126
126
|
when GraphQL::Schema::InputObject
|
|
127
127
|
stringify_args(arg_owner, args.to_h, context)
|
|
128
128
|
else
|
|
129
|
-
|
|
129
|
+
if arg_owner.is_a?(Class) && arg_owner < GraphQL::Schema::Enum
|
|
130
|
+
# `prepare:` may have made the value something other than
|
|
131
|
+
# a defined value of this enum -- use _that_ in this case.
|
|
132
|
+
arg_owner.coerce_isolated_input(args) || args
|
|
133
|
+
else
|
|
134
|
+
args
|
|
135
|
+
end
|
|
130
136
|
end
|
|
131
137
|
end
|
|
132
138
|
|