graphql 2.5.9 → 2.5.26
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/analysis.rb +20 -13
- 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/active_record_association_source.rb +14 -2
- data/lib/graphql/dataloader/async_dataloader.rb +22 -11
- data/lib/graphql/dataloader/null_dataloader.rb +54 -9
- data/lib/graphql/dataloader.rb +75 -23
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/execution/field_resolve_step.rb +631 -0
- data/lib/graphql/execution/finalize.rb +217 -0
- data/lib/graphql/execution/input_values.rb +261 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
- data/lib/graphql/execution/interpreter/resolve.rb +10 -16
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +13 -0
- data/lib/graphql/execution/interpreter/runtime.rb +28 -33
- data/lib/graphql/execution/interpreter.rb +8 -22
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/load_argument_step.rb +64 -0
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/execution/next.rb +90 -0
- data/lib/graphql/execution/prepare_object_step.rb +128 -0
- data/lib/graphql/execution/runner.rb +410 -0
- data/lib/graphql/execution/selections_step.rb +91 -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/lexer.rb +20 -9
- data/lib/graphql/language/nodes.rb +5 -1
- data/lib/graphql/language/parser.rb +1 -0
- 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 +11 -4
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/query/partial.rb +18 -3
- data/lib/graphql/query.rb +10 -1
- data/lib/graphql/runtime_error.rb +6 -0
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/argument.rb +17 -0
- data/lib/graphql/schema/build_from_definition.rb +15 -2
- data/lib/graphql/schema/directive.rb +45 -13
- data/lib/graphql/schema/field/connection_extension.rb +4 -37
- data/lib/graphql/schema/field/scope_extension.rb +18 -13
- data/lib/graphql/schema/field.rb +87 -48
- data/lib/graphql/schema/field_extension.rb +11 -8
- data/lib/graphql/schema/interface.rb +26 -0
- data/lib/graphql/schema/list.rb +5 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -11
- 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/has_interfaces.rb +2 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -2
- 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/ractor_shareable.rb +79 -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 +45 -5
- data/lib/graphql/schema/visibility/migration.rb +2 -2
- data/lib/graphql/schema/visibility/profile.rb +140 -56
- data/lib/graphql/schema/visibility.rb +31 -18
- data/lib/graphql/schema/wrapper.rb +7 -1
- data/lib/graphql/schema.rb +108 -32
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- 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/unique_directives_per_location.rb +6 -2
- 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 +34 -10
- data/lib/graphql/subscriptions/event.rb +1 -0
- data/lib/graphql/subscriptions.rb +36 -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/null_trace.rb +1 -1
- data/lib/graphql/tracing/perfetto_trace.rb +209 -79
- data/lib/graphql/tracing/sentry_trace.rb +3 -1
- data/lib/graphql/tracing/trace.rb +6 -0
- data/lib/graphql/type_kinds.rb +1 -0
- 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 +7 -3
- metadata +21 -3
data/lib/graphql/schema.rb
CHANGED
|
@@ -7,6 +7,7 @@ require "graphql/schema/find_inherited_value"
|
|
|
7
7
|
require "graphql/schema/finder"
|
|
8
8
|
require "graphql/schema/introspection_system"
|
|
9
9
|
require "graphql/schema/late_bound_type"
|
|
10
|
+
require "graphql/schema/ractor_shareable"
|
|
10
11
|
require "graphql/schema/timeout"
|
|
11
12
|
require "graphql/schema/type_expression"
|
|
12
13
|
require "graphql/schema/unique_within_type"
|
|
@@ -148,10 +149,12 @@ module GraphQL
|
|
|
148
149
|
end
|
|
149
150
|
|
|
150
151
|
# @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
|
|
151
|
-
def default_trace_mode(new_mode =
|
|
152
|
-
if new_mode
|
|
152
|
+
def default_trace_mode(new_mode = NOT_CONFIGURED)
|
|
153
|
+
if !NOT_CONFIGURED.equal?(new_mode)
|
|
153
154
|
@default_trace_mode = new_mode
|
|
154
|
-
elsif defined?(@default_trace_mode)
|
|
155
|
+
elsif defined?(@default_trace_mode) &&
|
|
156
|
+
!@default_trace_mode.nil? # This `nil?` check seems necessary because of
|
|
157
|
+
# Ractors silently initializing @default_trace_mode somehow
|
|
155
158
|
@default_trace_mode
|
|
156
159
|
elsif superclass.respond_to?(:default_trace_mode)
|
|
157
160
|
superclass.default_trace_mode
|
|
@@ -327,10 +330,16 @@ module GraphQL
|
|
|
327
330
|
find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
|
|
328
331
|
end
|
|
329
332
|
|
|
333
|
+
attr_writer :null_context
|
|
334
|
+
|
|
335
|
+
def null_context
|
|
336
|
+
@null_context || GraphQL::Query::NullContext.instance
|
|
337
|
+
end
|
|
338
|
+
|
|
330
339
|
# Build a map of `{ name => type }` and return it
|
|
331
340
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
|
332
341
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
|
333
|
-
def types(context =
|
|
342
|
+
def types(context = null_context)
|
|
334
343
|
if use_visibility_profile?
|
|
335
344
|
types = Visibility::Profile.from_context(context, self)
|
|
336
345
|
return types.all_types_h
|
|
@@ -363,9 +372,10 @@ module GraphQL
|
|
|
363
372
|
# @param context [GraphQL::Query::Context] Used for filtering definitions at query-time
|
|
364
373
|
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
365
374
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
|
366
|
-
def get_type(type_name, context =
|
|
375
|
+
def get_type(type_name, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
367
376
|
if use_visibility_profile
|
|
368
|
-
|
|
377
|
+
profile = Visibility::Profile.from_context(context, self)
|
|
378
|
+
return profile.type(type_name)
|
|
369
379
|
end
|
|
370
380
|
local_entry = own_types[type_name]
|
|
371
381
|
type_defn = case local_entry
|
|
@@ -613,7 +623,7 @@ module GraphQL
|
|
|
613
623
|
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
614
624
|
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
|
615
625
|
# @return [Array<Module>] Possible types for `type`, if it's given.
|
|
616
|
-
def possible_types(type = nil, context =
|
|
626
|
+
def possible_types(type = nil, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
617
627
|
if use_visibility_profile
|
|
618
628
|
if type
|
|
619
629
|
return Visibility::Profile.from_context(context, self).possible_types(type)
|
|
@@ -697,7 +707,21 @@ module GraphQL
|
|
|
697
707
|
GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
|
|
698
708
|
end
|
|
699
709
|
|
|
700
|
-
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?)
|
|
711
|
+
if use_visibility_profile
|
|
712
|
+
profile = Visibility::Profile.from_context(context, self)
|
|
713
|
+
parent_type = case type_or_name
|
|
714
|
+
when String
|
|
715
|
+
profile.type(type_or_name)
|
|
716
|
+
when Module
|
|
717
|
+
type_or_name
|
|
718
|
+
when LateBoundType
|
|
719
|
+
profile.type(type_or_name.name)
|
|
720
|
+
else
|
|
721
|
+
raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
|
|
722
|
+
end
|
|
723
|
+
return profile.field(parent_type, field_name)
|
|
724
|
+
end
|
|
701
725
|
parent_type = case type_or_name
|
|
702
726
|
when LateBoundType
|
|
703
727
|
get_type(type_or_name.name, context)
|
|
@@ -720,7 +744,7 @@ module GraphQL
|
|
|
720
744
|
end
|
|
721
745
|
end
|
|
722
746
|
|
|
723
|
-
def get_fields(type, context =
|
|
747
|
+
def get_fields(type, context = null_context)
|
|
724
748
|
type.fields(context)
|
|
725
749
|
end
|
|
726
750
|
|
|
@@ -1105,20 +1129,21 @@ module GraphQL
|
|
|
1105
1129
|
end
|
|
1106
1130
|
end
|
|
1107
1131
|
|
|
1108
|
-
NEW_HANDLER_HASH = ->(h, k) {
|
|
1109
|
-
h[k] = {
|
|
1110
|
-
class: k,
|
|
1111
|
-
handler: nil,
|
|
1112
|
-
subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
1132
|
def error_handlers
|
|
1117
|
-
@error_handlers ||=
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1133
|
+
@error_handlers ||= begin
|
|
1134
|
+
new_handler_hash = ->(h, k) {
|
|
1135
|
+
h[k] = {
|
|
1136
|
+
class: k,
|
|
1137
|
+
handler: nil,
|
|
1138
|
+
subclass_handlers: Hash.new(&new_handler_hash),
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
{
|
|
1142
|
+
class: nil,
|
|
1143
|
+
handler: nil,
|
|
1144
|
+
subclass_handlers: Hash.new(&new_handler_hash),
|
|
1145
|
+
}
|
|
1146
|
+
end
|
|
1122
1147
|
end
|
|
1123
1148
|
|
|
1124
1149
|
# @api private
|
|
@@ -1209,6 +1234,7 @@ module GraphQL
|
|
|
1209
1234
|
vis = self.visibility
|
|
1210
1235
|
child_class.visibility = vis.dup_for(child_class)
|
|
1211
1236
|
end
|
|
1237
|
+
child_class.null_context = Query::NullContext.new(schema: child_class)
|
|
1212
1238
|
super
|
|
1213
1239
|
end
|
|
1214
1240
|
|
|
@@ -1310,10 +1336,11 @@ module GraphQL
|
|
|
1310
1336
|
def type_error(type_error, context)
|
|
1311
1337
|
case type_error
|
|
1312
1338
|
when GraphQL::InvalidNullError
|
|
1313
|
-
execution_error = GraphQL::ExecutionError.new(type_error.message,
|
|
1314
|
-
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]
|
|
1315
1341
|
|
|
1316
1342
|
context.errors << execution_error
|
|
1343
|
+
execution_error
|
|
1317
1344
|
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
|
|
1318
1345
|
raise type_error
|
|
1319
1346
|
when GraphQL::IntegerDecodingError
|
|
@@ -1335,6 +1362,16 @@ module GraphQL
|
|
|
1335
1362
|
lazy_methods.set(lazy_class, value_method)
|
|
1336
1363
|
end
|
|
1337
1364
|
|
|
1365
|
+
def resolves_lazies?
|
|
1366
|
+
lazy_method_count = 0
|
|
1367
|
+
lazy_methods.each do |k, v|
|
|
1368
|
+
if !v.nil?
|
|
1369
|
+
lazy_method_count += 1
|
|
1370
|
+
end
|
|
1371
|
+
end
|
|
1372
|
+
lazy_method_count > 2
|
|
1373
|
+
end
|
|
1374
|
+
|
|
1338
1375
|
def instrument(instrument_step, instrumenter, options = {})
|
|
1339
1376
|
warn <<~WARN
|
|
1340
1377
|
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
|
@@ -1405,7 +1442,16 @@ module GraphQL
|
|
|
1405
1442
|
end
|
|
1406
1443
|
|
|
1407
1444
|
def tracers
|
|
1408
|
-
find_inherited_value(:tracers, EMPTY_ARRAY)
|
|
1445
|
+
inherited = find_inherited_value(:tracers, EMPTY_ARRAY)
|
|
1446
|
+
if inherited.length > 0
|
|
1447
|
+
if own_tracers.length > 0
|
|
1448
|
+
inherited + own_tracers
|
|
1449
|
+
else
|
|
1450
|
+
inherited
|
|
1451
|
+
end
|
|
1452
|
+
else
|
|
1453
|
+
own_tracers
|
|
1454
|
+
end
|
|
1409
1455
|
end
|
|
1410
1456
|
|
|
1411
1457
|
# Mix `trace_mod` into this schema's `Trace` class so that its methods will be called at runtime.
|
|
@@ -1515,7 +1561,8 @@ module GraphQL
|
|
|
1515
1561
|
end
|
|
1516
1562
|
|
|
1517
1563
|
def query_analyzers
|
|
1518
|
-
find_inherited_value(:query_analyzers, EMPTY_ARRAY)
|
|
1564
|
+
inherited_qa = find_inherited_value(:query_analyzers, EMPTY_ARRAY)
|
|
1565
|
+
inherited_qa.empty? ? own_query_analyzers : (inherited_qa + own_query_analyzers)
|
|
1519
1566
|
end
|
|
1520
1567
|
|
|
1521
1568
|
# @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on multiplexes to this schema
|
|
@@ -1689,18 +1736,38 @@ module GraphQL
|
|
|
1689
1736
|
# If you need to support previous, non-spec behavior which allowed selecting union fields
|
|
1690
1737
|
# but *not* selecting any fields on that union, set this to `true` to continue allowing that behavior.
|
|
1691
1738
|
#
|
|
1692
|
-
# If this is `true`, then {.
|
|
1739
|
+
# If this is `true`, then {.legacy_invalid_empty_selections_on_union_with_type} will be called with {Query} objects
|
|
1693
1740
|
# with that kind of selections. You must implement that method
|
|
1694
1741
|
# @param new_value [Boolean]
|
|
1695
1742
|
# @return [true, false, nil]
|
|
1696
1743
|
def allow_legacy_invalid_empty_selections_on_union(new_value = NOT_CONFIGURED)
|
|
1697
1744
|
if NOT_CONFIGURED.equal?(new_value)
|
|
1698
|
-
@allow_legacy_invalid_empty_selections_on_union
|
|
1745
|
+
if defined?(@allow_legacy_invalid_empty_selections_on_union)
|
|
1746
|
+
@allow_legacy_invalid_empty_selections_on_union
|
|
1747
|
+
else
|
|
1748
|
+
find_inherited_value(:allow_legacy_invalid_empty_selections_on_union)
|
|
1749
|
+
end
|
|
1699
1750
|
else
|
|
1700
1751
|
@allow_legacy_invalid_empty_selections_on_union = new_value
|
|
1701
1752
|
end
|
|
1702
1753
|
end
|
|
1703
1754
|
|
|
1755
|
+
# This method is called during validation when a previously-allowed, but non-spec
|
|
1756
|
+
# query is encountered where a union field has no child selections on it.
|
|
1757
|
+
#
|
|
1758
|
+
# If `legacy_invalid_empty_selections_on_union_with_type` is overridden, this method will not be called.
|
|
1759
|
+
#
|
|
1760
|
+
# You should implement this method or `legacy_invalid_empty_selections_on_union_with_type`
|
|
1761
|
+
# to log the violation so that you can contact clients and notify them about changing their queries.
|
|
1762
|
+
# Then return a suitable value to tell GraphQL-Ruby how to continue.
|
|
1763
|
+
# @param query [GraphQL::Query]
|
|
1764
|
+
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1765
|
+
# @return [String] A validation error to return for this query
|
|
1766
|
+
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1767
|
+
def legacy_invalid_empty_selections_on_union(query)
|
|
1768
|
+
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"
|
|
1769
|
+
end
|
|
1770
|
+
|
|
1704
1771
|
# This method is called during validation when a previously-allowed, but non-spec
|
|
1705
1772
|
# query is encountered where a union field has no child selections on it.
|
|
1706
1773
|
#
|
|
@@ -1708,11 +1775,12 @@ module GraphQL
|
|
|
1708
1775
|
# and notify them about changing their queries. Then return a suitable value to
|
|
1709
1776
|
# tell GraphQL-Ruby how to continue.
|
|
1710
1777
|
# @param query [GraphQL::Query]
|
|
1778
|
+
# @param type [Module] A GraphQL type definition
|
|
1711
1779
|
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1712
1780
|
# @return [String] A validation error to return for this query
|
|
1713
1781
|
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1714
|
-
def
|
|
1715
|
-
|
|
1782
|
+
def legacy_invalid_empty_selections_on_union_with_type(query, type)
|
|
1783
|
+
legacy_invalid_empty_selections_on_union(query)
|
|
1716
1784
|
end
|
|
1717
1785
|
|
|
1718
1786
|
# This setting controls how GraphQL-Ruby handles overlapping selections on scalar types when the types
|
|
@@ -1726,7 +1794,11 @@ module GraphQL
|
|
|
1726
1794
|
# @return [true, false, nil]
|
|
1727
1795
|
def allow_legacy_invalid_return_type_conflicts(new_value = NOT_CONFIGURED)
|
|
1728
1796
|
if NOT_CONFIGURED.equal?(new_value)
|
|
1729
|
-
@allow_legacy_invalid_return_type_conflicts
|
|
1797
|
+
if defined?(@allow_legacy_invalid_return_type_conflicts)
|
|
1798
|
+
@allow_legacy_invalid_return_type_conflicts
|
|
1799
|
+
else
|
|
1800
|
+
find_inherited_value(:allow_legacy_invalid_return_type_conflicts)
|
|
1801
|
+
end
|
|
1730
1802
|
else
|
|
1731
1803
|
@allow_legacy_invalid_return_type_conflicts = new_value
|
|
1732
1804
|
end
|
|
@@ -1774,7 +1846,11 @@ module GraphQL
|
|
|
1774
1846
|
# complexity_cost_calculation_mode(:compare)
|
|
1775
1847
|
def complexity_cost_calculation_mode(new_mode = NOT_CONFIGURED)
|
|
1776
1848
|
if NOT_CONFIGURED.equal?(new_mode)
|
|
1777
|
-
@complexity_cost_calculation_mode
|
|
1849
|
+
if defined?(@complexity_cost_calculation_mode)
|
|
1850
|
+
@complexity_cost_calculation_mode
|
|
1851
|
+
else
|
|
1852
|
+
find_inherited_value(:complexity_cost_calculation_mode)
|
|
1853
|
+
end
|
|
1778
1854
|
else
|
|
1779
1855
|
@complexity_cost_calculation_mode = new_mode
|
|
1780
1856
|
end
|
|
@@ -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,
|