graphql 2.0.31 → 2.5.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  4. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  5. data/lib/generators/graphql/install_generator.rb +49 -0
  6. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  7. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  8. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  9. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  10. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  11. data/lib/generators/graphql/templates/base_field.erb +2 -0
  12. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  13. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  14. data/lib/generators/graphql/templates/base_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  16. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  17. data/lib/generators/graphql/templates/base_union.erb +2 -0
  18. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  19. data/lib/generators/graphql/templates/loader.erb +2 -0
  20. data/lib/generators/graphql/templates/mutation.erb +2 -0
  21. data/lib/generators/graphql/templates/node_type.erb +2 -0
  22. data/lib/generators/graphql/templates/query_type.erb +2 -0
  23. data/lib/generators/graphql/templates/schema.erb +5 -0
  24. data/lib/generators/graphql/type_generator.rb +1 -1
  25. data/lib/graphql/analysis/analyzer.rb +90 -0
  26. data/lib/graphql/analysis/field_usage.rb +82 -0
  27. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  28. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  29. data/lib/graphql/analysis/query_complexity.rb +263 -0
  30. data/lib/graphql/analysis/query_depth.rb +58 -0
  31. data/lib/graphql/analysis/visitor.rb +280 -0
  32. data/lib/graphql/analysis.rb +95 -1
  33. data/lib/graphql/autoload.rb +38 -0
  34. data/lib/graphql/backtrace/table.rb +118 -55
  35. data/lib/graphql/backtrace.rb +1 -19
  36. data/lib/graphql/coercion_error.rb +1 -9
  37. data/lib/graphql/current.rb +57 -0
  38. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  39. data/lib/graphql/dashboard/installable.rb +22 -0
  40. data/lib/graphql/dashboard/limiters.rb +93 -0
  41. data/lib/graphql/dashboard/operation_store.rb +199 -0
  42. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  43. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  44. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  45. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  46. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  47. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  48. data/lib/graphql/dashboard/statics/icon.png +0 -0
  49. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  50. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  51. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  52. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  53. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  54. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
  55. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  56. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  57. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  58. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  59. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  60. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  61. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  62. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  63. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  64. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  65. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  66. data/lib/graphql/dashboard.rb +161 -0
  67. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  68. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  69. data/lib/graphql/dataloader/async_dataloader.rb +112 -0
  70. data/lib/graphql/dataloader/null_dataloader.rb +51 -10
  71. data/lib/graphql/dataloader/request.rb +5 -0
  72. data/lib/graphql/dataloader/source.rb +35 -12
  73. data/lib/graphql/dataloader.rb +224 -149
  74. data/lib/graphql/date_encoding_error.rb +1 -1
  75. data/lib/graphql/dig.rb +2 -1
  76. data/lib/graphql/duration_encoding_error.rb +16 -0
  77. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  78. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  79. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  80. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
  81. data/lib/graphql/execution/interpreter/runtime.rb +363 -434
  82. data/lib/graphql/execution/interpreter.rb +91 -164
  83. data/lib/graphql/execution/lookahead.rb +105 -31
  84. data/lib/graphql/execution/multiplex.rb +6 -5
  85. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  86. data/lib/graphql/introspection/directive_type.rb +1 -1
  87. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  88. data/lib/graphql/introspection/entry_points.rb +11 -5
  89. data/lib/graphql/introspection/field_type.rb +1 -1
  90. data/lib/graphql/introspection/schema_type.rb +8 -11
  91. data/lib/graphql/introspection/type_type.rb +5 -5
  92. data/lib/graphql/invalid_name_error.rb +1 -1
  93. data/lib/graphql/invalid_null_error.rb +20 -17
  94. data/lib/graphql/language/block_string.rb +34 -18
  95. data/lib/graphql/language/cache.rb +13 -0
  96. data/lib/graphql/language/comment.rb +18 -0
  97. data/lib/graphql/language/definition_slice.rb +1 -1
  98. data/lib/graphql/language/document_from_schema_definition.rb +90 -61
  99. data/lib/graphql/language/lexer.rb +319 -193
  100. data/lib/graphql/language/nodes.rb +139 -77
  101. data/lib/graphql/language/parser.rb +807 -1985
  102. data/lib/graphql/language/printer.rb +324 -151
  103. data/lib/graphql/language/sanitized_printer.rb +21 -23
  104. data/lib/graphql/language/static_visitor.rb +171 -0
  105. data/lib/graphql/language/visitor.rb +62 -119
  106. data/lib/graphql/language.rb +71 -1
  107. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  108. data/lib/graphql/pagination/array_connection.rb +6 -6
  109. data/lib/graphql/pagination/connection.rb +28 -1
  110. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  111. data/lib/graphql/query/context/scoped_context.rb +101 -0
  112. data/lib/graphql/query/context.rb +81 -144
  113. data/lib/graphql/query/null_context.rb +7 -16
  114. data/lib/graphql/query/partial.rb +179 -0
  115. data/lib/graphql/query/validation_pipeline.rb +4 -4
  116. data/lib/graphql/query/variable_validation_error.rb +1 -1
  117. data/lib/graphql/query/variables.rb +3 -3
  118. data/lib/graphql/query.rb +126 -81
  119. data/lib/graphql/railtie.rb +16 -6
  120. data/lib/graphql/rake_task.rb +3 -12
  121. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  122. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  123. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  124. data/lib/graphql/rubocop.rb +2 -0
  125. data/lib/graphql/schema/addition.rb +26 -13
  126. data/lib/graphql/schema/always_visible.rb +7 -2
  127. data/lib/graphql/schema/argument.rb +73 -14
  128. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  129. data/lib/graphql/schema/build_from_definition.rb +123 -66
  130. data/lib/graphql/schema/directive/flagged.rb +4 -2
  131. data/lib/graphql/schema/directive/one_of.rb +12 -0
  132. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  133. data/lib/graphql/schema/directive.rb +55 -3
  134. data/lib/graphql/schema/enum.rb +110 -27
  135. data/lib/graphql/schema/enum_value.rb +11 -3
  136. data/lib/graphql/schema/field/connection_extension.rb +2 -16
  137. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  138. data/lib/graphql/schema/field.rb +173 -119
  139. data/lib/graphql/schema/field_extension.rb +1 -1
  140. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  141. data/lib/graphql/schema/input_object.rb +123 -65
  142. data/lib/graphql/schema/interface.rb +34 -16
  143. data/lib/graphql/schema/introspection_system.rb +8 -17
  144. data/lib/graphql/schema/late_bound_type.rb +4 -0
  145. data/lib/graphql/schema/list.rb +3 -3
  146. data/lib/graphql/schema/loader.rb +3 -4
  147. data/lib/graphql/schema/member/base_dsl_methods.rb +17 -1
  148. data/lib/graphql/schema/member/has_arguments.rb +132 -100
  149. data/lib/graphql/schema/member/has_dataloader.rb +62 -0
  150. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  151. data/lib/graphql/schema/member/has_directives.rb +5 -5
  152. data/lib/graphql/schema/member/has_fields.rb +111 -16
  153. data/lib/graphql/schema/member/has_interfaces.rb +27 -13
  154. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  155. data/lib/graphql/schema/member/has_validators.rb +1 -1
  156. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  157. data/lib/graphql/schema/member/scoped.rb +19 -0
  158. data/lib/graphql/schema/member/type_system_helpers.rb +18 -5
  159. data/lib/graphql/schema/member/validates_input.rb +3 -3
  160. data/lib/graphql/schema/member.rb +1 -0
  161. data/lib/graphql/schema/mutation.rb +7 -0
  162. data/lib/graphql/schema/object.rb +33 -8
  163. data/lib/graphql/schema/printer.rb +9 -7
  164. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  165. data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
  166. data/lib/graphql/schema/resolver.rb +45 -31
  167. data/lib/graphql/schema/scalar.rb +4 -9
  168. data/lib/graphql/schema/subscription.rb +63 -10
  169. data/lib/graphql/schema/timeout.rb +19 -2
  170. data/lib/graphql/schema/type_expression.rb +2 -2
  171. data/lib/graphql/schema/union.rb +2 -2
  172. data/lib/graphql/schema/unique_within_type.rb +1 -1
  173. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  174. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  175. data/lib/graphql/schema/validator.rb +3 -1
  176. data/lib/graphql/schema/visibility/migration.rb +188 -0
  177. data/lib/graphql/schema/visibility/profile.rb +445 -0
  178. data/lib/graphql/schema/visibility/visit.rb +190 -0
  179. data/lib/graphql/schema/visibility.rb +311 -0
  180. data/lib/graphql/schema/warden.rb +275 -103
  181. data/lib/graphql/schema.rb +924 -210
  182. data/lib/graphql/static_validation/all_rules.rb +3 -3
  183. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  184. data/lib/graphql/static_validation/literal_validator.rb +6 -7
  185. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  186. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  187. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  188. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  189. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  190. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  191. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  192. data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
  193. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  194. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  195. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  196. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  197. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  198. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  199. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  200. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  201. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -5
  202. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  203. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  204. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  205. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  206. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  207. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  208. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  209. data/lib/graphql/static_validation/validation_context.rb +21 -5
  210. data/lib/graphql/static_validation/validator.rb +9 -1
  211. data/lib/graphql/static_validation.rb +0 -1
  212. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
  213. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  214. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  215. data/lib/graphql/subscriptions/event.rb +21 -4
  216. data/lib/graphql/subscriptions/serialize.rb +3 -1
  217. data/lib/graphql/subscriptions.rb +21 -17
  218. data/lib/graphql/testing/helpers.rb +161 -0
  219. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  220. data/lib/graphql/testing.rb +3 -0
  221. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  222. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  223. data/lib/graphql/tracing/appoptics_trace.rb +11 -3
  224. data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
  225. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  226. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  227. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  228. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  229. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  230. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  231. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  232. data/lib/graphql/tracing/detailed_trace.rb +141 -0
  233. data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
  234. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  235. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  236. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  237. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  238. data/lib/graphql/tracing/notifications_trace.rb +184 -34
  239. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  240. data/lib/graphql/tracing/null_trace.rb +9 -0
  241. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  242. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  243. data/lib/graphql/tracing/perfetto_trace.rb +818 -0
  244. data/lib/graphql/tracing/platform_trace.rb +5 -0
  245. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  246. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
  247. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  248. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  249. data/lib/graphql/tracing/scout_trace.rb +32 -55
  250. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  251. data/lib/graphql/tracing/sentry_trace.rb +82 -0
  252. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  253. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  254. data/lib/graphql/tracing/trace.rb +112 -1
  255. data/lib/graphql/tracing.rb +31 -28
  256. data/lib/graphql/type_kinds.rb +2 -1
  257. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  258. data/lib/graphql/types/relay/connection_behaviors.rb +43 -3
  259. data/lib/graphql/types/relay/edge_behaviors.rb +18 -1
  260. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  261. data/lib/graphql/types.rb +18 -10
  262. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  263. data/lib/graphql/version.rb +1 -1
  264. data/lib/graphql.rb +68 -54
  265. data/readme.md +12 -2
  266. metadata +220 -40
  267. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  268. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  269. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  270. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  271. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  272. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  273. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  274. data/lib/graphql/analysis/ast.rb +0 -81
  275. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  276. data/lib/graphql/backtrace/trace.rb +0 -96
  277. data/lib/graphql/backtrace/tracer.rb +0 -80
  278. data/lib/graphql/deprecation.rb +0 -9
  279. data/lib/graphql/filter.rb +0 -59
  280. data/lib/graphql/language/parser.y +0 -560
  281. data/lib/graphql/language/token.rb +0 -34
  282. data/lib/graphql/schema/base_64_bp.rb +0 -26
  283. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  284. data/lib/graphql/schema/null_mask.rb +0 -11
  285. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
  286. data/lib/graphql/static_validation/type_stack.rb +0 -216
  287. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -41,6 +41,32 @@ module GraphQL
