graphql 2.3.4 → 2.3.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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
- data/lib/graphql/analysis/visitor.rb +282 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/dataloader/async_dataloader.rb +2 -0
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +7 -4
- data/lib/graphql/execution/interpreter/runtime.rb +40 -59
- data/lib/graphql/execution/interpreter.rb +2 -2
- data/lib/graphql/language/nodes.rb +17 -22
- data/lib/graphql/language/parser.rb +54 -13
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query.rb +1 -1
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/schema/addition.rb +21 -11
- data/lib/graphql/schema/argument.rb +19 -5
- data/lib/graphql/schema/directive.rb +2 -0
- data/lib/graphql/schema/field.rb +8 -0
- data/lib/graphql/schema/has_single_input_argument.rb +1 -0
- data/lib/graphql/schema/input_object.rb +1 -0
- data/lib/graphql/schema/introspection_system.rb +2 -2
- data/lib/graphql/schema/late_bound_type.rb +4 -0
- data/lib/graphql/schema/list.rb +2 -2
- data/lib/graphql/schema/member/has_arguments.rb +2 -35
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -2
- data/lib/graphql/schema/resolver.rb +1 -0
- data/lib/graphql/schema/warden.rb +2 -3
- data/lib/graphql/schema.rb +20 -20
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/type_kinds.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +0 -8
- metadata +10 -11
- data/lib/graphql/analysis/ast/analyzer.rb +0 -91
- data/lib/graphql/analysis/ast/field_usage.rb +0 -84
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -185
- data/lib/graphql/analysis/ast/visitor.rb +0 -284
- data/lib/graphql/analysis/ast.rb +0 -94
data/lib/graphql/schema/field.rb
CHANGED
@@ -47,6 +47,7 @@ module GraphQL
|
|
47
47
|
def dummy
|
48
48
|
@dummy ||= begin
|
49
49
|
d = Class.new(GraphQL::Schema::Resolver)
|
50
|
+
d.graphql_name "#{self.graphql_name}DummyResolver"
|
50
51
|
d.argument_class(self.argument_class)
|
51
52
|
# TODO make this lazier?
|
52
53
|
d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
|
@@ -25,10 +25,10 @@ module GraphQL
|
|
25
25
|
load_constant(:DirectiveLocationEnum)
|
26
26
|
]
|
27
27
|
@types = {}
|
28
|
-
@possible_types = {}
|
28
|
+
@possible_types = {}.tap(&:compare_by_identity)
|
29
29
|
type_defns.each do |t|
|
30
30
|
@types[t.graphql_name] = t
|
31
|
-
@possible_types[t
|
31
|
+
@possible_types[t] = [t]
|
32
32
|
end
|
33
33
|
@entry_point_fields =
|
34
34
|
if schema.disable_introspection_entry_points?
|
data/lib/graphql/schema/list.rb
CHANGED
@@ -52,7 +52,7 @@ module GraphQL
|
|
52
52
|
unless item_result.valid?
|
53
53
|
if max_errors
|
54
54
|
if max_errors == 0
|
55
|
-
|
55
|
+
add_max_errors_reached_message(result)
|
56
56
|
break
|
57
57
|
end
|
58
58
|
|
@@ -76,7 +76,7 @@ module GraphQL
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
def
|
79
|
+
def add_max_errors_reached_message(result)
|
80
80
|
message = "Too many errors processing list variable, max validation error limit reached. Execution aborted"
|
81
81
|
item_result = GraphQL::Query::InputValidationResult.from_problem(message)
|
82
82
|
result.merge_result!(nil, item_result)
|
@@ -38,39 +38,6 @@ module GraphQL
|
|
38
38
|
end
|
39
39
|
arg_defn = self.argument_class.new(*args, **kwargs, &block)
|
40
40
|
add_argument(arg_defn)
|
41
|
-
|
42
|
-
if self.is_a?(Class) && !method_defined?(:"load_#{arg_defn.keyword}")
|
43
|
-
method_owner = if self < GraphQL::Schema::InputObject || self < GraphQL::Schema::Directive
|
44
|
-
"self."
|
45
|
-
elsif self < GraphQL::Schema::Resolver
|
46
|
-
""
|
47
|
-
else
|
48
|
-
raise "Unexpected argument owner: #{self}"
|
49
|
-
end
|
50
|
-
if loads && arg_defn.type.list?
|
51
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
52
|
-
def #{method_owner}load_#{arg_defn.keyword}(values, context = nil)
|
53
|
-
argument = get_argument("#{arg_defn.graphql_name}", context || self.context)
|
54
|
-
(context || self.context).query.after_lazy(values) do |values2|
|
55
|
-
GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, value, context || self.context) })
|
56
|
-
end
|
57
|
-
end
|
58
|
-
RUBY
|
59
|
-
elsif loads
|
60
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
61
|
-
def #{method_owner}load_#{arg_defn.keyword}(value, context = nil)
|
62
|
-
argument = get_argument("#{arg_defn.graphql_name}", context || self.context)
|
63
|
-
load_application_object(argument, value, context || self.context)
|
64
|
-
end
|
65
|
-
RUBY
|
66
|
-
else
|
67
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
68
|
-
def #{method_owner}load_#{arg_defn.keyword}(value, _context = nil)
|
69
|
-
value
|
70
|
-
end
|
71
|
-
RUBY
|
72
|
-
end
|
73
|
-
end
|
74
41
|
arg_defn
|
75
42
|
end
|
76
43
|
|
@@ -259,8 +226,8 @@ module GraphQL
|
|
259
226
|
#
|
260
227
|
# @param values [Hash<String, Object>]
|
261
228
|
# @param context [GraphQL::Query::Context]
|
262
|
-
# @yield [Interpreter::Arguments, Execution::Lazy<
|
263
|
-
# @return [Interpreter::Arguments, Execution::Lazy<
|
229
|
+
# @yield [Interpreter::Arguments, Execution::Lazy<Interpreter::Arguments>]
|
230
|
+
# @return [Interpreter::Arguments, Execution::Lazy<Interpreter::Arguments>]
|
264
231
|
def coerce_arguments(parent_object, values, context, &block)
|
265
232
|
# Cache this hash to avoid re-merging it
|
266
233
|
arg_defns = context.warden.arguments(self)
|
@@ -91,7 +91,7 @@ module GraphQL
|
|
91
91
|
private
|
92
92
|
|
93
93
|
# Modify `target` by adding items from `dirs` such that:
|
94
|
-
# - Any name conflict is
|
94
|
+
# - Any name conflict is overridden by the incoming member of `dirs`
|
95
95
|
# - Any other member of `dirs` is appended
|
96
96
|
# @param target [Array<GraphQL::Schema::Directive>]
|
97
97
|
# @param dirs [Array<GraphQL::Schema::Directive>]
|
@@ -76,7 +76,7 @@ module GraphQL
|
|
76
76
|
|
77
77
|
private
|
78
78
|
|
79
|
-
# If one of
|
79
|
+
# If one of these values is accessed, initialize all the instance variables to retain
|
80
80
|
# a consistent object shape.
|
81
81
|
def initialize_relay_metadata
|
82
82
|
if !defined?(@connection_type)
|
@@ -4,12 +4,11 @@ module GraphQL
|
|
4
4
|
class Schema
|
5
5
|
class Member
|
6
6
|
module TypeSystemHelpers
|
7
|
-
def initialize(
|
7
|
+
def initialize(...)
|
8
8
|
super
|
9
9
|
@to_non_null_type ||= nil
|
10
10
|
@to_list_type ||= nil
|
11
11
|
end
|
12
|
-
ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
|
13
12
|
|
14
13
|
# @return [Schema::NonNull] Make a non-null-type representation of this type
|
15
14
|
def to_non_null_type
|
@@ -25,6 +25,7 @@ module GraphQL
|
|
25
25
|
extend GraphQL::Schema::Member::HasValidators
|
26
26
|
include Schema::Member::HasPath
|
27
27
|
extend Schema::Member::HasPath
|
28
|
+
extend Schema::Member::HasDirectives
|
28
29
|
|
29
30
|
# @param object [Object] The application object that this field is being resolved on
|
30
31
|
# @param context [GraphQL::Query::Context]
|
@@ -295,7 +295,7 @@ module GraphQL
|
|
295
295
|
true
|
296
296
|
else
|
297
297
|
if @context.respond_to?(:logger) && (logger = @context.logger)
|
298
|
-
logger.debug { "Interface `#{type_defn.graphql_name}` hidden because it has no visible
|
298
|
+
logger.debug { "Interface `#{type_defn.graphql_name}` hidden because it has no visible implementers" }
|
299
299
|
end
|
300
300
|
false
|
301
301
|
end
|
@@ -363,8 +363,7 @@ module GraphQL
|
|
363
363
|
end
|
364
364
|
|
365
365
|
def referenced?(type_defn)
|
366
|
-
|
367
|
-
members = @schema.references_to(graphql_name)
|
366
|
+
members = @schema.references_to(type_defn)
|
368
367
|
members.any? { |m| visible?(m) }
|
369
368
|
end
|
370
369
|
|
data/lib/graphql/schema.rb
CHANGED
@@ -187,7 +187,7 @@ module GraphQL
|
|
187
187
|
# {default_trace_mode} is used when no `trace_mode: ...` is requested.
|
188
188
|
#
|
189
189
|
# When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
|
190
|
-
# unless `trace_mode` is explicitly given. (This class will not
|
190
|
+
# unless `trace_mode` is explicitly given. (This class will not receive any default trace modules.)
|
191
191
|
#
|
192
192
|
# Subclasses of the schema will use `trace_class` as a base class for this mode and those
|
193
193
|
# subclass also will _not_ receive default tracing modules.
|
@@ -506,7 +506,7 @@ module GraphQL
|
|
506
506
|
if type.kind.union?
|
507
507
|
type.possible_types(context: context)
|
508
508
|
else
|
509
|
-
stored_possible_types = own_possible_types[type
|
509
|
+
stored_possible_types = own_possible_types[type]
|
510
510
|
visible_possible_types = if stored_possible_types && type.kind.interface?
|
511
511
|
stored_possible_types.select do |possible_type|
|
512
512
|
possible_type.interfaces(context).include?(type)
|
@@ -515,7 +515,7 @@ module GraphQL
|
|
515
515
|
stored_possible_types
|
516
516
|
end
|
517
517
|
visible_possible_types ||
|
518
|
-
introspection_system.possible_types[type
|
518
|
+
introspection_system.possible_types[type] ||
|
519
519
|
(
|
520
520
|
superclass.respond_to?(:possible_types) ?
|
521
521
|
superclass.possible_types(type, context) :
|
@@ -553,14 +553,9 @@ module GraphQL
|
|
553
553
|
attr_writer :dataloader_class
|
554
554
|
|
555
555
|
def references_to(to_type = nil, from: nil)
|
556
|
-
@own_references_to ||= {}
|
557
556
|
if to_type
|
558
|
-
if !to_type.is_a?(String)
|
559
|
-
to_type = to_type.graphql_name
|
560
|
-
end
|
561
|
-
|
562
557
|
if from
|
563
|
-
refs =
|
558
|
+
refs = own_references_to[to_type] ||= []
|
564
559
|
refs << from
|
565
560
|
else
|
566
561
|
get_references_to(to_type) || EMPTY_ARRAY
|
@@ -571,9 +566,9 @@ module GraphQL
|
|
571
566
|
# So optimize the most common case -- don't create a duplicate Hash.
|
572
567
|
inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
|
573
568
|
if inherited_value.any?
|
574
|
-
inherited_value.merge(
|
569
|
+
inherited_value.merge(own_references_to)
|
575
570
|
else
|
576
|
-
|
571
|
+
own_references_to
|
577
572
|
end
|
578
573
|
end
|
579
574
|
end
|
@@ -1287,7 +1282,7 @@ module GraphQL
|
|
1287
1282
|
|
1288
1283
|
# Execute a query on itself.
|
1289
1284
|
# @see {Query#initialize} for arguments.
|
1290
|
-
# @return [
|
1285
|
+
# @return [GraphQL::Query::Result] query result, ready to be serialized as JSON
|
1291
1286
|
def execute(query_str = nil, **kwargs)
|
1292
1287
|
if query_str
|
1293
1288
|
kwargs[:query] = query_str
|
@@ -1327,7 +1322,7 @@ module GraphQL
|
|
1327
1322
|
# @see {Execution::Multiplex#run_all} for multiplex keyword arguments
|
1328
1323
|
# @param queries [Array<Hash>] Keyword arguments for each query
|
1329
1324
|
# @param context [Hash] Multiplex-level context
|
1330
|
-
# @return [Array<
|
1325
|
+
# @return [Array<GraphQL::Query::Result>] One result for each query in the input
|
1331
1326
|
def multiplex(queries, **kwargs)
|
1332
1327
|
GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
|
1333
1328
|
end
|
@@ -1460,7 +1455,8 @@ module GraphQL
|
|
1460
1455
|
own_union_memberships.merge!(addition.union_memberships)
|
1461
1456
|
|
1462
1457
|
addition.references.each { |thing, pointers|
|
1463
|
-
|
1458
|
+
prev_refs = own_references_to[thing] || []
|
1459
|
+
own_references_to[thing] = prev_refs | pointers.to_a
|
1464
1460
|
}
|
1465
1461
|
|
1466
1462
|
addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }
|
@@ -1488,6 +1484,10 @@ module GraphQL
|
|
1488
1484
|
@own_types ||= {}
|
1489
1485
|
end
|
1490
1486
|
|
1487
|
+
def own_references_to
|
1488
|
+
@own_references_to ||= {}.tap(&:compare_by_identity)
|
1489
|
+
end
|
1490
|
+
|
1491
1491
|
def non_introspection_types
|
1492
1492
|
find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
|
1493
1493
|
end
|
@@ -1501,7 +1501,7 @@ module GraphQL
|
|
1501
1501
|
end
|
1502
1502
|
|
1503
1503
|
def own_possible_types
|
1504
|
-
@own_possible_types ||= {}
|
1504
|
+
@own_possible_types ||= {}.tap(&:compare_by_identity)
|
1505
1505
|
end
|
1506
1506
|
|
1507
1507
|
def own_union_memberships
|
@@ -1529,15 +1529,15 @@ module GraphQL
|
|
1529
1529
|
end
|
1530
1530
|
|
1531
1531
|
# This is overridden in subclasses to check the inheritance chain
|
1532
|
-
def get_references_to(
|
1533
|
-
|
1532
|
+
def get_references_to(type_defn)
|
1533
|
+
own_references_to[type_defn]
|
1534
1534
|
end
|
1535
1535
|
end
|
1536
1536
|
|
1537
1537
|
module SubclassGetReferencesTo
|
1538
|
-
def get_references_to(
|
1539
|
-
own_refs =
|
1540
|
-
inherited_refs = superclass.references_to(
|
1538
|
+
def get_references_to(type_defn)
|
1539
|
+
own_refs = own_references_to[type_defn]
|
1540
|
+
inherited_refs = superclass.references_to(type_defn)
|
1541
1541
|
if inherited_refs&.any?
|
1542
1542
|
if own_refs&.any?
|
1543
1543
|
own_refs + inherited_refs
|
@@ -396,7 +396,7 @@ module GraphQL
|
|
396
396
|
end
|
397
397
|
|
398
398
|
# Given two list of parents, find out if they are mutually exclusive
|
399
|
-
# In this context, `parents`
|
399
|
+
# In this context, `parents` represents the "self scope" of the field,
|
400
400
|
# what types may be found at this point in the query.
|
401
401
|
def mutually_exclusive?(parents1, parents2)
|
402
402
|
if parents1.empty? || parents2.empty?
|
@@ -107,7 +107,7 @@ module GraphQL
|
|
107
107
|
when 2
|
108
108
|
true
|
109
109
|
else
|
110
|
-
raise ArgumentError, "#{@serializer} must
|
110
|
+
raise ArgumentError, "#{@serializer} must respond to `.load` accepting one or two arguments"
|
111
111
|
end
|
112
112
|
@transmit_ns = namespace
|
113
113
|
super
|
@@ -9,7 +9,7 @@ module GraphQL
|
|
9
9
|
# Assign the result to `context.namespace(:subscriptions)[:subscription_broadcastable]`
|
10
10
|
# @api private
|
11
11
|
# @see Subscriptions#broadcastable? for a public API
|
12
|
-
class BroadcastAnalyzer < GraphQL::Analysis::
|
12
|
+
class BroadcastAnalyzer < GraphQL::Analysis::Analyzer
|
13
13
|
def initialize(subject)
|
14
14
|
super
|
15
15
|
@default_broadcastable = subject.schema.subscriptions.default_broadcastable
|
@@ -235,7 +235,7 @@ module GraphQL
|
|
235
235
|
if !query.valid?
|
236
236
|
raise "Invalid query: #{query.validation_errors.map(&:to_h).inspect}"
|
237
237
|
end
|
238
|
-
GraphQL::Analysis
|
238
|
+
GraphQL::Analysis.analyze_query(query, @schema.query_analyzers)
|
239
239
|
query.context.namespace(:subscriptions)[:subscription_broadcastable]
|
240
240
|
end
|
241
241
|
|
data/lib/graphql/type_kinds.rb
CHANGED
@@ -16,7 +16,7 @@ module GraphQL
|
|
16
16
|
@description = description
|
17
17
|
end
|
18
18
|
|
19
|
-
# Does this TypeKind have multiple possible
|
19
|
+
# Does this TypeKind have multiple possible implementers?
|
20
20
|
# @deprecated Use `abstract?` instead of `resolves?`.
|
21
21
|
def resolves?; @abstract; end
|
22
22
|
# Is this TypeKind abstract?
|
data/lib/graphql/version.rb
CHANGED
data/lib/graphql.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.3.
|
4
|
+
version: 2.3.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -306,14 +306,13 @@ files:
|
|
306
306
|
- lib/generators/graphql/union_generator.rb
|
307
307
|
- lib/graphql.rb
|
308
308
|
- lib/graphql/analysis.rb
|
309
|
-
- lib/graphql/analysis/
|
310
|
-
- lib/graphql/analysis/
|
311
|
-
- lib/graphql/analysis/
|
312
|
-
- lib/graphql/analysis/
|
313
|
-
- lib/graphql/analysis/
|
314
|
-
- lib/graphql/analysis/
|
315
|
-
- lib/graphql/analysis/
|
316
|
-
- lib/graphql/analysis/ast/visitor.rb
|
309
|
+
- lib/graphql/analysis/analyzer.rb
|
310
|
+
- lib/graphql/analysis/field_usage.rb
|
311
|
+
- lib/graphql/analysis/max_query_complexity.rb
|
312
|
+
- lib/graphql/analysis/max_query_depth.rb
|
313
|
+
- lib/graphql/analysis/query_complexity.rb
|
314
|
+
- lib/graphql/analysis/query_depth.rb
|
315
|
+
- lib/graphql/analysis/visitor.rb
|
317
316
|
- lib/graphql/analysis_error.rb
|
318
317
|
- lib/graphql/backtrace.rb
|
319
318
|
- lib/graphql/backtrace/inspect_result.rb
|
@@ -645,7 +644,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
645
644
|
- !ruby/object:Gem::Version
|
646
645
|
version: '0'
|
647
646
|
requirements: []
|
648
|
-
rubygems_version: 3.5.
|
647
|
+
rubygems_version: 3.5.12
|
649
648
|
signing_key:
|
650
649
|
specification_version: 4
|
651
650
|
summary: A GraphQL language and runtime for Ruby
|
@@ -1,91 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Analysis
|
4
|
-
module AST
|
5
|
-
# Query analyzer for query ASTs. Query analyzers respond to visitor style methods
|
6
|
-
# but are prefixed by `enter` and `leave`.
|
7
|
-
#
|
8
|
-
# When an analyzer is initialized with a Multiplex, you can always get the current query from
|
9
|
-
# `visitor.query` in the visit methods.
|
10
|
-
#
|
11
|
-
# @param [GraphQL::Query, GraphQL::Execution::Multiplex] The query or multiplex to analyze
|
12
|
-
class Analyzer
|
13
|
-
def initialize(subject)
|
14
|
-
@subject = subject
|
15
|
-
|
16
|
-
if subject.is_a?(GraphQL::Query)
|
17
|
-
@query = subject
|
18
|
-
@multiplex = nil
|
19
|
-
else
|
20
|
-
@multiplex = subject
|
21
|
-
@query = nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# Analyzer hook to decide at analysis time whether a query should
|
26
|
-
# be analyzed or not.
|
27
|
-
# @return [Boolean] If the query should be analyzed or not
|
28
|
-
def analyze?
|
29
|
-
true
|
30
|
-
end
|
31
|
-
|
32
|
-
# Analyzer hook to decide at analysis time whether analysis
|
33
|
-
# requires a visitor pass; can be disabled for precomputed results.
|
34
|
-
# @return [Boolean] If analysis requires visitation or not
|
35
|
-
def visit?
|
36
|
-
true
|
37
|
-
end
|
38
|
-
|
39
|
-
# The result for this analyzer. Returning {GraphQL::AnalysisError} results
|
40
|
-
# in a query error.
|
41
|
-
# @return [Any] The analyzer result
|
42
|
-
def result
|
43
|
-
raise GraphQL::RequiredImplementationMissingError
|
44
|
-
end
|
45
|
-
|
46
|
-
class << self
|
47
|
-
private
|
48
|
-
|
49
|
-
def build_visitor_hooks(member_name)
|
50
|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
51
|
-
def on_enter_#{member_name}(node, parent, visitor)
|
52
|
-
end
|
53
|
-
|
54
|
-
def on_leave_#{member_name}(node, parent, visitor)
|
55
|
-
end
|
56
|
-
EOS
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
build_visitor_hooks :argument
|
61
|
-
build_visitor_hooks :directive
|
62
|
-
build_visitor_hooks :document
|
63
|
-
build_visitor_hooks :enum
|
64
|
-
build_visitor_hooks :field
|
65
|
-
build_visitor_hooks :fragment_spread
|
66
|
-
build_visitor_hooks :inline_fragment
|
67
|
-
build_visitor_hooks :input_object
|
68
|
-
build_visitor_hooks :list_type
|
69
|
-
build_visitor_hooks :non_null_type
|
70
|
-
build_visitor_hooks :null_value
|
71
|
-
build_visitor_hooks :operation_definition
|
72
|
-
build_visitor_hooks :type_name
|
73
|
-
build_visitor_hooks :variable_definition
|
74
|
-
build_visitor_hooks :variable_identifier
|
75
|
-
build_visitor_hooks :abstract_node
|
76
|
-
|
77
|
-
protected
|
78
|
-
|
79
|
-
# @return [GraphQL::Query, GraphQL::Execution::Multiplex] Whatever this analyzer is analyzing
|
80
|
-
attr_reader :subject
|
81
|
-
|
82
|
-
# @return [GraphQL::Query, nil] `nil` if this analyzer is visiting a multiplex
|
83
|
-
# (When this is `nil`, use `visitor.query` inside visit methods to get the current query)
|
84
|
-
attr_reader :query
|
85
|
-
|
86
|
-
# @return [GraphQL::Execution::Multiplex, nil] `nil` if this analyzer is visiting a query
|
87
|
-
attr_reader :multiplex
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Analysis
|
4
|
-
module AST
|
5
|
-
class FieldUsage < Analyzer
|
6
|
-
def initialize(query)
|
7
|
-
super
|
8
|
-
@used_fields = Set.new
|
9
|
-
@used_deprecated_fields = Set.new
|
10
|
-
@used_deprecated_arguments = Set.new
|
11
|
-
@used_deprecated_enum_values = Set.new
|
12
|
-
end
|
13
|
-
|
14
|
-
def on_leave_field(node, parent, visitor)
|
15
|
-
field_defn = visitor.field_definition
|
16
|
-
field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}"
|
17
|
-
@used_fields << field
|
18
|
-
@used_deprecated_fields << field if field_defn.deprecation_reason
|
19
|
-
arguments = visitor.query.arguments_for(node, field_defn)
|
20
|
-
# If there was an error when preparing this argument object,
|
21
|
-
# then this might be an error or something:
|
22
|
-
if arguments.respond_to?(:argument_values)
|
23
|
-
extract_deprecated_arguments(arguments.argument_values)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def result
|
28
|
-
{
|
29
|
-
used_fields: @used_fields.to_a,
|
30
|
-
used_deprecated_fields: @used_deprecated_fields.to_a,
|
31
|
-
used_deprecated_arguments: @used_deprecated_arguments.to_a,
|
32
|
-
used_deprecated_enum_values: @used_deprecated_enum_values.to_a,
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def extract_deprecated_arguments(argument_values)
|
39
|
-
argument_values.each_pair do |_argument_name, argument|
|
40
|
-
if argument.definition.deprecation_reason
|
41
|
-
@used_deprecated_arguments << argument.definition.path
|
42
|
-
end
|
43
|
-
|
44
|
-
arg_val = argument.value
|
45
|
-
|
46
|
-
next if arg_val.nil?
|
47
|
-
|
48
|
-
argument_type = argument.definition.type
|
49
|
-
if argument_type.non_null?
|
50
|
-
argument_type = argument_type.of_type
|
51
|
-
end
|
52
|
-
|
53
|
-
if argument_type.kind.input_object?
|
54
|
-
extract_deprecated_arguments(argument.original_value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
|
55
|
-
elsif argument_type.kind.enum?
|
56
|
-
extract_deprecated_enum_value(argument_type, arg_val)
|
57
|
-
elsif argument_type.list?
|
58
|
-
inner_type = argument_type.unwrap
|
59
|
-
case inner_type.kind
|
60
|
-
when TypeKinds::INPUT_OBJECT
|
61
|
-
argument.original_value.each do |value|
|
62
|
-
extract_deprecated_arguments(value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
|
63
|
-
end
|
64
|
-
when TypeKinds::ENUM
|
65
|
-
arg_val.each do |value|
|
66
|
-
extract_deprecated_enum_value(inner_type, value)
|
67
|
-
end
|
68
|
-
else
|
69
|
-
# Not a kind of input that we track
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def extract_deprecated_enum_value(enum_type, value)
|
76
|
-
enum_value = @query.warden.enum_values(enum_type).find { |ev| ev.value == value }
|
77
|
-
if enum_value&.deprecation_reason
|
78
|
-
@used_deprecated_enum_values << enum_value.path
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Analysis
|
4
|
-
module AST
|
5
|
-
# Used under the hood to implement complexity validation,
|
6
|
-
# see {Schema#max_complexity} and {Query#max_complexity}
|
7
|
-
class MaxQueryComplexity < QueryComplexity
|
8
|
-
def result
|
9
|
-
return if subject.max_complexity.nil?
|
10
|
-
|
11
|
-
total_complexity = max_possible_complexity
|
12
|
-
|
13
|
-
if total_complexity > subject.max_complexity
|
14
|
-
GraphQL::AnalysisError.new("Query has complexity of #{total_complexity}, which exceeds max complexity of #{subject.max_complexity}")
|
15
|
-
else
|
16
|
-
nil
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Analysis
|
4
|
-
module AST
|
5
|
-
class MaxQueryDepth < QueryDepth
|
6
|
-
def result
|
7
|
-
configured_max_depth = if query
|
8
|
-
query.max_depth
|
9
|
-
else
|
10
|
-
multiplex.schema.max_depth
|
11
|
-
end
|
12
|
-
|
13
|
-
if configured_max_depth && @max_depth > configured_max_depth
|
14
|
-
GraphQL::AnalysisError.new("Query has depth of #{@max_depth}, which exceeds max depth of #{configured_max_depth}")
|
15
|
-
else
|
16
|
-
nil
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|