graphql 1.13.12 → 2.0.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (273) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +1 -1
  3. data/lib/generators/graphql/relay.rb +3 -17
  4. data/lib/generators/graphql/templates/schema.erb +3 -0
  5. data/lib/graphql/analysis/ast/field_usage.rb +3 -1
  6. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -1
  7. data/lib/graphql/analysis/ast/query_complexity.rb +1 -1
  8. data/lib/graphql/analysis/ast/query_depth.rb +0 -1
  9. data/lib/graphql/analysis/ast/visitor.rb +43 -36
  10. data/lib/graphql/analysis/ast.rb +2 -12
  11. data/lib/graphql/analysis.rb +0 -7
  12. data/lib/graphql/backtrace/table.rb +2 -20
  13. data/lib/graphql/backtrace/trace.rb +96 -0
  14. data/lib/graphql/backtrace/tracer.rb +2 -3
  15. data/lib/graphql/backtrace.rb +7 -8
  16. data/lib/graphql/dataloader/null_dataloader.rb +3 -1
  17. data/lib/graphql/dataloader/source.rb +9 -0
  18. data/lib/graphql/dataloader.rb +4 -1
  19. data/lib/graphql/dig.rb +1 -1
  20. data/lib/graphql/execution/errors.rb +12 -82
  21. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  22. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -3
  23. data/lib/graphql/execution/interpreter/resolve.rb +26 -0
  24. data/lib/graphql/execution/interpreter/runtime.rb +300 -222
  25. data/lib/graphql/execution/interpreter.rb +187 -78
  26. data/lib/graphql/execution/lazy.rb +7 -21
  27. data/lib/graphql/execution/lookahead.rb +44 -40
  28. data/lib/graphql/execution/multiplex.rb +3 -174
  29. data/lib/graphql/execution.rb +11 -4
  30. data/lib/graphql/filter.rb +7 -2
  31. data/lib/graphql/introspection/directive_type.rb +2 -2
  32. data/lib/graphql/introspection/dynamic_fields.rb +3 -8
  33. data/lib/graphql/introspection/entry_points.rb +2 -15
  34. data/lib/graphql/introspection/field_type.rb +1 -1
  35. data/lib/graphql/introspection/schema_type.rb +2 -2
  36. data/lib/graphql/introspection/type_type.rb +13 -6
  37. data/lib/graphql/introspection.rb +4 -3
  38. data/lib/graphql/language/document_from_schema_definition.rb +43 -44
  39. data/lib/graphql/language/lexer.rb +216 -1488
  40. data/lib/graphql/language/nodes.rb +66 -40
  41. data/lib/graphql/language/parser.rb +539 -510
  42. data/lib/graphql/language/parser.y +53 -44
  43. data/lib/graphql/language/printer.rb +37 -21
  44. data/lib/graphql/language/visitor.rb +191 -83
  45. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  46. data/lib/graphql/pagination/array_connection.rb +4 -2
  47. data/lib/graphql/pagination/connection.rb +33 -6
  48. data/lib/graphql/pagination/connections.rb +3 -28
  49. data/lib/graphql/pagination/relation_connection.rb +2 -0
  50. data/lib/graphql/query/context.rb +156 -196
  51. data/lib/graphql/query/input_validation_result.rb +10 -1
  52. data/lib/graphql/query/null_context.rb +1 -4
  53. data/lib/graphql/query/validation_pipeline.rb +12 -37
  54. data/lib/graphql/query/variable_validation_error.rb +2 -2
  55. data/lib/graphql/query/variables.rb +35 -21
  56. data/lib/graphql/query.rb +39 -46
  57. data/lib/graphql/railtie.rb +0 -104
  58. data/lib/graphql/rake_task/validate.rb +1 -1
  59. data/lib/graphql/rake_task.rb +29 -1
  60. data/lib/graphql/relay/range_add.rb +9 -20
  61. data/lib/graphql/relay.rb +0 -15
  62. data/lib/graphql/schema/addition.rb +7 -9
  63. data/lib/graphql/schema/argument.rb +38 -47
  64. data/lib/graphql/schema/build_from_definition.rb +47 -21
  65. data/lib/graphql/schema/directive/one_of.rb +12 -0
  66. data/lib/graphql/schema/directive/transform.rb +1 -1
  67. data/lib/graphql/schema/directive.rb +12 -23
  68. data/lib/graphql/schema/enum.rb +29 -41
  69. data/lib/graphql/schema/enum_value.rb +2 -25
  70. data/lib/graphql/schema/field/connection_extension.rb +4 -0
  71. data/lib/graphql/schema/field.rb +256 -349
  72. data/lib/graphql/schema/field_extension.rb +1 -4
  73. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  74. data/lib/graphql/schema/input_object.rb +57 -69
  75. data/lib/graphql/schema/interface.rb +0 -35
  76. data/lib/graphql/schema/introspection_system.rb +3 -8
  77. data/lib/graphql/schema/late_bound_type.rb +8 -2
  78. data/lib/graphql/schema/list.rb +18 -9
  79. data/lib/graphql/schema/loader.rb +1 -2
  80. data/lib/graphql/schema/member/base_dsl_methods.rb +17 -19
  81. data/lib/graphql/schema/member/build_type.rb +5 -7
  82. data/lib/graphql/schema/member/has_arguments.rb +147 -56
  83. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  84. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  85. data/lib/graphql/schema/member/has_directives.rb +81 -61
  86. data/lib/graphql/schema/member/has_fields.rb +97 -40
  87. data/lib/graphql/schema/member/has_interfaces.rb +49 -10
  88. data/lib/graphql/schema/member/has_validators.rb +32 -6
  89. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
  90. data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
  91. data/lib/graphql/schema/member/validates_input.rb +3 -3
  92. data/lib/graphql/schema/member.rb +0 -6
  93. data/lib/graphql/schema/mutation.rb +0 -9
  94. data/lib/graphql/schema/non_null.rb +3 -9
  95. data/lib/graphql/schema/object.rb +15 -52
  96. data/lib/graphql/schema/relay_classic_mutation.rb +53 -42
  97. data/lib/graphql/schema/resolver/has_payload_type.rb +20 -10
  98. data/lib/graphql/schema/resolver.rb +43 -44
  99. data/lib/graphql/schema/scalar.rb +8 -23
  100. data/lib/graphql/schema/subscription.rb +0 -7
  101. data/lib/graphql/schema/timeout.rb +24 -28
  102. data/lib/graphql/schema/type_membership.rb +3 -0
  103. data/lib/graphql/schema/union.rb +10 -17
  104. data/lib/graphql/schema/validator.rb +1 -1
  105. data/lib/graphql/schema/warden.rb +37 -9
  106. data/lib/graphql/schema/wrapper.rb +0 -5
  107. data/lib/graphql/schema.rb +265 -968
  108. data/lib/graphql/static_validation/all_rules.rb +1 -0
  109. data/lib/graphql/static_validation/base_visitor.rb +4 -21
  110. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  111. data/lib/graphql/static_validation/error.rb +2 -2
  112. data/lib/graphql/static_validation/literal_validator.rb +19 -1
  113. data/lib/graphql/static_validation/rules/directives_are_defined.rb +11 -5
  114. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +12 -12
  115. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  116. data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
  117. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  118. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  119. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +12 -6
  120. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
  121. data/lib/graphql/static_validation/validator.rb +3 -25
  122. data/lib/graphql/static_validation.rb +0 -2
  123. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +7 -1
  124. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +38 -1
  125. data/lib/graphql/subscriptions/event.rb +3 -8
  126. data/lib/graphql/subscriptions/instrumentation.rb +0 -51
  127. data/lib/graphql/subscriptions.rb +32 -20
  128. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  129. data/lib/graphql/tracing/appoptics_trace.rb +231 -0
  130. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  131. data/lib/graphql/tracing/data_dog_trace.rb +148 -0
  132. data/lib/graphql/tracing/data_dog_tracing.rb +21 -2
  133. data/lib/graphql/tracing/legacy_trace.rb +65 -0
  134. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  135. data/lib/graphql/tracing/notifications_trace.rb +42 -0
  136. data/lib/graphql/tracing/platform_trace.rb +109 -0
  137. data/lib/graphql/tracing/platform_tracing.rb +33 -43
  138. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  139. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +1 -1
  140. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  141. data/lib/graphql/tracing/scout_trace.rb +72 -0
  142. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  143. data/lib/graphql/tracing/trace.rb +75 -0
  144. data/lib/graphql/tracing.rb +16 -40
  145. data/lib/graphql/type_kinds.rb +6 -3
  146. data/lib/graphql/types/iso_8601_date.rb +4 -1
  147. data/lib/graphql/types/iso_8601_date_time.rb +4 -0
  148. data/lib/graphql/types/relay/base_connection.rb +16 -6
  149. data/lib/graphql/types/relay/connection_behaviors.rb +29 -27
  150. data/lib/graphql/types/relay/edge_behaviors.rb +16 -5
  151. data/lib/graphql/types/relay/node_behaviors.rb +12 -2
  152. data/lib/graphql/types/relay/page_info_behaviors.rb +7 -2
  153. data/lib/graphql/types/relay.rb +0 -3
  154. data/lib/graphql/types/string.rb +1 -1
  155. data/lib/graphql/version.rb +1 -1
  156. data/lib/graphql.rb +17 -74
  157. metadata +33 -133
  158. data/lib/graphql/analysis/analyze_query.rb +0 -98
  159. data/lib/graphql/analysis/field_usage.rb +0 -45
  160. data/lib/graphql/analysis/max_query_complexity.rb +0 -26
  161. data/lib/graphql/analysis/max_query_depth.rb +0 -26
  162. data/lib/graphql/analysis/query_complexity.rb +0 -88
  163. data/lib/graphql/analysis/query_depth.rb +0 -43
  164. data/lib/graphql/analysis/reducer_state.rb +0 -48
  165. data/lib/graphql/argument.rb +0 -131
  166. data/lib/graphql/authorization.rb +0 -82
  167. data/lib/graphql/backtrace/legacy_tracer.rb +0 -56
  168. data/lib/graphql/backwards_compatibility.rb +0 -61
  169. data/lib/graphql/base_type.rb +0 -232
  170. data/lib/graphql/boolean_type.rb +0 -2
  171. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  172. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  173. data/lib/graphql/compatibility/execution_specification.rb +0 -436
  174. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  175. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -215
  176. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
  177. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  178. data/lib/graphql/compatibility/query_parser_specification.rb +0 -266
  179. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -682
  180. data/lib/graphql/compatibility.rb +0 -5
  181. data/lib/graphql/define/assign_argument.rb +0 -12
  182. data/lib/graphql/define/assign_connection.rb +0 -13
  183. data/lib/graphql/define/assign_enum_value.rb +0 -18
  184. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  185. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  186. data/lib/graphql/define/assign_object_field.rb +0 -42
  187. data/lib/graphql/define/defined_object_proxy.rb +0 -53
  188. data/lib/graphql/define/instance_definable.rb +0 -255
  189. data/lib/graphql/define/no_definition_error.rb +0 -7
  190. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  191. data/lib/graphql/define/type_definer.rb +0 -31
  192. data/lib/graphql/define.rb +0 -31
  193. data/lib/graphql/deprecated_dsl.rb +0 -55
  194. data/lib/graphql/directive/deprecated_directive.rb +0 -2
  195. data/lib/graphql/directive/include_directive.rb +0 -2
  196. data/lib/graphql/directive/skip_directive.rb +0 -2
  197. data/lib/graphql/directive.rb +0 -107
  198. data/lib/graphql/enum_type.rb +0 -133
  199. data/lib/graphql/execution/execute.rb +0 -333
  200. data/lib/graphql/execution/flatten.rb +0 -40
  201. data/lib/graphql/execution/instrumentation.rb +0 -92
  202. data/lib/graphql/execution/lazy/resolve.rb +0 -91
  203. data/lib/graphql/execution/typecast.rb +0 -50
  204. data/lib/graphql/field/resolve.rb +0 -59
  205. data/lib/graphql/field.rb +0 -226
  206. data/lib/graphql/float_type.rb +0 -2
  207. data/lib/graphql/function.rb +0 -128
  208. data/lib/graphql/id_type.rb +0 -2
  209. data/lib/graphql/input_object_type.rb +0 -138
  210. data/lib/graphql/int_type.rb +0 -2
  211. data/lib/graphql/interface_type.rb +0 -72
  212. data/lib/graphql/internal_representation/document.rb +0 -27
  213. data/lib/graphql/internal_representation/node.rb +0 -206
  214. data/lib/graphql/internal_representation/print.rb +0 -51
  215. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  216. data/lib/graphql/internal_representation/scope.rb +0 -88
  217. data/lib/graphql/internal_representation/visit.rb +0 -36
  218. data/lib/graphql/internal_representation.rb +0 -7
  219. data/lib/graphql/language/lexer.rl +0 -260
  220. data/lib/graphql/list_type.rb +0 -80
  221. data/lib/graphql/non_null_type.rb +0 -71
  222. data/lib/graphql/object_type.rb +0 -130
  223. data/lib/graphql/query/arguments.rb +0 -189
  224. data/lib/graphql/query/arguments_cache.rb +0 -24
  225. data/lib/graphql/query/executor.rb +0 -52
  226. data/lib/graphql/query/literal_input.rb +0 -136
  227. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  228. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  229. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  230. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  231. data/lib/graphql/query/serial_execution.rb +0 -40
  232. data/lib/graphql/relay/array_connection.rb +0 -83
  233. data/lib/graphql/relay/base_connection.rb +0 -189
  234. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  235. data/lib/graphql/relay/connection_resolve.rb +0 -43
  236. data/lib/graphql/relay/connection_type.rb +0 -54
  237. data/lib/graphql/relay/edge.rb +0 -27
  238. data/lib/graphql/relay/edge_type.rb +0 -19
  239. data/lib/graphql/relay/edges_instrumentation.rb +0 -39
  240. data/lib/graphql/relay/global_id_resolve.rb +0 -17
  241. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  242. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  243. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  244. data/lib/graphql/relay/mutation/result.rb +0 -38
  245. data/lib/graphql/relay/mutation.rb +0 -106
  246. data/lib/graphql/relay/node.rb +0 -39
  247. data/lib/graphql/relay/page_info.rb +0 -7
  248. data/lib/graphql/relay/relation_connection.rb +0 -188
  249. data/lib/graphql/relay/type_extensions.rb +0 -32
  250. data/lib/graphql/scalar_type.rb +0 -91
  251. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  252. data/lib/graphql/schema/default_parse_error.rb +0 -10
  253. data/lib/graphql/schema/default_type_error.rb +0 -17
  254. data/lib/graphql/schema/member/accepts_definition.rb +0 -164
  255. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -58
  256. data/lib/graphql/schema/member/instrumentation.rb +0 -131
  257. data/lib/graphql/schema/middleware_chain.rb +0 -82
  258. data/lib/graphql/schema/possible_types.rb +0 -44
  259. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  260. data/lib/graphql/schema/timeout_middleware.rb +0 -88
  261. data/lib/graphql/schema/traversal.rb +0 -228
  262. data/lib/graphql/schema/validation.rb +0 -313
  263. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  264. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  265. data/lib/graphql/string_type.rb +0 -2
  266. data/lib/graphql/subscriptions/subscription_root.rb +0 -76
  267. data/lib/graphql/tracing/skylight_tracing.rb +0 -70
  268. data/lib/graphql/types/relay/default_relay.rb +0 -31
  269. data/lib/graphql/types/relay/node_field.rb +0 -24
  270. data/lib/graphql/types/relay/nodes_field.rb +0 -43
  271. data/lib/graphql/union_type.rb +0 -115
  272. data/lib/graphql/upgrader/member.rb +0 -937
  273. data/lib/graphql/upgrader/schema.rb +0 -38