41
41
  end
42
42
  end
43
43
 
44
+ # @return [String, nil]
45
+ def deprecation_reason
46
+ super || @resolver_class&.deprecation_reason
47
+ end
48
+
49
+ def directives
50
+ if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
51
+ if !(own_dirs = super).empty?
52
+ new_dirs = own_dirs.dup
53
+ r_dirs.each do |r_dir|
54
+ if r_dir.class.repeatable? ||
55
+ ( (r_dir_name = r_dir.graphql_name) &&
56
+ (!new_dirs.any? { |d| d.graphql_name == r_dir_name })
57
+ )
58
+ new_dirs << r_dir
59
+ end
60
+ end
61
+ new_dirs
62
+ else
63
+ r_dirs
64
+ end
65
+ else
66
+ super
67
+ end
68
+ end
69
+
44
70
  # @return [Class] The thing this field was defined on (type, mutation, resolver)
45
71
  attr_accessor :owner
46
72
 
@@ -69,7 +95,7 @@ module GraphQL
69
95
  end
70
96
 
71
97
  def inspect
72
- "#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
98
+ "#<#{self.class} #{path}#{!all_argument_definitions.empty? ? "(...)" : ""}: #{type.to_type_signature}>"
73
99
  end
74
100
 
75
101
  alias :mutation :resolver
