graphql 1.12.24 → 1.13.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -8
  3. data/lib/generators/graphql/enum_generator.rb +4 -10
  4. data/lib/generators/graphql/field_extractor.rb +31 -0
  5. data/lib/generators/graphql/input_generator.rb +50 -0
  6. data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
  7. data/lib/generators/graphql/install_generator.rb +10 -3
  8. data/lib/generators/graphql/interface_generator.rb +7 -7
  9. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  10. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  11. data/lib/generators/graphql/mutation_generator.rb +5 -30
  12. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  13. data/lib/generators/graphql/object_generator.rb +8 -37
  14. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  15. data/lib/generators/graphql/scalar_generator.rb +4 -2
  16. data/lib/generators/graphql/templates/enum.erb +5 -1
  17. data/lib/generators/graphql/templates/input.erb +9 -0
  18. data/lib/generators/graphql/templates/interface.erb +4 -2
  19. data/lib/generators/graphql/templates/mutation.erb +1 -1
  20. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  21. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  22. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  23. data/lib/generators/graphql/templates/object.erb +4 -2
  24. data/lib/generators/graphql/templates/scalar.erb +3 -1
  25. data/lib/generators/graphql/templates/union.erb +4 -2
  26. data/lib/generators/graphql/type_generator.rb +46 -10
  27. data/lib/generators/graphql/union_generator.rb +5 -5
  28. data/lib/graphql/analysis/ast/field_usage.rb +2 -2
  29. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  30. data/lib/graphql/analysis/ast/visitor.rb +5 -4
  31. data/lib/graphql/argument.rb +1 -1
  32. data/lib/graphql/backtrace/table.rb +1 -1
  33. data/lib/graphql/base_type.rb +5 -3
  34. data/lib/graphql/boolean_type.rb +1 -1
  35. data/lib/graphql/dataloader/source.rb +2 -2
  36. data/lib/graphql/dataloader.rb +55 -22
  37. data/lib/graphql/date_encoding_error.rb +16 -0
  38. data/lib/graphql/define/instance_definable.rb +15 -0
  39. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  40. data/lib/graphql/directive/include_directive.rb +1 -1
  41. data/lib/graphql/directive/skip_directive.rb +1 -1
  42. data/lib/graphql/directive.rb +1 -5
  43. data/lib/graphql/enum_type.rb +7 -3
  44. data/lib/graphql/execution/errors.rb +1 -0
  45. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  46. data/lib/graphql/execution/interpreter/arguments_cache.rb +6 -4
  47. data/lib/graphql/execution/interpreter/runtime.rb +66 -38
  48. data/lib/graphql/execution/lookahead.rb +2 -2
  49. data/lib/graphql/execution/multiplex.rb +4 -1
  50. data/lib/graphql/field.rb +1 -1
  51. data/lib/graphql/float_type.rb +1 -1
  52. data/lib/graphql/id_type.rb +1 -1
  53. data/lib/graphql/input_object_type.rb +1 -1
  54. data/lib/graphql/int_type.rb +1 -1
  55. data/lib/graphql/interface_type.rb +1 -1
  56. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  57. data/lib/graphql/introspection/directive_type.rb +5 -3
  58. data/lib/graphql/introspection/entry_points.rb +2 -2
  59. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  60. data/lib/graphql/introspection/field_type.rb +3 -3
  61. data/lib/graphql/introspection/input_value_type.rb +4 -4
  62. data/lib/graphql/introspection/schema_type.rb +9 -4
  63. data/lib/graphql/introspection/type_type.rb +18 -12
  64. data/lib/graphql/introspection.rb +4 -1
  65. data/lib/graphql/language/block_string.rb +2 -6
  66. data/lib/graphql/language/document_from_schema_definition.rb +11 -4
  67. data/lib/graphql/language/lexer.rb +50 -28
  68. data/lib/graphql/language/lexer.rl +2 -4
  69. data/lib/graphql/language/nodes.rb +4 -3
  70. data/lib/graphql/language/parser.rb +841 -820
  71. data/lib/graphql/language/parser.y +13 -6
  72. data/lib/graphql/language/printer.rb +10 -1
  73. data/lib/graphql/language/sanitized_printer.rb +5 -5
  74. data/lib/graphql/language/token.rb +0 -4
  75. data/lib/graphql/name_validator.rb +0 -4
  76. data/lib/graphql/object_type.rb +2 -2
  77. data/lib/graphql/pagination/active_record_relation_connection.rb +43 -6
  78. data/lib/graphql/pagination/relation_connection.rb +59 -29
  79. data/lib/graphql/query/arguments.rb +1 -1
  80. data/lib/graphql/query/arguments_cache.rb +1 -1
  81. data/lib/graphql/query/context.rb +15 -2
  82. data/lib/graphql/query/input_validation_result.rb +9 -0
  83. data/lib/graphql/query/literal_input.rb +1 -1
  84. data/lib/graphql/query/null_context.rb +12 -7
  85. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  86. data/lib/graphql/query/validation_pipeline.rb +2 -3
  87. data/lib/graphql/query/variable_validation_error.rb +2 -2
  88. data/lib/graphql/query/variables.rb +35 -4
  89. data/lib/graphql/query.rb +0 -1
  90. data/lib/graphql/relay/connection_type.rb +15 -2
  91. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  92. data/lib/graphql/relay/global_id_resolve.rb +1 -2
  93. data/lib/graphql/relay/mutation.rb +1 -1
  94. data/lib/graphql/relay/page_info.rb +1 -1
  95. data/lib/graphql/relay/range_add.rb +4 -0
  96. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  97. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  98. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  99. data/lib/graphql/rubocop.rb +4 -0
  100. data/lib/graphql/scalar_type.rb +1 -1
  101. data/lib/graphql/schema/addition.rb +37 -28
  102. data/lib/graphql/schema/argument.rb +30 -15
  103. data/lib/graphql/schema/build_from_definition.rb +6 -5
  104. data/lib/graphql/schema/directive/feature.rb +1 -1
  105. data/lib/graphql/schema/directive/flagged.rb +2 -2
  106. data/lib/graphql/schema/directive/include.rb +1 -1
  107. data/lib/graphql/schema/directive/skip.rb +1 -1
  108. data/lib/graphql/schema/directive/transform.rb +1 -1
  109. data/lib/graphql/schema/directive.rb +23 -4
  110. data/lib/graphql/schema/enum.rb +61 -12
  111. data/lib/graphql/schema/enum_value.rb +6 -0
  112. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  113. data/lib/graphql/schema/field.rb +261 -83
  114. data/lib/graphql/schema/field_extension.rb +89 -2
  115. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  116. data/lib/graphql/schema/finder.rb +5 -5
  117. data/lib/graphql/schema/input_object.rb +24 -7
  118. data/lib/graphql/schema/interface.rb +11 -20
  119. data/lib/graphql/schema/introspection_system.rb +1 -1
  120. data/lib/graphql/schema/list.rb +21 -4
  121. data/lib/graphql/schema/loader.rb +3 -0
  122. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  123. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
  124. data/lib/graphql/schema/member/build_type.rb +0 -4
  125. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  126. data/lib/graphql/schema/member/has_arguments.rb +56 -14
  127. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  128. data/lib/graphql/schema/member/has_fields.rb +76 -18
  129. data/lib/graphql/schema/member/has_interfaces.rb +100 -0
  130. data/lib/graphql/schema/member/validates_input.rb +2 -2
  131. data/lib/graphql/schema/member.rb +1 -0
  132. data/lib/graphql/schema/non_null.rb +9 -3
  133. data/lib/graphql/schema/object.rb +10 -75
  134. data/lib/graphql/schema/printer.rb +1 -1
  135. data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
  136. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  137. data/lib/graphql/schema/resolver.rb +37 -17
  138. data/lib/graphql/schema/scalar.rb +15 -1
  139. data/lib/graphql/schema/subscription.rb +11 -1
  140. data/lib/graphql/schema/traversal.rb +1 -1
  141. data/lib/graphql/schema/type_expression.rb +1 -1
  142. data/lib/graphql/schema/type_membership.rb +18 -4
  143. data/lib/graphql/schema/union.rb +8 -1
  144. data/lib/graphql/schema/validator/format_validator.rb +0 -4
  145. data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
  146. data/lib/graphql/schema/validator/required_validator.rb +29 -15
  147. data/lib/graphql/schema/validator.rb +4 -7
  148. data/lib/graphql/schema/warden.rb +126 -53
  149. data/lib/graphql/schema.rb +120 -24
  150. data/lib/graphql/static_validation/all_rules.rb +1 -0
  151. data/lib/graphql/static_validation/base_visitor.rb +6 -6
  152. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  153. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  154. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  155. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  156. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  157. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  158. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  159. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  160. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
  161. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  162. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  163. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +13 -7
  164. data/lib/graphql/static_validation/validation_context.rb +4 -0
  165. data/lib/graphql/string_type.rb +1 -1
  166. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -4
  167. data/lib/graphql/subscriptions/event.rb +20 -12
  168. data/lib/graphql/subscriptions/serialize.rb +22 -2
  169. data/lib/graphql/subscriptions.rb +17 -19
  170. data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
  171. data/lib/graphql/tracing/data_dog_tracing.rb +24 -2
  172. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  173. data/lib/graphql/tracing/platform_tracing.rb +20 -10
  174. data/lib/graphql/types/iso_8601_date.rb +13 -5
  175. data/lib/graphql/types/iso_8601_date_time.rb +8 -1
  176. data/lib/graphql/types/relay/connection_behaviors.rb +28 -10
  177. data/lib/graphql/types/relay/default_relay.rb +5 -1
  178. data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
  179. data/lib/graphql/types/relay/has_node_field.rb +1 -1
  180. data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
  181. data/lib/graphql/types/relay/node_field.rb +2 -3
  182. data/lib/graphql/types/relay/nodes_field.rb +19 -3
  183. data/lib/graphql/types/string.rb +1 -1
  184. data/lib/graphql/union_type.rb +1 -1
  185. data/lib/graphql/version.rb +1 -1
  186. data/lib/graphql.rb +22 -32
  187. metadata +31 -11
  188. /data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +0 -0
  189. /data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +0 -0
