graphql 1.9.17 → 1.11.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (230) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +18 -2
  3. data/lib/generators/graphql/install_generator.rb +27 -0
  4. data/lib/generators/graphql/object_generator.rb +52 -8
  5. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  6. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  7. data/lib/generators/graphql/templates/base_field.erb +2 -0
  8. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  9. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  10. data/lib/generators/graphql/templates/base_mutation.erb +2 -0
  11. data/lib/generators/graphql/templates/base_object.erb +2 -0
  12. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  13. data/lib/generators/graphql/templates/base_union.erb +2 -0
  14. data/lib/generators/graphql/templates/enum.erb +2 -0
  15. data/lib/generators/graphql/templates/graphql_controller.erb +14 -10
  16. data/lib/generators/graphql/templates/interface.erb +2 -0
  17. data/lib/generators/graphql/templates/loader.erb +2 -0
  18. data/lib/generators/graphql/templates/mutation.erb +2 -0
  19. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  20. data/lib/generators/graphql/templates/object.erb +2 -0
  21. data/lib/generators/graphql/templates/query_type.erb +2 -0
  22. data/lib/generators/graphql/templates/scalar.erb +2 -0
  23. data/lib/generators/graphql/templates/schema.erb +10 -0
  24. data/lib/generators/graphql/templates/union.erb +3 -1
  25. data/lib/graphql/analysis/ast/field_usage.rb +1 -1
  26. data/lib/graphql/analysis/ast/query_complexity.rb +178 -67
  27. data/lib/graphql/analysis/ast/visitor.rb +3 -3
  28. data/lib/graphql/analysis/ast.rb +12 -11
  29. data/lib/graphql/argument.rb +10 -38
  30. data/lib/graphql/backtrace/table.rb +10 -2
  31. data/lib/graphql/backtrace/tracer.rb +2 -1
  32. data/lib/graphql/base_type.rb +4 -0
  33. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  34. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
  35. data/lib/graphql/define/assign_enum_value.rb +1 -1
  36. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  37. data/lib/graphql/define/assign_object_field.rb +3 -3
  38. data/lib/graphql/define/defined_object_proxy.rb +3 -0
  39. data/lib/graphql/define/instance_definable.rb +18 -108
  40. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  41. data/lib/graphql/directive.rb +8 -1
  42. data/lib/graphql/enum_type.rb +5 -71
  43. data/lib/graphql/execution/directive_checks.rb +2 -2
  44. data/lib/graphql/execution/errors.rb +2 -3
  45. data/lib/graphql/execution/execute.rb +1 -1
  46. data/lib/graphql/execution/instrumentation.rb +1 -1
  47. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  48. data/lib/graphql/execution/interpreter/arguments.rb +51 -0
  49. data/lib/graphql/execution/interpreter/arguments_cache.rb +79 -0
  50. data/lib/graphql/execution/interpreter/handles_raw_value.rb +25 -0
  51. data/lib/graphql/execution/interpreter/runtime.rb +227 -254
  52. data/lib/graphql/execution/interpreter.rb +34 -11
  53. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  54. data/lib/graphql/execution/lookahead.rb +39 -114
  55. data/lib/graphql/execution/multiplex.rb +14 -5
  56. data/lib/graphql/field.rb +14 -118
  57. data/lib/graphql/filter.rb +1 -1
  58. data/lib/graphql/function.rb +1 -30
  59. data/lib/graphql/input_object_type.rb +6 -24
  60. data/lib/graphql/integer_decoding_error.rb +17 -0
  61. data/lib/graphql/interface_type.rb +7 -23
  62. data/lib/graphql/internal_representation/scope.rb +2 -2
  63. data/lib/graphql/internal_representation/visit.rb +2 -2
  64. data/lib/graphql/introspection/base_object.rb +2 -5
  65. data/lib/graphql/introspection/directive_type.rb +1 -1
  66. data/lib/graphql/introspection/entry_points.rb +7 -7
  67. data/lib/graphql/introspection/field_type.rb +7 -3
  68. data/lib/graphql/introspection/input_value_type.rb +33 -9
  69. data/lib/graphql/introspection/introspection_query.rb +6 -92
  70. data/lib/graphql/introspection/schema_type.rb +4 -9
  71. data/lib/graphql/introspection/type_type.rb +11 -7
  72. data/lib/graphql/introspection.rb +96 -0
  73. data/lib/graphql/invalid_null_error.rb +18 -0
  74. data/lib/graphql/language/block_string.rb +24 -5
  75. data/lib/graphql/language/definition_slice.rb +21 -10
  76. data/lib/graphql/language/document_from_schema_definition.rb +89 -64
  77. data/lib/graphql/language/lexer.rb +7 -3
  78. data/lib/graphql/language/lexer.rl +7 -3
  79. data/lib/graphql/language/nodes.rb +52 -91
  80. data/lib/graphql/language/parser.rb +719 -717
  81. data/lib/graphql/language/parser.y +104 -98
  82. data/lib/graphql/language/printer.rb +1 -1
  83. data/lib/graphql/language/sanitized_printer.rb +222 -0
  84. data/lib/graphql/language/visitor.rb +2 -2
  85. data/lib/graphql/language.rb +2 -1
  86. data/lib/graphql/name_validator.rb +6 -7
  87. data/lib/graphql/non_null_type.rb +0 -10
  88. data/lib/graphql/object_type.rb +45 -56
  89. data/lib/graphql/pagination/active_record_relation_connection.rb +41 -0
  90. data/lib/graphql/pagination/array_connection.rb +77 -0
  91. data/lib/graphql/pagination/connection.rb +208 -0
  92. data/lib/graphql/pagination/connections.rb +145 -0
  93. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  94. data/lib/graphql/pagination/relation_connection.rb +185 -0
  95. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  96. data/lib/graphql/pagination.rb +6 -0
  97. data/lib/graphql/query/arguments.rb +4 -2
  98. data/lib/graphql/query/context.rb +36 -9
  99. data/lib/graphql/query/fingerprint.rb +26 -0
  100. data/lib/graphql/query/input_validation_result.rb +23 -6
  101. data/lib/graphql/query/literal_input.rb +30 -10
  102. data/lib/graphql/query/null_context.rb +5 -1
  103. data/lib/graphql/query/validation_pipeline.rb +4 -1
  104. data/lib/graphql/query/variable_validation_error.rb +1 -1
  105. data/lib/graphql/query/variables.rb +16 -7
  106. data/lib/graphql/query.rb +64 -15
  107. data/lib/graphql/rake_task/validate.rb +3 -0
  108. data/lib/graphql/rake_task.rb +9 -9
  109. data/lib/graphql/relay/array_connection.rb +10 -12
  110. data/lib/graphql/relay/base_connection.rb +23 -13
  111. data/lib/graphql/relay/connection_type.rb +2 -1
  112. data/lib/graphql/relay/edge_type.rb +1 -0
  113. data/lib/graphql/relay/edges_instrumentation.rb +1 -1
  114. data/lib/graphql/relay/mutation.rb +1 -86
  115. data/lib/graphql/relay/node.rb +2 -2
  116. data/lib/graphql/relay/range_add.rb +14 -5
  117. data/lib/graphql/relay/relation_connection.rb +8 -10
  118. data/lib/graphql/scalar_type.rb +15 -59
  119. data/lib/graphql/schema/argument.rb +113 -11
  120. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  121. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
  122. data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
  123. data/lib/graphql/schema/build_from_definition.rb +212 -190
  124. data/lib/graphql/schema/built_in_types.rb +5 -5
  125. data/lib/graphql/schema/default_type_error.rb +2 -0
  126. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  127. data/lib/graphql/schema/directive/include.rb +1 -1
  128. data/lib/graphql/schema/directive/skip.rb +1 -1
  129. data/lib/graphql/schema/directive.rb +34 -3
  130. data/lib/graphql/schema/enum.rb +52 -4
  131. data/lib/graphql/schema/enum_value.rb +6 -1
  132. data/lib/graphql/schema/field/connection_extension.rb +44 -20
  133. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  134. data/lib/graphql/schema/field.rb +200 -129
  135. data/lib/graphql/schema/find_inherited_value.rb +13 -0
  136. data/lib/graphql/schema/finder.rb +13 -11
  137. data/lib/graphql/schema/input_object.rb +131 -22
  138. data/lib/graphql/schema/interface.rb +26 -8
  139. data/lib/graphql/schema/introspection_system.rb +108 -37
  140. data/lib/graphql/schema/late_bound_type.rb +3 -2
  141. data/lib/graphql/schema/list.rb +47 -0
  142. data/lib/graphql/schema/loader.rb +134 -96
  143. data/lib/graphql/schema/member/base_dsl_methods.rb +29 -12
  144. data/lib/graphql/schema/member/build_type.rb +19 -5
  145. data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
  146. data/lib/graphql/schema/member/has_arguments.rb +105 -5
  147. data/lib/graphql/schema/member/has_ast_node.rb +20 -0
  148. data/lib/graphql/schema/member/has_fields.rb +20 -10
  149. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  150. data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
  151. data/lib/graphql/schema/member/validates_input.rb +33 -0
  152. data/lib/graphql/schema/member.rb +6 -0
  153. data/lib/graphql/schema/mutation.rb +5 -1
  154. data/lib/graphql/schema/non_null.rb +30 -0
  155. data/lib/graphql/schema/object.rb +65 -12
  156. data/lib/graphql/schema/possible_types.rb +9 -4
  157. data/lib/graphql/schema/printer.rb +0 -15
  158. data/lib/graphql/schema/relay_classic_mutation.rb +5 -3
  159. data/lib/graphql/schema/resolver/has_payload_type.rb +5 -2
  160. data/lib/graphql/schema/resolver.rb +26 -18
  161. data/lib/graphql/schema/scalar.rb +27 -3
  162. data/lib/graphql/schema/subscription.rb +8 -18
  163. data/lib/graphql/schema/timeout.rb +29 -15
  164. data/lib/graphql/schema/traversal.rb +1 -1
  165. data/lib/graphql/schema/type_expression.rb +21 -13
  166. data/lib/graphql/schema/type_membership.rb +2 -2
  167. data/lib/graphql/schema/union.rb +37 -3
  168. data/lib/graphql/schema/unique_within_type.rb +1 -2
  169. data/lib/graphql/schema/validation.rb +10 -2
  170. data/lib/graphql/schema/warden.rb +115 -29
  171. data/lib/graphql/schema.rb +903 -195
  172. data/lib/graphql/static_validation/all_rules.rb +1 -0
  173. data/lib/graphql/static_validation/base_visitor.rb +10 -6
  174. data/lib/graphql/static_validation/literal_validator.rb +52 -27
  175. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +43 -83
  176. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +17 -5
  177. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +33 -25
  178. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  179. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  180. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  181. data/lib/graphql/static_validation/rules/fields_will_merge.rb +29 -21
  182. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  183. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  184. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  185. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -2
  186. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -5
  187. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
  188. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
  189. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  190. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
  191. data/lib/graphql/static_validation/type_stack.rb +2 -2
  192. data/lib/graphql/static_validation/validation_context.rb +1 -1
  193. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  194. data/lib/graphql/static_validation/validator.rb +30 -8
  195. data/lib/graphql/static_validation.rb +1 -0
  196. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +89 -19
  197. data/lib/graphql/subscriptions/broadcast_analyzer.rb +84 -0
  198. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  199. data/lib/graphql/subscriptions/event.rb +23 -5
  200. data/lib/graphql/subscriptions/instrumentation.rb +10 -5
  201. data/lib/graphql/subscriptions/serialize.rb +22 -4
  202. data/lib/graphql/subscriptions/subscription_root.rb +15 -5
  203. data/lib/graphql/subscriptions.rb +108 -35
  204. data/lib/graphql/tracing/active_support_notifications_tracing.rb +14 -10
  205. data/lib/graphql/tracing/appoptics_tracing.rb +171 -0
  206. data/lib/graphql/tracing/appsignal_tracing.rb +8 -0
  207. data/lib/graphql/tracing/data_dog_tracing.rb +8 -0
  208. data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
  209. data/lib/graphql/tracing/platform_tracing.rb +53 -9
  210. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  211. data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
  212. data/lib/graphql/tracing/scout_tracing.rb +19 -0
  213. data/lib/graphql/tracing/skylight_tracing.rb +8 -0
  214. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  215. data/lib/graphql/tracing.rb +14 -34
  216. data/lib/graphql/types/big_int.rb +1 -1
  217. data/lib/graphql/types/int.rb +9 -2
  218. data/lib/graphql/types/iso_8601_date.rb +3 -3
  219. data/lib/graphql/types/iso_8601_date_time.rb +25 -10
  220. data/lib/graphql/types/relay/base_connection.rb +11 -7
  221. data/lib/graphql/types/relay/base_edge.rb +2 -1
  222. data/lib/graphql/types/string.rb +7 -1
  223. data/lib/graphql/unauthorized_error.rb +1 -1
  224. data/lib/graphql/union_type.rb +13 -28
  225. data/lib/graphql/unresolved_type_error.rb +2 -2
  226. data/lib/graphql/version.rb +1 -1
  227. data/lib/graphql.rb +31 -6
  228. data/readme.md +1 -1
  229. metadata +34 -9
  230. data/lib/graphql/literal_validation_error.rb +0 -6
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/execution/interpreter/argument_value"
3
+ require "graphql/execution/interpreter/arguments"
4
+ require "graphql/execution/interpreter/arguments_cache"
2
5
  require "graphql/execution/interpreter/execution_errors"
