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
@@ -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