graphql 1.9.17 → 1.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

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
@@ -13,8 +13,10 @@ module GraphQL
13
13
  include GraphQL::Schema::Member::CachedGraphQLDefinition
14
14
  include GraphQL::Schema::Member::AcceptsDefinition
15
15
  include GraphQL::Schema::Member::HasArguments
16
+ include GraphQL::Schema::Member::HasAstNode
16
17
  include GraphQL::Schema::Member::HasPath
17
18
  extend GraphQL::Schema::FindInheritedValue
19
+ include GraphQL::Schema::FindInheritedValue::EmptyObjects
18
20
 
19
21
  # @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
20
22
  attr_reader :name
@@ -34,10 +36,19 @@ module GraphQL
34
36
  # @return [Symbol] The method on the type to look up
35
37
  attr_reader :resolver_method
36
38
 
37
- # @return [Class] The type that this field belongs to
38
- attr_reader :owner
39
+ # @return [Class] The thing this field was defined on (type, mutation, resolver)
40
+ attr_accessor :owner
39
41
 
40
- # @return [Symobol] the original name of the field, passed in by the user
42
+ # @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type)
43
+ def owner_type
44
+ @owner_type ||= if owner < GraphQL::Schema::Mutation
45
+ owner.payload_type
46
+ else
47
+ owner
48
+ end
49
+ end
50
+
51
+ # @return [Symbol] the original name of the field, passed in by the user
41
52
  attr_reader :original_name
42
53
 
43
54
  # @return [Class, nil] The {Schema::Resolver} this field was derived from, if there is one
@@ -45,13 +56,18 @@ module GraphQL
45
56
  @resolver_class
46
57
  end
47
58
 
59
+ # @return [Boolean] Is this field a predefined introspection field?
60
+ def introspection?
61
+ @introspection
62
+ end
63
+
48
64
  alias :mutation :resolver
49
65
 
50
66
  # @return [Boolean] Apply tracing to this field? (Default: skip scalars, this is the override value)
51
67
  attr_reader :trace
52
68
 
53
69
  # @return [String, nil]
54
- attr_reader :subscription_scope
70
+ attr_accessor :subscription_scope
55
71
 
56
72
  # Create a field instance from a list of arguments, keyword arguments, and a block.
57
73
  #
@@ -152,6 +168,14 @@ module GraphQL
152
168
  end
153
169
  end
154
170
 
171
+ # @return Boolean
172
+ attr_reader :relay_node_field
173
+
174
+ # @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
175
+ def method_conflict_warning?
176
+ @method_conflict_warning
177
+ end
178
+
155
179
  # @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
156
180
  # @param type [Class, GraphQL::BaseType, Array] The return type of this field
157
181
  # @param owner [Class] The type that this field belongs to
@@ -162,7 +186,8 @@ module GraphQL
162
186
  # @param hash_key [String, Symbol] The hash key to lookup on the underlying object (if its a Hash) to resolve this field (defaults to `name` or `name.to_s`)
163
187
  # @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
164
188
  # @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
165
- # @param max_page_size [Integer] For connections, the maximum number of items to return from this field
189
+ # @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
190
+ # @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
166
191
  # @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
167
192
  # @param resolve [<#call(obj, args, ctx)>] **deprecated** for compatibility with <1.8.0
168
193
  # @param field [GraphQL::Field, GraphQL::Schema::Field] **deprecated** for compatibility with <1.8.0
@@ -175,7 +200,10 @@ module GraphQL
175
200
  # @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
176
201
  # @param extensions [Array<Class, Hash<Class => Object>>] Named extensions to apply to this field (see also {#extension})
177
202
  # @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
178
- def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: nil, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, extras: [], extensions: [], resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, arguments: {}, &definition_block)
203
+ # @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
204
+ # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
205
+ # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
206
+ def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, &definition_block)
179
207
  if name.nil?
180
208
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
181
209
  end
@@ -191,8 +219,9 @@ module GraphQL
191
219
  raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
192
220
  end
193
221
  @original_name = name
194
- @underscored_name = -Member::BuildType.underscore(name.to_s)
195
- @name = -(camelize ? Member::BuildType.camelize(name.to_s) : name.to_s)
222
+ name_s = -name.to_s
223
+ @underscored_name = -Member::BuildType.underscore(name_s)
224
+ @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
196
225
  @description = description
197
226
  if field.is_a?(GraphQL::Schema::Field)
198
227
  raise ArgumentError, "Instead of passing a field as `field:`, use `add_field(field)` to add an already-defined field."