3
6
  require "graphql/execution/interpreter/hash_response"
4
7
  require "graphql/execution/interpreter/runtime"
5
8
  require "graphql/execution/interpreter/resolve"
9
+ require "graphql/execution/interpreter/handles_raw_value"
6
10
 
7
11
  module GraphQL
8
12
  module Execution
@@ -17,17 +21,13 @@ module GraphQL
17
21
  runtime.final_value
18
22
  end
19
23
 
20
- def self.use(schema_defn)
21
- schema_defn.target.interpreter = true
22
- # Reach through the legacy objects for the actual class defn
23
- schema_class = schema_defn.target.class
24
- # This is not good, since both of these are holding state now,
25
- # we have to update both :(
26
- [schema_class, schema_defn].each do |schema_config|
27
- schema_config.query_execution_strategy(GraphQL::Execution::Interpreter)
28
- schema_config.mutation_execution_strategy(GraphQL::Execution::Interpreter)
29
- schema_config.subscription_execution_strategy(GraphQL::Execution::Interpreter)
30
- end
24
+ def self.use(schema_class)
25
+ schema_class.interpreter = true
26
+ schema_class.query_execution_strategy(GraphQL::Execution::Interpreter)
27
+ schema_class.mutation_execution_strategy(GraphQL::Execution::Interpreter)
28
+ schema_class.subscription_execution_strategy(GraphQL::Execution::Interpreter)
29
+ schema_class.add_subscription_extension_if_necessary
30
+ GraphQL::Schema::Object.include(HandlesRawValue)
31
31
  end