@@ -5,14 +5,13 @@ require "graphql/schema/field/scope_extension"
5
5
  module GraphQL
6
6
  class Schema
7
7
  class Field
8
- include GraphQL::Schema::Member::CachedGraphQLDefinition
9
- include GraphQL::Schema::Member::AcceptsDefinition
10
8
  include GraphQL::Schema::Member::HasArguments
9
+ include GraphQL::Schema::Member::HasArguments::FieldConfigured
11
10
  include GraphQL::Schema::Member::HasAstNode
12
11
  include GraphQL::Schema::Member::HasPath
13
12
  include GraphQL::Schema::Member::HasValidators
14
13
  extend GraphQL::Schema::FindInheritedValue
15
- include GraphQL::Schema::FindInheritedValue::EmptyObjects
14
+ include GraphQL::EmptyObjects
16
15
  include GraphQL::Schema::Member::HasDirectives
17
16
  include GraphQL::Schema::Member::HasDeprecationReason
18
17
 
@@ -30,8 +29,17 @@ module GraphQL
30
29
  # @return [String] Method or hash key on the underlying object to look up
31
30
  attr_reader :method_str
32
31
 
32
+ attr_reader :hash_key
33
+ attr_reader :dig_keys
34
+
33
35
  # @return [Symbol] The method on the type to look up