@@ -218,32 +247,34 @@ module GraphQL
218
247
  end
219
248
 
220
249
  # TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
221
- method_name = method || hash_key || @underscored_name
222
- resolver_method ||= @underscored_name.to_sym
250
+ method_name = method || hash_key || name_s
251
+ resolver_method ||= name_s.to_sym
223
252
 
224
- @method_str = method_name.to_s
253
+ @method_str = -method_name.to_s
225
254
  @method_sym = method_name.to_sym
226
255
  @resolver_method = resolver_method
227
256
  @complexity = complexity
228
257
  @return_type_expr = type
229
258
  @return_type_null = null
230
259
  @connection = connection
231
- @max_page_size = max_page_size
260
+ @has_max_page_size = max_page_size != :not_given
261
+ @max_page_size = max_page_size == :not_given ? nil : max_page_size
232
262
  @introspection = introspection
233
263
  @extras = extras
264
+ @broadcastable = broadcastable
234
265
  @resolver_class = resolver_class
235
266
  @scope = scope
236
267
  @trace = trace
237
268
  @relay_node_field = relay_node_field
238
269
  @relay_nodes_field = relay_nodes_field
270
+ @ast_node = ast_node
271
+ @method_conflict_warning = method_conflict_warning
239
272
 
240
- # Override the default from HasArguments
241
- @own_arguments = {}
242
273
  arguments.each do |name, arg|
243
274
  if arg.is_a?(Hash)
244
275
  argument(name: name, **arg)
245
276
  else
246
- @own_arguments[name] = arg
277
+ add_argument(arg)
247
278
  end
248
279
  end
249
280
 
@@ -251,7 +282,7 @@ module GraphQL
251
282
  @subscription_scope = subscription_scope
252
283
 
253
284
  # Do this last so we have as much context as possible when initializing them:
254
- @extensions = []
285
+ @extensions = EMPTY_ARRAY
255
286
  if extensions.any?
256
287
  self.extensions(extensions)
257
288
  end
@@ -262,8 +293,8 @@ module GraphQL
262
293
  end
263
294
  # The problem with putting this after the definition_block
264
295
  # is that it would override arguments
265
- if connection?
266
- self.extension(self.class.connection_extension)
296
+ if connection? && connection_extension
297
+ self.extension(connection_extension)
267
298
  end
268
299
 
269
300
  if definition_block
@@ -275,6 +306,13 @@ module GraphQL
275
306
  end
276
307
  end
277
308
 
309
+ # If true, subscription updates with this field can be shared between viewers
310
+ # @return [Boolean, nil]
311
+ # @see GraphQL::Subscriptions::BroadcastAnalyzer
312
+ def broadcastable?
313
+ @broadcastable
314
+ end
315
+
278
316
  # @param text [String]
279
317
  # @return [String]
280
318
  def description(text = nil)
@@ -305,6 +343,9 @@ module GraphQL
305
343
  # Read the value
306
344
  @extensions
307
345
  else
346
+ if @extensions.frozen?
347
+ @extensions = @extensions.dup
348
+ end
308
349
  new_extensions.each do |extension|
309
350
  if extension.is_a?(Hash)
310
351
  extension = extension.to_a[0]
@@ -342,12 +383,15 @@ module GraphQL
342
383
  # Read the value
343
384
  @extras
344
385
  else
386
+ if @extras.frozen?
387
+ @extras = @extras.dup
388
+ end
345
389
  # Append to the set of extras on this field
346
390
  @extras.concat(new_extras)
347
391
  end
348
392
  end
349
393
 
350
- def complexity(new_complexity)
394
+ def complexity(new_complexity = nil)
351
395
  case new_complexity
352
396
  when Proc
353
397
  if new_complexity.parameters.size != 3
@@ -360,12 +404,19 @@ module GraphQL
360
404
  end
361
405
  when Numeric
362
406
  @complexity = new_complexity
407
+ when nil
408
+ @complexity
363
409
  else
364
410
  raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
365
411
  end
366
412
  end
367
413
 
368
- # @return [Integer, nil] Applied to connections if present
414
+ # @return [Boolean] True if this field's {#max_page_size} should override the schema default.
415
+ def has_max_page_size?
416
+ @has_max_page_size
417
+ end
418
+
419
+ # @return [Integer, nil] Applied to connections if {#has_max_page_size?}
369
420
  attr_reader :max_page_size
370
421
 
371
422
  # @return [GraphQL::Field]