32
32
 
33
33
  def self.begin_multiplex(multiplex)
@@ -93,6 +93,29 @@ module GraphQL
93
93
  tracer.trace("execute_query_lazy", {multiplex: multiplex, query: query}) do
94
94
  Interpreter::Resolve.resolve_all(final_values)
95
95
  end
96
+ queries.each do |query|
97
+ runtime = query.context.namespace(:interpreter)[:runtime]
98
+ if runtime
99
+ runtime.delete_interpreter_context(:current_path)
100
+ runtime.delete_interpreter_context(:current_field)
101
+ runtime.delete_interpreter_context(:current_object)
102
+ runtime.delete_interpreter_context(:current_arguments)
103
+ end
104
+ end
105
+ nil
106
+ end
107
+
108
+ class ListResultFailedError < GraphQL::Error
109
+ def initialize(value:, path:, field:)
110
+ message = "Failed to build a GraphQL list result for field `#{field.path}` at path `#{path.join(".")}`.\n".dup
111
+
112
+ message << "Expected `#{value.inspect}` to implement `.each` to satisfy the GraphQL return type `#{field.type.to_type_signature}`.\n"
113
+
114
+ if field.connection?
115
+ message << "\nThis field was treated as a Relay-style connection; add `connection: false` to the `field(...)` to disable this behavior."
116
+ end
117
+ super(message)
118
+ end
96
119
  end