34
- attr_reader :resolver_method
36
+ def resolver_method
37
+ if @resolver_class
38
+ @resolver_class.resolver_method
39
+ else
40
+ @resolver_method
41
+ end
42
+ end
35
43
 
36
44
  # @return [Class] The thing this field was defined on (type, mutation, resolver)
37
45
  attr_accessor :owner
@@ -70,7 +78,10 @@ module GraphQL
70
78
  attr_reader :trace
71
79
 
72
80
  # @return [String, nil]
73
- attr_accessor :subscription_scope
81
+ def subscription_scope
82
+ @subscription_scope || (@resolver_class.respond_to?(:subscription_scope) ? @resolver_class.subscription_scope : nil)
83
+ end
84
+ attr_writer :subscription_scope
74
85
 
75
86
  # Create a field instance from a list of arguments, keyword arguments, and a block.
76
87
  #
@@ -84,21 +95,9 @@ module GraphQL
84
95
  # @return [GraphQL::Schema:Field] an instance of `self
85
96
  # @see {.initialize} for other options
86
97
  def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
87
- if kwargs[:field]
88
- if kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodeField.graphql_definition
89
- GraphQL::Deprecation.warn("Legacy-style `GraphQL::Relay::Node.field` is being added to a class-based type. See `GraphQL::Types::Relay::NodeField` for a replacement.")
90
- return GraphQL::Types::Relay::NodeField
91
- elsif kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodesField.graphql_definition
92
- GraphQL::Deprecation.warn("Legacy-style `GraphQL::Relay::Node.plural_field` is being added to a class-based type. See `GraphQL::Types::Relay::NodesField` for a replacement.")
93
- return GraphQL::Types::Relay::NodesField
94
- end
95
- end
96
-
97
- if (parent_config = resolver || mutation || subscription)
98
- # Get the parent config, merge in local overrides
99
- kwargs = parent_config.field_options.merge(kwargs)
98
+ if (resolver_class = resolver || mutation || subscription)
100
99
  # Add a reference to that parent class
