graphql 2.2.17 → 2.5.16

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 (240) 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_generator.rb +46 -0
  4. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  5. data/lib/generators/graphql/templates/base_resolver.erb +2 -0
  6. data/lib/generators/graphql/templates/schema.erb +3 -0
  7. data/lib/generators/graphql/type_generator.rb +1 -1
  8. data/lib/graphql/analysis/analyzer.rb +90 -0
  9. data/lib/graphql/analysis/field_usage.rb +82 -0
  10. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  11. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  12. data/lib/graphql/analysis/query_complexity.rb +263 -0
  13. data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
  14. data/lib/graphql/analysis/visitor.rb +280 -0
  15. data/lib/graphql/analysis.rb +95 -1
  16. data/lib/graphql/autoload.rb +38 -0
  17. data/lib/graphql/backtrace/table.rb +118 -55
  18. data/lib/graphql/backtrace.rb +1 -19
  19. data/lib/graphql/current.rb +57 -0
  20. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  21. data/lib/graphql/dashboard/installable.rb +22 -0
  22. data/lib/graphql/dashboard/limiters.rb +93 -0
  23. data/lib/graphql/dashboard/operation_store.rb +199 -0
  24. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  25. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  26. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  27. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  28. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  29. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  30. data/lib/graphql/dashboard/statics/icon.png +0 -0
  31. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  36. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  37. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  38. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  39. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  40. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  41. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  42. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  43. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  44. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  45. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  46. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  47. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  48. data/lib/graphql/dashboard.rb +158 -0
  49. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  50. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  51. data/lib/graphql/dataloader/async_dataloader.rb +46 -19
  52. data/lib/graphql/dataloader/null_dataloader.rb +51 -10
  53. data/lib/graphql/dataloader/source.rb +20 -9
  54. data/lib/graphql/dataloader.rb +153 -45
  55. data/lib/graphql/date_encoding_error.rb +1 -1
  56. data/lib/graphql/dig.rb +2 -1
  57. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  58. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  59. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  60. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +63 -5
  61. data/lib/graphql/execution/interpreter/runtime.rb +321 -222
  62. data/lib/graphql/execution/interpreter.rb +23 -30
  63. data/lib/graphql/execution/lookahead.rb +18 -11
  64. data/lib/graphql/execution/multiplex.rb +6 -5
  65. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  66. data/lib/graphql/introspection/directive_type.rb +1 -1
  67. data/lib/graphql/introspection/entry_points.rb +2 -2
  68. data/lib/graphql/introspection/field_type.rb +1 -1
  69. data/lib/graphql/introspection/schema_type.rb +6 -11
  70. data/lib/graphql/introspection/type_type.rb +5 -5
  71. data/lib/graphql/invalid_name_error.rb +1 -1
  72. data/lib/graphql/invalid_null_error.rb +20 -17
  73. data/lib/graphql/language/cache.rb +13 -0
  74. data/lib/graphql/language/comment.rb +18 -0
  75. data/lib/graphql/language/document_from_schema_definition.rb +64 -35
  76. data/lib/graphql/language/lexer.rb +72 -42
  77. data/lib/graphql/language/nodes.rb +93 -52
  78. data/lib/graphql/language/parser.rb +168 -61
  79. data/lib/graphql/language/printer.rb +31 -15
  80. data/lib/graphql/language/sanitized_printer.rb +1 -1
  81. data/lib/graphql/language.rb +61 -1
  82. data/lib/graphql/pagination/connection.rb +1 -1
  83. data/lib/graphql/query/context/scoped_context.rb +1 -1
  84. data/lib/graphql/query/context.rb +46 -47
  85. data/lib/graphql/query/null_context.rb +3 -5
  86. data/lib/graphql/query/partial.rb +179 -0
  87. data/lib/graphql/query/validation_pipeline.rb +2 -2
  88. data/lib/graphql/query/variable_validation_error.rb +1 -1
  89. data/lib/graphql/query.rb +123 -69
  90. data/lib/graphql/railtie.rb +7 -0
  91. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  92. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  93. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  94. data/lib/graphql/rubocop.rb +2 -0
  95. data/lib/graphql/schema/addition.rb +26 -13
  96. data/lib/graphql/schema/always_visible.rb +7 -2
  97. data/lib/graphql/schema/argument.rb +57 -8
  98. data/lib/graphql/schema/build_from_definition.rb +116 -49
  99. data/lib/graphql/schema/directive/flagged.rb +4 -2
  100. data/lib/graphql/schema/directive.rb +54 -2
  101. data/lib/graphql/schema/enum.rb +107 -24
  102. data/lib/graphql/schema/enum_value.rb +10 -2
  103. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  104. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  105. data/lib/graphql/schema/field.rb +134 -45
  106. data/lib/graphql/schema/field_extension.rb +1 -1
  107. data/lib/graphql/schema/has_single_input_argument.rb +6 -2
  108. data/lib/graphql/schema/input_object.rb +122 -64
  109. data/lib/graphql/schema/interface.rb +23 -5
  110. data/lib/graphql/schema/introspection_system.rb +6 -17
  111. data/lib/graphql/schema/late_bound_type.rb +4 -0
  112. data/lib/graphql/schema/list.rb +3 -3
  113. data/lib/graphql/schema/loader.rb +3 -2
  114. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
  115. data/lib/graphql/schema/member/has_arguments.rb +44 -58
  116. data/lib/graphql/schema/member/has_dataloader.rb +62 -0
  117. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  118. data/lib/graphql/schema/member/has_directives.rb +4 -4
  119. data/lib/graphql/schema/member/has_fields.rb +26 -6
  120. data/lib/graphql/schema/member/has_interfaces.rb +6 -6
  121. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  122. data/lib/graphql/schema/member/has_validators.rb +1 -1
  123. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  124. data/lib/graphql/schema/member/type_system_helpers.rb +17 -4
  125. data/lib/graphql/schema/member.rb +1 -0
  126. data/lib/graphql/schema/mutation.rb +7 -0
  127. data/lib/graphql/schema/object.rb +25 -8
  128. data/lib/graphql/schema/printer.rb +1 -0
  129. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  130. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  131. data/lib/graphql/schema/resolver.rb +29 -23
  132. data/lib/graphql/schema/scalar.rb +1 -6
  133. data/lib/graphql/schema/subscription.rb +52 -6
  134. data/lib/graphql/schema/timeout.rb +19 -2
  135. data/lib/graphql/schema/type_expression.rb +2 -2
  136. data/lib/graphql/schema/union.rb +1 -1
  137. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  138. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  139. data/lib/graphql/schema/validator.rb +3 -1
  140. data/lib/graphql/schema/visibility/migration.rb +188 -0
  141. data/lib/graphql/schema/visibility/profile.rb +445 -0
  142. data/lib/graphql/schema/visibility/visit.rb +190 -0
  143. data/lib/graphql/schema/visibility.rb +311 -0
  144. data/lib/graphql/schema/warden.rb +190 -20
  145. data/lib/graphql/schema.rb +695 -167
  146. data/lib/graphql/static_validation/all_rules.rb +2 -2
  147. data/lib/graphql/static_validation/base_visitor.rb +6 -5
  148. data/lib/graphql/static_validation/literal_validator.rb +4 -4
  149. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  150. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  151. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  152. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  153. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  154. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  155. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  156. data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
  157. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  158. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  159. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  160. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  161. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  162. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  163. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  164. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  165. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  166. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  167. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  168. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  169. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  170. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  171. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  172. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  173. data/lib/graphql/static_validation/validation_context.rb +18 -2
  174. data/lib/graphql/static_validation/validator.rb +6 -1
  175. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +5 -3
  176. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  177. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  178. data/lib/graphql/subscriptions/event.rb +13 -2
  179. data/lib/graphql/subscriptions/serialize.rb +1 -1
  180. data/lib/graphql/subscriptions.rb +7 -5
  181. data/lib/graphql/testing/helpers.rb +48 -16
  182. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  183. data/lib/graphql/testing.rb +1 -0
  184. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  185. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  186. data/lib/graphql/tracing/appoptics_trace.rb +5 -1
  187. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  188. data/lib/graphql/tracing/appsignal_trace.rb +32 -59
  189. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  190. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  191. data/lib/graphql/tracing/data_dog_trace.rb +46 -162
  192. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  193. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  194. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  195. data/lib/graphql/tracing/detailed_trace.rb +141 -0
  196. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  197. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  198. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  199. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  200. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  201. data/lib/graphql/tracing/notifications_trace.rb +183 -37
  202. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  203. data/lib/graphql/tracing/null_trace.rb +9 -0
  204. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  205. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  206. data/lib/graphql/tracing/perfetto_trace.rb +818 -0
  207. data/lib/graphql/tracing/platform_tracing.rb +1 -1
  208. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  209. data/lib/graphql/tracing/prometheus_trace.rb +73 -73
  210. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  211. data/lib/graphql/tracing/scout_trace.rb +32 -58
  212. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  213. data/lib/graphql/tracing/sentry_trace.rb +64 -98
  214. data/lib/graphql/tracing/statsd_trace.rb +33 -45
  215. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  216. data/lib/graphql/tracing/trace.rb +111 -1
  217. data/lib/graphql/tracing.rb +31 -30
  218. data/lib/graphql/type_kinds.rb +2 -1
  219. data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
  220. data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
  221. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  222. data/lib/graphql/types.rb +18 -11
  223. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  224. data/lib/graphql/version.rb +1 -1
  225. data/lib/graphql.rb +64 -54
  226. metadata +197 -22
  227. data/lib/graphql/analysis/ast/analyzer.rb +0 -91
  228. data/lib/graphql/analysis/ast/field_usage.rb +0 -82
  229. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  230. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  231. data/lib/graphql/analysis/ast/query_complexity.rb +0 -182
  232. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  233. data/lib/graphql/analysis/ast.rb +0 -94
  234. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  235. data/lib/graphql/backtrace/trace.rb +0 -93
  236. data/lib/graphql/backtrace/tracer.rb +0 -80
  237. data/lib/graphql/language/token.rb +0 -34
  238. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  239. data/lib/graphql/schema/null_mask.rb +0 -11
  240. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -10,6 +10,14 @@ module GraphQL