97
120
  end
98
121
  end
@@ -36,6 +36,10 @@ module GraphQL
36
36
  @storage.compute_if_absent(value.class) { find_superclass_method(value.class) }
37
37
  end
38
38
 
39
+ def each
40
+ @storage.each_pair { |k, v| yield(k, v) }
41
+ end
42
+
39
43
  protected
40
44
 
41
45
  attr_reader :storage
@@ -51,7 +51,17 @@ module GraphQL
51
51
 
52
52
  # @return [Hash<Symbol, Object>]
53
53
  def arguments
54
- @arguments ||= @field && ArgumentHelpers.arguments(@query, nil, @field, ast_nodes.first)
54
+ if defined?(@arguments)
55
+ @arguments
56
+ else
57
+ @arguments = if @field
58
+ @query.schema.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
59
+ args.is_a?(Execution::Interpreter::Arguments) ? args.keyword_arguments : args
60
+ end
61
+ else
62
+ nil
63
+ end
64
+ end
55
65
  end
56
66
 
57
67
  # True if this node has a selection on `field_name`.
@@ -81,7 +91,7 @@ module GraphQL
81
91
  def selection(field_name, selected_type: @selected_type, arguments: nil)
82
92
  next_field_name = normalize_name(field_name)
83
93
 
84
- next_field_defn = FieldHelpers.get_field(@query.schema, selected_type, next_field_name)
94
+ next_field_defn = get_class_based_field(selected_type, next_field_name)
85
95
  if next_field_defn
86
96
  next_nodes = []
87
97
  @ast_nodes.each do |ast_node|
@@ -127,7 +137,7 @@ module GraphQL
127
137
 
128
138
  subselections_by_type.each do |type, ast_nodes_by_response_key|
129
139
  ast_nodes_by_response_key.each do |response_key, ast_nodes|
130
- field_defn = FieldHelpers.get_field(@query.schema, type, ast_nodes.first.name)
140
+ field_defn = get_class_based_field(type, ast_nodes.first.name)
131
141
  lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type)
132
142
  subselections.push(lookahead)
133
143
  end
@@ -203,8 +213,29 @@ module GraphQL
203
213
  end
204
214
  end
205
215
 
216
+ # Wrap get_field and ensure that it returns a GraphQL::Schema::Field.
217
+ # Remove this when legacy execution is removed.
218
+ def get_class_based_field(type, name)
219
+ f = @query.get_field(type, name)
220
+ f && f.type_class
221
+ end
222
+
223
+ def skipped_by_directive?(ast_selection)
224
+ ast_selection.directives.each do |directive|
225
+ dir_defn = @query.schema.directives.fetch(directive.name)
226
+ directive_class = dir_defn.type_class
227
+ if directive_class
228
+ dir_args = @query.arguments_for(directive, dir_defn)
229
+ return true unless directive_class.static_include?(dir_args, @query.context)
230
+ end
231
+ end
232
+ false
233
+ end
234
+
206
235
  def find_selections(subselections_by_type, selections_on_type, selected_type, ast_selections, arguments)
207
236
  ast_selections.each do |ast_selection|
237
+ next if skipped_by_directive?(ast_selection)
238
+
208
239
  case ast_selection
209
240
  when GraphQL::Language::Nodes::Field
210
241
  response_key = ast_selection.alias || ast_selection.name
@@ -213,7 +244,7 @@ module GraphQL
213
244
  elsif arguments.nil? || arguments.empty?
214
245
  selections_on_type[response_key] = [ast_selection]
215
246
  else
216
- field_defn = FieldHelpers.get_field(@query.schema, selected_type, ast_selection.name)
247
+ field_defn = get_class_based_field(selected_type, ast_selection.name)
217
248
  if arguments_match?(arguments, field_defn, ast_selection)
218
249
  selections_on_type[response_key] = [ast_selection]
219
250
  end
@@ -223,14 +254,14 @@ module GraphQL
223
254
  subselections_on_type = selections_on_type
224
255
  if (t = ast_selection.type)
225
256
  # Assuming this is valid, that `t` will be found.
226
- on_type = @query.schema.types[t.name].metadata[:type_class]
257
+ on_type = @query.schema.get_type(t.name).type_class
227
258
  subselections_on_type = subselections_by_type[on_type] ||= {}
228
259
  end
229
260
  find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