@@ -83,48 +109,6 @@ module GraphQL
83
109
  end
84
110
  attr_writer :subscription_scope
85
111
 
86
- # Create a field instance from a list of arguments, keyword arguments, and a block.
87
- #
88
- # This method implements prioritization between the `resolver` or `mutation` defaults
89
- # and the local overrides via other keywords.
90
- #
91
- # It also normalizes positional arguments into keywords for {Schema::Field#initialize}.
92
- # @param resolver [Class] A {GraphQL::Schema::Resolver} class to use for field configuration
93
- # @param mutation [Class] A {GraphQL::Schema::Mutation} class to use for field configuration
94
- # @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration
95
- # @return [GraphQL::Schema:Field] an instance of `self`
96
- # @see {.initialize} for other options
97
- def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
98
- if (resolver_class = resolver || mutation || subscription)
99
- # Add a reference to that parent class
100
- kwargs[:resolver_class] = resolver_class
101
- end
102
-
103
- if name
104
- kwargs[:name] = name
105
- end
106
-
107
- if !type.nil?
108
- if desc
109
- if kwargs[:description]
110
- raise ArgumentError, "Provide description as a positional argument or `description:` keyword, but not both (#{desc.inspect}, #{kwargs[:description].inspect})"
111
- end
112
-
113
- kwargs[:description] = desc
114
- kwargs[:type] = type
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
117
- kwargs[:description] = type
118
- else
119
- kwargs[:type] = type
120
- end
121
- if type.is_a?(Class) && type < GraphQL::Schema::Mutation
122
- raise ArgumentError, "Use `field #{name.inspect}, mutation: Mutation, ...` to provide a mutation to this field instead"
123
- end
124
- end
125
- new(**kwargs, &block)
126
- end
127
-
128
112
  # Can be set with `connection: true|false` or inferred from a type name ending in `*Connection`