101
- kwargs[:resolver_class] = parent_config
100
+ kwargs[:resolver_class] = resolver_class
102
101
  end
103
102
 
104
103
  if name
@@ -106,9 +105,6 @@ module GraphQL
106
105
  end
107
106
 
108
107
  if !type.nil?
109
- if type.is_a?(GraphQL::Field)
110
- raise ArgumentError, "A GraphQL::Field was passed as the second argument, use the `field:` keyword for this instead."
111
- end
112
108
  if desc
113
109
  if kwargs[:description]
114
110
  raise ArgumentError, "Provide description as a positional argument or `description:` keyword, but not both (#{desc.inspect}, #{kwargs[:description].inspect})"
@@ -116,8 +112,8 @@ module GraphQL
116
112
 
117
113
  kwargs[:description] = desc
118
114
  kwargs[:type] = type
119
- elsif (kwargs[:field] || kwargs[:function] || resolver || mutation) && type.is_a?(String)
120
- # The return type should be copied from `field` or `function`, and the second positional argument is the description
115
+ elsif (resolver || mutation) && type.is_a?(String)
116
+ # The return type should be copied from the resolver, and the second positional argument is the description
121
117
  kwargs[:description] = type
122
118
  else
123
119
  kwargs[:type] = type
@@ -134,10 +130,10 @@ module GraphQL
134
130
  def connection?
135
131
  if @connection.nil?
136
132
  # Provide default based on type name
137
- return_type_name = if (contains_type = @field || @function)
138
- Member::BuildType.to_type_name(contains_type.type)
139
- elsif @return_type_expr
133
+ return_type_name = if @return_type_expr
140
134
  Member::BuildType.to_type_name(@return_type_expr)
135
+ elsif @resolver_class && @resolver_class.type
136
+ Member::BuildType.to_type_name(@resolver_class.type)
141
137
  else
142
138
  # As a last ditch, try to force loading the return type:
143
139
  type.unwrap.name
@@ -153,8 +149,18 @@ module GraphQL
153
149
  if !@scope.nil?
154
150
  # The default was overridden
155
151
  @scope
152
+ elsif @return_type_expr
153
+ # Detect a list return type, but don't call `type` since that may eager-load an otherwise lazy-loaded type
154
+ @return_type_expr.is_a?(Array) ||
155
+ (@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) ||
156
+ connection?
157
+ elsif @resolver_class
158
+ resolver_type = @resolver_class.type_expr
159
+ resolver_type.is_a?(Array) ||
160
+ (resolver_type.is_a?(String) && resolver_type.include?("[")) ||
161
+ connection?
156
162
  else
157
- @return_type_expr && (@return_type_expr.is_a?(Array) || (@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) || connection?)
163
+ false
158
164
  end
159
165
  end
160
166
 
@@ -176,6 +182,8 @@ module GraphQL
176
182
 
177
183
  # @return Boolean
178
184
  attr_reader :relay_node_field
185
+ # @return Boolean
186
+ attr_reader :relay_nodes_field
179
187
 
180
188
  # @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
181
189
  def method_conflict_warning?
@@ -185,7 +193,7 @@ module GraphQL
185
193
  # @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
186
194
  # @param type [Class, GraphQL::BaseType, Array] The return type of this field
187
195
  # @param owner [Class] The type that this field belongs to
188
- # @param null [Boolean] `true` if this field may return `null`, `false` if it is never `null`
196
+ # @param null [Boolean] (defaults to `true`) `true` if this field may return `null`, `false` if it is never `null`
189
197
  # @param description [String] Field description
190
198
  # @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
191
199
  # @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
@@ -195,10 +203,8 @@ module GraphQL
195
203
  # @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
196
204
  # @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
197
205
  # @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
206
+ # @param default_page_size [Integer, nil] For connections, the default number of items to return from this field, or `nil` to return unlimited results.
198
207
  # @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
199
- # @param resolve [<#call(obj, args, ctx)>] **deprecated** for compatibility with <1.8.0
200
- # @param field [GraphQL::Field, GraphQL::Schema::Field] **deprecated** for compatibility with <1.8.0
201
- # @param function [GraphQL::Function] **deprecated** for compatibility with <1.8.0
202
208
  # @param resolver_class [Class] (Private) A {Schema::Resolver} which this field was derived from. Use `resolver:` to create a field with a resolver.
203
209
  # @param arguments [{String=>GraphQL::Schema::Argument, Hash}] Arguments for this field (may be added in the block, also)
204
210
  # @param camelize [Boolean] If true, the field name will be camelized when building the schema
@@ -212,31 +218,25 @@ module GraphQL
212
218
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
213
219
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
214
220
  # @param validates [Array<Hash>] Configurations for validating this field
215
- # @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
216
- def initialize(type: nil, name: nil, owner: nil, null: true, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, dig: 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, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
221
+ # @fallback_value [Object] A fallback value if the method is not defined
222
+ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, 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: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, &definition_block)
217
223
  if name.nil?
218
224
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
219
225
  end
220
- if !(field || function || resolver_class)
226
+ if !(resolver_class)
221
227
  if type.nil?
222
228
  raise ArgumentError, "missing second `type` argument or keyword `type:`"
223
229
  end
224
230
  end