230
261
  when GraphQL::Language::Nodes::FragmentSpread
231
262
  frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
232
263
  # Again, assuming a valid AST
233
- on_type = @query.schema.types[frag_defn.type.name].metadata[:type_class]
264
+ on_type = @query.schema.get_type(frag_defn.type.name).type_class
234
265
  subselections_on_type = subselections_by_type[on_type] ||= {}
235
266
  find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
236
267
  else
@@ -242,6 +273,7 @@ module GraphQL
242
273
  # If a selection on `node` matches `field_name` (which is backed by `field_defn`)
243
274
  # and matches the `arguments:` constraints, then add that node to `matches`
244
275
  def find_selected_nodes(node, field_name, field_defn, arguments:, matches:)
276
+ return if skipped_by_directive?(node)
245
277
  case node
246
278
  when GraphQL::Language::Nodes::Field
247
279
  if node.name == field_name
@@ -263,120 +295,13 @@ module GraphQL
263
295
  end
264
296
 
265
297
  def arguments_match?(arguments, field_defn, field_node)
266
- query_kwargs = ArgumentHelpers.arguments(@query, nil, field_defn, field_node)
298
+ query_kwargs = @query.arguments_for(field_node, field_defn)
267
299
  arguments.all? do |arg_name, arg_value|
268
300
  arg_name = normalize_keyword(arg_name)
269
301
  # Make sure the constraint is present with a matching value
270
302
  query_kwargs.key?(arg_name) && query_kwargs[arg_name] == arg_value
271
303
  end
272
304
  end
273
-
274
- # TODO Dedup with interpreter
275
- module ArgumentHelpers
276
- module_function
277
-
278
- def arguments(query, graphql_object, arg_owner, ast_node)
279
- kwarg_arguments = {}
280
- arg_defns = arg_owner.arguments
281
- ast_node.arguments.each do |arg|
282
- arg_defn = arg_defns[arg.name] || raise("Invariant: missing argument definition for #{arg.name.inspect} in #{arg_defns.keys} from #{arg_owner}")
283
- # Need to distinguish between client-provided `nil`
284
- # and nothing-at-all
285
- is_present, value = arg_to_value(query, graphql_object, arg_defn.type, arg.value)
286
- if is_present
287
- # This doesn't apply to directives, which are legacy
288
- # Can remove this when Skip and Include use classes or something.
289
- if graphql_object
290
- value = arg_defn.prepare_value(graphql_object, value)
291
- end
292
- kwarg_arguments[arg_defn.keyword] = value
293
- end
294
- end
295
- arg_defns.each do |name, arg_defn|
296
- if arg_defn.default_value? && !kwarg_arguments.key?(arg_defn.keyword)
297
- kwarg_arguments[arg_defn.keyword] = arg_defn.default_value
298
- end
299
- end
300
- kwarg_arguments
301
- end
302
-
303
- # Get a Ruby-ready value from a client query.
304
- # @param graphql_object [Object] The owner of the field whose argument this is
305
- # @param arg_type [Class, GraphQL::Schema::NonNull, GraphQL::Schema::List]
306
- # @param ast_value [GraphQL::Language::Nodes::VariableIdentifier, String, Integer, Float, Boolean]
307
- # @return [Array(is_present, value)]
308
- def arg_to_value(query, graphql_object, arg_type, ast_value)
309
- if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
310
- # If it's not here, it will get added later
311
- if query.variables.key?(ast_value.name)
312
- return true, query.variables[ast_value.name]
313
- else
314
- return false, nil
315
- end
316
- elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
317
- return true, nil
318
- elsif arg_type.is_a?(GraphQL::Schema::NonNull)
319
- arg_to_value(query, graphql_object, arg_type.of_type, ast_value)
320
- elsif arg_type.is_a?(GraphQL::Schema::List)
321
- # Treat a single value like a list
322
- arg_value = Array(ast_value)
323
- list = []
324
- arg_value.map do |inner_v|
325
- _present, value = arg_to_value(query, graphql_object, arg_type.of_type, inner_v)
326
- list << value
327
- end
328
- return true, list
329
- elsif arg_type.is_a?(Class) && arg_type < GraphQL::Schema::InputObject
330
- # For these, `prepare` is applied during `#initialize`.
331
- # Pass `nil` so it will be skipped in `#arguments`.
332
- # What a mess.
333
- args = arguments(query, nil, arg_type, ast_value)
334
- # We're not tracking defaults_used, but for our purposes
335
- # we compare the value to the default value.
336
- return true, arg_type.new(ruby_kwargs: args, context: query.context, defaults_used: nil)
337
- else
338
- flat_value = flatten_ast_value(query, ast_value)
339
- return true, arg_type.coerce_input(flat_value, query.context)
340
- end
341
- end
342
-
343
- def flatten_ast_value(query, v)
344
- case v
345
- when GraphQL::Language::Nodes::Enum
346
- v.name
347
- when GraphQL::Language::Nodes::InputObject
348
- h = {}
349
- v.arguments.each do |arg|
350
- h[arg.name] = flatten_ast_value(query, arg.value)
351
- end
352
- h
353
- when Array
354
- v.map { |v2| flatten_ast_value(query, v2) }
355
- when GraphQL::Language::Nodes::VariableIdentifier
356
- flatten_ast_value(query.variables[v.name])
357
- else
358
- v
359
- end
360
- end
361
- end
362
-
363
- # TODO dedup with interpreter
364
- module FieldHelpers
365
- module_function
366
-
367
- def get_field(schema, owner_type, field_name)
368
- field_defn = owner_type.get_field(field_name)
369
- field_defn ||= if owner_type == schema.query.metadata[:type_class] && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
370
- entry_point_field.metadata[:type_class]
371
- elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
372
- dynamic_field.metadata[:type_class]
373
- else
374
- nil
375
- end
376
-
377
- field_defn
378
- end
379
- end
380
305
  end