129
113
  # @return [Boolean] if true, this field will be wrapped with Relay connection behavior
130
114
  def connection?
@@ -134,11 +118,16 @@ module GraphQL
134
118
  Member::BuildType.to_type_name(@return_type_expr)
135
119
  elsif @resolver_class && @resolver_class.type
136
120
  Member::BuildType.to_type_name(@resolver_class.type)
137
- else
121
+ elsif type
138
122
  # As a last ditch, try to force loading the return type:
139
123
  type.unwrap.name
140
124
  end
141
- @connection = return_type_name.end_with?("Connection")
125
+ if return_type_name
126
+ @connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
127
+ else
128
+ # TODO set this when type is set by method
129
+ false # not loaded yet?
130
+ end
142
131
  else
143
132
  @connection
144
133
  end
@@ -195,6 +184,7 @@ module GraphQL
195
184
  # @param owner [Class] The type that this field belongs to
196
185
  # @param null [Boolean] (defaults to `true`) `true` if this field may return `null`, `false` if it is never `null`
197
186
  # @param description [String] Field description
187
+ # @param comment [String] Field comment
198
188
  # @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
199
189
  # @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
200
190
  # @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`)
@@ -218,14 +208,19 @@ module GraphQL
218
208
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
219
209
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
220
210
  # @param validates [Array<Hash>] Configurations for validating this field
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)
211
+ # @param fallback_value [Object] A fallback value if the method is not defined
212
+ # @param dynamic_introspection [Boolean] (Private, used by GraphQL-Ruby)
213
+ # @param relay_node_field [Boolean] (Private, used by GraphQL-Ruby)
214
+ # @param relay_nodes_field [Boolean] (Private, used by GraphQL-Ruby)
215
+ # @param extras [Array<:ast_node, :parent, :lookahead, :owner, :execution_errors, :graphql_name, :argument_details, Symbol>] Extra arguments to be injected into the resolver for this field
216
+ # @param definition_block [Proc] an additional block for configuring the field. Receive the field as a block param, or, if no block params are defined, then the block is `instance_eval`'d on the new {Field}.
217
+ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: 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, dynamic_introspection: false, &definition_block)
223
218
  if name.nil?
224
219
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
225
220
  end
226
221
  if !(resolver_class)
227
- if type.nil?
228
- raise ArgumentError, "missing second `type` argument or keyword `type:`"
222
+ if type.nil? && !block_given?
223
+ raise ArgumentError, "missing second `type` argument, keyword `type:`, or a block containing `type(...)`"
229
224
  end
230
225
  end
231
226
  @original_name = name
@@ -233,8 +228,9 @@ module GraphQL
233
228
 
234
229
  @underscored_name = -Member::BuildType.underscore(name_s)
235
230
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
236
-
231
+ NameValidator.validate!(@name)
237
232
  @description = description
233
+ @comment = comment
238
234
  @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
239
235
 