10
10
 
11
11
  include GraphQL::Dig
12
12
 
13
+ # Raised when an InputObject doesn't have any arguments defined and hasn't explicitly opted out of this requirement
14
+ class ArgumentsAreRequiredError < GraphQL::Error
15
+ def initialize(input_object_type)
16
+ message = "Input Object types must have arguments, but #{input_object_type.graphql_name} doesn't have any. Define an argument for this type, remove it from your schema, or add `has_no_arguments(true)` to its definition."
17
+ super(message)
18
+ end
19
+ end
20
+
13
21
  # @return [GraphQL::Query::Context] The context for this query
14
22
  attr_reader :context
15
23
  # @return [GraphQL::Execution::Interpereter::Arguments] The underlying arguments instance
@@ -23,13 +31,14 @@ module GraphQL
23
31
  @ruby_style_hash = ruby_kwargs
24
32
  @arguments = arguments
25
33
  # Apply prepares, not great to have it duplicated here.
26
- self.class.arguments(context).each_value do |arg_defn|
34
+ arg_defns = context ? context.types.arguments(self.class) : self.class.arguments(context).each_value
35
+ arg_defns.each do |arg_defn|
27
36
  ruby_kwargs_key = arg_defn.keyword
28
37
  if @ruby_style_hash.key?(ruby_kwargs_key)