225
- if (field || function || resolve) && extras.any?
226
- 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:`"
227
- end
228
231
  @original_name = name
229
232
  name_s = -name.to_s
233
+
230
234
  @underscored_name = -Member::BuildType.underscore(name_s)
231
235
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
236
+
232
237
  @description = description
233
- if field.is_a?(GraphQL::Schema::Field)
234
- raise ArgumentError, "Instead of passing a field as `field:`, use `add_field(field)` to add an already-defined field."
235
- else
236
- @field = field
237
- end
238
- @function = function
239
- @resolve = resolve
238
+ @type = @owner_type = @own_validators = @own_directives = @own_arguments = nil # these will be prepared later if necessary
239
+
240
240
  self.deprecation_reason = deprecation_reason
241
241
 
242
242
  if method && hash_key && dig
@@ -253,20 +253,31 @@ module GraphQL
253
253
  end
254
254
  end
255
255
 
256
- # TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
257
256
  method_name = method || hash_key || name_s
258
257
  @dig_keys = dig
259
- resolver_method ||= name_s.to_sym
258
+ if hash_key
259
+ @hash_key = hash_key
260
+ @hash_key_str = hash_key.to_s
261
+ else
262
+ @hash_key = NOT_CONFIGURED
263
+ @hash_key_str = NOT_CONFIGURED
264
+ end
260
265
 
261
266
  @method_str = -method_name.to_s
262
267
  @method_sym = method_name.to_sym
263
- @resolver_method = resolver_method
268
+ @resolver_method = (resolver_method || name_s).to_sym
264
269
  @complexity = complexity
265
270
  @return_type_expr = type
266
- @return_type_null = null
271
+ @return_type_null = if !null.nil?
272
+ null
273
+ elsif resolver_class
274
+ nil
275
+ else
276
+ true
277
+ end
267
278
  @connection = connection
268
- @has_max_page_size = max_page_size != :not_given
269
- @max_page_size = max_page_size == :not_given ? nil : max_page_size
279
+ @max_page_size = max_page_size
280
+ @default_page_size = default_page_size
270
281
  @introspection = introspection
271
282
  @extras = extras
272
283
  @broadcastable = broadcastable
@@ -277,7 +288,7 @@ module GraphQL
277
288
  @relay_nodes_field = relay_nodes_field
278
289
  @ast_node = ast_node
279
290
  @method_conflict_warning = method_conflict_warning
280
- @legacy_edge_class = legacy_edge_class
291
+ @fallback_value = fallback_value
281
292
 
282
293
  arguments.each do |name, arg|
283
294
  case arg
@@ -314,13 +325,19 @@ module GraphQL
314
325
  self.extensions(extensions)
315
326
  end
316
327
 
328
+ if resolver_class && resolver_class.extensions.any?
329
+ self.extensions(resolver_class.extensions)
330
+ end
331
+
317
332
  if directives.any?
318
333
  directives.each do |(dir_class, options)|
319
334
  self.directive(dir_class, **options)
320
335
  end
321
336
  end
322
337
 
323
- self.validates(validates)
338
+ if !validates.empty?
339
+ self.validates(validates)
340
+ end
324
341
 
325
342
  if definition_block
326
343
  if definition_block.arity == 1
@@ -338,7 +355,13 @@ module GraphQL
338
355
  # @return [Boolean, nil]
339
356
  # @see GraphQL::Subscriptions::BroadcastAnalyzer
340
357
  def broadcastable?
341
- @broadcastable
358
+ if !NOT_CONFIGURED.equal?(@broadcastable)
359
+ @broadcastable
360
+ elsif @resolver_class
361
+ @resolver_class.broadcastable?
362
+ else
363
+ nil
364
+ end
342
365
  end
343
366
 
344
367
  # @param text [String]
@@ -346,8 +369,12 @@ module GraphQL
346
369
  def description(text = nil)
347
370
  if text
348
371
  @description = text
349
- else
372
+ elsif !NOT_CONFIGURED.equal?(@description)
350
373
  @description
374
+ elsif @resolver_class
375
+ @resolver_class.description
376
+ else
377
+ nil
351
378
  end
352
379
  end
353
380
 
@@ -411,7 +438,12 @@ module GraphQL
411
438
  def extras(new_extras = nil)
412
439
  if new_extras.nil?
413
440
  # Read the value
414
- @extras
441
+ field_extras = @extras
442
+ if @resolver_class && @resolver_class.extras.any?
443
+ field_extras + @resolver_class.extras
444
+ else
445
+ field_extras
446
+ end
415
447
  else
416
448
  if @extras.frozen?
417
449
  @extras = @extras.dup
@@ -439,11 +471,11 @@ module GraphQL
439
471
  end
440
472
 
441
473
  if max_possible_page_size.nil?
442
- max_possible_page_size = max_page_size || query.schema.default_max_page_size
474
+ max_possible_page_size = default_page_size || query.schema.default_page_size || max_page_size || query.schema.default_max_page_size
443
475
  end
444
476
 
445
477
  if max_possible_page_size.nil?
446
- raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `max_page_size` or `default_max_page_size`"
478
+ raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `default_page_size`, `max_page_size` or `default_max_page_size`"
447
479
  else
448
480
  metadata_complexity = 0
449
481
  lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
@@ -471,7 +503,13 @@ module GraphQL
471
503
  case defined_complexity
472
504
  when Proc
473
505
  arguments = query.arguments_for(nodes.first, self)
474
- defined_complexity.call(query.context, arguments.keyword_arguments, child_complexity)
506
+ if arguments.is_a?(GraphQL::ExecutionError)
507
+ return child_complexity
508
+ elsif arguments.respond_to?(:keyword_arguments)
509
+ arguments = arguments.keyword_arguments
510
+ end
511
+
512
+ defined_complexity.call(query.context, arguments, child_complexity)
475
513
  when Numeric
476
514
  defined_complexity + child_complexity
477
515
  else
@@ -494,7 +532,11 @@ module GraphQL
494
532
  when Numeric
495
533
  @complexity = new_complexity
496
534
  when nil
497
- @complexity
535
+ if @resolver_class
536
+ @complexity || @resolver_class.complexity || 1
537
+ else
538
+ @complexity || 1
539
+ end
498
540
  else
499
541
  raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
500
542
  end
@@ -502,105 +544,49 @@ module GraphQL
502
544
 
503
545
  # @return [Boolean] True if this field's {#max_page_size} should override the schema default.
504
546
  def has_max_page_size?
505
- @has_max_page_size
547
+ !NOT_CONFIGURED.equal?(@max_page_size) || (@resolver_class && @resolver_class.has_max_page_size?)
506
548
  end
507
549
 
508
550
  # @return [Integer, nil] Applied to connections if {#has_max_page_size?}
509
- attr_reader :max_page_size
510
-
511
- prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
512
-
513
- # @return [GraphQL::Field]
514
- def to_graphql
515
- field_defn = if @field
516
- @field.dup
517
- elsif @function
518
- GraphQL::Function.build_field(@function)
551
+ def max_page_size
552
+ if !NOT_CONFIGURED.equal?(@max_page_size)
553
+ @max_page_size
554
+ elsif @resolver_class && @resolver_class.has_max_page_size?
555
+ @resolver_class.max_page_size
519
556
  else
520
- GraphQL::Field.new
521
- end
522
-
523
- field_defn.name = @name
524
- if @return_type_expr
525
- field_defn.type = -> { type }
526
- end
527
-
528
- if @description
529
- field_defn.description = @description
530
- end
531
-
532
- if self.deprecation_reason
533
- field_defn.deprecation_reason = self.deprecation_reason
534
- end
535
-
536
- if @resolver_class
537
- if @resolver_class < GraphQL::Schema::Mutation
538
- field_defn.mutation = @resolver_class
539
- end
540
- field_defn.metadata[:resolver] = @resolver_class
541
- end
542
-
543
- if !@trace.nil?
544
- field_defn.trace = @trace
545
- end
546
-
547
- if @relay_node_field
548
- field_defn.relay_node_field = @relay_node_field
549
- end
550
-
551
- if @relay_nodes_field
552
- field_defn.relay_nodes_field = @relay_nodes_field
557
+ nil
553
558
  end
559
+ end
554
560
 
555
- if @legacy_edge_class
556
- field_defn.edge_class = @legacy_edge_class
557
- end
558
-
559
- field_defn.resolve = self.method(:resolve_field)
560
- field_defn.connection = connection?
561
- field_defn.connection_max_page_size = max_page_size
562
- field_defn.introspection = @introspection
563
- field_defn.complexity = @complexity
564
- field_defn.subscription_scope = @subscription_scope
565
- field_defn.ast_node = ast_node
566
-
567
- all_argument_definitions.each do |defn|
568
- arg_graphql = defn.deprecated_to_graphql
569
- field_defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
570
- end
561
+ # @return [Boolean] True if this field's {#default_page_size} should override the schema default.
562
+ def has_default_page_size?
563
+ !NOT_CONFIGURED.equal?(@default_page_size) || (@resolver_class && @resolver_class.has_default_page_size?)
564
+ end
571
565
 
572
- # Support a passed-in proc, one way or another
573
- @resolve_proc = if @resolve
574
- @resolve
575
- elsif @function
576
- @function
577
- elsif @field
578
- @field.resolve_proc
566
+ # @return [Integer, nil] Applied to connections if {#has_default_page_size?}
567
+ def default_page_size
568
+ if !NOT_CONFIGURED.equal?(@default_page_size)
569
+ @default_page_size
570
+ elsif @resolver_class && @resolver_class.has_default_page_size?
571
+ @resolver_class.default_page_size
572
+ else
573
+ nil
579
574
  end
580
-
581
- # Ok, `self` isn't a class, but this is for consistency with the classes
582
- field_defn.metadata[:type_class] = self
583
- field_defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(field_defn)
584
- field_defn
585
575
  end
586
576
 
587
577
  class MissingReturnTypeError < GraphQL::Error; end
588
578
  attr_writer :type
589
579
 
590
580
  def type
591
- @type ||= if @function
592
- Member::BuildType.parse_type(@function.type, null: false)
593
- elsif @field
594
- Member::BuildType.parse_type(@field.type, null: false)
595
- elsif @return_type_expr.nil?
596
- # Not enough info to determine type
597
- message = "Can't determine the return type for #{self.path}"
598
- if @resolver_class
599
- message += " (it has `resolver: #{@resolver_class}`, consider configuration a `type ...` for that class)"
581
+ if @resolver_class
582
+ return_type = @return_type_expr || @resolver_class.type_expr
583
+ if return_type.nil?
584
+ raise MissingReturnTypeError, "Can't determine the return type for #{self.path} (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)"
600
585
  end