@@ -416,6 +467,7 @@ module GraphQL
416
467
  field_defn.introspection = @introspection
417
468
  field_defn.complexity = @complexity
418
469
  field_defn.subscription_scope = @subscription_scope
470
+ field_defn.ast_node = ast_node
419
471
 
420
472
  arguments.each do |name, defn|
421
473
  arg_graphql = defn.to_graphql
@@ -433,14 +485,25 @@ module GraphQL
433
485
 
434
486
  # Ok, `self` isn't a class, but this is for consistency with the classes
435
487
  field_defn.metadata[:type_class] = self
436
-
488
+ field_defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(field_defn)
437
489
  field_defn
438
490
  end
439
491
 
492
+ attr_writer :type
493
+
440
494
  def type
441
- @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
442
- rescue
443
- raise ArgumentError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: #{$!.message}", $!.backtrace
495
+ @type ||= if @function
496
+ Member::BuildType.parse_type(@function.type, null: false)
497
+ elsif @field
498
+ Member::BuildType.parse_type(@field.type, null: false)
499
+ else
500
+ Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
501
+ end
502
+ rescue GraphQL::Schema::InvalidDocumentError => err
503
+ # Let this propagate up
504
+ raise err
505
+ rescue StandardError => err
506
+ raise ArgumentError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
444
507
  end
445
508
 
446
509
  def visible?(context)
@@ -459,14 +522,14 @@ module GraphQL
459
522
  end
460
523
  end
461
524
 
462
- def authorized?(object, context)
525
+ def authorized?(object, args, context)
463
526
  if @resolver_class
464
527
  # The resolver will check itself during `resolve()`
465
528
  @resolver_class.authorized?(object, context)
466
529
  else
467
530
  # Faster than `.any?`
468
531
  arguments.each_value do |arg|
469
- if !arg.authorized?(object, context)
532
+ if args.key?(arg.keyword) && !arg.authorized?(object, args[arg.keyword], context)
470
533
  return false
471
534
  end
472
535
  end
@@ -485,21 +548,22 @@ module GraphQL
485
548
  # Some legacy fields can have `nil` here, not exactly sure why.
486
549
  # @see https://github.com/rmosolgo/graphql-ruby/issues/1990 before removing
487
550
  inner_obj = after_obj && after_obj.object
488
- if authorized?(inner_obj, query_ctx)
489
- ruby_args = to_ruby_args(after_obj, args, ctx)
490
- # Then if it passed, resolve the field
491
- if @resolve_proc
492
- # Might be nil, still want to call the func in that case
493
- with_extensions(inner_obj, ruby_args, query_ctx) do |extended_obj, extended_args|
494
- # Pass the GraphQL args here for compatibility:
495
- @resolve_proc.call(extended_obj, args, ctx)
551
+ ctx.schema.after_lazy(to_ruby_args(after_obj, args, ctx)) do |ruby_args|
552
+ if authorized?(inner_obj, ruby_args, query_ctx)
553
+ # Then if it passed, resolve the field
554
+ if @resolve_proc
555
+ # Might be nil, still want to call the func in that case
556
+ with_extensions(inner_obj, ruby_args, query_ctx) do |extended_obj, extended_args|
557
+ # Pass the GraphQL args here for compatibility:
558
+ @resolve_proc.call(extended_obj, args, ctx)
559
+ end
560
+ else
561
+ public_send_field(after_obj, ruby_args, query_ctx)
496
562
  end
497
563
  else
498
- public_send_field(after_obj, ruby_args, ctx)
564
+ err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
565
+ query_ctx.schema.unauthorized_field(err)
499
566
  end
500
- else
501
- err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
502
- query_ctx.schema.unauthorized_field(err)
503
567
  end
504
568
  end
505
569
  end
@@ -516,34 +580,13 @@ module GraphQL
516
580
  begin
517
581
  # Unwrap the GraphQL object to get the application object.
518
582
  application_object = object.object
519
- if self.authorized?(application_object, ctx)
520
- # Apply field extensions
521
- with_extensions(object, args, ctx) do |extended_obj, extended_args|
522
- field_receiver = if @resolver_class
523
- resolver_obj = if extended_obj.is_a?(GraphQL::Schema::Object)
524
- extended_obj.object
525
- else
526
- extended_obj
527
- end
528
- @resolver_class.new(object: resolver_obj, context: ctx, field: self)
529
- else
530
- extended_obj
531
- end
532
-
533
- if field_receiver.respond_to?(@resolver_method)
534
- # Call the method with kwargs, if there are any
535
- if extended_args.any?
536
- field_receiver.public_send(@resolver_method, **extended_args)
537
- else
538
- field_receiver.public_send(@resolver_method)
539
- end
540
- else
541
- resolve_field_method(field_receiver, extended_args, ctx)
542
- end
583
+ ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
584
+ if is_authorized
585
+ public_send_field(object, args, ctx)
586
+ else
587
+ err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
588
+ ctx.schema.unauthorized_field(err)
543
589
  end