29
38
  # Weirdly, procs are applied during coercion, but not methods.
30
39
  # Probably because these methods require a `self`.
31
40
  if arg_defn.prepare.is_a?(Symbol) || context.nil?
32
- prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
41
+ prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key], context: context)
33
42
  overwrite_argument(ruby_kwargs_key, prepared_value)
34
43
  end
35
44
  end
@@ -44,42 +53,18 @@ module GraphQL
44
53
  to_h
45
54
  end
46
55
 
47
- def prepare
48
- if @context
49
- object = @context[:current_object]
50
- # Pass this object's class with `as` so that messages are rendered correctly from inherited validators
51
- Schema::Validator.validate!(self.class.validators, object, @context, @ruby_style_hash, as: self.class)
52
- self
56
+ def deconstruct_keys(keys = nil)
57
+ if keys.nil?
58
+ @ruby_style_hash
53
59
  else
54
- self
60
+ new_h = {}
61
+ keys.each { |k| @ruby_style_hash.key?(k) && new_h[k] = @ruby_style_hash[k] }
62
+ new_h
55
63
  end
56
64
  end
57
65
 
58
- def self.authorized?(obj, value, ctx)
59
- # Authorize each argument (but this doesn't apply if `prepare` is implemented):
60
- if value.respond_to?(:key?)
61
- arguments(ctx).each do |_name, input_obj_arg|
62
- if value.key?(input_obj_arg.keyword) &&
63
- !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
64
- return false
65
- end
66
- end
67
- end
68
- # It didn't early-return false:
69
- true
70
- end
71
-
72
- def self.one_of
73
- if !one_of?
74
- if all_argument_definitions.any? { |arg| arg.type.non_null? }
75
- raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
76
- end
77
- directive(GraphQL::Schema::Directive::OneOf)
78
- end
79
- end
80
-
81
- def self.one_of?
82
- false # Re-defined when `OneOf` is added
66
+ def prepare
67
+ self
83
68
  end
