graphql 2.5.11 → 2.5.23
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/detailed_trace_generator.rb +77 -0
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/graphql/dashboard/application_controller.rb +41 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -0
- data/lib/graphql/dashboard/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +2 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +1 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +2 -2
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +1 -1
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +7 -7
- data/lib/graphql/dashboard.rb +11 -73
- data/lib/graphql/dataloader/async_dataloader.rb +22 -11
- data/lib/graphql/dataloader/null_dataloader.rb +48 -10
- data/lib/graphql/dataloader.rb +75 -23
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/execution/interpreter/resolve.rb +7 -13
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +13 -0
- data/lib/graphql/execution/interpreter/runtime.rb +24 -18
- data/lib/graphql/execution/interpreter.rb +8 -22
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/execution/next/field_resolve_step.rb +743 -0
- data/lib/graphql/execution/next/load_argument_step.rb +64 -0
- data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
- data/lib/graphql/execution/next/runner.rb +411 -0
- data/lib/graphql/execution/next/selections_step.rb +37 -0
- data/lib/graphql/execution/next.rb +72 -0
- data/lib/graphql/execution.rb +8 -4
- data/lib/graphql/execution_error.rb +17 -10
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +11 -3
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_null_error.rb +11 -5
- data/lib/graphql/language/document_from_schema_definition.rb +2 -1
- data/lib/graphql/language.rb +21 -12
- data/lib/graphql/pagination/connection.rb +2 -0
- data/lib/graphql/pagination/connections.rb +32 -0
- data/lib/graphql/query/context.rb +4 -3
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/schema/argument.rb +12 -0
- data/lib/graphql/schema/build_from_definition.rb +10 -1
- data/lib/graphql/schema/directive.rb +22 -4
- data/lib/graphql/schema/field/connection_extension.rb +15 -35
- data/lib/graphql/schema/field/scope_extension.rb +22 -13
- data/lib/graphql/schema/field.rb +79 -48
- data/lib/graphql/schema/field_extension.rb +33 -0
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +43 -14
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +37 -0
- data/lib/graphql/schema/member/has_fields.rb +86 -5
- data/lib/graphql/schema/member.rb +5 -0
- data/lib/graphql/schema/non_null.rb +1 -1
- data/lib/graphql/schema/object.rb +1 -0
- data/lib/graphql/schema/resolver.rb +60 -1
- data/lib/graphql/schema/subscription.rb +0 -2
- data/lib/graphql/schema/validator/required_validator.rb +33 -2
- data/lib/graphql/schema/visibility/profile.rb +68 -49
- data/lib/graphql/schema/visibility.rb +3 -3
- data/lib/graphql/schema/wrapper.rb +7 -1
- data/lib/graphql/schema.rb +53 -10
- data/lib/graphql/static_validation/base_visitor.rb +90 -66
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +322 -256
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +25 -1
- data/lib/graphql/subscriptions/event.rb +1 -0
- data/lib/graphql/subscriptions.rb +21 -1
- data/lib/graphql/testing/helpers.rb +12 -9
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +1 -0
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
- data/lib/graphql/tracing/detailed_trace.rb +70 -7
- data/lib/graphql/tracing/perfetto_trace.rb +209 -79
- data/lib/graphql/tracing/sentry_trace.rb +3 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +8 -6
- data/lib/graphql/types/relay/edge_behaviors.rb +4 -3
- data/lib/graphql/types/relay/has_node_field.rb +13 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
- data/lib/graphql/types/relay/node_behaviors.rb +13 -2
- data/lib/graphql/unauthorized_error.rb +9 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +8 -2
- metadata +17 -3
data/lib/graphql/schema.rb
CHANGED
|
@@ -330,10 +330,16 @@ module GraphQL
|
|
|
330
330
|
find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
|
|
331
331
|
end
|
|
332
332
|
|
|
333
|
+
attr_writer :null_context
|
|
334
|
+
|
|
335
|
+
def null_context
|
|
336
|
+
@null_context || GraphQL::Query::NullContext.instance
|
|
337
|
+
end
|
|
338
|
+
|
|
333
339
|
# Build a map of `{ name => type }` and return it
|
|
334
340
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
|
335
341
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
|
336
|
-
def types(context =
|
|
342
|
+
def types(context = null_context)
|
|
337
343
|
if use_visibility_profile?
|
|
338
344
|
types = Visibility::Profile.from_context(context, self)
|
|
339
345
|
return types.all_types_h
|
|
@@ -366,7 +372,7 @@ module GraphQL
|
|
|
366
372
|
# @param context [GraphQL::Query::Context] Used for filtering definitions at query-time
|
|
367
373
|
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
368
374
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
|
369
|
-
def get_type(type_name, context =
|
|
375
|
+
def get_type(type_name, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
370
376
|
if use_visibility_profile
|
|
371
377
|
profile = Visibility::Profile.from_context(context, self)
|
|
372
378
|
return profile.type(type_name)
|
|
@@ -617,7 +623,7 @@ module GraphQL
|
|
|
617
623
|
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
618
624
|
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
|
619
625
|
# @return [Array<Module>] Possible types for `type`, if it's given.
|
|
620
|
-
def possible_types(type = nil, context =
|
|
626
|
+
def possible_types(type = nil, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
621
627
|
if use_visibility_profile
|
|
622
628
|
if type
|
|
623
629
|
return Visibility::Profile.from_context(context, self).possible_types(type)
|
|
@@ -701,7 +707,7 @@ module GraphQL
|
|
|
701
707
|
GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
|
|
702
708
|
end
|
|
703
709
|
|
|
704
|
-
def get_field(type_or_name, field_name, context =
|
|
710
|
+
def get_field(type_or_name, field_name, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
705
711
|
if use_visibility_profile
|
|
706
712
|
profile = Visibility::Profile.from_context(context, self)
|
|
707
713
|
parent_type = case type_or_name
|
|
@@ -738,7 +744,7 @@ module GraphQL
|
|
|
738
744
|
end
|
|
739
745
|
end
|
|
740
746
|
|
|
741
|
-
def get_fields(type, context =
|
|
747
|
+
def get_fields(type, context = null_context)
|
|
742
748
|
type.fields(context)
|
|
743
749
|
end
|
|
744
750
|
|
|
@@ -1228,6 +1234,7 @@ module GraphQL
|
|
|
1228
1234
|
vis = self.visibility
|
|
1229
1235
|
child_class.visibility = vis.dup_for(child_class)
|
|
1230
1236
|
end
|
|
1237
|
+
child_class.null_context = Query::NullContext.new(schema: child_class)
|
|
1231
1238
|
super
|
|
1232
1239
|
end
|
|
1233
1240
|
|
|
@@ -1329,10 +1336,11 @@ module GraphQL
|
|
|
1329
1336
|
def type_error(type_error, context)
|
|
1330
1337
|
case type_error
|
|
1331
1338
|
when GraphQL::InvalidNullError
|
|
1332
|
-
execution_error = GraphQL::ExecutionError.new(type_error.message,
|
|
1333
|
-
execution_error.path = context[:current_path]
|
|
1339
|
+
execution_error = GraphQL::ExecutionError.new(type_error.message, ast_nodes: type_error.ast_nodes)
|
|
1340
|
+
execution_error.path = type_error.path || context[:current_path]
|
|
1334
1341
|
|
|
1335
1342
|
context.errors << execution_error
|
|
1343
|
+
execution_error
|
|
1336
1344
|
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
|
|
1337
1345
|
raise type_error
|
|
1338
1346
|
when GraphQL::IntegerDecodingError
|
|
@@ -1354,6 +1362,24 @@ module GraphQL
|
|
|
1354
1362
|
lazy_methods.set(lazy_class, value_method)
|
|
1355
1363
|
end
|
|
1356
1364
|
|
|
1365
|
+
def uses_raw_value?
|
|
1366
|
+
!!@uses_raw_value
|
|
1367
|
+
end
|
|
1368
|
+
|
|
1369
|
+
def uses_raw_value(new_val)
|
|
1370
|
+
@uses_raw_value = new_val
|
|
1371
|
+
end
|
|
1372
|
+
|
|
1373
|
+
def resolves_lazies?
|
|
1374
|
+
lazy_method_count = 0
|
|
1375
|
+
lazy_methods.each do |k, v|
|
|
1376
|
+
if !v.nil?
|
|
1377
|
+
lazy_method_count += 1
|
|
1378
|
+
end
|
|
1379
|
+
end
|
|
1380
|
+
lazy_method_count > 2
|
|
1381
|
+
end
|
|
1382
|
+
|
|
1357
1383
|
def instrument(instrument_step, instrumenter, options = {})
|
|
1358
1384
|
warn <<~WARN
|
|
1359
1385
|
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
|
@@ -1708,7 +1734,7 @@ module GraphQL
|
|
|
1708
1734
|
# If you need to support previous, non-spec behavior which allowed selecting union fields
|
|
1709
1735
|
# but *not* selecting any fields on that union, set this to `true` to continue allowing that behavior.
|
|
1710
1736
|
#
|
|
1711
|
-
# If this is `true`, then {.
|
|
1737
|
+
# If this is `true`, then {.legacy_invalid_empty_selections_on_union_with_type} will be called with {Query} objects
|
|
1712
1738
|
# with that kind of selections. You must implement that method
|
|
1713
1739
|
# @param new_value [Boolean]
|
|
1714
1740
|
# @return [true, false, nil]
|
|
@@ -1724,6 +1750,22 @@ module GraphQL
|
|
|
1724
1750
|
end
|
|
1725
1751
|
end
|
|
1726
1752
|
|
|
1753
|
+
# This method is called during validation when a previously-allowed, but non-spec
|
|
1754
|
+
# query is encountered where a union field has no child selections on it.
|
|
1755
|
+
#
|
|
1756
|
+
# If `legacy_invalid_empty_selections_on_union_with_type` is overridden, this method will not be called.
|
|
1757
|
+
#
|
|
1758
|
+
# You should implement this method or `legacy_invalid_empty_selections_on_union_with_type`
|
|
1759
|
+
# to log the violation so that you can contact clients and notify them about changing their queries.
|
|
1760
|
+
# Then return a suitable value to tell GraphQL-Ruby how to continue.
|
|
1761
|
+
# @param query [GraphQL::Query]
|
|
1762
|
+
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1763
|
+
# @return [String] A validation error to return for this query
|
|
1764
|
+
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1765
|
+
def legacy_invalid_empty_selections_on_union(query)
|
|
1766
|
+
raise "Implement `def self.legacy_invalid_empty_selections_on_union_with_type(query, type)` or `def self.legacy_invalid_empty_selections_on_union(query)` to handle this scenario"
|
|
1767
|
+
end
|
|
1768
|
+
|
|
1727
1769
|
# This method is called during validation when a previously-allowed, but non-spec
|
|
1728
1770
|
# query is encountered where a union field has no child selections on it.
|
|
1729
1771
|
#
|
|
@@ -1731,11 +1773,12 @@ module GraphQL
|
|
|
1731
1773
|
# and notify them about changing their queries. Then return a suitable value to
|
|
1732
1774
|
# tell GraphQL-Ruby how to continue.
|
|
1733
1775
|
# @param query [GraphQL::Query]
|
|
1776
|
+
# @param type [Module] A GraphQL type definition
|
|
1734
1777
|
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1735
1778
|
# @return [String] A validation error to return for this query
|
|
1736
1779
|
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1737
|
-
def
|
|
1738
|
-
|
|
1780
|
+
def legacy_invalid_empty_selections_on_union_with_type(query, type)
|
|
1781
|
+
legacy_invalid_empty_selections_on_union(query)
|
|
1739
1782
|
end
|
|
1740
1783
|
|
|
1741
1784
|
# This setting controls how GraphQL-Ruby handles overlapping selections on scalar types when the types
|
|
@@ -4,25 +4,26 @@ module GraphQL
|
|
|
4
4
|
class BaseVisitor < GraphQL::Language::StaticVisitor
|
|
5
5
|
def initialize(document, context)
|
|
6
6
|
@path = []
|
|
7
|
-
@
|
|
8
|
-
@
|
|
9
|
-
@
|
|
10
|
-
@
|
|
11
|
-
@
|
|
7
|
+
@path_depth = 0
|
|
8
|
+
@current_object_type = nil
|
|
9
|
+
@parent_object_type = nil
|
|
10
|
+
@current_field_definition = nil
|
|
11
|
+
@current_argument_definition = nil
|
|
12
|
+
@parent_argument_definition = nil
|
|
13
|
+
@current_directive_definition = nil
|
|
12
14
|
@context = context
|
|
13
15
|
@types = context.query.types
|
|
14
16
|
@schema = context.schema
|
|
17
|
+
@inline_fragment_paths = {}
|
|
18
|
+
@field_unwrapped_types = {}.compare_by_identity
|
|
15
19
|
super(document)
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
attr_reader :context
|
|
19
23
|
|
|
20
|
-
# @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
|
|
21
|
-
attr_reader :object_types
|
|
22
|
-
|
|
23
24
|
# @return [Array<String>] The nesting of the current position in the AST
|
|
24
25
|
def path
|
|
25
|
-
@path
|
|
26
|
+
@path[0, @path_depth]
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
# Build a class to visit the AST and perform validation,
|
|
@@ -55,86 +56,125 @@ module GraphQL
|
|
|
55
56
|
module ContextMethods
|
|
56
57
|
def on_operation_definition(node, parent)
|
|
57
58
|
object_type = @schema.root_type_for_operation(node.operation_type)
|
|
58
|
-
|
|
59
|
-
@
|
|
59
|
+
prev_parent_ot = @parent_object_type
|
|
60
|
+
@parent_object_type = @current_object_type
|
|
61
|
+
@current_object_type = object_type
|
|
62
|
+
@path[@path_depth] = "#{node.operation_type}#{node.name ? " #{node.name}" : ""}"
|
|
63
|
+
@path_depth += 1
|
|
60
64
|
super
|
|
61
|
-
@
|
|
62
|
-
@
|
|
65
|
+
@current_object_type = @parent_object_type
|
|
66
|
+
@parent_object_type = prev_parent_ot
|
|
67
|
+
@path_depth -= 1
|
|
63
68
|
end
|
|
64
69
|
|
|
65
70
|
def on_fragment_definition(node, parent)
|
|
66
|
-
|
|
67
|
-
@
|
|
68
|
-
|
|
71
|
+
object_type = if node.type
|
|
72
|
+
@types.type(node.type.name)
|
|
73
|
+
else
|
|
74
|
+
@current_object_type
|
|
69
75
|
end
|
|
76
|
+
prev_parent_ot = @parent_object_type
|
|
77
|
+
@parent_object_type = @current_object_type
|
|
78
|
+
@current_object_type = object_type
|
|
79
|
+
@path[@path_depth] = "fragment #{node.name}"
|
|
80
|
+
@path_depth += 1
|
|
81
|
+
super
|
|
82
|
+
@current_object_type = @parent_object_type
|
|
83
|
+
@parent_object_type = prev_parent_ot
|
|
84
|
+
@path_depth -= 1
|
|
70
85
|
end
|
|
71
86
|
|
|
87
|
+
INLINE_FRAGMENT_NO_TYPE = "..."
|
|
88
|
+
|
|
72
89
|
def on_inline_fragment(node, parent)
|
|
73
|
-
|
|
74
|
-
@
|
|
75
|
-
|
|
90
|
+
if node.type
|
|
91
|
+
object_type = @types.type(node.type.name)
|
|
92
|
+
@path[@path_depth] = @inline_fragment_paths[node.type.name] ||= -"... on #{node.type.to_query_string}"
|
|
93
|
+
@path_depth += 1
|
|
94
|
+
else
|
|
95
|
+
object_type = @current_object_type
|
|
96
|
+
@path[@path_depth] = INLINE_FRAGMENT_NO_TYPE
|
|
97
|
+
@path_depth += 1
|
|
76
98
|
end
|
|
99
|
+
prev_parent_ot = @parent_object_type
|
|
100
|
+
@parent_object_type = @current_object_type
|
|
101
|
+
@current_object_type = object_type
|
|
102
|
+
super
|
|
103
|
+
@current_object_type = @parent_object_type
|
|
104
|
+
@parent_object_type = prev_parent_ot
|
|
105
|
+
@path_depth -= 1
|
|
77
106
|
end
|
|
78
107
|
|
|
79
108
|
def on_field(node, parent)
|
|
80
|
-
parent_type = @
|
|
109
|
+
parent_type = @current_object_type
|
|
81
110
|
field_definition = @types.field(parent_type, node.name)
|
|
82
|
-
@
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
111
|
+
prev_field_definition = @current_field_definition
|
|
112
|
+
@current_field_definition = field_definition
|
|
113
|
+
prev_parent_ot = @parent_object_type
|
|
114
|
+
@parent_object_type = @current_object_type
|
|
115
|
+
if field_definition
|
|
116
|
+
@current_object_type = @field_unwrapped_types[field_definition] ||= field_definition.type.unwrap
|
|
86
117
|
else
|
|
87
|
-
|
|
118
|
+
@current_object_type = nil
|
|
88
119
|
end
|
|
89
|
-
@path
|
|
120
|
+
@path[@path_depth] = node.alias || node.name
|
|
121
|
+
@path_depth += 1
|
|
90
122
|
super
|
|
91
|
-
@
|
|
92
|
-
@
|
|
93
|
-
@
|
|
123
|
+
@current_field_definition = prev_field_definition
|
|
124
|
+
@current_object_type = @parent_object_type
|
|
125
|
+
@parent_object_type = prev_parent_ot
|
|
126
|
+
@path_depth -= 1
|
|
94
127
|
end
|
|
95
128
|
|
|
96
129
|
def on_directive(node, parent)
|
|
97
130
|
directive_defn = @context.schema_directives[node.name]
|
|
98
|
-
@
|
|
131
|
+
prev_directive_definition = @current_directive_definition
|
|
132
|
+
@current_directive_definition = directive_defn
|
|
99
133
|
super
|
|
100
|
-
@
|
|
134
|
+
@current_directive_definition = prev_directive_definition
|
|
101
135
|
end
|
|
102
136
|
|
|
103
137
|
def on_argument(node, parent)
|
|
104
|
-
argument_defn = if (arg = @
|
|
138
|
+
argument_defn = if (arg = @current_argument_definition)
|
|
105
139
|
arg_type = arg.type.unwrap
|
|
106
140
|
if arg_type.kind.input_object?
|
|
107
141
|
@types.argument(arg_type, node.name)
|
|
108
142
|
else
|
|
109
143
|
nil
|
|
110
144
|
end
|
|
111
|
-
elsif (directive_defn = @
|
|
145
|
+
elsif (directive_defn = @current_directive_definition)
|
|
112
146
|
@types.argument(directive_defn, node.name)
|
|
113
|
-
elsif (field_defn = @
|
|
147
|
+
elsif (field_defn = @current_field_definition)
|
|
114
148
|
@types.argument(field_defn, node.name)
|
|
115
149
|
else
|
|
116
150
|
nil
|
|
117
151
|
end
|
|
118
152
|
|
|
119
|
-
@
|
|
120
|
-
@
|
|
153
|
+
prev_parent = @parent_argument_definition
|
|
154
|
+
@parent_argument_definition = @current_argument_definition
|
|
155
|
+
@current_argument_definition = argument_defn
|
|
156
|
+
@path[@path_depth] = node.name
|
|
157
|
+
@path_depth += 1
|
|
121
158
|
super
|
|
122
|
-
@
|
|
123
|
-
@
|
|
159
|
+
@current_argument_definition = @parent_argument_definition
|
|
160
|
+
@parent_argument_definition = prev_parent
|
|
161
|
+
@path_depth -= 1
|
|
124
162
|
end
|
|
125
163
|
|
|
126
164
|
def on_fragment_spread(node, parent)
|
|
127
|
-
@path
|
|
165
|
+
@path[@path_depth] = "... #{node.name}"
|
|
166
|
+
@path_depth += 1
|
|
128
167
|
super
|
|
129
|
-
@
|
|
168
|
+
@path_depth -= 1
|
|
130
169
|
end
|
|
131
170
|
|
|
132
171
|
def on_input_object(node, parent)
|
|
133
|
-
arg_defn = @
|
|
172
|
+
arg_defn = @current_argument_definition
|
|
134
173
|
if arg_defn && arg_defn.type.list?
|
|
135
|
-
@path
|
|
174
|
+
@path[@path_depth] = parent.children.index(node)
|
|
175
|
+
@path_depth += 1
|
|
136
176
|
super
|
|
137
|
-
@
|
|
177
|
+
@path_depth -= 1
|
|
138
178
|
else
|
|
139
179
|
super
|
|
140
180
|
end
|
|
@@ -142,48 +182,32 @@ module GraphQL
|
|
|
142
182
|
|
|
143
183
|
# @return [GraphQL::BaseType] The current object type
|
|
144
184
|
def type_definition
|
|
145
|
-
@
|
|
185
|
+
@current_object_type
|
|
146
186
|
end
|
|
147
187
|
|
|
148
188
|
# @return [GraphQL::BaseType] The type which the current type came from
|
|
149
189
|
def parent_type_definition
|
|
150
|
-
@
|
|
190
|
+
@parent_object_type
|
|
151
191
|
end
|
|
152
192
|
|
|
153
193
|
# @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
|
|
154
194
|
def field_definition
|
|
155
|
-
@
|
|
195
|
+
@current_field_definition
|
|
156
196
|
end
|
|
157
197
|
|
|
158
198
|
# @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
|
|
159
199
|
def directive_definition
|
|
160
|
-
@
|
|
200
|
+
@current_directive_definition
|
|
161
201
|
end
|
|
162
202
|
|
|
163
203
|
# @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
|
|
164
204
|
def argument_definition
|
|
165
|
-
#
|
|
166
|
-
|
|
167
|
-
@argument_definitions[-2]
|
|
205
|
+
# Return the parent argument definition (not the current one).
|
|
206
|
+
@parent_argument_definition
|
|
168
207
|
end
|
|
169
208
|
|
|
170
209
|
private
|
|
171
210
|
|
|
172
|
-
def on_fragment_with_type(node)
|
|
173
|
-
object_type = if node.type
|
|
174
|
-
@types.type(node.type.name)
|
|
175
|
-
else
|
|
176
|
-
@object_types.last
|
|
177
|
-
end
|
|
178
|
-
push_type(object_type)
|
|
179
|
-
yield(node)
|
|
180
|
-
@object_types.pop
|
|
181
|
-
@path.pop
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def push_type(t)
|
|
185
|
-
@object_types.push(t)
|
|
186
|
-
end
|
|
187
211
|
end
|
|
188
212
|
|
|
189
213
|
private
|
|
@@ -192,7 +216,7 @@ module GraphQL
|
|
|
192
216
|
if @context.too_many_errors?
|
|
193
217
|
throw :too_many_validation_errors
|
|
194
218
|
end
|
|
195
|
-
error.path ||= (path || @path
|
|
219
|
+
error.path ||= (path || @path[0, @path_depth])
|
|
196
220
|
context.errors << error
|
|
197
221
|
end
|
|
198
222
|
|
|
@@ -12,7 +12,7 @@ module GraphQL
|
|
|
12
12
|
return
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
if @context.schema.error_bubbling || context.errors.none? { |err| err.path.take(@
|
|
15
|
+
if @context.schema.error_bubbling || context.errors.none? { |err| err.path.take(@path_depth) == @path[0, @path_depth] }
|
|
16
16
|
parent_defn = parent_definition(parent)
|
|
17
17
|
|
|
18
18
|
if parent_defn && (arg_defn = @types.argument(parent_defn, node.name))
|
|
@@ -16,12 +16,24 @@ module GraphQL
|
|
|
16
16
|
|
|
17
17
|
def validate_arguments(node)
|
|
18
18
|
argument_defns = node.arguments
|
|
19
|
-
if
|
|
20
|
-
|
|
21
|
-
argument_defns.each
|
|
22
|
-
|
|
23
|
-
if
|
|
24
|
-
|
|
19
|
+
if argument_defns.size > 1
|
|
20
|
+
seen = {}
|
|
21
|
+
argument_defns.each do |a|
|
|
22
|
+
name = a.name
|
|
23
|
+
if seen.key?(name)
|
|
24
|
+
prev = seen[name]
|
|
25
|
+
if prev.is_a?(Array)
|
|
26
|
+
prev << a
|
|
27
|
+
else
|
|
28
|
+
seen[name] = [prev, a]
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
seen[name] = a
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
seen.each do |name, val|
|
|
35
|
+
if val.is_a?(Array)
|
|
36
|
+
add_error(GraphQL::StaticValidation::ArgumentNamesAreUniqueError.new("There can be only one argument named \"#{name}\"", nodes: val, name: name))
|
|
25
37
|
end
|
|
26
38
|
end
|
|
27
39
|
end
|
|
@@ -10,9 +10,12 @@ module GraphQL
|
|
|
10
10
|
elsif parent_defn
|
|
11
11
|
kind_of_node = node_type(parent)
|
|
12
12
|
error_arg_name = parent_name(parent, parent_defn)
|
|
13
|
-
|
|
13
|
+
suggestion = if @schema.did_you_mean
|
|
14
|
+
arg_names = context.types.arguments(parent_defn).map(&:graphql_name)
|
|
15
|
+
context.did_you_mean_suggestion(node.name, arg_names)
|
|
16
|
+
end
|
|
14
17
|
add_error(GraphQL::StaticValidation::ArgumentsAreDefinedError.new(
|
|
15
|
-
"#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'#{
|
|
18
|
+
"#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'#{suggestion}",
|
|
16
19
|
nodes: node,
|
|
17
20
|
name: error_arg_name,
|
|
18
21
|
type: kind_of_node,
|
|
@@ -10,9 +10,12 @@ module GraphQL
|
|
|
10
10
|
if !@types.directive_exists?(node.name)
|
|
11
11
|
@directives_are_defined_errors_by_name ||= {}
|
|
12
12
|
error = @directives_are_defined_errors_by_name[node.name] ||= begin
|
|
13
|
-
|
|
13
|
+
suggestion = if @schema.did_you_mean
|
|
14
|
+
@directive_names ||= @types.directives.map(&:graphql_name)
|
|
15
|
+
context.did_you_mean_suggestion(node.name, @directive_names)
|
|
16
|
+
end
|
|
14
17
|
err = GraphQL::StaticValidation::DirectivesAreDefinedError.new(
|
|
15
|
-
"Directive @#{node.name} is not defined#{
|
|
18
|
+
"Directive @#{node.name} is not defined#{suggestion}",
|
|
16
19
|
nodes: [],
|
|
17
20
|
directive: node.name
|
|
18
21
|
)
|
|
@@ -3,7 +3,7 @@ module GraphQL
|
|
|
3
3
|
module StaticValidation
|
|
4
4
|
module FieldsAreDefinedOnType
|
|
5
5
|
def on_field(node, parent)
|
|
6
|
-
parent_type = @
|
|
6
|
+
parent_type = @parent_object_type
|
|
7
7
|
field = context.query.types.field(parent_type, node.name)
|
|
8
8
|
|
|
9
9
|
if field.nil?
|
|
@@ -14,8 +14,9 @@ module GraphQL
|
|
|
14
14
|
node_name: parent_type.graphql_name
|
|
15
15
|
))
|
|
16
16
|
else
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
suggestion = if @schema.did_you_mean
|
|
18
|
+
context.did_you_mean_suggestion(node.name, possible_fields(context, parent_type))
|
|
19
|
+
end
|
|
19
20
|
message = "Field '#{node.name}' doesn't exist on type '#{parent_type.graphql_name}'#{suggestion}"
|
|
20
21
|
add_error(GraphQL::StaticValidation::FieldsAreDefinedOnTypeError.new(
|
|
21
22
|
message,
|
|
@@ -7,8 +7,7 @@ module GraphQL
|
|
|
7
7
|
include GraphQL::StaticValidation::Error::ErrorHelper
|
|
8
8
|
|
|
9
9
|
def on_field(node, parent)
|
|
10
|
-
|
|
11
|
-
if validate_field_selections(node, field_defn.type.unwrap)
|
|
10
|
+
if validate_field_selections(node, @current_object_type)
|
|
12
11
|
super
|
|
13
12
|
end
|
|
14
13
|
end
|
|
@@ -23,6 +22,17 @@ module GraphQL
|
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
def validate_field_selections(ast_node, resolved_type)
|
|
25
|
+
# Fast paths for the two most common cases:
|
|
26
|
+
# 1. Leaf type with no selections (scalars, enums) — most fields
|
|
27
|
+
# 2. Non-leaf type with selections (objects, interfaces)
|
|
28
|
+
if resolved_type
|
|
29
|
+
if ast_node.selections.empty?
|
|
30
|
+
return true if resolved_type.kind.leaf?
|
|
31
|
+
else
|
|
32
|
+
return true unless resolved_type.kind.leaf?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
26
36
|
msg = if resolved_type.nil?
|
|
27
37
|
nil
|
|
28
38
|
elsif resolved_type.kind.leaf?
|
|
@@ -49,7 +59,7 @@ module GraphQL
|
|
|
49
59
|
if !resolved_type.kind.fields?
|
|
50
60
|
case @schema.allow_legacy_invalid_empty_selections_on_union
|
|
51
61
|
when true
|
|
52
|
-
legacy_invalid_empty_selection_result = @schema.
|
|
62
|
+
legacy_invalid_empty_selection_result = @schema.legacy_invalid_empty_selections_on_union_with_type(@context.query, resolved_type)
|
|
53
63
|
case legacy_invalid_empty_selection_result
|
|
54
64
|
when :return_validation_error
|
|
55
65
|
# keep `return_validation_error = true`
|
|
@@ -61,7 +71,7 @@ module GraphQL
|
|
|
61
71
|
return_validation_error = false
|
|
62
72
|
legacy_invalid_empty_selection_result = nil
|
|
63
73
|
else
|
|
64
|
-
raise GraphQL::InvariantError, "Unexpected return value from
|
|
74
|
+
raise GraphQL::InvariantError, "Unexpected return value from legacy_invalid_empty_selections_on_union_with_type, must be `:return_validation_error`, String, or nil (got: #{legacy_invalid_empty_selection_result.inspect})"
|
|
65
75
|
end
|
|
66
76
|
when false
|
|
67
77
|
# pass -- error below
|