601
- raise MissingReturnTypeError, message
586
+ nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
587
+ Member::BuildType.parse_type(return_type, null: nullable)
602
588
  else
603
- Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
589
+ @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
604
590
  end
605
591
  rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
606
592
  # Let this propagate up
@@ -617,83 +603,47 @@ module GraphQL
617
603
  end
618
604
  end
619
605
 
620
- def accessible?(context)
621
- if @resolver_class
622
- @resolver_class.accessible?(context)
623
- else
624
- true
625
- end
626
- end
627
-
628
606
  def authorized?(object, args, context)
629
607
  if @resolver_class
630
608
  # The resolver _instance_ will check itself during `resolve()`
631
609
  @resolver_class.authorized?(object, context)
632
610
  else
633
- if (arg_values = context[:current_arguments])
634
- # ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
635
- using_arg_values = true
636
- arg_values = arg_values.argument_values
637
- else
638
- arg_values = args
639
- using_arg_values = false
640
- end
641
- # Faster than `.any?`
642
- arguments(context).each_value do |arg|
643
- arg_key = arg.keyword
644
- if arg_values.key?(arg_key)
645
- arg_value = arg_values[arg_key]
646
- if using_arg_values
647
- if arg_value.default_used?
648
- # pass -- no auth required for default used
649
- next
650
- else
651
- application_arg_value = arg_value.value
652
- if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
653
- application_arg_value.keyword_arguments
611
+ if args.size > 0
612
+ if (arg_values = context[:current_arguments])
613
+ # ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
614
+ using_arg_values = true
615
+ arg_values = arg_values.argument_values
616
+ else
617
+ arg_values = args
618
+ using_arg_values = false
619
+ end
620
+
621
+ args = context.warden.arguments(self)
622
+ args.each do |arg|
623
+ arg_key = arg.keyword
624
+ if arg_values.key?(arg_key)
625
+ arg_value = arg_values[arg_key]
626
+ if using_arg_values
627
+ if arg_value.default_used?
628
+ # pass -- no auth required for default used
629
+ next
630
+ else
631
+ application_arg_value = arg_value.value
632
+ if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
633
+ application_arg_value.keyword_arguments
634
+ end
654
635
  end