84
69
 
85
70
  def unwrap_value(value)
@@ -119,7 +104,42 @@ module GraphQL
119
104
  @ruby_style_hash.dup
120
105
  end
121
106
 
107
+ # @api private
108
+ def validate_for(context)
109
+ object = context[:current_object]
110
+ # Pass this object's class with `as` so that messages are rendered correctly from inherited validators
111
+ Schema::Validator.validate!(self.class.validators, object, context, @ruby_style_hash, as: self.class)
112
+ nil
113
+ end
114
+
122
115
  class << self
116
+ def authorized?(obj, value, ctx)
117
+ # Authorize each argument (but this doesn't apply if `prepare` is implemented):
118
+ if value.respond_to?(:key?)
119
+ ctx.types.arguments(self).each do |input_obj_arg|
120
+ if value.key?(input_obj_arg.keyword) &&
121
+ !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
122
+ return false
123
+ end
124
+ end
125
+ end
126
+ # It didn't early-return false:
127
+ true
128
+ end
129
+
130
+ def one_of
131
+ if !one_of?
132
+ if all_argument_definitions.any? { |arg| arg.type.non_null? }
133
+ raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
134
+ end
135
+ directive(GraphQL::Schema::Directive::OneOf)
136
+ end
137
+ end
138
+
139
+ def one_of?
140
+ false # Re-defined when `OneOf` is added
141
+ end
142
+
123
143
  def argument(*args, **kwargs, &block)
124
144
  argument_defn = super(*args, **kwargs, &block)
125
145
  if one_of?
@@ -131,12 +151,9 @@ module GraphQL
131
151
  end
132
152
  end
133
153
  # Add a method access
