graphql 1.12.20 → 1.13.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +3 -1
- data/lib/generators/graphql/install_generator.rb +9 -2
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/object_generator.rb +2 -1
- data/lib/generators/graphql/templates/schema.erb +2 -2
- data/lib/generators/graphql/type_generator.rb +0 -1
- data/lib/graphql/analysis/ast/field_usage.rb +2 -2
- data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
- data/lib/graphql/analysis/ast/visitor.rb +4 -4
- data/lib/graphql/backtrace/table.rb +1 -1
- data/lib/graphql/base_type.rb +4 -2
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/dataloader.rb +55 -22
- data/lib/graphql/directive/deprecated_directive.rb +1 -1
- data/lib/graphql/directive/include_directive.rb +1 -1
- data/lib/graphql/directive/skip_directive.rb +1 -1
- data/lib/graphql/directive.rb +0 -4
- data/lib/graphql/enum_type.rb +5 -1
- data/lib/graphql/execution/errors.rb +1 -0
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
- data/lib/graphql/execution/interpreter/runtime.rb +30 -18
- data/lib/graphql/execution/lookahead.rb +2 -2
- data/lib/graphql/execution/multiplex.rb +4 -1
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/int_type.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/input_value_type.rb +4 -4
- data/lib/graphql/introspection/schema_type.rb +2 -2
- data/lib/graphql/introspection/type_type.rb +10 -10
- data/lib/graphql/language/block_string.rb +2 -6
- data/lib/graphql/language/document_from_schema_definition.rb +4 -2
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/nodes.rb +12 -2
- data/lib/graphql/language/parser.rb +442 -434
- data/lib/graphql/language/parser.y +5 -4
- data/lib/graphql/language/printer.rb +6 -1
- data/lib/graphql/language/sanitized_printer.rb +5 -5
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/name_validator.rb +0 -4
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/arguments_cache.rb +1 -1
- data/lib/graphql/query/context.rb +15 -2
- data/lib/graphql/query/literal_input.rb +1 -1
- data/lib/graphql/query/null_context.rb +12 -7
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/relay/edges_instrumentation.rb +0 -1
- data/lib/graphql/relay/global_id_resolve.rb +1 -1
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/schema/addition.rb +37 -28
- data/lib/graphql/schema/argument.rb +8 -6
- data/lib/graphql/schema/build_from_definition.rb +5 -5
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +7 -3
- data/lib/graphql/schema/enum.rb +60 -10
- data/lib/graphql/schema/enum_value.rb +6 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +130 -39
- data/lib/graphql/schema/field_extension.rb +52 -2
- data/lib/graphql/schema/find_inherited_value.rb +1 -0
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +8 -5
- data/lib/graphql/schema/interface.rb +11 -20
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/list.rb +3 -1
- data/lib/graphql/schema/member/accepts_definition.rb +15 -3
- data/lib/graphql/schema/member/build_type.rb +0 -4
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/member/has_arguments.rb +55 -13
- data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +76 -18
- data/lib/graphql/schema/member/has_interfaces.rb +90 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/non_null.rb +3 -1
- data/lib/graphql/schema/object.rb +10 -75
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +29 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
- data/lib/graphql/schema/resolver.rb +19 -5
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/subscription.rb +11 -1
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +18 -4
- data/lib/graphql/schema/union.rb +8 -1
- data/lib/graphql/schema/validator/format_validator.rb +3 -5
- data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
- data/lib/graphql/schema/validator.rb +4 -7
- data/lib/graphql/schema/warden.rb +116 -52
- data/lib/graphql/schema.rb +106 -22
- data/lib/graphql/static_validation/base_visitor.rb +5 -5
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +15 -8
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
- data/lib/graphql/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -4
- data/lib/graphql/subscriptions/event.rb +20 -12
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
- data/lib/graphql/types/relay/default_relay.rb +5 -1
- data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
- data/lib/graphql/types/relay/has_node_field.rb +1 -1
- data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -32
- metadata +10 -5
data/lib/graphql/schema.rb
CHANGED
@@ -92,6 +92,8 @@ module GraphQL
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
+
class DuplicateNamesError < GraphQL::Error; end
|
96
|
+
|
95
97
|
class UnresolvedLateBoundTypeError < GraphQL::Error
|
96
98
|
attr_reader :type
|
97
99
|
def initialize(type:)
|
@@ -843,7 +845,7 @@ module GraphQL
|
|
843
845
|
# - Cause the Schema instance to be created, if it hasn't been created yet
|
844
846
|
# - Delegate to that instance
|
845
847
|
# Eventually, the methods will be moved into this class, removing the need for the singleton.
|
846
|
-
def_delegators :
|
848
|
+
def_delegators :deprecated_graphql_definition,
|
847
849
|
# Execution
|
848
850
|
:execution_strategy_for_operation,
|
849
851
|
# Configuration
|
@@ -852,6 +854,10 @@ module GraphQL
|
|
852
854
|
:id_from_object=, :object_from_id=,
|
853
855
|
:remove_handler
|
854
856
|
|
857
|
+
def deprecated_graphql_definition
|
858
|
+
graphql_definition(silence_deprecation_warning: true)
|
859
|
+
end
|
860
|
+
|
855
861
|
# @return [GraphQL::Subscriptions]
|
856
862
|
attr_accessor :subscriptions
|
857
863
|
|
@@ -894,8 +900,15 @@ module GraphQL
|
|
894
900
|
@find_cache[path] ||= @finder.find(path)
|
895
901
|
end
|
896
902
|
|
897
|
-
def graphql_definition
|
898
|
-
@graphql_definition ||=
|
903
|
+
def graphql_definition(silence_deprecation_warning: false)
|
904
|
+
@graphql_definition ||= begin
|
905
|
+
unless silence_deprecation_warning
|
906
|
+
message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Use a class-based definition instead."
|
907
|
+
caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
|
908
|
+
GraphQL::Deprecation.warn(message + caller_message)
|
909
|
+
end
|
910
|
+
to_graphql(silence_deprecation_warning: silence_deprecation_warning)
|
911
|
+
end
|
899
912
|
end
|
900
913
|
|
901
914
|
def default_filter
|
@@ -927,19 +940,20 @@ module GraphQL
|
|
927
940
|
find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
|
928
941
|
end
|
929
942
|
|
943
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
930
944
|
def to_graphql
|
931
945
|
schema_defn = self.new
|
932
946
|
schema_defn.raise_definition_error = true
|
933
|
-
schema_defn.query = query && query.graphql_definition
|
934
|
-
schema_defn.mutation = mutation && mutation.graphql_definition
|
935
|
-
schema_defn.subscription = subscription && subscription.graphql_definition
|
947
|
+
schema_defn.query = query && query.graphql_definition(silence_deprecation_warning: true)
|
948
|
+
schema_defn.mutation = mutation && mutation.graphql_definition(silence_deprecation_warning: true)
|
949
|
+
schema_defn.subscription = subscription && subscription.graphql_definition(silence_deprecation_warning: true)
|
936
950
|
schema_defn.validate_timeout = validate_timeout
|
937
951
|
schema_defn.validate_max_errors = validate_max_errors
|
938
952
|
schema_defn.max_complexity = max_complexity
|
939
953
|
schema_defn.error_bubbling = error_bubbling
|
940
954
|
schema_defn.max_depth = max_depth
|
941
955
|
schema_defn.default_max_page_size = default_max_page_size
|
942
|
-
schema_defn.orphan_types = orphan_types.map(
|
956
|
+
schema_defn.orphan_types = orphan_types.map { |t| t.graphql_definition(silence_deprecation_warning: true) }
|
943
957
|
schema_defn.disable_introspection_entry_points = disable_introspection_entry_points?
|
944
958
|
schema_defn.disable_schema_introspection_entry_point = disable_schema_introspection_entry_point?
|
945
959
|
schema_defn.disable_type_introspection_entry_point = disable_type_introspection_entry_point?
|
@@ -996,16 +1010,58 @@ module GraphQL
|
|
996
1010
|
# Build a map of `{ name => type }` and return it
|
997
1011
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
998
1012
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
999
|
-
def types
|
1000
|
-
non_introspection_types.merge(introspection_system.types)
|
1013
|
+
def types(context = GraphQL::Query::NullContext)
|
1014
|
+
all_types = non_introspection_types.merge(introspection_system.types)
|
1015
|
+
visible_types = {}
|
1016
|
+
all_types.each do |k, v|
|
1017
|
+
visible_types[k] =if v.is_a?(Array)
|
1018
|
+
visible_t = nil
|
1019
|
+
v.each do |t|
|
1020
|
+
if t.visible?(context)
|
1021
|
+
if visible_t.nil?
|
1022
|
+
visible_t = t
|
1023
|
+
else
|
1024
|
+
raise DuplicateNamesError, "Found two visible type definitions for `#{k}`: #{visible_t.inspect}, #{t.inspect}"
|
1025
|
+
end
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
visible_t
|
1029
|
+
else
|
1030
|
+
v
|
1031
|
+
end
|
1032
|
+
end
|
1033
|
+
visible_types
|
1001
1034
|
end
|
1002
1035
|
|
1003
1036
|
# @param type_name [String]
|
1004
1037
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
1005
|
-
def get_type(type_name)
|
1006
|
-
own_types[type_name]
|
1007
|
-
|
1008
|
-
|
1038
|
+
def get_type(type_name, context = GraphQL::Query::NullContext)
|
1039
|
+
local_entry = own_types[type_name]
|
1040
|
+
type_defn = case local_entry
|
1041
|
+
when nil
|
1042
|
+
nil
|
1043
|
+
when Array
|
1044
|
+
visible_t = nil
|
1045
|
+
warden = Warden.from_context(context)
|
1046
|
+
local_entry.each do |t|
|
1047
|
+
if warden.visible_type?(t, context)
|
1048
|
+
if visible_t.nil?
|
1049
|
+
visible_t = t
|
1050
|
+
else
|
1051
|
+
raise DuplicateNamesError, "Found two visible type definitions for `#{type_name}`: #{visible_t.inspect}, #{t.inspect}"
|
1052
|
+
end
|
1053
|
+
end
|
1054
|
+
end
|
1055
|
+
visible_t
|
1056
|
+
when Module
|
1057
|
+
local_entry
|
1058
|
+
else
|
1059
|
+
raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}"
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
type_defn ||
|
1063
|
+
introspection_system.types[type_name] || # todo context-specific introspection?
|
1064
|
+
(superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
|
1009
1065
|
end
|
1010
1066
|
|
1011
1067
|
# @api private
|
@@ -1182,19 +1238,19 @@ module GraphQL
|
|
1182
1238
|
GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
|
1183
1239
|
end
|
1184
1240
|
|
1185
|
-
def get_field(type_or_name, field_name)
|
1241
|
+
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
|
1186
1242
|
parent_type = case type_or_name
|
1187
1243
|
when LateBoundType
|
1188
|
-
get_type(type_or_name.name)
|
1244
|
+
get_type(type_or_name.name, context)
|
1189
1245
|
when String
|
1190
|
-
get_type(type_or_name)
|
1246
|
+
get_type(type_or_name, context)
|
1191
1247
|
when Module
|
1192
1248
|
type_or_name
|
1193
1249
|
else
|
1194
1250
|
raise ArgumentError, "unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
|
1195
1251
|
end
|
1196
1252
|
|
1197
|
-
if parent_type.kind.fields? && (field = parent_type.get_field(field_name))
|
1253
|
+
if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
|
1198
1254
|
field
|
1199
1255
|
elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
|
1200
1256
|
entry_point_field
|
@@ -1205,8 +1261,8 @@ module GraphQL
|
|
1205
1261
|
end
|
1206
1262
|
end
|
1207
1263
|
|
1208
|
-
def get_fields(type)
|
1209
|
-
type.fields
|
1264
|
+
def get_fields(type, context = GraphQL::Query::NullContext)
|
1265
|
+
type.fields(context)
|
1210
1266
|
end
|
1211
1267
|
|
1212
1268
|
def introspection(new_introspection_namespace = nil)
|
@@ -1405,7 +1461,6 @@ module GraphQL
|
|
1405
1461
|
if new_orphan_types.any?
|
1406
1462
|
new_orphan_types = new_orphan_types.flatten
|
1407
1463
|
add_type_and_traverse(new_orphan_types, root: false)
|
1408
|
-
@orphan_types = new_orphan_types
|
1409
1464
|
own_orphan_types.concat(new_orphan_types.flatten)
|
1410
1465
|
end
|
1411
1466
|
|
@@ -1715,7 +1770,7 @@ module GraphQL
|
|
1715
1770
|
if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot)
|
1716
1771
|
GraphQL::Deprecation.warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).")
|
1717
1772
|
else
|
1718
|
-
subscription.
|
1773
|
+
subscription.all_field_definitions.each do |field|
|
1719
1774
|
field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
|
1720
1775
|
end
|
1721
1776
|
end
|
@@ -1737,7 +1792,36 @@ module GraphQL
|
|
1737
1792
|
end
|
1738
1793
|
new_types = Array(t)
|
1739
1794
|
addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
|
1740
|
-
|
1795
|
+
addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time
|
1796
|
+
if (prev_entry = own_types[name])
|
1797
|
+
prev_entries = case prev_entry
|
1798
|
+
when Array
|
1799
|
+
prev_entry
|
1800
|
+
when Module
|
1801
|
+
own_types[name] = [prev_entry]
|
1802
|
+
else
|
1803
|
+
raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}"
|
1804
|
+
end
|
1805
|
+
|
1806
|
+
case types_entry
|
1807
|
+
when Array
|
1808
|
+
prev_entries.concat(types_entry)
|
1809
|
+
prev_entries.uniq! # in case any are being re-visited
|
1810
|
+
when Module
|
1811
|
+
if !prev_entries.include?(types_entry)
|
1812
|
+
prev_entries << types_entry
|
1813
|
+
end
|
1814
|
+
else
|
1815
|
+
raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}"
|
1816
|
+
end
|
1817
|
+
else
|
1818
|
+
if types_entry.is_a?(Array)
|
1819
|
+
types_entry.uniq!
|
1820
|
+
end
|
1821
|
+
own_types[name] = types_entry
|
1822
|
+
end
|
1823
|
+
end
|
1824
|
+
|
1741
1825
|
own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
|
1742
1826
|
own_union_memberships.merge!(addition.union_memberships)
|
1743
1827
|
|
@@ -94,7 +94,7 @@ module GraphQL
|
|
94
94
|
|
95
95
|
def on_field(node, parent)
|
96
96
|
parent_type = @object_types.last
|
97
|
-
field_definition = @schema.get_field(parent_type, node.name)
|
97
|
+
field_definition = @schema.get_field(parent_type, node.name, @context.query.context)
|
98
98
|
@field_definitions.push(field_definition)
|
99
99
|
if !field_definition.nil?
|
100
100
|
next_object_type = field_definition.type.unwrap
|
@@ -120,14 +120,14 @@ module GraphQL
|
|
120
120
|
argument_defn = if (arg = @argument_definitions.last)
|
121
121
|
arg_type = arg.type.unwrap
|
122
122
|
if arg_type.kind.input_object?
|
123
|
-
arg_type
|
123
|
+
@context.warden.get_argument(arg_type, node.name)
|
124
124
|
else
|
125
125
|
nil
|
126
126
|
end
|
127
127
|
elsif (directive_defn = @directive_definitions.last)
|
128
|
-
directive_defn
|
128
|
+
@context.warden.get_argument(directive_defn, node.name)
|
129
129
|
elsif (field_defn = @field_definitions.last)
|
130
|
-
field_defn
|
130
|
+
@context.warden.get_argument(field_defn, node.name)
|
131
131
|
else
|
132
132
|
nil
|
133
133
|
end
|
@@ -187,7 +187,7 @@ module GraphQL
|
|
187
187
|
|
188
188
|
def on_fragment_with_type(node)
|
189
189
|
object_type = if node.type
|
190
|
-
@
|
190
|
+
@context.warden.get_type(node.type.name)
|
191
191
|
else
|
192
192
|
@object_types.last
|
193
193
|
end
|
@@ -95,7 +95,7 @@ module GraphQL
|
|
95
95
|
def required_input_fields_are_present(type, ast_node)
|
96
96
|
# TODO - would be nice to use these to create an error message so the caller knows
|
97
97
|
# that required fields are missing
|
98
|
-
required_field_names =
|
98
|
+
required_field_names = @warden.arguments(type)
|
99
99
|
.select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
|
100
100
|
.map(&:name)
|
101
101
|
|
@@ -15,7 +15,7 @@ module GraphQL
|
|
15
15
|
if @context.schema.error_bubbling || context.errors.none? { |err| err.path.take(@path.size) == @path }
|
16
16
|
parent_defn = parent_definition(parent)
|
17
17
|
|
18
|
-
if parent_defn && (arg_defn = parent_defn
|
18
|
+
if parent_defn && (arg_defn = context.warden.get_argument(parent_defn, node.name))
|
19
19
|
validation_result = context.validate_literal(node.value, arg_defn.type)
|
20
20
|
if !validation_result.valid?
|
21
21
|
kind_of_node = node_type(parent)
|
@@ -50,15 +50,15 @@ module GraphQL
|
|
50
50
|
@arg_conflicts = nil
|
51
51
|
|
52
52
|
yield
|
53
|
-
|
54
|
-
field_conflicts.each_value { |error| add_error(error) }
|
55
|
-
arg_conflicts.each_value { |error| add_error(error) }
|
53
|
+
# don't initialize these if they weren't initialized in the block:
|
54
|
+
@field_conflicts && @field_conflicts.each_value { |error| add_error(error) }
|
55
|
+
@arg_conflicts && @arg_conflicts.each_value { |error| add_error(error) }
|
56
56
|
end
|
57
57
|
|
58
58
|
def conflicts_within_selection_set(node, parent_type)
|
59
59
|
return if parent_type.nil?
|
60
60
|
|
61
|
-
fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents:
|
61
|
+
fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents: nil)
|
62
62
|
|
63
63
|
# (A) Find find all conflicts "within" the fields of this selection set.
|
64
64
|
find_conflicts_within(fields)
|
@@ -198,10 +198,14 @@ module GraphQL
|
|
198
198
|
response_keys.each do |key, fields|
|
199
199
|
next if fields.size < 2
|
200
200
|
# find conflicts within nodes
|
201
|
-
|
202
|
-
|
201
|
+
i = 0
|
202
|
+
while i < fields.size
|
203
|
+
j = i + 1
|
204
|
+
while j < fields.size
|
203
205
|
find_conflict(key, fields[i], fields[j])
|
206
|
+
j += 1
|
204
207
|
end
|
208
|
+
i += 1
|
205
209
|
end
|
206
210
|
end
|
207
211
|
end
|
@@ -243,7 +247,9 @@ module GraphQL
|
|
243
247
|
end
|
244
248
|
|
245
249
|
def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive:)
|
246
|
-
return if field1.definition.nil? ||
|
250
|
+
return if field1.definition.nil? ||
|
251
|
+
field2.definition.nil? ||
|
252
|
+
(field1.node.selections.empty? && field2.node.selections.empty?)
|
247
253
|
|
248
254
|
return_type1 = field1.definition.type.unwrap
|
249
255
|
return_type2 = field2.definition.type.unwrap
|
@@ -323,6 +329,7 @@ module GraphQL
|
|
323
329
|
if node.selections.empty?
|
324
330
|
NO_SELECTIONS
|
325
331
|
else
|
332
|
+
parents ||= []
|
326
333
|
fields, fragment_spreads = find_fields_and_fragments(node.selections, owner_type: owner_type, parents: parents, fields: [], fragment_spreads: [])
|
327
334
|
response_keys = fields.group_by { |f| f.node.alias || f.node.name }
|
328
335
|
[response_keys, fragment_spreads]
|
@@ -333,7 +340,7 @@ module GraphQL
|
|
333
340
|
selections.each do |node|
|
334
341
|
case node
|
335
342
|
when GraphQL::Language::Nodes::Field
|
336
|
-
definition = context.
|
343
|
+
definition = context.query.get_field(owner_type, node.name)
|
337
344
|
fields << Field.new(node, definition, owner_type, parents)
|
338
345
|
when GraphQL::Language::Nodes::InlineFragment
|
339
346
|
fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
|
@@ -16,8 +16,10 @@ module GraphQL
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def assert_required_args(ast_node, defn)
|
19
|
+
args = defn.arguments(context.query.context)
|
20
|
+
return if args.empty?
|
19
21
|
present_argument_names = ast_node.arguments.map(&:name)
|
20
|
-
required_argument_names =
|
22
|
+
required_argument_names = context.warden.arguments(defn)
|
21
23
|
.select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
|
22
24
|
.map(&:name)
|
23
25
|
|
@@ -34,16 +34,16 @@ module GraphQL
|
|
34
34
|
parent_type = get_parent_type(context, parent)
|
35
35
|
return unless parent_type && parent_type.kind.input_object?
|
36
36
|
|
37
|
-
required_fields =
|
38
|
-
.select{|
|
39
|
-
.
|
37
|
+
required_fields = context.warden.arguments(parent_type)
|
38
|
+
.select{|arg| arg.type.kind.non_null?}
|
39
|
+
.map(&:graphql_name)
|
40
40
|
|
41
41
|
present_fields = ast_node.arguments.map(&:name)
|
42
42
|
missing_fields = required_fields - present_fields
|
43
43
|
|
44
44
|
missing_fields.each do |missing_field|
|
45
45
|
path = [*context.path, missing_field]
|
46
|
-
missing_field_type = parent_type
|
46
|
+
missing_field_type = context.warden.get_argument(parent_type, missing_field).type
|
47
47
|
add_error(RequiredInputObjectAttributesArePresentError.new(
|
48
48
|
"Argument '#{missing_field}' on InputObject '#{parent_type.to_type_signature}' is required. Expected type #{missing_field_type.to_type_signature}",
|
49
49
|
argument_name: missing_field,
|
@@ -22,15 +22,15 @@ module GraphQL
|
|
22
22
|
node_values = node_values.select { |value| value.is_a? GraphQL::Language::Nodes::VariableIdentifier }
|
23
23
|
|
24
24
|
if node_values.any?
|
25
|
-
|
25
|
+
argument_owner = case parent
|
26
26
|
when GraphQL::Language::Nodes::Field
|
27
|
-
context.field_definition
|
27
|
+
context.field_definition
|
28
28
|
when GraphQL::Language::Nodes::Directive
|
29
|
-
context.directive_definition
|
29
|
+
context.directive_definition
|
30
30
|
when GraphQL::Language::Nodes::InputObject
|
31
31
|
arg_type = context.argument_definition.type.unwrap
|
32
32
|
if arg_type.kind.input_object?
|
33
|
-
|
33
|
+
arg_type
|
34
34
|
else
|
35
35
|
# This is some kind of error
|
36
36
|
nil
|
@@ -43,7 +43,7 @@ module GraphQL
|
|
43
43
|
var_defn_ast = @declared_variables[node_value.name]
|
44
44
|
# Might be undefined :(
|
45
45
|
# VariablesAreUsedAndDefined can't finalize its search until the end of the document.
|
46
|
-
var_defn_ast &&
|
46
|
+
var_defn_ast && argument_owner && validate_usage(argument_owner, node, var_defn_ast)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
super
|
@@ -51,7 +51,7 @@ module GraphQL
|
|
51
51
|
|
52
52
|
private
|
53
53
|
|
54
|
-
def validate_usage(
|
54
|
+
def validate_usage(argument_owner, arg_node, ast_var)
|
55
55
|
var_type = context.schema.type_from_ast(ast_var.type, context: context)
|
56
56
|
if var_type.nil?
|
57
57
|
return
|
@@ -65,7 +65,7 @@ module GraphQL
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
arg_defn =
|
68
|
+
arg_defn = context.warden.get_argument(argument_owner, arg_node.name)
|
69
69
|
arg_defn_type = arg_defn.type
|
70
70
|
|
71
71
|
var_inner_type = var_type.unwrap
|
data/lib/graphql/string_type.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
GraphQL::STRING_TYPE = GraphQL::Types::String.graphql_definition
|
2
|
+
GraphQL::STRING_TYPE = GraphQL::Types::String.graphql_definition(silence_deprecation_warning: true)
|
@@ -170,10 +170,12 @@ module GraphQL
|
|
170
170
|
first_subscription_id = first_event.context.fetch(:subscription_id)
|
171
171
|
object ||= load_action_cable_message(message, first_event.context)
|
172
172
|
result = execute_update(first_subscription_id, first_event, object)
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
173
|
+
if !result.nil?
|
174
|
+
# Having calculated the result _once_, send the same payload to all subscribers
|
175
|
+
events.each do |event|
|
176
|
+
subscription_id = event.context.fetch(:subscription_id)
|
177
|
+
deliver(subscription_id, result)
|
178
|
+
end
|
177
179
|
end
|
178
180
|
end
|
179
181
|
end
|
@@ -214,6 +216,8 @@ module GraphQL
|
|
214
216
|
# The channel was closed, forget about it.
|
215
217
|
def delete_subscription(subscription_id)
|
216
218
|
query = @subscriptions.delete(subscription_id)
|
219
|
+
# In case this came from the server, tell the client to unsubscribe:
|
220
|
+
@action_cable.server.broadcast(stream_subscription_name(subscription_id), { more: false })
|
217
221
|
# This can be `nil` when `.trigger` happens inside an unsubscribed ActionCable channel,
|
218
222
|
# see https://github.com/rmosolgo/graphql-ruby/issues/2478
|
219
223
|
if query
|
@@ -23,15 +23,23 @@ module GraphQL
|
|
23
23
|
@arguments = arguments
|
24
24
|
@context = context
|
25
25
|
field ||= context.field
|
26
|
-
|
26
|
+
scope_key = field.subscription_scope
|
27
|
+
scope_val = scope || (context && scope_key && context[scope_key])
|
28
|
+
if scope_key &&
|
29
|
+
(subscription = field.resolver) &&
|
30
|
+
(subscription.respond_to?(:subscription_scope_optional?)) &&
|
31
|
+
!subscription.subscription_scope_optional? &&
|
32
|
+
scope_val.nil?
|
33
|
+
raise Subscriptions::SubscriptionScopeMissingError, "#{field.path} (#{subscription}) requires a `scope:` value to trigger updates (Set `subscription_scope ..., optional: true` to disable this requirement)"
|
34
|
+
end
|
27
35
|
|
28
|
-
@topic = self.class.serialize(name, arguments, field, scope: scope_val)
|
36
|
+
@topic = self.class.serialize(name, arguments, field, scope: scope_val, context: context)
|
29
37
|
end
|
30
38
|
|
31
39
|
# @return [String] an identifier for this unit of subscription
|
32
|
-
def self.serialize(_name, arguments, field, scope:)
|
40
|
+
def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext)
|
33
41
|
subscription = field.resolver || GraphQL::Schema::Subscription
|
34
|
-
normalized_args = stringify_args(field, arguments.to_h)
|
42
|
+
normalized_args = stringify_args(field, arguments.to_h, context)
|
35
43
|
subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
|
36
44
|
end
|
37
45
|
|
@@ -83,7 +91,7 @@ module GraphQL
|
|
83
91
|
end
|
84
92
|
end
|
85
93
|
|
86
|
-
def stringify_args(arg_owner, args)
|
94
|
+
def stringify_args(arg_owner, args, context)
|
87
95
|
arg_owner = arg_owner.respond_to?(:unwrap) ? arg_owner.unwrap : arg_owner # remove list and non-null wrappers
|
88
96
|
case args
|
89
97
|
when Hash
|
@@ -91,13 +99,13 @@ module GraphQL
|
|
91
99
|
args.each do |k, v|
|
92
100
|
arg_name = k.to_s
|
93
101
|
camelized_arg_name = GraphQL::Schema::Member::BuildType.camelize(arg_name)
|
94
|
-
arg_defn = get_arg_definition(arg_owner, camelized_arg_name)
|
102
|
+
arg_defn = get_arg_definition(arg_owner, camelized_arg_name, context)
|
95
103
|
|
96
104
|
if arg_defn
|
97
105
|
normalized_arg_name = camelized_arg_name
|
98
106
|
else
|
99
107
|
normalized_arg_name = arg_name
|
100
|
-
arg_defn = get_arg_definition(arg_owner, normalized_arg_name)
|
108
|
+
arg_defn = get_arg_definition(arg_owner, normalized_arg_name, context)
|
101
109
|
end
|
102
110
|
arg_base_type = arg_defn.type.unwrap
|
103
111
|
# In the case where the value being emitted is seen as a "JSON"
|
@@ -113,22 +121,22 @@ module GraphQL
|
|
113
121
|
end
|
114
122
|
next_args[normalized_arg_name] = sorted_value.respond_to?(:to_json) ? sorted_value.to_json : sorted_value
|
115
123
|
else
|
116
|
-
next_args[normalized_arg_name] = stringify_args(arg_base_type, v)
|
124
|
+
next_args[normalized_arg_name] = stringify_args(arg_base_type, v, context)
|
117
125
|
end
|
118
126
|
end
|
119
127
|
# Make sure they're deeply sorted
|
120
128
|
next_args.sort.to_h
|
121
129
|
when Array
|
122
|
-
args.map { |a| stringify_args(arg_owner, a) }
|
130
|
+
args.map { |a| stringify_args(arg_owner, a, context) }
|
123
131
|
when GraphQL::Schema::InputObject
|
124
|
-
stringify_args(arg_owner, args.to_h)
|
132
|
+
stringify_args(arg_owner, args.to_h, context)
|
125
133
|
else
|
126
134
|
args
|
127
135
|
end
|
128
136
|
end
|
129
137
|
|
130
|
-
def get_arg_definition(arg_owner, arg_name)
|
131
|
-
arg_owner.
|
138
|
+
def get_arg_definition(arg_owner, arg_name, context)
|
139
|
+
arg_owner.get_argument(arg_name, context) || arg_owner.arguments(context).each_value.find { |v| v.keyword.to_s == arg_name }
|
132
140
|
end
|
133
141
|
end
|
134
142
|
end
|
@@ -16,6 +16,13 @@ module GraphQL
|
|
16
16
|
class InvalidTriggerError < GraphQL::Error
|
17
17
|
end
|
18
18
|
|
19
|
+
# Raised when either:
|
20
|
+
# - An initial subscription didn't have a value for `context[subscription_scope]`
|
21
|
+
# - Or, an update didn't pass `.trigger(..., scope:)`
|
22
|
+
# When raised, the initial subscription or update fails completely.
|
23
|
+
class SubscriptionScopeMissingError < GraphQL::Error
|
24
|
+
end
|
25
|
+
|
19
26
|
# @see {Subscriptions#initialize} for options, concrete implementations may add options.
|
20
27
|
def self.use(defn, options = {})
|
21
28
|
schema = defn.is_a?(Class) ? defn : defn.target
|
@@ -76,7 +83,8 @@ module GraphQL
|
|
76
83
|
end
|
77
84
|
|
78
85
|
# Normalize symbol-keyed args to strings, try camelizing them
|
79
|
-
|
86
|
+
# Should this accept a real context somehow?
|
87
|
+
normalized_args = normalize_arguments(normalized_event_name, field, args, GraphQL::Query::NullContext)
|
80
88
|
|
81
89
|
event = Subscriptions::Event.new(
|
82
90
|
name: normalized_event_name,
|
@@ -151,16 +159,6 @@ module GraphQL
|
|
151
159
|
# @param object [Object]
|
152
160
|
# @return [void]
|
153
161
|
def execute_all(event, object)
|
154
|
-
each_subscription_id(event) do |subscription_id|
|
155
|
-
execute(subscription_id, event, object)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Get each `subscription_id` subscribed to `event.topic` and yield them
|
160
|
-
# @param event [GraphQL::Subscriptions::Event]
|
161
|
-
# @yieldparam subscription_id [String]
|
162
|
-
# @return [void]
|
163
|
-
def each_subscription_id(event)
|
164
162
|
raise GraphQL::RequiredImplementationMissingError
|
165
163
|
end
|
166
164
|
|
@@ -233,7 +231,7 @@ module GraphQL
|
|
233
231
|
# @param arg_owner [GraphQL::Field, GraphQL::BaseType]
|
234
232
|
# @param args [Hash, Array, Any] some GraphQL input value to coerce as `arg_owner`
|
235
233
|
# @return [Any] normalized arguments value
|
236
|
-
def normalize_arguments(event_name, arg_owner, args)
|
234
|
+
def normalize_arguments(event_name, arg_owner, args, context)
|
237
235
|
case arg_owner
|
238
236
|
when GraphQL::Field, GraphQL::InputObjectType, GraphQL::Schema::Field, Class
|
239
237
|
if arg_owner.is_a?(Class) && !arg_owner.kind.input_object?
|
@@ -244,19 +242,19 @@ module GraphQL
|
|
244
242
|
missing_arg_names = []
|
245
243
|
args.each do |k, v|
|
246
244
|
arg_name = k.to_s
|
247
|
-
arg_defn = arg_owner.
|
245
|
+
arg_defn = arg_owner.get_argument(arg_name, context)
|
248
246
|
if arg_defn
|
249
247
|
normalized_arg_name = arg_name
|
250
248
|
else
|
251
249
|
normalized_arg_name = normalize_name(arg_name)
|
252
|
-
arg_defn = arg_owner.
|
250
|
+
arg_defn = arg_owner.get_argument(normalized_arg_name, context)
|
253
251
|
end
|
254
252
|
|
255
253
|
if arg_defn
|
256
254
|
if arg_defn.loads
|
257
255
|
normalized_arg_name = arg_defn.keyword.to_s
|
258
256
|
end
|
259
|
-
normalized = normalize_arguments(event_name, arg_defn.type, v)
|
257
|
+
normalized = normalize_arguments(event_name, arg_defn.type, v, context)
|
260
258
|
normalized_args[normalized_arg_name] = normalized
|
261
259
|
else
|
262
260
|
# Couldn't find a matching argument definition
|
@@ -266,12 +264,12 @@ module GraphQL
|
|
266
264
|
|
267
265
|
# Backfill default values so that trigger arguments
|
268
266
|
# match query arguments.
|
269
|
-
arg_owner.arguments.each do |
|
267
|
+
arg_owner.arguments(context).each do |_name, arg_defn|
|
270
268
|
if arg_defn.default_value? && !normalized_args.key?(arg_defn.name)
|
271
269
|
default_value = arg_defn.default_value
|
272
270
|
# We don't have an underlying "object" here, so it can't call methods.
|
273
271
|
# This is broken.
|
274
|
-
normalized_args[arg_defn.name] = arg_defn.prepare_value(nil, default_value, context:
|
272
|
+
normalized_args[arg_defn.name] = arg_defn.prepare_value(nil, default_value, context: context)
|
275
273
|
end
|
276
274
|
end
|
277
275
|
|
@@ -290,9 +288,9 @@ module GraphQL
|
|
290
288
|
|
291
289
|
normalized_args
|
292
290
|
when GraphQL::ListType, GraphQL::Schema::List
|
293
|
-
args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a) }
|
291
|
+
args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) }
|
294
292
|
when GraphQL::NonNullType, GraphQL::Schema::NonNull
|
295
|
-
normalize_arguments(event_name, arg_owner.of_type, args)
|
293
|
+
normalize_arguments(event_name, arg_owner.of_type, args, context)
|
296
294
|
else
|
297
295
|
args
|
298
296
|
end
|