240
236
  self.deprecation_reason = deprecation_reason
@@ -267,6 +263,7 @@ module GraphQL
267
263
  @method_sym = method_name.to_sym
268
264
  @resolver_method = (resolver_method || name_s).to_sym
269
265
  @complexity = complexity
266
+ @dynamic_introspection = dynamic_introspection
270
267
  @return_type_expr = type
271
268
  @return_type_null = if !null.nil?
272
269
  null
@@ -289,6 +286,7 @@ module GraphQL
289
286
  @ast_node = ast_node
290
287
  @method_conflict_warning = method_conflict_warning
291
288
  @fallback_value = fallback_value
289
+ @definition_block = definition_block
292
290
 
293
291
  arguments.each do |name, arg|
294
292
  case arg
@@ -308,28 +306,17 @@ module GraphQL
308
306
 
309
307
  @extensions = EMPTY_ARRAY
310
308
  @call_after_define = false
311
- # This should run before connection extension,
312
- # but should it run after the definition block?
313
- if scoped?
314
- self.extension(ScopeExtension)
315
- end
316
-
317
- # The problem with putting this after the definition_block
318
- # is that it would override arguments
319
- if connection? && connection_extension
320
- self.extension(connection_extension)
321
- end
322
-
309
+ set_pagination_extensions(connection_extension: NOT_CONFIGURED.equal?(connection_extension) ? self.class.connection_extension : connection_extension)
323
310
  # Do this last so we have as much context as possible when initializing them:
324
- if extensions.any?
311
+ if !extensions.empty?
325
312
  self.extensions(extensions)
326
313
  end
327
314
 
328
- if resolver_class && resolver_class.extensions.any?
315
+ if resolver_class && !resolver_class.extensions.empty?
329
316
  self.extensions(resolver_class.extensions)
330
317
  end
331
318
 
332
- if directives.any?
319
+ if !directives.empty?
333
320
  directives.each do |(dir_class, options)|
334
321
  self.directive(dir_class, **options)
335
322
  end
@@ -339,18 +326,33 @@ module GraphQL
339
326
  self.validates(validates)
340
327
  end
341
328
 
342
- if block_given?
343
- if definition_block.arity == 1
344
- yield self
329
+ if @definition_block.nil?
330
+ self.extensions.each(&:after_define_apply)
331
+ @call_after_define = true
332
+ end
333
+ end
334
+
335
+ # Calls the definition block, if one was given.
336
+ # This is deferred so that references to the return type
337
+ # can be lazily evaluated, reducing Rails boot time.
338
+ # @return [self]
339
+ # @api private
340
+ def ensure_loaded
341
+ if @definition_block
342
+ if @definition_block.arity == 1
343
+ @definition_block.call(self)
345
344
  else
346
- instance_eval(&definition_block)
345
+ instance_exec(self, &@definition_block)
347
346
  end
347
+ self.extensions.each(&:after_define_apply)
348
+ @call_after_define = true
349
+ @definition_block = nil
348
350
  end
349
-
350
- self.extensions.each(&:after_define_apply)
351
- @call_after_define = true
351
+ self
352
352
  end
353
353
 
354
+ attr_accessor :dynamic_introspection
355
+
354
356
  # If true, subscription updates with this field can be shared between viewers
355
357
  # @return [Boolean, nil]
356
358
  # @see GraphQL::Subscriptions::BroadcastAnalyzer
@@ -378,6 +380,20 @@ module GraphQL
378
380
  end
379
381
  end
380
382
 
383
+ # @param text [String]
384
+ # @return [String, nil]
385
+ def comment(text = nil)
386
+ if text
387
+ @comment = text
388
+ elsif !NOT_CONFIGURED.equal?(@comment)
389
+ @comment
390
+ elsif @resolver_class
391
+ @resolver_class.comment
392
+ else
393
+ nil
394
+ end
395
+ end
396
+
381
397
  # Read extension instances from this field,
382
398
  # or add new classes/options to be initialized on this field.
383
399
  # Extensions are executed in the order they are added.
@@ -398,7 +414,7 @@ module GraphQL
398
414
  new_extensions.each do |extension_config|
399
415
  if extension_config.is_a?(Hash)
400
416
  extension_class, options = *extension_config.to_a[0]
401
- self.extension(extension_class, options)
417
+ self.extension(extension_class, **options)
402
418
  else
403
419
  self.extension(extension_config)
404
420
  end
@@ -418,7 +434,7 @@ module GraphQL
418
434
  # @param extension_class [Class] subclass of {Schema::FieldExtension}