@@ -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:)
@@ -159,7 +161,7 @@ module GraphQL
159
161
  include LazyHandlingMethods
160
162
  extend LazyHandlingMethods
161
163
 
162
- accepts_definitions \
164
+ deprecated_accepts_definitions \
163
165
  :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
164
166
  :validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
165
167
  :orphan_types, :resolve_type, :type_error, :parse_error,
@@ -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 :graphql_definition,
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
 
@@ -886,6 +892,17 @@ module GraphQL
886
892
  GraphQL::Language::DocumentFromSchemaDefinition.new(self).document
887
893
  end
888
894
 
895
+ # @return [String, nil]
896
+ def description(new_description = nil)
897
+ if new_description
898
+ @description = new_description
899
+ elsif defined?(@description)
900
+ @description
901
+ else
902
+ find_inherited_value(:description, nil)
903
+ end
904
+ end
905
+
889
906
  def find(path)
890
907
  if !@finder
891
908
  @find_cache = {}
@@ -894,8 +911,15 @@ module GraphQL
894
911
  @find_cache[path] ||= @finder.find(path)
895
912
  end
896
913
 
897
- def graphql_definition
898
- @graphql_definition ||= to_graphql
914
+ def graphql_definition(silence_deprecation_warning: false)
915
+ @graphql_definition ||= begin
916
+ unless silence_deprecation_warning
917
+ message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Use a class-based definition instead."
918
+ caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
919
+ GraphQL::Deprecation.warn(message + caller_message)
920
+ end
921
+ to_graphql(silence_deprecation_warning: silence_deprecation_warning)
922
+ end
899
923
  end