134
- method_name = argument_defn.keyword
135
- class_eval <<-RUBY, __FILE__, __LINE__
136
- def #{method_name}
137
- self[#{method_name.inspect}]
138
- end
139
- RUBY
154
+ suppress_redefinition_warning do
155
+ define_accessor_method(argument_defn.keyword)
156
+ end
140
157
  argument_defn
141
158
  end
142
159
 
@@ -148,7 +165,7 @@ module GraphQL
148
165
  INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object."
149
166
 
150
167
  def validate_non_null_input(input, ctx, max_errors: nil)
151
- warden = ctx.warden
168
+ types = ctx.types
152
169
 
153
170
  if input.is_a?(Array)
154
171
  return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
@@ -159,30 +176,37 @@ module GraphQL
159
176
  return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
160
177
  end
161
178
 
162
- # Inject missing required arguments
163
- missing_required_inputs = self.arguments(ctx).reduce({}) do |m, (argument_name, argument)|
164
- if !input.key?(argument_name) && argument.type.non_null? && warden.get_argument(self, argument_name)
165
- m[argument_name] = nil
166
- end
167
-
168
- m
169
- end
170
179
 
171
180
  result = nil
172
- [input, missing_required_inputs].each do |args_to_validate|
173
- args_to_validate.each do |argument_name, value|
174
- argument = warden.get_argument(self, argument_name)
175
- # Items in the input that are unexpected
176
- if argument.nil?
177
- result ||= Query::InputValidationResult.new
178
- result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
179
- else
180
- # Items in the input that are expected, but have invalid values
181
- argument_result = argument.type.validate_input(value, ctx)
181
+
182
+
183
+ input.each do |argument_name, value|
184
+ argument = types.argument(self, argument_name)
185
+ if argument.nil? && ctx.is_a?(Query::NullContext) && argument_name.is_a?(Symbol)
186
+ # Validating definition directive arguments which come in as Symbols
187
+ argument = types.arguments(self).find { |arg| arg.keyword == argument_name }
188
+ end
189
+ # Items in the input that are unexpected
190
+ if argument.nil?
191
+ result ||= Query::InputValidationResult.new
192
+ result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
193
+ else
194
+ # Items in the input that are expected, but have invalid values
195
+ argument_result = argument.type.validate_input(value, ctx)
196
+ if !argument_result.valid?
182
197
  result ||= Query::InputValidationResult.new
183
- if !argument_result.valid?
184
- result.merge_result!(argument_name, argument_result)
185
- end
198
+ result.merge_result!(argument_name, argument_result)
199
+ end
200
+ end
201
+ end
202
+
203
+ # Check for missing non-null arguments
204
+ ctx.types.arguments(self).each do |argument|
205
+ if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value?
206
+ result ||= Query::InputValidationResult.new
207
+ argument_result = argument.type.validate_input(nil, ctx)
208
+ if !argument_result.valid?
209
+ result.merge_result!(argument.graphql_name, argument_result)
186
210
  end
187
211
  end
188
212
  end
@@ -215,8 +239,7 @@ module GraphQL
215
239
  if resolved_arguments.is_a?(GraphQL::Error)
216
240
  raise resolved_arguments
217
241
  else
218
- input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
219
- input_obj_instance.prepare
242
+ self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
220
243
  end
221
244
  end
222
245
  end
@@ -242,6 +265,41 @@ module GraphQL
242
265
 
243
266
  result
244
267
  end
268
+
269
+ # @param new_has_no_arguments [Boolean] Call with `true` to make this InputObject type ignore the requirement to have any defined arguments.
270
+ # @return [void]
271
+ def has_no_arguments(new_has_no_arguments)
272
+ @has_no_arguments = new_has_no_arguments
273
+ nil
274
+ end
275
+
276
+ # @return [Boolean] `true` if `has_no_arguments(true)` was configued
277
+ def has_no_arguments?
278
+ @has_no_arguments
279
+ end
280
+
281
+ def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
282
+ if require_defined_arguments && !has_no_arguments? && !any_arguments?
283
+ warn(GraphQL::Schema::InputObject::ArgumentsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
284
+ end
285
+ super(context, false)
286
+ end
287
+
288
+ private
289
+
290
+ # Suppress redefinition warning for objectId arguments
291
+ def suppress_redefinition_warning
292
+ verbose = $VERBOSE
293
+ $VERBOSE = nil
294
+ yield
295
+ ensure
296
+ $VERBOSE = verbose
297
+ end
298
+
299
+ def define_accessor_method(method_name)
300
+ define_method(method_name) { self[method_name] }
301
+ alias_method(method_name, method_name)
302
+ end
245
303
  end
246
304
 
247
305
  private
@@ -13,6 +13,7 @@ module GraphQL
13
13
  include GraphQL::Schema::Member::Scoped
14
14
  include GraphQL::Schema::Member::HasAstNode
15
15
  include GraphQL::Schema::Member::HasUnresolvedTypeError
16
+ include GraphQL::Schema::Member::HasDataloader
16
17
  include GraphQL::Schema::Member::HasDirectives
17
18
  include GraphQL::Schema::Member::HasInterfaces
18
19
 
@@ -63,6 +64,7 @@ module GraphQL
63
64
 
64
65
  child_class.introspection(introspection)
65
66
  child_class.description(description)
67
+ child_class.comment(nil)
66
68
  # If interfaces are mixed into each other, only define this class once
67
69
  if !child_class.const_defined?(:UnresolvedTypeError, false)
68
70
  add_unresolved_type_error(child_class)
@@ -82,13 +84,29 @@ module GraphQL
82
84
  super
83
85
  end
84
86
 
87
+ # Register other Interface or Object types as implementers of this Interface.
88
+ #
89
+ # When those Interfaces or Objects aren't used as the return values of fields,
90
+ # they may have to be registered using this method so that GraphQL-Ruby can find them.
91
+ # @param types [Class, Module]
92
+ # @return [Array<Module, Class>] Implementers of this interface, if they're registered
85
93
  def orphan_types(*types)
86
- if types.any?
87
- @orphan_types = types
94
+ if !types.empty?
95
+ @orphan_types ||= []
96
+ @orphan_types.concat(types)
88
97
  else
89
- all_orphan_types = @orphan_types || []
90
- all_orphan_types += super if defined?(super)
91
- all_orphan_types.uniq
98
+ if defined?(@orphan_types)
99
+ all_orphan_types = @orphan_types.dup
100
+ if defined?(super)
101
+ all_orphan_types += super
102
+ all_orphan_types.uniq!
103
+ end
104
+ all_orphan_types
105
+ elsif defined?(super)
106
+ super
107
+ else
108
+ EmptyObjects::EMPTY_ARRAY
109
+ end
92
110
  end
93
111
  end
94
112
 
@@ -25,10 +25,10 @@ module GraphQL
25
25
  load_constant(:DirectiveLocationEnum)
26
26
  ]
27
27
  @types = {}
28
- @possible_types = {}
28
+ @possible_types = {}.compare_by_identity
29
29
  type_defns.each do |t|
30
30
  @types[t.graphql_name] = t
31
- @possible_types[t.graphql_name] = [t]
31
+ @possible_types[t] = [t]
32
32
  end
33
33
  @entry_point_fields =
34
34
  if schema.disable_introspection_entry_points?
@@ -69,7 +69,7 @@ module GraphQL
69
69
  def resolve_late_bindings
70
70
  @types.each do |name, t|
71
71
  if t.kind.fields?
72
- t.fields.each do |_name, field_defn|
72
+ t.all_field_definitions.each do |field_defn|
73
73
  field_defn.type = resolve_late_binding(field_defn.type)
74
74
  end
75
75
  end
@@ -90,7 +90,8 @@ module GraphQL
90
90
  def resolve_late_binding(late_bound_type)
91
91
  case late_bound_type
92
92
  when GraphQL::Schema::LateBoundType
93
- @schema.get_type(late_bound_type.name)
93
+ type_name = late_bound_type.name
94
+ @types[type_name] || @schema.get_type(type_name)
94
95
  when GraphQL::Schema::List
95
96
  resolve_late_binding(late_bound_type.of_type).to_list_type
96
97
  when GraphQL::Schema::NonNull
@@ -113,19 +114,7 @@ module GraphQL
113
114
 
114
115
  def get_fields_from_class(class_sym:)
115
116
  object_type_defn = load_constant(class_sym)
116
-
117
- if object_type_defn.is_a?(Module)
118
- object_type_defn.fields
119
- else
120
- extracted_field_defns = {}
121
- object_class = object_type_defn.metadata[:type_class]
122
- object_type_defn.all_fields.each do |field_defn|
123
- inner_resolve = field_defn.resolve_proc
124
- resolve_with_instantiate = PerFieldProxyResolve.new(object_class: object_class, inner_resolve: inner_resolve)
125
- extracted_field_defns[field_defn.name] = field_defn.redefine(resolve: resolve_with_instantiate)
126
- end
127
- extracted_field_defns
128
- end
117
+ object_type_defn.fields
129
118
  end
130
119
 
131
120
  # This is probably not 100% robust -- but it has to be good enough to avoid modifying the built-in introspection types
@@ -25,6 +25,10 @@ module GraphQL
25
25
  @to_list_type ||= GraphQL::Schema::List.new(self)
26
26
  end
27
27
 
28
+ def to_type_signature
29
+ name
30
+ end
31
+
28
32
  def inspect
29
33
  "#<LateBoundType @name=#{name}>"
30
34
  end
@@ -4,7 +4,7 @@ module GraphQL
4
4
  class Schema
5
5
  # Represents a list type in the schema.
6
6
  # Wraps a {Schema::Member} as a list type.
7
- # @see {Schema::Member::TypeSystemHelpers#to_list_type}
7
+ # @see Schema::Member::TypeSystemHelpers#to_list_type Create a list type from another GraphQL type
8
8
  class List < GraphQL::Schema::Wrapper
9
9
  include Schema::Member::ValidatesInput
10
10
 
@@ -52,7 +52,7 @@ module GraphQL
52
52
  unless item_result.valid?
53
53
  if max_errors
54
54
  if max_errors == 0
55
- add_max_errros_reached_message(result)
55
+ add_max_errors_reached_message(result)
56
56
  break
57
57
  end
58
58
 
@@ -76,7 +76,7 @@ module GraphQL
76
76
  end
77
77
  end
78
78
 
79
- def add_max_errros_reached_message(result)
79
+ def add_max_errors_reached_message(result)
80
80
  message = "Too many errors processing list variable, max validation error limit reached. Execution aborted"
81
81
  item_result = GraphQL::Query::InputValidationResult.from_problem(message)
82
82
  result.merge_result!(nil, item_result)
@@ -32,7 +32,8 @@ module GraphQL
32
32
  end
33
33
 
34
34
  Class.new(GraphQL::Schema) do
35
- orphan_types(types.values)
35
+ add_type_and_traverse(types.values, root: false)
36
+ orphan_types(types.values.select { |t| t.kind.object? })
36
37
  directives(directives)
37
38
  description(schema["description"])
38
39
 
@@ -186,7 +187,7 @@ module GraphQL
186
187
  camelize: false,
187
188
  connection_extension: nil,
188
189
  ) do