544
- else
545
- err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
546
- ctx.schema.unauthorized_field(err)
547
590
  end
548
591
  rescue GraphQL::UnauthorizedFieldError => err
549
592
  err.field ||= self
@@ -555,46 +598,9 @@ module GraphQL
555
598
  err
556
599
  end
557
600
 
558
- # Find a way to resolve this field, checking:
559
- #
560
- # - Hash keys, if the wrapped object is a hash;
561
- # - A method on the wrapped object;
562
- # - Or, raise not implemented.
563
- #
564
- # This can be overridden by defining a method on the object type.
565
- # @param obj [GraphQL::Schema::Object]
566
- # @param ruby_kwargs [Hash<Symbol => Object>]
567
- # @param ctx [GraphQL::Query::Context]
568
- def resolve_field_method(obj, ruby_kwargs, ctx)
569
- if obj.object.is_a?(Hash)
570
- inner_object = obj.object
571
- if inner_object.key?(@method_sym)
572
- inner_object[@method_sym]
573
- else
574
- inner_object[@method_str]
575
- end
576
- elsif obj.object.respond_to?(@method_sym)
577
- if ruby_kwargs.any?
578
- obj.object.public_send(@method_sym, **ruby_kwargs)
579
- else
580
- obj.object.public_send(@method_sym)
581
- end
582
- else
583
- raise <<-ERR
584
- Failed to implement #{@owner.graphql_name}.#{@name}, tried:
585
-
586
- - `#{obj.class}##{@resolver_method}`, which did not exist
587
- - `#{obj.object.class}##{@method_sym}`, which did not exist
588
- - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
589
-
590
- To implement this field, define one of the methods above (and check for typos)
591
- ERR
592
- end
593
- end
594
-
595
601
  # @param ctx [GraphQL::Query::Context::FieldResolutionContext]
596
602
  def fetch_extra(extra_name, ctx)
597
- if extra_name != :path && respond_to?(extra_name)
603
+ if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
598
604
  self.public_send(extra_name)
599
605
  elsif ctx.respond_to?(extra_name)
600
606
  ctx.public_send(extra_name)
@@ -617,11 +623,36 @@ module GraphQL
617
623
  if graphql_args.any? || @extras.any?
618
624
  # Splat the GraphQL::Arguments to Ruby keyword arguments
619
625
  ruby_kwargs = graphql_args.to_kwargs
626
+ maybe_lazies = []
620
627
  # Apply any `prepare` methods. Not great code organization, can this go somewhere better?
621
628
  arguments.each do |name, arg_defn|
622
629
  ruby_kwargs_key = arg_defn.keyword
623
- if ruby_kwargs.key?(ruby_kwargs_key) && arg_defn.prepare
624
- ruby_kwargs[ruby_kwargs_key] = arg_defn.prepare_value(obj, ruby_kwargs[ruby_kwargs_key])
630
+
631
+ if ruby_kwargs.key?(ruby_kwargs_key)
632
+ loads = arg_defn.loads
633
+ value = ruby_kwargs[ruby_kwargs_key]
634
+ loaded_value = if loads && !arg_defn.from_resolver?
635
+ if arg_defn.type.list?
636
+ loaded_values = value.map { |val| load_application_object(arg_defn, loads, val, field_ctx.query.context) }
637
+ field_ctx.schema.after_any_lazies(loaded_values) { |result| result }
638
+ else
639
+ load_application_object(arg_defn, loads, value, field_ctx.query.context)
640
+ end
641
+ elsif arg_defn.type.list? && value.is_a?(Array)
642
+ field_ctx.schema.after_any_lazies(value, &:itself)
643
+ else
644
+ value
645
+ end
646
+
647
+ maybe_lazies << field_ctx.schema.after_lazy(loaded_value) do |loaded_value|
648
+ prepared_value = if arg_defn.prepare
649
+ arg_defn.prepare_value(obj, loaded_value)
650
+ else
651
+ loaded_value
652
+ end
653
+
654
+ ruby_kwargs[ruby_kwargs_key] = prepared_value
655
+ end
625
656
  end