900
924
 
901
925
  def default_filter
@@ -927,19 +951,20 @@ module GraphQL
927
951
  find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
928
952
  end
929
953
 
954
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
930
955
  def to_graphql
931
956
  schema_defn = self.new
932
957
  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
958
+ schema_defn.query = query && query.graphql_definition(silence_deprecation_warning: true)
959
+ schema_defn.mutation = mutation && mutation.graphql_definition(silence_deprecation_warning: true)
960
+ schema_defn.subscription = subscription && subscription.graphql_definition(silence_deprecation_warning: true)
936
961
  schema_defn.validate_timeout = validate_timeout
937
962
  schema_defn.validate_max_errors = validate_max_errors
938
963
  schema_defn.max_complexity = max_complexity
939
964
  schema_defn.error_bubbling = error_bubbling
940
965
  schema_defn.max_depth = max_depth
941
966
  schema_defn.default_max_page_size = default_max_page_size
942
- schema_defn.orphan_types = orphan_types.map(&:graphql_definition)
967
+ schema_defn.orphan_types = orphan_types.map { |t| t.graphql_definition(silence_deprecation_warning: true) }
943
968
  schema_defn.disable_introspection_entry_points = disable_introspection_entry_points?
944
969
  schema_defn.disable_schema_introspection_entry_point = disable_schema_introspection_entry_point?