419
435
  # @param options [Hash] if provided, given as `options:` when initializing `extension`.
420
436
  # @return [void]
421
- def extension(extension_class, options = nil)
437
+ def extension(extension_class, **options)
422
438
  extension_inst = extension_class.new(field: self, options: options)
423
439
  if @extensions.frozen?
424
440
  @extensions = @extensions.dup
@@ -439,7 +455,7 @@ module GraphQL
439
455
  if new_extras.nil?
440
456
  # Read the value
441
457
  field_extras = @extras
442
- if @resolver_class && @resolver_class.extras.any?
458
+ if @resolver_class && !@resolver_class.extras.empty?
443
459
  field_extras + @resolver_class.extras
444
460
  else
445
461
  field_extras
@@ -468,6 +484,8 @@ module GraphQL
468
484
  if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
469
485
  max_possible_page_size = arguments[:last]
470
486
  end
487
+ elsif arguments.is_a?(GraphQL::ExecutionError) || arguments.is_a?(GraphQL::UnauthorizedError)
488
+ raise arguments
471
489
  end
472
490
 
473
491
  if max_possible_page_size.nil?
@@ -480,41 +498,24 @@ module GraphQL
480
498
  metadata_complexity = 0
481
499
  lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
482
500
 
483
- if (page_info_lookahead = lookahead.selection(:page_info)).selected?
484
- metadata_complexity += 1 # pageInfo
485
- metadata_complexity += page_info_lookahead.selections.size # subfields
486
- end
487
-
488
- if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
489
- metadata_complexity += 1
501
+ lookahead.selections.each do |next_lookahead|
502
+ # this includes `pageInfo`, `nodes` and `edges` and any custom fields
503
+ # TODO this doesn't support procs yet -- unlikely to need it.
504
+ metadata_complexity += next_lookahead.field.complexity
505
+ if next_lookahead.name != :nodes && next_lookahead.name != :edges
506
+ # subfields, eg, for pageInfo -- assumes no subselections
507
+ metadata_complexity += next_lookahead.selections.size
508
+ end
490
509
  end
491
510
 
492
- nodes_edges_complexity = 0
493
- nodes_edges_complexity += 1 if lookahead.selects?(:edges)
494
- nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
495
-
496
511
  # Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
497
- items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
498
- # Add 1 for _this_ field
499
- 1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
512
+ items_complexity = child_complexity - metadata_complexity
513
+ subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity
514
+ # Apply this field's own complexity
515
+ apply_own_complexity_to(subfields_complexity, query, nodes)
500
516
  end
501
517
  else
502
- defined_complexity = complexity
503
- case defined_complexity
504
- when Proc
505
- arguments = query.arguments_for(nodes.first, self)
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)
513
- when Numeric
514
- defined_complexity + child_complexity
515
- else
516
- raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
517
- end
518
+ apply_own_complexity_to(child_complexity, query, nodes)
518
519
  end
519
520
  end
520
521
 
@@ -574,19 +575,40 @@ module GraphQL
574
575
  end
575
576
  end
576
577
 
578
+ def freeze
579
+ type
580
+ owner_type
581
+ arguments_statically_coercible?
582
+ connection?
583
+ super
584
+ end
585
+
577
586
  class MissingReturnTypeError < GraphQL::Error; end
578
587
  attr_writer :type
579
588
 
580
- def type
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)"
589
+ # Get or set the return type of this field.
590
+ #
591
+ # It may return nil if no type was configured or if the given definition block wasn't called yet.
592
+ # @param new_type [Module, GraphQL::Schema::NonNull, GraphQL::Schema::List] A GraphQL return type
593
+ # @return [Module, GraphQL::Schema::NonNull, GraphQL::Schema::List, nil] the configured type for this field
594
+ def type(new_type = NOT_CONFIGURED)
595
+ if NOT_CONFIGURED.equal?(new_type)
596
+ if @resolver_class
597
+ return_type = @return_type_expr || @resolver_class.type_expr
598
+ if return_type.nil?
599
+ 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
+ end
601
+ nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
602
+ Member::BuildType.parse_type(return_type, null: nullable)
603
+ elsif !@return_type_expr.nil?
604
+ @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
585
605
  end
586
- nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
587
- Member::BuildType.parse_type(return_type, null: nullable)
588
606
  else
589
- @type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
607
+ @return_type_expr = new_type
608
+ # If `type` is set in the definition block, then the `connection_extension: ...` given as a keyword won't be used, hmm...
609
+ # Also, arguments added by `connection_extension` will clobber anything previously defined,
610
+ # so `type(...)` should go first.
611
+ set_pagination_extensions(connection_extension: self.class.connection_extension)
590
612
  end