189
- if field_hash["args"].any?
190
+ if !field_hash["args"].empty?
190
191
  loader.build_arguments(self, field_hash["args"], type_resolver)
191
192
  end
192
193
  end
@@ -50,12 +50,27 @@ module GraphQL
50
50
  end
51
51
  end
52
52
 
53
+ # Call this method to provide a new comment; OR
54
+ # call it without an argument to get the comment
55
+ # @param new_comment [String]
56
+ # @return [String, nil]
57
+ def comment(new_comment = NOT_CONFIGURED)
58
+ if !NOT_CONFIGURED.equal?(new_comment)
59
+ @comment = new_comment
60
+ elsif defined?(@comment)
61
+ @comment
62
+ else
63
+ nil
64
+ end
65
+ end
66
+
53
67
  # This pushes some configurations _down_ the inheritance tree,
54
68
  # in order to prevent repetitive lookups at runtime.
55
69
  module ConfigurationExtension
56
70
  def inherited(child_class)
57
71
  child_class.introspection(introspection)
58
72
  child_class.description(description)
73
+ child_class.comment(nil)
59
74
  child_class.default_graphql_name = nil
60
75
 
61
76
  if defined?(@graphql_name) && @graphql_name && (self.name.nil? || graphql_name != default_graphql_name)