945
970
  schema_defn.disable_type_introspection_entry_point = disable_type_introspection_entry_point?
@@ -996,16 +1021,58 @@ module GraphQL
996
1021
  # Build a map of `{ name => type }` and return it
997
1022
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
998
1023
  # @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)
1024
+ def types(context = GraphQL::Query::NullContext)
1025
+ all_types = non_introspection_types.merge(introspection_system.types)
1026
+ visible_types = {}
1027
+ all_types.each do |k, v|
1028
+ visible_types[k] =if v.is_a?(Array)
1029
+ visible_t = nil
1030
+ v.each do |t|
1031
+ if t.visible?(context)
1032
+ if visible_t.nil?
1033
+ visible_t = t
1034
+ else
1035
+ raise DuplicateNamesError, "Found two visible type definitions for `#{k}`: #{visible_t.inspect}, #{t.inspect}"
1036
+ end
1037
+ end
1038
+ end
1039
+ visible_t
1040
+ else
1041
+ v
1042
+ end
1043
+ end
1044
+ visible_types
1001
1045
  end
1002
1046
 
1003
1047
  # @param type_name [String]
1004
1048
  # @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
- introspection_system.types[type_name] ||
1008
- find_inherited_value(:types, EMPTY_HASH)[type_name]
1049
+ def get_type(type_name, context = GraphQL::Query::NullContext)
1050
+ local_entry = own_types[type_name]
1051
+ type_defn = case local_entry
1052
+ when nil
1053
+ nil
1054
+ when Array
1055
+ visible_t = nil
1056
+ warden = Warden.from_context(context)
1057
+ local_entry.each do |t|
1058
+ if warden.visible_type?(t, context)
1059
+ if visible_t.nil?
1060
+ visible_t = t
1061
+ else
1062
+ raise DuplicateNamesError, "Found two visible type definitions for `#{type_name}`: #{visible_t.inspect}, #{t.inspect}"
1063
+ end
1064
+ end
1065
+ end
1066
+ visible_t
1067
+ when Module
1068
+ local_entry
1069
+ else
1070
+ raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}"
1071
+ end
1072
+
1073
+ type_defn ||
1074
+ introspection_system.types[type_name] || # todo context-specific introspection?
1075
+ (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
1009
1076
  end
1010
1077
 
1011
1078
  # @api private
@@ -1182,19 +1249,19 @@ module GraphQL
1182
1249
  GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
1183
1250
  end
1184
1251
 
1185
- def get_field(type_or_name, field_name)
1252
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
1186
1253
  parent_type = case type_or_name
1187
1254
  when LateBoundType
1188
- get_type(type_or_name.name)
1255
+ get_type(type_or_name.name, context)
1189
1256
  when String
1190
- get_type(type_or_name)
1257
+ get_type(type_or_name, context)
1191
1258
  when Module
1192
1259
  type_or_name
1193
1260
  else
1194
- raise ArgumentError, "unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
1261
+ raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
1195
1262
  end
1196
1263
 
1197
- if parent_type.kind.fields? && (field = parent_type.get_field(field_name))
1264
+ if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
1198
1265
  field
1199
1266
  elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
1200
1267
  entry_point_field
@@ -1205,8 +1272,8 @@ module GraphQL
1205
1272
  end
1206
1273
  end
1207
1274
 
1208
- def get_fields(type)
1209
- type.fields
1275
+ def get_fields(type, context = GraphQL::Query::NullContext)
1276
+ type.fields(context)
1210
1277
  end
1211
1278
 
1212
1279
  def introspection(new_introspection_namespace = nil)
@@ -1405,7 +1472,6 @@ module GraphQL
1405
1472
  if new_orphan_types.any?
1406
1473
  new_orphan_types = new_orphan_types.flatten
1407
1474
  add_type_and_traverse(new_orphan_types, root: false)
1408
- @orphan_types = new_orphan_types
1409
1475
  own_orphan_types.concat(new_orphan_types.flatten)
1410
1476
  end
1411
1477
 
@@ -1664,6 +1730,7 @@ module GraphQL
1664
1730
  {
1665
1731
  backtrace: ctx[:backtrace],
1666
1732
  tracers: ctx[:tracers],
1733
+ dataloader: ctx[:dataloader],
1667
1734
  }
1668
1735
  else
1669
1736
  {}
@@ -1715,7 +1782,7 @@ module GraphQL
1715
1782
  if subscription.singleton_class.ancestors.include?(Subscriptions::SubscriptionRoot)
1716
1783
  GraphQL::Deprecation.warn("`extend Subscriptions::SubscriptionRoot` is no longer required; you may remove it from #{self}'s `subscription` root type (#{subscription}).")
1717
1784
  else
1718
- subscription.fields.each do |name, field|
1785
+ subscription.all_field_definitions.each do |field|
1719
1786
  field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
1720
1787
  end
1721
1788
  end
@@ -1737,7 +1804,36 @@ module GraphQL
1737
1804
  end
1738
1805
  new_types = Array(t)
1739
1806
  addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
1740
- own_types.merge!(addition.types)
1807
+ addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time
1808
+ if (prev_entry = own_types[name])
1809
+ prev_entries = case prev_entry
1810
+ when Array
1811
+ prev_entry
1812
+ when Module
1813
+ own_types[name] = [prev_entry]
1814
+ else
1815
+ raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}"
1816
+ end
1817
+
1818
+ case types_entry
1819
+ when Array
1820
+ prev_entries.concat(types_entry)
1821
+ prev_entries.uniq! # in case any are being re-visited
1822
+ when Module
1823
+ if !prev_entries.include?(types_entry)
1824
+ prev_entries << types_entry
1825
+ end
1826
+ else
1827
+ raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}"
1828
+ end
1829
+ else
1830
+ if types_entry.is_a?(Array)
1831
+ types_entry.uniq!
1832
+ end
1833
+ own_types[name] = types_entry
1834
+ end
1835
+ end
1836
+
1741
1837
  own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