591
613
  rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
592
614
  # Let this propagate up
@@ -618,7 +640,7 @@ module GraphQL
618
640
  using_arg_values = false
619
641
  end
620
642
 
621
- args = context.warden.arguments(self)
643
+ args = context.types.arguments(self)
622
644
  args.each do |arg|
623
645
  arg_key = arg.keyword
624
646
  if arg_values.key?(arg_key)
@@ -659,7 +681,7 @@ module GraphQL
659
681
  method_to_call = nil
660
682
  method_args = nil
661
683
 
662
- Schema::Validator.validate!(validators, application_object, query_ctx, args)
684
+ @own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args)
663
685
 
664
686
  query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
665
687
  if is_authorized
@@ -691,7 +713,7 @@ module GraphQL
691
713
  method_to_call = resolver_method
692
714
  method_receiver = obj
693
715
  # Call the method with kwargs, if there are any
694
- if ruby_kwargs.any?
716
+ if !ruby_kwargs.empty?
695
717
  obj.public_send(resolver_method, **ruby_kwargs)
696
718
  else
697
719
  obj.public_send(resolver_method)
@@ -701,7 +723,7 @@ module GraphQL
701
723
  inner_object.dig(*@dig_keys)
702
724
  elsif inner_object.key?(@method_sym)
703
725
  inner_object[@method_sym]
704
- elsif inner_object.key?(@method_str)
726
+ elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
705
727
  inner_object[@method_str]
706
728
  elsif @fallback_value != NOT_CONFIGURED
707
729
  @fallback_value
@@ -711,7 +733,7 @@ module GraphQL
711
733
  elsif inner_object.respond_to?(@method_sym)
712
734
  method_to_call = @method_sym
713
735
  method_receiver = obj.object
714
- if ruby_kwargs.any?
736
+ if !ruby_kwargs.empty?
715
737
  inner_object.public_send(@method_sym, **ruby_kwargs)
716
738
  else
717
739
  inner_object.public_send(@method_sym)
@@ -798,7 +820,7 @@ module GraphQL
798
820
  unsatisfied_ruby_kwargs.clear
799
821
  end
800
822
 
801
- if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
823
+ if !unsatisfied_ruby_kwargs.empty? || !unsatisfied_method_params.empty?
802
824
  raise FieldImplementationFailed.new, <<-ERR
803
825
  Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
804
826
 
@@ -879,6 +901,38 @@ ERR
879
901
  yield(obj, args)
880
902
  end
881
903
  end
904
+
905
+ def apply_own_complexity_to(child_complexity, query, nodes)
906
+ case (own_complexity = complexity)
907
+ when Numeric
908
+ own_complexity + child_complexity
909
+ when Proc
910
+ arguments = query.arguments_for(nodes.first, self)
911
+ if arguments.is_a?(GraphQL::ExecutionError)
912
+ return child_complexity
913
+ elsif arguments.respond_to?(:keyword_arguments)
914
+ arguments = arguments.keyword_arguments
915
+ end
916
+
917
+ own_complexity.call(query.context, arguments, child_complexity)
918
+ else
919
+ raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
920
+ end
921
+ end
922
+
923
+ def set_pagination_extensions(connection_extension:)
924
+ # This should run before connection extension,
925
+ # but should it run after the definition block?
926
+ if scoped?
927
+ self.extension(ScopeExtension, call_after_define: false)
928
+ end
929
+
930
+ # The problem with putting this after the definition_block
931
+ # is that it would override arguments
932
+ if connection? && connection_extension
933
+ self.extension(connection_extension, call_after_define: false)
934
+ end
935
+ end
882
936
  end
883
937
  end
884
938
  end
@@ -104,7 +104,7 @@ module GraphQL
104
104
  end
105
105
  end
106
106
  end
107
- if (extras = self.class.extras).any?
107
+ if !(extras = self.class.extras).empty?
108
108
  @added_extras = extras - field.extras
109
109
  field.extras(@added_extras)