381
306
  end
382
307
  end
@@ -34,8 +34,7 @@ module GraphQL
34
34
  @schema = schema
35
35
  @queries = queries
36
36
  @context = context
37
- # TODO remove support for global tracers
38
- @tracers = schema.tracers + GraphQL::Tracing.tracers + (context[:tracers] || [])
37
+ @tracers = schema.tracers + (context[:tracers] || [])
39
38
  # Support `context: {backtrace: true}`
40
39
  if context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
41
40
  @tracers << GraphQL::Backtrace::Tracer
@@ -44,9 +43,9 @@ module GraphQL
44
43
  end
45
44
 
46
45
  class << self
47
- def run_all(schema, query_options, *args)
48
- queries = query_options.map { |opts| GraphQL::Query.new(schema, nil, opts) }
49
- run_queries(schema, queries, *args)
46
+ def run_all(schema, query_options, **kwargs)
47
+ queries = query_options.map { |opts| GraphQL::Query.new(schema, nil, **opts) }
48
+ run_queries(schema, queries, **kwargs)
50
49
  end
51
50
 
52
51
  # @param schema [GraphQL::Schema]
@@ -95,6 +94,7 @@ module GraphQL
95
94
  query.result
96
95
  end
97
96
  rescue Exception
97
+ # TODO rescue at a higher level so it will catch errors in analysis, too
98
98
  # Assign values here so that the query's `@executed` becomes true
99
99
  queries.map { |q| q.result_values ||= {} }
100
100
  raise
@@ -173,6 +173,15 @@ module GraphQL
173
173
  def instrument_and_analyze(multiplex)
174
174
  GraphQL::Execution::Instrumentation.apply_instrumenters(multiplex) do
175
175
  schema = multiplex.schema
176
+ if schema.interpreter? && schema.analysis_engine != GraphQL::Analysis::AST
177
+ raise <<-ERR
178
+ Can't use `GraphQL::Execution::Interpreter` without `GraphQL::Analysis::AST`, please add this plugin to your schema:
179
+
180
+ use GraphQL::Analysis::AST
181
+
182
+ For information about the new analysis engine: https://graphql-ruby.org/queries/ast_analysis.html
183
+ ERR
184
+ end
176
185
  multiplex_analyzers = schema.multiplex_analyzers
177
186
  if multiplex.max_complexity
178
187
  multiplex_analyzers += if schema.using_ast_analysis?
data/lib/graphql/field.rb CHANGED
@@ -2,123 +2,7 @@
2
2
  require "graphql/field/resolve"
3
3
 
4
4
  module GraphQL