@@ -38,39 +38,6 @@ module GraphQL
38
38
  end
39
39
  arg_defn = self.argument_class.new(*args, **kwargs, &block)
40
40
  add_argument(arg_defn)
41
-
42
- if self.is_a?(Class) && !method_defined?(:"load_#{arg_defn.keyword}")
43
- method_owner = if self < GraphQL::Schema::InputObject || self < GraphQL::Schema::Directive
44
- "self."
45
- elsif self < GraphQL::Schema::Resolver
46
- ""
47
- else
48
- raise "Unexpected argument owner: #{self}"
49
- end
50
- if loads && arg_defn.type.list?
51
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
52
- def #{method_owner}load_#{arg_defn.keyword}(values, context = nil)
53
- argument = get_argument("#{arg_defn.graphql_name}", context || self.context)
54
- (context || self.context).query.after_lazy(values) do |values2|
55
- GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, value, context || self.context) })
56
- end
57
- end
58
- RUBY
59
- elsif loads
60
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
61
- def #{method_owner}load_#{arg_defn.keyword}(value, context = nil)
62
- argument = get_argument("#{arg_defn.graphql_name}", context || self.context)
63
- load_application_object(argument, value, context || self.context)
64
- end
65
- RUBY
66
- else
67
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
68
- def #{method_owner}load_#{arg_defn.keyword}(value, _context = nil)
69
- value
70
- end
71
- RUBY
72
- end
73
- end
74
41
  arg_defn
75
42
  end
76
43
 
@@ -109,8 +76,8 @@ module GraphQL
109
76
  end
110
77
 
111
78
  # @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
112
- def arguments(context = GraphQL::Query::NullContext.instance)
113
- if own_arguments.any?
79
+ def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil)
80
+ if !own_arguments.empty?
114
81
  own_arguments_that_apply = {}
115
82
  own_arguments.each do |name, args_entry|
116
83
  if (visible_defn = Warden.visible_entry?(:visible_argument?, args_entry, context))
@@ -123,7 +90,7 @@ module GraphQL
123
90
  end
124
91
 
125
92
  def any_arguments?
126
- own_arguments.any?
93
+ !own_arguments.empty?
127
94
  end
128
95
 
129
96
  module ClassConfigured
@@ -133,12 +100,12 @@ module GraphQL
133
100
  end
134
101
 
135
102
  module InheritedArguments
136
- def arguments(context = GraphQL::Query::NullContext.instance)
137
- own_arguments = super
138
- inherited_arguments = superclass.arguments(context)
103
+ def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
104
+ own_arguments = super(context, require_defined_arguments)
105
+ inherited_arguments = superclass.arguments(context, false)
139
106
 
140
- if own_arguments.any?
141
- if inherited_arguments.any?
107
+ if !own_arguments.empty?
108
+ if !inherited_arguments.empty?
142
109
  # Local definitions override inherited ones
143
110
  inherited_arguments.merge(own_arguments)
144
111
  else
@@ -168,10 +135,11 @@ module GraphQL
168
135
 
169
136
  def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
170
137
  warden = Warden.from_context(context)
138
+ skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
171
139
  for ancestor in ancestors
172
140
  if ancestor.respond_to?(:own_arguments) &&
173
141
  (a = ancestor.own_arguments[argument_name]) &&