636
+ else
637
+ application_arg_value = arg_value
655
638
  end
656
- else
657
- application_arg_value = arg_value
658
- end
659
-
660
- if !arg.authorized?(object, application_arg_value, context)
661
- return false
662
- end
663
- end
664
- end
665
- true
666
- end
667
- end
668
639
 
669
- # Implement {GraphQL::Field}'s resolve API.
670
- #
671
- # Eventually, we might hook up field instances to execution in another way. TBD.
672
- # @see #resolve for how the interpreter hooks up to it
673
- def resolve_field(obj, args, ctx)
674
- ctx.schema.after_lazy(obj) do |after_obj|
675
- # First, apply auth ...
676
- query_ctx = ctx.query.context
677
- # Some legacy fields can have `nil` here, not exactly sure why.
678
- # @see https://github.com/rmosolgo/graphql-ruby/issues/1990 before removing
679
- inner_obj = after_obj && after_obj.object
680
- ctx.schema.after_lazy(to_ruby_args(after_obj, args, ctx)) do |ruby_args|
681
- if authorized?(inner_obj, ruby_args, query_ctx)
682
- # Then if it passed, resolve the field
683
- if @resolve_proc
684
- # Might be nil, still want to call the func in that case
685
- with_extensions(inner_obj, ruby_args, query_ctx) do |extended_obj, extended_args|
686
- # Pass the GraphQL args here for compatibility:
687
- @resolve_proc.call(extended_obj, args, ctx)
640
+ if !arg.authorized?(object, application_arg_value, context)
641
+ return false
688
642
  end
689
- else
690
- public_send_field(after_obj, ruby_args, query_ctx)
691
643
  end
692
- else
693
- err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
694
- query_ctx.schema.unauthorized_field(err)
695
644
  end
696
645
  end
646
+ true
697
647
  end
698
648
  end
699
649
 
@@ -702,34 +652,112 @@ module GraphQL
702
652
  # @param object [GraphQL::Schema::Object] An instance of some type class, wrapping an application object
703
653
  # @param args [Hash] A symbol-keyed hash of Ruby keyword arguments. (Empty if no args)
704
654
  # @param ctx [GraphQL::Query::Context]
705
- def resolve(object, args, ctx)
706
- if @resolve_proc
707
- raise "Can't run resolve proc for #{path} when using GraphQL::Execution::Interpreter"
708
- end
709
- begin
710
- # Unwrap the GraphQL object to get the application object.
711
- application_object = object.object
655
+ def resolve(object, args, query_ctx)
656
+ # Unwrap the GraphQL object to get the application object.
657
+ application_object = object.object
658
+ method_receiver = nil
659
+ method_to_call = nil
660
+ method_args = nil
661
+
662
+ Schema::Validator.validate!(validators, application_object, query_ctx, args)
663
+
664
+ query_ctx.schema.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
665
+ if is_authorized
666
+ with_extensions(object, args, query_ctx) do |obj, ruby_kwargs|
667
+ method_args = ruby_kwargs
668
+ if @resolver_class
669
+ if obj.is_a?(GraphQL::Schema::Object)
670
+ obj = obj.object
671
+ end
672
+ obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
673
+ end
712
674
 
713
- Schema::Validator.validate!(validators, application_object, ctx, args)
675
+ inner_object = obj.object
714
676
 