1742
1838
  own_union_memberships.merge!(addition.union_memberships)
1743
1839
 
@@ -33,6 +33,7 @@ module GraphQL
33
33
  GraphQL::StaticValidation::VariablesAreUsedAndDefined,
34
34
  GraphQL::StaticValidation::VariableUsagesAreAllowed,
35
35
  GraphQL::StaticValidation::MutationRootExists,
36
+ GraphQL::StaticValidation::QueryRootExists,
36
37
  GraphQL::StaticValidation::SubscriptionRootExists,
37
38
  GraphQL::StaticValidation::InputObjectNamesAreUnique,
38
39
  ]
@@ -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
@@ -110,7 +110,7 @@ module GraphQL
110
110
  end
111
111
 
112
112
  def on_directive(node, parent)
113
- directive_defn = @schema.directives[node.name]
113
+ directive_defn = @context.schema_directives[node.name]
114
114
  @directive_definitions.push(directive_defn)
115
115
  super
116
116
  @directive_definitions.pop
@@ -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.arguments[node.name]
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.arguments[node.name]
128
+ @context.warden.get_argument(directive_defn, node.name)
129
129
  elsif (field_defn = @field_definitions.last)
130
- field_defn.arguments[node.name]
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
- @schema.get_type(node.type.name)
190
+ @context.warden.get_type(node.type.name)
191
191
  else
192
192
  @object_types.last
193
193
  end
@@ -70,7 +70,6 @@ module GraphQL
70
70
  @dependency_map ||= resolve_dependencies(&block)
71
71
  end
72
72
 
73
-
74
73
  # Map definition AST nodes to the definition AST nodes they depend on.
75
74
  # Expose circular dependencies.
76
75
  class DependencyMap
@@ -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 = type.arguments.each_value
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.arguments[node.name])
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)
@@ -59,7 +59,7 @@ module GraphQL
59
59
  end
60
60
  end
61
61
  when GraphQL::Language::Nodes::Directive
62
- context.schema.directives[parent.name]
62
+ context.schema_directives[parent.name]
63
63
  when GraphQL::Language::Nodes::Field
64
64
  context.field_definition
65
65
  else
@@ -5,7 +5,7 @@ module GraphQL
5
5
  include GraphQL::Language
6
6
 
7
7
  def on_directive(node, parent)
8
- validate_location(node, parent, context.schema.directives)
8
+ validate_location(node, parent, context.schema_directives)
9
9
  super
10
10
  end
11
11
 
@@ -340,7 +340,7 @@ module GraphQL
340
340
  selections.each do |node|
341
341
  case node
342
342
  when GraphQL::Language::Nodes::Field