5
- # {Field}s belong to {ObjectType}s and {InterfaceType}s.
6
- #
7
- # They're usually created with the `field` helper. If you create it by hand, make sure {#name} is a String.
8
- #
9
- # A field must have a return type, but if you want to defer the return type calculation until later,
10
- # you can pass a proc for the return type. That proc will be called when the schema is defined.
11
- #
12
- # @example Lazy type resolution
13
- # # If the field's type isn't defined yet, you can pass a proc
14
- # field :city, -> { TypeForModelName.find("City") }
15
- #
16
- # For complex field definition, you can pass a block to the `field` helper, eg `field :name do ... end`.
17
- # This block is equivalent to calling `GraphQL::Field.define { ... }`.
18
- #
19
- # @example Defining a field with a block
20
- # field :city, CityType do
21
- # # field definition continues inside the block
22
- # end
23
- #
24
- # ## Resolve
25
- #
26
- # Fields have `resolve` functions to determine their values at query-time.
27
- # The default implementation is to call a method on the object based on the field name.
28
- #
29
- # @example Create a field which calls a method with the same name.
30
- # GraphQL::ObjectType.define do
31
- # field :name, types.String, "The name of this thing "
32
- # end
33
- #
34
- # You can specify a custom proc with the `resolve` helper.
35
- #
36
- # There are some shortcuts for common `resolve` implementations:
37
- # - Provide `property:` to call a method with a different name than the field name
38
- # - Provide `hash_key:` to resolve the field by doing a key lookup, eg `obj[:my_hash_key]`
39
- #
40
- # @example Create a field that calls a different method on the object
41
- # GraphQL::ObjectType.define do
42
- # # use the `property` keyword:
43
- # field :firstName, types.String, property: :first_name
44
- # end
45
- #
46
- # @example Create a field looks up with `[hash_key]`
47
- # GraphQL::ObjectType.define do
48
- # # use the `hash_key` keyword:
49
- # field :firstName, types.String, hash_key: :first_name
50
- # end
51
- #
52
- # ## Arguments
53
- #
54
- # Fields can take inputs; they're called arguments. You can define them with the `argument` helper.
55
- #
56
- # @example Create a field with an argument
57
- # field :students, types[StudentType] do
58
- # argument :grade, types.Int
59
- # resolve ->(obj, args, ctx) {
60
- # Student.where(grade: args[:grade])
61
- # }
62
- # end
63
- #
64
- # They can have default values which will be provided to `resolve` if the query doesn't include a value.
65
- #
66
- # @example Argument with a default value
67
- # field :events, types[EventType] do
68
- # # by default, don't include past events
69
- # argument :includePast, types.Boolean, default_value: false
70
- # resolve ->(obj, args, ctx) {
71
- # args[:includePast] # => false if no value was provided in the query
72
- # # ...
73
- # }
74
- # end
75
- #
76
- # Only certain types maybe used for inputs:
77
- #
78
- # - Scalars
79
- # - Enums
80
- # - Input Objects
81
- # - Lists of those types
82
- #
83
- # Input types may also be non-null -- in that case, the query will fail
84
- # if the input is not present.
85
- #
86
- # ## Complexity
87
- #
88
- # Fields can have _complexity_ values which describe the computation cost of resolving the field.
89
- # You can provide the complexity as a constant with `complexity:` or as a proc, with the `complexity` helper.
90
- #
91
- # @example Custom complexity values
92
- # # Complexity can be a number or a proc.
93
- #
94
- # # Complexity can be defined with a keyword:
95
- # field :expensive_calculation, !types.Int, complexity: 10
96
- #
97
- # # Or inside the block:
98
- # field :expensive_calculation_2, !types.Int do
99
- # complexity ->(ctx, args, child_complexity) { ctx[:current_user].staff? ? 0 : 10 }
100
- # end
101
- #
102
- # @example Calculating the complexity of a list field
103
- # field :items, types[ItemType] do
104
- # argument :limit, !types.Int
105
- # # Multiply the child complexity by the possible items on the list
106
- # complexity ->(ctx, args, child_complexity) { child_complexity * args[:limit] }
107
- # end
108
- #
109
- # @example Creating a field, then assigning it to a type
110
- # name_field = GraphQL::Field.define do
111
- # name("Name")
112
- # type(!types.String)
113
- # description("The name of this thing")
114
- # resolve ->(object, arguments, context) { object.name }
115
- # end
116
- #
117
- # NamedType = GraphQL::ObjectType.define do
118
- # # The second argument may be a GraphQL::Field
119
- # field :name, name_field
120
- # end
121
- #
5
+ # @api deprecated
122
6
  class Field
123
7
  include GraphQL::Define::InstanceDefinable
124
8
  accepts_definitions :name, :description, :deprecation_reason,
@@ -196,6 +80,10 @@ module GraphQL
196
80
 
197
81
  attr_accessor :ast_node
198
82
 
83
+ # Future-compatible alias
84
+ # @see {GraphQL::SchemaMember}
85
+ alias :graphql_definition :itself
86
+
199
87
  # @return [Boolean]
200
88
  def connection?
201
89
  @connection
@@ -266,7 +154,7 @@ module GraphQL
266
154
  end
267
155
 
268
156
  def name=(new_name)
269
- old_name = @name
157
+ old_name = defined?(@name) ? @name : nil
270
158
  @name = new_name
271
159
 
272
160
  if old_name != new_name && @resolve_proc.is_a?(Field::Resolve::NameResolve)
@@ -315,6 +203,14 @@ module GraphQL
315
203
  }
316
204
  end
317
205
 
206
+ def type_class
207
+ metadata[:type_class]
208
+ end
209
+
210
+ def get_argument(argument_name)
211
+ arguments[argument_name]
212
+ end
213
+
318
214
  private
319
215
 
320
216
  def build_default_resolver
@@ -33,7 +33,7 @@ module GraphQL
33
33
  end
34
34
 
35
35
  def self.build(onlies)
36
- case onlies
36
+ case onlies.size
37
37
  when 0
38
38
  nil
39
39
  when 1
@@ -1,35 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- # A reusable container for field logic, including arguments, resolve, return type, and documentation.
4
- #
5
- # Class-level values defined with the DSL will be inherited,
6
- # so {GraphQL::Function}s can extend one another.
7
- #
8
- # It's OK to override the instance methods here in order to customize behavior of instances.
9
- #
10
- # @example A reusable GraphQL::Function attached as a field
11
- # class FindRecord < GraphQL::Function
12
- # attr_reader :type
13
- #
14
- # def initialize(model:, type:)
15
- # @model = model
16
- # @type = type
17
- # end
18
- #
19
- # argument :id, GraphQL::ID_TYPE
20
- #
21
- # def call(obj, args, ctx)
22
- # @model.find(args.id)
23
- # end
24
- # end
25
- #
26
- # QueryType = GraphQL::ObjectType.define do
27
- # name "Query"
28
- # field :post, function: FindRecord.new(model: Post, type: PostType)
29
- # field :comment, function: FindRecord.new(model: Comment, type: CommentType)
30
- # end
31
- #
32
- # @see {GraphQL::Schema::Resolver} for a replacement for `GraphQL::Function`
3
+ # @api deprecated
33
4
  class Function