110
110
  else
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ module HasSingleInputArgument
6
+ def resolve_with_support(**inputs)
7
+ if inputs[:input].is_a?(InputObject)
8
+ input = inputs[:input].to_kwargs
9
+ else
10
+ input = inputs[:input]
11
+ end
12
+
13
+ new_extras = field ? field.extras : []
14
+ all_extras = self.class.extras + new_extras
15
+
16
+ # Transfer these from the top-level hash to the
17
+ # shortcutted `input:` object
18
+ all_extras.each do |ext|
19
+ # It's possible that the `extra` was not passed along by this point,
20
+ # don't re-add it if it wasn't given here.
21
+ if inputs.key?(ext)
22
+ input[ext] = inputs[ext]
23
+ end
24
+ end
25
+
26
+ if input
27
+ # This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
28
+ input_kwargs = input.to_h
29
+ else
30
+ # Relay Classic Mutations with no `argument`s
31
+ # don't require `input:`
32
+ input_kwargs = {}
33
+ end
34
+
35
+ if !input_kwargs.empty?
36
+ super(**input_kwargs)
37
+ else
38
+ super()
39
+ end
40
+ end
41
+
42
+ def self.included(base)
43
+ base.extend(ClassMethods)
44
+ end
45
+
46
+ module ClassMethods
47
+ def dummy
48
+ @dummy ||= begin
49
+ d = Class.new(GraphQL::Schema::Resolver)
50
+ d.graphql_name "#{self.graphql_name}DummyResolver"
51
+ d.argument_class(self.argument_class)
52
+ # TODO make this lazier?
53
+ d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
54
+ d
55
+ end
56
+ end
57
+
58
+ def field_arguments(context = GraphQL::Query::NullContext.instance)
59
+ dummy.arguments(context)
60
+ end
61
+
62
+ def get_field_argument(name, context = GraphQL::Query::NullContext.instance)
63
+ dummy.get_argument(name, context)
64
+ end
65
+
66
+ def own_field_arguments
67
+ dummy.own_arguments
68
+ end
69
+
70
+ def any_field_arguments?
71
+ dummy.any_arguments?
72
+ end
73
+
74
+ def all_field_argument_definitions
75
+ dummy.all_argument_definitions
76
+ end
77
+
78
+ # Also apply this argument to the input type:
79
+ def argument(*args, own_argument: false, **kwargs, &block)
80
+ it = input_type # make sure any inherited arguments are already added to it
81
+ arg = super(*args, **kwargs, &block)
82
+
83
+ # This definition might be overriding something inherited;
84
+ # if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
85
+ prev_args = it.own_arguments[arg.graphql_name]
86
+ case prev_args
87
+ when GraphQL::Schema::Argument
88
+ if prev_args.owner != self
89
+ it.own_arguments.delete(arg.graphql_name)
90
+ end
91
+ when Array
92
+ prev_args.reject! { |a| a.owner != self }
93
+ if prev_args.empty?
94
+ it.own_arguments.delete(arg.graphql_name)
95
+ end
96
+ end
97
+
98
+ it.add_argument(arg)
99
+ arg
100
+ end
101
+
102
+ # The base class for generated input object types
103
+ # @param new_class [Class] The base class to use for generating input object definitions
104
+ # @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
105
+ def input_object_class(new_class = nil)
106
+ if new_class
107
+ @input_object_class = new_class
108
+ end
109
+ @input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject)
110
+ end
111
+
112
+ # @param new_input_type [Class, nil] If provided, it configures this mutation to accept `new_input_type` instead of generating an input type
113
+ # @return [Class] The generated {Schema::InputObject} class for this mutation's `input`
114
+ def input_type(new_input_type = nil)
115
+ if new_input_type
116
+ @input_type = new_input_type
117
+ end
118
+ @input_type ||= generate_input_type
119
+ end
120
+
121
+ private
122
+
123
+ # Generate the input type for the `input:` argument
124
+ # To customize how input objects are generated, override this method
125
+ # @return [Class] a subclass of {.input_object_class}
126
+ def generate_input_type
127
+ mutation_args = all_argument_definitions
128
+ mutation_class = self
129
+ Class.new(input_object_class) do
130
+ class << self
131
+ def default_graphql_name
132
+ "#{self.mutation.graphql_name}Input"
133
+ end
134
+
135
+ def description(new_desc = nil)
136
+ super || "Autogenerated input type of #{self.mutation.graphql_name}"
137
+ end
138
+ end
139
+ # For compatibility, in case no arguments are defined:
140
+ has_no_arguments(true)
141
+ mutation(mutation_class)
142
+ # these might be inherited:
143
+ mutation_args.each do |arg|
144
+ add_argument(arg)
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ def authorize_arguments(args, values)
153
+ # remove the `input` wrapper to match values
154
+ input_type = args.find { |a| a.graphql_name == "input" }.type.unwrap
155
+ input_args = context.types.arguments(input_type)
156
+ super(input_args, values)
157
+ end
158
+ end
159
+ end
160
+ end