715
- ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
716
- if is_authorized
717
- public_send_field(object, args, ctx)
718
- else
719
- raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
677
+ if !NOT_CONFIGURED.equal?(@hash_key)
678
+ hash_value = if inner_object.is_a?(Hash)
679
+ inner_object.key?(@hash_key) ? inner_object[@hash_key] : inner_object[@hash_key_str]
680
+ elsif inner_object.respond_to?(:[])
681
+ inner_object[@hash_key]
682
+ else
683
+ nil
684
+ end
685
+ if hash_value == false
686
+ hash_value
687
+ else
688
+ hash_value || (@fallback_value != NOT_CONFIGURED ? @fallback_value : nil)
689
+ end
690
+ elsif obj.respond_to?(resolver_method)
691
+ method_to_call = resolver_method
692
+ method_receiver = obj
693
+ # Call the method with kwargs, if there are any
694
+ if ruby_kwargs.any?
695
+ obj.public_send(resolver_method, **ruby_kwargs)
696
+ else
697
+ obj.public_send(resolver_method)
698
+ end
699
+ elsif inner_object.is_a?(Hash)
700
+ if @dig_keys
701
+ inner_object.dig(*@dig_keys)
702
+ elsif inner_object.key?(@method_sym)
703
+ inner_object[@method_sym]
704
+ elsif inner_object.key?(@method_str)
705
+ inner_object[@method_str]
706
+ elsif @fallback_value != NOT_CONFIGURED
707
+ @fallback_value
708
+ else
709
+ nil
710
+ end
711
+ elsif inner_object.respond_to?(@method_sym)
712
+ method_to_call = @method_sym
713
+ method_receiver = obj.object
714
+ if ruby_kwargs.any?
715
+ inner_object.public_send(@method_sym, **ruby_kwargs)
716
+ else
717
+ inner_object.public_send(@method_sym)
718
+ end
719
+ elsif @fallback_value != NOT_CONFIGURED
720
+ @fallback_value
721
+ else
722
+ raise <<-ERR
723
+ Failed to implement #{@owner.graphql_name}.#{@name}, tried:
724
+
725
+ - `#{obj.class}##{resolver_method}`, which did not exist
726
+ - `#{inner_object.class}##{@method_sym}`, which did not exist
727
+ - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{inner_object}`, but it wasn't a Hash
728
+
729
+ To implement this field, define one of the methods above (and check for typos), or supply a `fallback_value`.
730
+ ERR
731
+ end
720
732
  end
733
+ else
734
+ raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: query_ctx, field: self)
721
735
  end
722
- rescue GraphQL::UnauthorizedFieldError => err
723
- err.field ||= self
724
- ctx.schema.unauthorized_field(err)
725
- rescue GraphQL::UnauthorizedError => err
726
- ctx.schema.unauthorized_object(err)
727
736
  end
737
+ rescue GraphQL::UnauthorizedFieldError => err
738
+ err.field ||= self
739
+ begin
740
+ query_ctx.schema.unauthorized_field(err)
741
+ rescue GraphQL::ExecutionError => err
742
+ err
743
+ end
744
+ rescue GraphQL::UnauthorizedError => err
745
+ begin
746
+ query_ctx.schema.unauthorized_object(err)
747
+ rescue GraphQL::ExecutionError => err
748
+ err
749
+ end
750
+ rescue ArgumentError
751
+ if method_receiver && method_to_call
752
+ assert_satisfactory_implementation(method_receiver, method_to_call, method_args)
753
+ end
754
+ # if the line above doesn't raise, re-raise
755
+ raise
728
756
  rescue GraphQL::ExecutionError => err
729
757
  err
730
758
  end
731
759
 
732
- # @param ctx [GraphQL::Query::Context::FieldResolutionContext]
760
+ # @param ctx [GraphQL::Query::Context]
733
761
  def fetch_extra(extra_name, ctx)
734
762
  if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
735
763
  self.public_send(extra_name)
@@ -742,127 +770,6 @@ module GraphQL
742
770
 
743
771
  private
744
772
 
745
- NO_ARGS = {}.freeze
746
-
747
- # Convert a GraphQL arguments instance into a Ruby-style hash.
748
- #
749
- # @param obj [GraphQL::Schema::Object] The object where this field is being resolved
750
- # @param graphql_args [GraphQL::Query::Arguments]
751
- # @param field_ctx [GraphQL::Query::Context::FieldResolutionContext]
752
- # @return [Hash<Symbol => Any>]
753
- def to_ruby_args(obj, graphql_args, field_ctx)
754
- if graphql_args.any? || @extras.any?
755
- # Splat the GraphQL::Arguments to Ruby keyword arguments
756
- ruby_kwargs = graphql_args.to_kwargs
757
- maybe_lazies = []
758
- # Apply any `prepare` methods. Not great code organization, can this go somewhere better?
759
- arguments(field_ctx).each do |name, arg_defn|
760
- ruby_kwargs_key = arg_defn.keyword
761
-
762
- if ruby_kwargs.key?(ruby_kwargs_key)
763
- loads = arg_defn.loads
764
- value = ruby_kwargs[ruby_kwargs_key]
765
- loaded_value = if loads && !arg_defn.from_resolver?
766
- if arg_defn.type.list?
767
- loaded_values = value.map { |val| load_application_object(arg_defn, loads, val, field_ctx.query.context) }
768
- field_ctx.schema.after_any_lazies(loaded_values) { |result| result }
769
- else
770
- load_application_object(arg_defn, loads, value, field_ctx.query.context)
771
- end
772
- elsif arg_defn.type.list? && value.is_a?(Array)
773
- field_ctx.schema.after_any_lazies(value, &:itself)
774
- else
775
- value
776
- end
777
-
778
- maybe_lazies << field_ctx.schema.after_lazy(loaded_value) do |loaded_value|
779
- prepared_value = if arg_defn.prepare
780
- arg_defn.prepare_value(obj, loaded_value)
781
- else
782
- loaded_value
783
- end
784
-
785
- ruby_kwargs[ruby_kwargs_key] = prepared_value
786
- end
787
- end
788
- end
789
-
790
- @extras.each do |extra_arg|
791
- ruby_kwargs[extra_arg] = fetch_extra(extra_arg, field_ctx)
792
- end
793
-
794
- field_ctx.schema.after_any_lazies(maybe_lazies) do
795
- ruby_kwargs
796
- end
797
- else
798
- NO_ARGS
799
- end
800
- end
801
-
802
- def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
803
- with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
804
- begin
805
- method_receiver = nil
806
- method_to_call = nil
807
- if @resolver_class
808
- if obj.is_a?(GraphQL::Schema::Object)
809
- obj = obj.object
810
- end
811
- obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
812
- end
813
-
814
- # Find a way to resolve this field, checking:
815
- #
816
- # - A method on the type instance;
817
- # - Hash keys, if the wrapped object is a hash;
818
- # - A method on the wrapped object;
819
- # - Or, raise not implemented.
820
- #
821
- if obj.respond_to?(@resolver_method)
822
- method_to_call = @resolver_method
823
- method_receiver = obj
824
- # Call the method with kwargs, if there are any
825
- if ruby_kwargs.any?
826
- obj.public_send(@resolver_method, **ruby_kwargs)
827
- else
828
- obj.public_send(@resolver_method)
829
- end
830
- elsif obj.object.is_a?(Hash)
831
- inner_object = obj.object
832
- if @dig_keys
833
- inner_object.dig(*@dig_keys)
834
- elsif inner_object.key?(@method_sym)
835
- inner_object[@method_sym]
836
- else
837
- inner_object[@method_str]
838
- end
839
- elsif obj.object.respond_to?(@method_sym)
840
- method_to_call = @method_sym
841
- method_receiver = obj.object
842
- if ruby_kwargs.any?
843
- obj.object.public_send(@method_sym, **ruby_kwargs)
844
- else
845
- obj.object.public_send(@method_sym)
846
- end
847
- else
848
- raise <<-ERR
849
- Failed to implement #{@owner.graphql_name}.#{@name}, tried:
850
-
851
- - `#{obj.class}##{@resolver_method}`, which did not exist
852
- - `#{obj.object.class}##{@method_sym}`, which did not exist
853
- - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
854
-
855
- To implement this field, define one of the methods above (and check for typos)
856
- ERR
857
- end
858
- rescue ArgumentError
859
- assert_satisfactory_implementation(method_receiver, method_to_call, ruby_kwargs)
860
- # if the line above doesn't raise, re-raise
861
- raise
862
- end
863
- end
864
- end
865
-
866
773
  def assert_satisfactory_implementation(receiver, method_name, ruby_kwargs)
867
774
  method_defn = receiver.method(method_name)
868
775
  unsatisfied_ruby_kwargs = ruby_kwargs.dup
@@ -893,7 +800,7 @@ module GraphQL
893
800
 
894
801
  if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
895
802
  raise FieldImplementationFailed.new, <<-ERR
896
- Failed to call #{method_name} on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
803
+ Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
897
804
 
898
805
  #{ unsatisfied_ruby_kwargs
899
806
  .map { |key, value| "- `#{key}: #{value}` was given by GraphQL but not defined in the Ruby method. Add `#{key}:` to the method parameters." }