343
- definition = context.schema.get_field(owner_type, node.name)
343
+ definition = context.query.get_field(owner_type, node.name)
344
344
  fields << Field.new(node, definition, owner_type, parents)
345
345
  when GraphQL::Language::Nodes::InlineFragment
346
346
  fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ module QueryRootExists
5
+ def on_operation_definition(node, _parent)
6
+ if (node.operation_type == 'query' || node.operation_type.nil?) && context.warden.root_type_for_operation("query").nil?
7
+ add_error(GraphQL::StaticValidation::QueryRootExistsError.new(
8
+ 'Schema is not configured for queries',
9
+ nodes: node
10
+ ))
11
+ else
12
+ super
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ class QueryRootExistsError < StaticValidation::Error
5
+
6
+ def initialize(message, path: nil, nodes: [])
7
+ super(message, path: path, nodes: nodes)
8
+ end
9
+
10
+ # A hash representation of this Message
11
+ def to_h
12
+ extensions = {
13
+ "code" => code,
14
+ }
15
+
16
+ super.merge({
17
+ "extensions" => extensions
18
+ })
19
+ end
20
+
21
+ def code
22
+ "missingQueryConfiguration"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -8,7 +8,7 @@ module GraphQL
8
8
  end
9
9
 
10
10
  def on_directive(node, _parent)
11
- directive_defn = context.schema.directives[node.name]
11
+ directive_defn = context.schema_directives[node.name]
12
12
  assert_required_args(node, directive_defn)
13
13
  super
14
14
  end
@@ -16,10 +16,10 @@ module GraphQL
16
16
  private
17
17
 
18
18
  def assert_required_args(ast_node, defn)
19
- args = defn.arguments
19
+ args = defn.arguments(context.query.context)
20
20
  return if args.empty?
21
21
  present_argument_names = ast_node.arguments.map(&:name)
22
- required_argument_names = defn.arguments.each_value
22
+ required_argument_names = context.warden.arguments(defn)
23
23
  .select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
24
24
  .map(&:name)
25
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 = parent_type.arguments
38
- .select{|k,v| v.type.kind.non_null?}
39
- .keys
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.arguments[missing_field].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,
@@ -40,7 +40,7 @@ module GraphQL
40
40
  nodes: [used_directives[directive_name], ast_directive],
41
41
  directive: directive_name,
42
42
  ))
43
- else
43
+ elsif !((dir_defn = context.schema_directives[directive_name]) && dir_defn.repeatable?)
44
44
  used_directives[directive_name] = ast_directive
45
45
  end
46
46
  end
@@ -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
- arguments = case parent
25
+ argument_owner = case parent
26
26
  when GraphQL::Language::Nodes::Field
27
- context.field_definition.arguments
27
+ context.field_definition
28
28
  when GraphQL::Language::Nodes::Directive
29
- context.directive_definition.arguments
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
- arguments = arg_type.arguments
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 && arguments && validate_usage(arguments, node, 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(arguments, arg_node, ast_var)
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,9 +65,15 @@ module GraphQL
65
65
  end
66
66
  end
67
67
 
68
- arg_defn = arguments[arg_node.name]
68
+ arg_defn = context.warden.get_argument(argument_owner, arg_node.name)
69
69
  arg_defn_type = arg_defn.type
70
70
 
71
+ # If the argument is non-null, but it was given a default value,
72
+ # then treat it as nullable in practice, see https://github.com/rmosolgo/graphql-ruby/issues/3793
73
+ if arg_defn_type.non_null? && arg_defn.default_value?
74
+ arg_defn_type = arg_defn_type.of_type
75
+ end
76
+
71
77
  var_inner_type = var_type.unwrap
72
78
  arg_inner_type = arg_defn_type.unwrap
73
79
 
@@ -44,6 +44,10 @@ module GraphQL
44
44
  def too_many_errors?
45
45
  @errors.length >= @max_errors
46
46
  end
47
+
48
+ def schema_directives
49
+ @schema_directives ||= schema.directives
50
+ end
47
51
  end
48
52
  end
49
53
  end
@@ -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
- # Having calculated the result _once_, send the same payload to all subscribers
174
- events.each do |event|
175
- subscription_id = event.context.fetch(:subscription_id)
176
- deliver(subscription_id, result)
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
- scope_val = scope || (context && field.subscription_scope && context[field.subscription_scope])
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.arguments[arg_name] || arg_owner.arguments.each_value.find { |v| v.keyword.to_s == arg_name }
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