626
657
  end
627
658
 
@@ -629,30 +660,60 @@ module GraphQL
629
660
  ruby_kwargs[extra_arg] = fetch_extra(extra_arg, field_ctx)
630
661
  end
631
662
 
632
- ruby_kwargs
663
+ field_ctx.schema.after_any_lazies(maybe_lazies) do
664
+ ruby_kwargs
665
+ end
633
666
  else
634
667
  NO_ARGS
635
668
  end
636
669
  end
637
670
 
638
- def public_send_field(obj, ruby_kwargs, field_ctx)
639
- query_ctx = field_ctx.query.context
640
- with_extensions(obj, ruby_kwargs, query_ctx) do |extended_obj, extended_args|
671
+ def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
672
+ with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
641
673
  if @resolver_class
642
- if extended_obj.is_a?(GraphQL::Schema::Object)
643
- extended_obj = extended_obj.object
674
+ if obj.is_a?(GraphQL::Schema::Object)
675
+ obj = obj.object
644
676
  end
645
- extended_obj = @resolver_class.new(object: extended_obj, context: query_ctx, field: self)
677
+ obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
646
678
  end
647
679
 
648
- if extended_obj.respond_to?(@resolver_method)
649
- if extended_args.any?
650
- extended_obj.public_send(@resolver_method, **extended_args)
680
+ # Find a way to resolve this field, checking:
681
+ #
682
+ # - A method on the type instance;
683
+ # - Hash keys, if the wrapped object is a hash;
684
+ # - A method on the wrapped object;
685
+ # - Or, raise not implemented.
686
+ #
687
+ if obj.respond_to?(@resolver_method)
688
+ # Call the method with kwargs, if there are any
689
+ if ruby_kwargs.any?
690
+ obj.public_send(@resolver_method, **ruby_kwargs)
691
+ else
692
+ obj.public_send(@resolver_method)
693
+ end
694
+ elsif obj.object.is_a?(Hash)
695
+ inner_object = obj.object
696
+ if inner_object.key?(@method_sym)
697
+ inner_object[@method_sym]
698
+ else
699
+ inner_object[@method_str]
700
+ end
701
+ elsif obj.object.respond_to?(@method_sym)
702
+ if ruby_kwargs.any?
703
+ obj.object.public_send(@method_sym, **ruby_kwargs)
651
704
  else
652
- extended_obj.public_send(@resolver_method)
705
+ obj.object.public_send(@method_sym)
653
706
  end
654
707
  else
655
- resolve_field_method(extended_obj, extended_args, query_ctx)
708
+ raise <<-ERR
709
+ Failed to implement #{@owner.graphql_name}.#{@name}, tried:
710
+
711
+ - `#{obj.class}##{@resolver_method}`, which did not exist
712
+ - `#{obj.object.class}##{@method_sym}`, which did not exist
713
+ - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
714
+
715
+ To implement this field, define one of the methods above (and check for typos)
716
+ ERR
656
717
  end
657
718
  end
658
719
  end
@@ -664,32 +725,42 @@ module GraphQL
664
725
  if @extensions.empty?
665
726
  yield(obj, args)
666
727
  else
667
- # Save these so that the originals can be re-given to `after_resolve` handlers.
668
- original_args = args
669
- original_obj = obj
670
-
671
- memos = []
672
- value = run_extensions_before_resolve(memos, obj, args, ctx) do |extended_obj, extended_args|
673
- yield(extended_obj, extended_args)
728
+ # This is a hack to get the _last_ value for extended obj and args,
729
+ # in case one of the extensions doesn't `yield`.
730
+ # (There's another implementation that uses multiple-return, but I'm wary of the perf cost of the extra arrays)
731
+ extended = { args: args, obj: obj, memos: nil }
732
+ value = run_extensions_before_resolve(obj, args, ctx, extended) do |obj, args|
733
+ yield(obj, args)
674
734
  end
675
735
 
736
+ extended_obj = extended[:obj]
737
+ extended_args = extended[:args]
738
+ memos = extended[:memos] || EMPTY_HASH
739
+
676
740
  ctx.schema.after_lazy(value) do |resolved_value|
677
- @extensions.each_with_index do |ext, idx|
741
+ idx = 0
742
+ @extensions.each do |ext|
678
743
  memo = memos[idx]
679
744
  # TODO after_lazy?