34
5
  # @return [Hash<String => GraphQL::Argument>] Arguments, keyed by name
35
6
  def arguments
@@ -1,28 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- # {InputObjectType}s are key-value inputs for fields.
4
- #
5
- # Input objects have _arguments_ which are identical to {GraphQL::Field} arguments.
6
- # They map names to types and support default values.
7
- # Their input types can be any input types, including {InputObjectType}s.
8
- #
9
- # @example An input type with name and number
10
- # PlayerInput = GraphQL::InputObjectType.define do
11
- # name("Player")
12
- # argument :name, !types.String
13
- # argument :number, !types.Int
14
- # end
15
- #
16
- # In a `resolve` function, you can access the values by making nested lookups on `args`.
17
- #
18
- # @example Accessing input values in a resolve function
19
- # resolve ->(obj, args, ctx) {
20
- # args[:player][:name] # => "Tony Gwynn"
21
- # args[:player][:number] # => 19
22
- # args[:player].to_h # { "name" => "Tony Gwynn", "number" => 19 }
23
- # # ...
24
- # }
25
- #
3
+ # @api deprecated
26
4
  class InputObjectType < GraphQL::BaseType
27
5
  accepts_definitions(
28
6
  :arguments, :mutation,
@@ -80,6 +58,10 @@ module GraphQL
80
58
  result
81
59
  end
82
60
 
61
+ def get_argument(argument_name)
62
+ arguments[argument_name]
63
+ end
64
+
83
65
  private
84
66
 
85
67
  def coerce_non_null_input(value, ctx)
@@ -136,7 +118,7 @@ module GraphQL
136
118
  # Items in the input that are unexpected
137
119
  input.each do |name, value|
138
120
  if visible_arguments_map[name].nil?
139
- result.add_problem("Field is not defined on #{self.name}", [name])
121
+ result.add_problem("Field is not defined on #{self.graphql_name}", [name])
140
122
  end
141
123
  end
142
124
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ # This error is raised when `Types::Int` is given an input value outside of 32-bit integer range.
4
+ #
5
+ # For really big integer values, consider `GraphQL::Types::BigInt`
6
+ #
7
+ # @see GraphQL::Types::Int which raises this error
8
+ class IntegerDecodingError < GraphQL::RuntimeTypeError
9
+ # The value which couldn't be decoded
10
+ attr_reader :integer_value
11
+
12
+ def initialize(value)
13
+ @integer_value = value
14
+ super("Integer out of bounds: #{value}. \nConsider using GraphQL::Types::BigInt instead.")
15
+ end
16
+ end
17
+ end
@@ -1,31 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- # An Interface contains a collection of types which implement some of the same fields.
4
- #
5
- # Interfaces can have fields, defined with `field`, just like an object type.
6
- #
7
- # Objects which implement this field _inherit_ field definitions from the interface.
8
- # An object type can override the inherited definition by redefining that field.
9
- #
10
- # @example An interface with three fields
11
- # DeviceInterface = GraphQL::InterfaceType.define do
12
- # name("Device")
13
- # description("Hardware devices for computing")
14
- #
15
- # field :ram, types.String
16
- # field :processor, ProcessorType
17
- # field :release_year, types.Int
18
- # end
19
- #
20
- # @example Implementing an interface with an object type
21
- # Laptoptype = GraphQL::ObjectType.define do
22
- # interfaces [DeviceInterface]
23
- # end
24
- #
3
+ # @api deprecated
25
4
  class InterfaceType < GraphQL::BaseType
26
5
  accepts_definitions :fields, :orphan_types, :resolve_type, field: GraphQL::Define::AssignObjectField
27
6
 
28
7
  attr_accessor :fields, :orphan_types, :resolve_type_proc
8
+ attr_writer :type_membership_class
29
9
  ensure_defined :fields, :orphan_types, :resolve_type_proc, :resolve_type
30
10
 
31
11
  def initialize
@@ -71,7 +51,7 @@ module GraphQL
71
51
  # @return [GraphQL::ObjectType, nil] The type named `type_name` if it exists and implements this {InterfaceType}, (else `nil`)
72
52
  def get_possible_type(type_name, ctx)
73
53
  type = ctx.query.get_type(type_name)
74
- type if type && ctx.query.schema.possible_types(self).include?(type)
54
+ type if type && ctx.query.warden.possible_types(self).include?(type)
75
55
  end
76
56
 
77
57
  # Check if a type is a possible type of this {InterfaceType}
@@ -82,5 +62,9 @@ module GraphQL
82
62
  type_name = type.is_a?(String) ? type : type.graphql_name
83
63
  !get_possible_type(type_name, ctx).nil?
84
64
  end
65
+
66
+ def type_membership_class
67
+ @type_membership_class || GraphQL::Schema::TypeMembership
68
+ end
85
69
  end
86
70
  end
@@ -66,11 +66,11 @@ module GraphQL
66
66
  # Call the block for each type in `self`.
67
67
  # This uses the simplest possible expression of `self`,
68
68
  # so if this scope is defined by an abstract type, it gets yielded.
69
- def each
69
+ def each(&block)
70
70
  if @abstract_type
71
71
  yield(@type)
72
72
  else
73
- @types.each { |t| yield(t) }
73
+ @types.each(&block)
74
74
  end
75
75
  end
76
76