174
- (a = Warden.visible_entry?(:visible_argument?, a, context, warden))
142
+ (skip_visible || (a = Warden.visible_entry?(:visible_argument?, a, context, warden)))
175
143
  return a
176
144
  end
177
145
  end
@@ -181,12 +149,12 @@ module GraphQL
181
149
  end
182
150
 
183
151
  module FieldConfigured
184
- def arguments(context = GraphQL::Query::NullContext.instance)
152
+ def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil)
185
153
  own_arguments = super
186
154
  if @resolver_class
187
155
  inherited_arguments = @resolver_class.field_arguments(context)
188
- if own_arguments.any?
189
- if inherited_arguments.any?
156
+ if !own_arguments.empty?
157
+ if !inherited_arguments.empty?
190
158
  inherited_arguments.merge(own_arguments)
191
159
  else
192
160
  own_arguments
@@ -230,16 +198,20 @@ module GraphQL
230
198
  end
231
199
 
232
200
  def all_argument_definitions
233
- all_defns = own_arguments.values
234
- all_defns.flatten!
235
- all_defns
201
+ if !own_arguments.empty?
202
+ all_defns = own_arguments.values
203
+ all_defns.flatten!
204
+ all_defns
205
+ else
206
+ EmptyObjects::EMPTY_ARRAY
207
+ end
236
208
  end
237
209
 
238
210
  # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
239
211
  def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
240
212
  warden = Warden.from_context(context)
241
- if (arg_config = own_arguments[argument_name]) && (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden))
242
- visible_arg
213
+ if (arg_config = own_arguments[argument_name]) && ((context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)) || (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden)))
214
+ visible_arg || arg_config
243
215
  elsif defined?(@resolver_class) && @resolver_class
244
216
  @resolver_class.get_field_argument(argument_name, context)
245
217
  else
@@ -259,11 +231,11 @@ module GraphQL
259
231
  #
260
232
  # @param values [Hash<String, Object>]
261
233
  # @param context [GraphQL::Query::Context]
262
- # @yield [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
263
- # @return [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
234
+ # @yield [Interpreter::Arguments, Execution::Lazy<Interpreter::Arguments>]
235
+ # @return [Interpreter::Arguments, Execution::Lazy<Interpreter::Arguments>]
264
236
  def coerce_arguments(parent_object, values, context, &block)
265
237
  # Cache this hash to avoid re-merging it
266
- arg_defns = context.warden.arguments(self)
238
+ arg_defns = context.types.arguments(self)
267
239
  total_args_count = arg_defns.size
268
240
 
269
241
  finished_args = nil
@@ -387,7 +359,8 @@ module GraphQL
387
359
  if application_object.nil?
388
360
  nil
389
361
  else
390
- maybe_lazy_resolve_type = context.schema.resolve_type(argument.loads, application_object, context)
362
+ arg_loads_type = argument.loads
363
+ maybe_lazy_resolve_type = context.schema.resolve_type(arg_loads_type, application_object, context)
391
364
  context.query.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result|
392
365
  if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
393
366
  application_object_type, application_object = resolve_type_result
@@ -396,10 +369,17 @@ module GraphQL
396
369
  # application_object is already assigned
397
370
  end
398
371
 
399
- if !(
400
- context.warden.possible_types(argument.loads).include?(application_object_type) ||
401
- context.warden.loadable?(argument.loads, context)
402
- )
372
+ passes_possible_types_check = if context.types.loadable?(arg_loads_type, context)
373
+ if arg_loads_type.kind.abstract?
374
+ # This union/interface is used in `loads:` but not otherwise visible to this query
375
+ context.types.loadable_possible_types(arg_loads_type, context).include?(application_object_type)
376
+ else
377
+ true
378
+ end
379
+ else
380
+ context.types.possible_types(arg_loads_type).include?(application_object_type)
381
+ end
382
+ if !passes_possible_types_check
403
383
  err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
404
384
  application_object = load_application_object_failed(err)
405
385
  end
@@ -433,6 +413,12 @@ module GraphQL
433
413
  end
434
414
  end
435
415
 
416
+ # Called when an argument's `loads:` configuration fails to fetch an application object.
417
+ # By default, this method raises the given error, but you can override it to handle failures differently.
418
+ #
419
+ # @param err [GraphQL::LoadApplicationObjectFailedError] The error that occurred
420
+ # @return [Object, nil] If a value is returned, it will be used instead of the failed load
421
+ # @api public
436
422
  def load_application_object_failed(err)
437
423
  raise err
438
424
  end