680
- resolved_value = ext.after_resolve(object: original_obj, arguments: original_args, context: ctx, value: resolved_value, memo: memo)
745
+ resolved_value = ext.after_resolve(object: extended_obj, arguments: extended_args, context: ctx, value: resolved_value, memo: memo)
746
+ idx += 1
681
747
  end
682
748
  resolved_value
683
749
  end
684
750
  end
685
751
  end
686
752
 
687
- def run_extensions_before_resolve(memos, obj, args, ctx, idx: 0)
753
+ def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0)
688
754
  extension = @extensions[idx]
689
755
  if extension
690
756
  extension.resolve(object: obj, arguments: args, context: ctx) do |extended_obj, extended_args, memo|
691
- memos << memo
692
- run_extensions_before_resolve(memos, extended_obj, extended_args, ctx, idx: idx + 1) { |o, a| yield(o, a) }
757
+ if memo
758
+ memos = extended[:memos] ||= {}
759
+ memos[idx] = memo
760
+ end
761
+ extended[:obj] = extended_obj
762
+ extended[:args] = extended_args
763
+ run_extensions_before_resolve(extended_obj, extended_args, ctx, extended, idx: idx + 1) { |o, a| yield(o, a) }
693
764
  end
694
765
  else
695
766
  yield(obj, args)
@@ -1,6 +1,19 @@
1
1
  module GraphQL
2
2
  class Schema
3
3
  module FindInheritedValue
4
+ module EmptyObjects
5
+ EMPTY_HASH = {}.freeze
6
+ EMPTY_ARRAY = [].freeze
7
+ end
8
+
9
+ def self.extended(child_cls)
10
+ child_cls.singleton_class.include(EmptyObjects)
11
+ end
12
+
13
+ def self.included(child_cls)
14
+ child_cls.include(EmptyObjects)
15
+ end
16
+
4
17
  private
5
18
 
6
19
  def find_inherited_value(method_name, default_value = nil)
@@ -38,7 +38,7 @@ module GraphQL
38
38
 
39
39
  find_in_directive(directive, path: path)
40
40
  else
41
- type = schema.types[type_or_directive]
41
+ type = schema.get_type(type_or_directive)
42
42
 
43
43
  if type.nil?
44
44
  raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema."
@@ -66,22 +66,24 @@ module GraphQL
66
66
  end
67
67
 
68
68
  def find_in_type(type, path:)
69
- case type
70
- when GraphQL::ObjectType
69
+ case type.kind.name
70
+ when "OBJECT"
71
71
  find_in_fields_type(type, kind: "object", path: path)
72
- when GraphQL::InterfaceType
72
+ when "INTERFACE"
73
73
  find_in_fields_type(type, kind: "interface", path: path)
74
- when GraphQL::InputObjectType
74
+ when "INPUT_OBJECT"
75
75
  find_in_input_object(type, path: path)
76
- when GraphQL::UnionType
76
+ when "UNION"
77
77
  # Error out if path that was provided is too long
78
78
  # i.e UnionType.PossibleType.aField
79
79
  # Use PossibleType.aField instead.
80
80
  if invalid = path.first
81
81
  raise MemberNotFoundError, "Cannot select union possible type `#{invalid}`. Select the type directly instead."
82
82
  end
83
- when GraphQL::EnumType
83
+ when "ENUM"
84
84
  find_in_enum_type(type, path: path)
85
+ else
86
+ raise "Unexpected find_in_type: #{type.inspect} (#{path})"
85
87
  end
86
88
  end
87
89
 
@@ -90,7 +92,7 @@ module GraphQL
90
92
  field = schema.get_field(type, field_name)
91
93
 
92
94
  if field.nil?
93
- raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type}`."
95
+ raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type.graphql_name}`."
94
96
  end
95
97
 
96
98
  return field if path.empty?
@@ -117,10 +119,10 @@ module GraphQL
117
119
 
118
120
  def find_in_input_object(input_object, path:)
119
121
  field_name = path.shift
120
- input_field = input_object.input_fields[field_name]
122
+ input_field = input_object.arguments[field_name]
121
123
 
122
124
  if input_field.nil?
123
- raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object}`."
125
+ raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`."
124
126
  end
125
127
 
126
128
  # Error out if path that was provided is too long
@@ -137,7 +139,7 @@ module GraphQL
137
139
  enum_value = enum_type.values[value_name]
138
140
 
139
141
  if enum_value.nil?
140
- raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type}`."
142
+ raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`."
141
143
  end
142
144
 
143
145
  # Error out if path that was provided is too long