graphql 2.3.7 → 2.4.5

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

Potentially problematic release.


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

Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +46 -0
  3. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  4. data/lib/generators/graphql/templates/base_resolver.erb +2 -0
  5. data/lib/generators/graphql/type_generator.rb +1 -1
  6. data/lib/graphql/analysis/field_usage.rb +1 -1
  7. data/lib/graphql/analysis/query_complexity.rb +3 -3
  8. data/lib/graphql/analysis/visitor.rb +8 -7
  9. data/lib/graphql/analysis.rb +4 -4
  10. data/lib/graphql/autoload.rb +37 -0
  11. data/lib/graphql/current.rb +52 -0
  12. data/lib/graphql/dataloader/async_dataloader.rb +7 -6
  13. data/lib/graphql/dataloader/source.rb +7 -4
  14. data/lib/graphql/dataloader.rb +40 -19
  15. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  16. data/lib/graphql/execution/interpreter/resolve.rb +13 -9
  17. data/lib/graphql/execution/interpreter/runtime.rb +35 -31
  18. data/lib/graphql/execution/interpreter.rb +6 -4
  19. data/lib/graphql/execution/lookahead.rb +18 -11
  20. data/lib/graphql/introspection/directive_type.rb +1 -1
  21. data/lib/graphql/introspection/entry_points.rb +2 -2
  22. data/lib/graphql/introspection/field_type.rb +1 -1
  23. data/lib/graphql/introspection/schema_type.rb +6 -11
  24. data/lib/graphql/introspection/type_type.rb +5 -5
  25. data/lib/graphql/invalid_null_error.rb +1 -1
  26. data/lib/graphql/language/cache.rb +13 -0
  27. data/lib/graphql/language/comment.rb +18 -0
  28. data/lib/graphql/language/document_from_schema_definition.rb +62 -34
  29. data/lib/graphql/language/lexer.rb +18 -15
  30. data/lib/graphql/language/nodes.rb +24 -16
  31. data/lib/graphql/language/parser.rb +14 -1
  32. data/lib/graphql/language/printer.rb +31 -15
  33. data/lib/graphql/language/sanitized_printer.rb +1 -1
  34. data/lib/graphql/language.rb +6 -6
  35. data/lib/graphql/pagination/connection.rb +1 -1
  36. data/lib/graphql/query/context/scoped_context.rb +1 -1
  37. data/lib/graphql/query/context.rb +13 -6
  38. data/lib/graphql/query/null_context.rb +3 -5
  39. data/lib/graphql/query/variable_validation_error.rb +1 -1
  40. data/lib/graphql/query.rb +72 -18
  41. data/lib/graphql/railtie.rb +7 -0
  42. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  43. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  44. data/lib/graphql/rubocop.rb +2 -0
  45. data/lib/graphql/schema/addition.rb +2 -1
  46. data/lib/graphql/schema/always_visible.rb +6 -2
  47. data/lib/graphql/schema/argument.rb +14 -1
  48. data/lib/graphql/schema/build_from_definition.rb +9 -1
  49. data/lib/graphql/schema/directive/flagged.rb +2 -2
  50. data/lib/graphql/schema/directive.rb +1 -1
  51. data/lib/graphql/schema/enum.rb +71 -23
  52. data/lib/graphql/schema/enum_value.rb +10 -2
  53. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  54. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  55. data/lib/graphql/schema/field.rb +102 -47
  56. data/lib/graphql/schema/field_extension.rb +1 -1
  57. data/lib/graphql/schema/has_single_input_argument.rb +5 -2
  58. data/lib/graphql/schema/input_object.rb +90 -39
  59. data/lib/graphql/schema/interface.rb +22 -5
  60. data/lib/graphql/schema/introspection_system.rb +5 -16
  61. data/lib/graphql/schema/loader.rb +1 -1
  62. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
  63. data/lib/graphql/schema/member/has_arguments.rb +25 -20
  64. data/lib/graphql/schema/member/has_directives.rb +3 -3
  65. data/lib/graphql/schema/member/has_fields.rb +26 -6
  66. data/lib/graphql/schema/member/has_interfaces.rb +4 -4
  67. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  68. data/lib/graphql/schema/member/has_validators.rb +1 -1
  69. data/lib/graphql/schema/object.rb +8 -0
  70. data/lib/graphql/schema/printer.rb +1 -0
  71. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  72. data/lib/graphql/schema/resolver.rb +12 -14
  73. data/lib/graphql/schema/subscription.rb +2 -2
  74. data/lib/graphql/schema/type_expression.rb +2 -2
  75. data/lib/graphql/schema/union.rb +1 -1
  76. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  77. data/lib/graphql/schema/validator/required_validator.rb +28 -4
  78. data/lib/graphql/schema/validator.rb +3 -1
  79. data/lib/graphql/schema/visibility/migration.rb +187 -0
  80. data/lib/graphql/schema/visibility/profile.rb +353 -0
  81. data/lib/graphql/schema/visibility/visit.rb +190 -0
  82. data/lib/graphql/schema/visibility.rb +294 -0
  83. data/lib/graphql/schema/warden.rb +166 -16
  84. data/lib/graphql/schema.rb +348 -94
  85. data/lib/graphql/static_validation/base_visitor.rb +6 -5
  86. data/lib/graphql/static_validation/literal_validator.rb +4 -4
  87. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  88. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  89. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  90. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  91. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  92. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  93. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +2 -2
  94. data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -7
  95. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  96. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  97. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  98. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  99. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  100. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  101. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  102. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  103. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  104. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  105. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  106. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  107. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  108. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  109. data/lib/graphql/static_validation/validation_context.rb +18 -2
  110. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
  111. data/lib/graphql/subscriptions/broadcast_analyzer.rb +10 -4
  112. data/lib/graphql/subscriptions/event.rb +1 -1
  113. data/lib/graphql/subscriptions.rb +6 -4
  114. data/lib/graphql/testing/helpers.rb +10 -6
  115. data/lib/graphql/tracing/notifications_trace.rb +2 -2
  116. data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
  117. data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
  118. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  119. data/lib/graphql/types.rb +18 -11
  120. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  121. data/lib/graphql/version.rb +1 -1
  122. data/lib/graphql.rb +81 -45
  123. metadata +31 -8
  124. data/lib/graphql/language/token.rb +0 -34
  125. data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -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,7 +31,8 @@ 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.
@@ -44,6 +53,16 @@ module GraphQL
44
53
  to_h
45
54
  end
46
55
 
56
+ def deconstruct_keys(keys = nil)
57
+ if keys.nil?
58
+ @ruby_style_hash
59
+ else
60
+ new_h = {}
61
+ keys.each { |k| @ruby_style_hash.key?(k) && new_h[k] = @ruby_style_hash[k] }
62
+ new_h
63
+ end
64
+ end
65
+
47
66
  def prepare
48
67
  if @context
49
68
  object = @context[:current_object]
@@ -55,33 +74,6 @@ module GraphQL
55
74
  end
56
75
  end
57
76
 
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
83
- end
84
-
85
77
  def unwrap_value(value)
86
78
  case value
87
79
  when Array
@@ -120,6 +112,33 @@ module GraphQL
120
112
  end
121
113
 
122
114
  class << self
115
+ def authorized?(obj, value, ctx)
116
+ # Authorize each argument (but this doesn't apply if `prepare` is implemented):
117
+ if value.respond_to?(:key?)
118
+ ctx.types.arguments(self).each do |input_obj_arg|
119
+ if value.key?(input_obj_arg.keyword) &&
120
+ !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
121
+ return false
122
+ end
123
+ end
124
+ end
125
+ # It didn't early-return false:
126
+ true
127
+ end
128
+
129
+ def one_of
130
+ if !one_of?
131
+ if all_argument_definitions.any? { |arg| arg.type.non_null? }
132
+ raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
133
+ end
134
+ directive(GraphQL::Schema::Directive::OneOf)
135
+ end
136
+ end
137
+
138
+ def one_of?
139
+ false # Re-defined when `OneOf` is added
140
+ end
141
+
123
142
  def argument(*args, **kwargs, &block)
124
143
  argument_defn = super(*args, **kwargs, &block)
125
144
  if one_of?
@@ -132,12 +151,14 @@ module GraphQL
132
151
  end
133
152
  # Add a method access
134
153
  method_name = argument_defn.keyword
135
- class_eval <<-RUBY, __FILE__, __LINE__
136
- def #{method_name}
137
- self[#{method_name.inspect}]
138
- end
139
- alias_method :#{method_name}, :#{method_name}
140
- RUBY
154
+ suppress_redefinition_warning do
155
+ class_eval <<-RUBY, __FILE__, __LINE__
156
+ def #{method_name}
157
+ self[#{method_name.inspect}]
158
+ end
159
+ alias_method :#{method_name}, :#{method_name}
160
+ RUBY
161
+ end
141
162
  argument_defn
142
163
  end
143
164
 
@@ -149,7 +170,7 @@ module GraphQL
149
170
  INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object."
150
171
 
151
172
  def validate_non_null_input(input, ctx, max_errors: nil)
152
- warden = ctx.warden
173
+ types = ctx.types
153
174
 
154
175
  if input.is_a?(Array)
155
176
  return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
@@ -161,9 +182,9 @@ module GraphQL
161
182
  end
162
183
 
163
184
  # Inject missing required arguments
164
- missing_required_inputs = self.arguments(ctx).reduce({}) do |m, (argument_name, argument)|
165
- if !input.key?(argument_name) && argument.type.non_null? && warden.get_argument(self, argument_name)
166
- m[argument_name] = nil
185
+ missing_required_inputs = ctx.types.arguments(self).reduce({}) do |m, (argument)|
186
+ if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value? && types.argument(self, argument.graphql_name)
187
+ m[argument.graphql_name] = nil
167
188
  end
168
189
 
169
190
  m
@@ -172,7 +193,7 @@ module GraphQL
172
193
  result = nil
173
194
  [input, missing_required_inputs].each do |args_to_validate|
174
195
  args_to_validate.each do |argument_name, value|
175
- argument = warden.get_argument(self, argument_name)
196
+ argument = types.argument(self, argument_name)
176
197
  # Items in the input that are unexpected
177
198
  if argument.nil?
178
199
  result ||= Query::InputValidationResult.new
@@ -242,6 +263,36 @@ module GraphQL
242
263
 
243
264
  result
244
265
  end
266
+
267
+ # @param new_has_no_arguments [Boolean] Call with `true` to make this InputObject type ignore the requirement to have any defined arguments.
268
+ # @return [void]
269
+ def has_no_arguments(new_has_no_arguments)
270
+ @has_no_arguments = new_has_no_arguments
271
+ nil
272
+ end
273
+
274
+ # @return [Boolean] `true` if `has_no_arguments(true)` was configued
275
+ def has_no_arguments?
276
+ @has_no_arguments
277
+ end
278
+
279
+ def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
280
+ if require_defined_arguments && !has_no_arguments? && !any_arguments?
281
+ warn(GraphQL::Schema::InputObject::ArgumentsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
282
+ end
283
+ super(context, false)
284
+ end
285
+
286
+ private
287
+
288
+ # Suppress redefinition warning for objectId arguments
289
+ def suppress_redefinition_warning
290
+ verbose = $VERBOSE
291
+ $VERBOSE = nil
292
+ yield
293
+ ensure
294
+ $VERBOSE = verbose
295
+ end
245
296
  end
246
297
 
247
298
  private
@@ -63,6 +63,7 @@ module GraphQL
63
63
 
64
64
  child_class.introspection(introspection)
65
65
  child_class.description(description)
66
+ child_class.comment(nil)
66
67
  # If interfaces are mixed into each other, only define this class once
67
68
  if !child_class.const_defined?(:UnresolvedTypeError, false)
68
69
  add_unresolved_type_error(child_class)
@@ -82,13 +83,29 @@ module GraphQL
82
83
  super
83
84
  end
84
85
 
86
+ # Register other Interface or Object types as implementers of this Interface.
87
+ #
88
+ # When those Interfaces or Objects aren't used as the return values of fields,
89
+ # they may have to be registered using this method so that GraphQL-Ruby can find them.
90
+ # @param types [Class, Module]
91
+ # @return [Array<Module, Class>] Implementers of this interface, if they're registered
85
92
  def orphan_types(*types)
86
- if types.any?
87
- @orphan_types = types
93
+ if !types.empty?
94
+ @orphan_types ||= []
95
+ @orphan_types.concat(types)
88
96
  else
89
- all_orphan_types = @orphan_types || []
90
- all_orphan_types += super if defined?(super)
91
- all_orphan_types.uniq
97
+ if defined?(@orphan_types)
98
+ all_orphan_types = @orphan_types.dup
99
+ if defined?(super)
100
+ all_orphan_types += super
101
+ all_orphan_types.uniq!
102
+ end
103
+ all_orphan_types
104
+ elsif defined?(super)
105
+ super
106
+ else
107
+ EmptyObjects::EMPTY_ARRAY
108
+ end
92
109
  end
93
110
  end
94
111
 
@@ -25,7 +25,7 @@ module GraphQL
25
25
  load_constant(:DirectiveLocationEnum)
26
26
  ]
27
27
  @types = {}
28
- @possible_types = {}.tap(&:compare_by_identity)
28
+ @possible_types = {}.compare_by_identity
29
29
  type_defns.each do |t|
30
30
  @types[t.graphql_name] = t
31
31
  @possible_types[t] = [t]
@@ -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
@@ -187,7 +187,7 @@ module GraphQL
187
187
  camelize: false,
188
188
  connection_extension: nil,
189
189
  ) do
190
- if field_hash["args"].any?
190
+ if !field_hash["args"].empty?
191
191
  loader.build_arguments(self, field_hash["args"], type_resolver)
192
192
  end
193
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)
@@ -76,8 +76,8 @@ module GraphQL
76
76
  end
77
77
 
78
78
  # @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
79
- def arguments(context = GraphQL::Query::NullContext.instance)
80
- if own_arguments.any?
79
+ def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil)
80
+ if !own_arguments.empty?
81
81
  own_arguments_that_apply = {}
82
82
  own_arguments.each do |name, args_entry|
83
83
  if (visible_defn = Warden.visible_entry?(:visible_argument?, args_entry, context))
@@ -90,7 +90,7 @@ module GraphQL
90
90
  end
91
91
 
92
92
  def any_arguments?
93
- own_arguments.any?
93
+ !own_arguments.empty?
94
94
  end
95
95
 
96
96
  module ClassConfigured
@@ -100,12 +100,12 @@ module GraphQL
100
100
  end
101
101
 
102
102
  module InheritedArguments
103
- def arguments(context = GraphQL::Query::NullContext.instance)
104
- own_arguments = super
105
- 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)
106
106
 
107
- if own_arguments.any?
108
- if inherited_arguments.any?
107
+ if !own_arguments.empty?
108
+ if !inherited_arguments.empty?
109
109
  # Local definitions override inherited ones
110
110
  inherited_arguments.merge(own_arguments)
111
111
  else
@@ -135,10 +135,11 @@ module GraphQL
135
135
 
136
136
  def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
137
137
  warden = Warden.from_context(context)
138
+ skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
138
139
  for ancestor in ancestors
139
140
  if ancestor.respond_to?(:own_arguments) &&
140
141
  (a = ancestor.own_arguments[argument_name]) &&
141
- (a = Warden.visible_entry?(:visible_argument?, a, context, warden))
142
+ (skip_visible || (a = Warden.visible_entry?(:visible_argument?, a, context, warden)))
142
143
  return a
143
144
  end
144
145
  end
@@ -148,12 +149,12 @@ module GraphQL
148
149
  end
149
150
 
150
151
  module FieldConfigured
151
- def arguments(context = GraphQL::Query::NullContext.instance)
152
+ def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil)
152
153
  own_arguments = super
153
154
  if @resolver_class
154
155
  inherited_arguments = @resolver_class.field_arguments(context)
155
- if own_arguments.any?
156
- if inherited_arguments.any?
156
+ if !own_arguments.empty?
157
+ if !inherited_arguments.empty?
157
158
  inherited_arguments.merge(own_arguments)
158
159
  else
159
160
  own_arguments
@@ -197,16 +198,20 @@ module GraphQL
197
198
  end
198
199
 
199
200
  def all_argument_definitions
200
- all_defns = own_arguments.values
201
- all_defns.flatten!
202
- 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
203
208
  end
204
209
 
205
210
  # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
206
211
  def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
207
212
  warden = Warden.from_context(context)
208
- if (arg_config = own_arguments[argument_name]) && (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden))
209
- 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
210
215
  elsif defined?(@resolver_class) && @resolver_class
211
216
  @resolver_class.get_field_argument(argument_name, context)
212
217
  else
@@ -230,7 +235,7 @@ module GraphQL
230
235
  # @return [Interpreter::Arguments, Execution::Lazy<Interpreter::Arguments>]
231
236
  def coerce_arguments(parent_object, values, context, &block)
232
237
  # Cache this hash to avoid re-merging it
233
- arg_defns = context.warden.arguments(self)
238
+ arg_defns = context.types.arguments(self)
234
239
  total_args_count = arg_defns.size
235
240
 
236
241
  finished_args = nil
@@ -364,8 +369,8 @@ module GraphQL
364
369
  end
365
370
 
366
371
  if !(
367
- context.warden.possible_types(argument.loads).include?(application_object_type) ||
368
- context.warden.loadable?(argument.loads, context)
372
+ context.types.possible_types(argument.loads).include?(application_object_type) ||
373
+ context.types.loadable?(argument.loads, context)
369
374
  )
370
375
  err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
371
376
  application_object = load_application_object_failed(err)
@@ -55,14 +55,14 @@ module GraphQL
55
55
  else
56
56
  GraphQL::EmptyObjects::EMPTY_ARRAY
57
57
  end
58
- if inherited_directives.any? && directives
58
+ if !inherited_directives.empty? && directives
59
59
  dirs = []
60
60
  merge_directives(dirs, inherited_directives)
61
61
  merge_directives(dirs, directives)
62
62
  dirs
63
63
  elsif directives
64
64
  directives
65
- elsif inherited_directives.any?
65
+ elsif !inherited_directives.empty?
66
66
  inherited_directives
67
67
  else
68
68
  GraphQL::EmptyObjects::EMPTY_ARRAY
@@ -71,7 +71,7 @@ module GraphQL
71
71
  dirs = nil
72
72
  schema_member.ancestors.reverse_each do |ancestor|
73
73
  if ancestor.respond_to?(:own_directives) &&
74
- (anc_dirs = ancestor.own_directives).any?
74
+ !(anc_dirs = ancestor.own_directives).empty?
75
75
  dirs ||= []
76
76
  merge_directives(dirs, anc_dirs)
77
77
  end
@@ -79,6 +79,18 @@ module GraphQL
79
79
  end
80
80
  end
81
81
 
82
+ # @param new_has_no_fields [Boolean] Call with `true` to make this Object type ignore the requirement to have any defined fields.
83
+ # @return [void]
84
+ def has_no_fields(new_has_no_fields)
85
+ @has_no_fields = new_has_no_fields
86
+ nil
87
+ end
88
+
89
+ # @return [Boolean] `true` if `has_no_fields(true)` was configued
90
+ def has_no_fields?
91
+ @has_no_fields
92
+ end
93
+
82
94
  # @return [Hash<String => GraphQL::Schema::Field, Array<GraphQL::Schema::Field>>] Fields defined on this class _specifically_, not parent classes
83
95
  def own_fields
84
96
  @own_fields ||= {}
@@ -99,11 +111,12 @@ module GraphQL
99
111
  module InterfaceMethods
100
112
  def get_field(field_name, context = GraphQL::Query::NullContext.instance)
101
113
  warden = Warden.from_context(context)
114
+ skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
102
115
  for ancestor in ancestors
103
116
  if ancestor.respond_to?(:own_fields) &&
104
117
  (f_entry = ancestor.own_fields[field_name]) &&
105
- (f = Warden.visible_entry?(:visible_field?, f_entry, context, warden))
106
- return f
118
+ (skip_visible || (f_entry = Warden.visible_entry?(:visible_field?, f_entry, context, warden)))
119
+ return f_entry
107
120
  end
108
121
  end
109
122
  nil
@@ -120,7 +133,7 @@ module GraphQL
120
133
  # Choose the most local definition that passes `.visible?` --
121
134
  # stop checking for fields by name once one has been found.
122
135
  if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
123
- visible_fields[field_name] = f
136
+ visible_fields[field_name] = f.ensure_loaded
124
137
  end
125
138
  end
126
139
  end
@@ -134,13 +147,14 @@ module GraphQL
134
147
  # Objects need to check that the interface implementation is visible, too
135
148
  warden = Warden.from_context(context)
136
149
  ancs = ancestors
150
+ skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
137
151
  i = 0
138
152
  while (ancestor = ancs[i])
139
153
  if ancestor.respond_to?(:own_fields) &&
140
154
  visible_interface_implementation?(ancestor, context, warden) &&
141
155
  (f_entry = ancestor.own_fields[field_name]) &&
142
- (f = Warden.visible_entry?(:visible_field?, f_entry, context, warden))
143
- return f
156
+ (skip_visible || (f_entry = Warden.visible_entry?(:visible_field?, f_entry, context, warden)))
157
+ return (skip_visible ? f_entry : f_entry.ensure_loaded)
144
158
  end
145
159
  i += 1
146
160
  end
@@ -153,17 +167,22 @@ module GraphQL
153
167
  warden = Warden.from_context(context)
154
168
  # Local overrides take precedence over inherited fields
155
169
  visible_fields = {}
170
+ had_any_fields_at_all = false
156
171
  for ancestor in ancestors
157
172
  if ancestor.respond_to?(:own_fields) && visible_interface_implementation?(ancestor, context, warden)
158
173
  ancestor.own_fields.each do |field_name, fields_entry|
174
+ had_any_fields_at_all = true
159
175
  # Choose the most local definition that passes `.visible?` --
160
176
  # stop checking for fields by name once one has been found.
161
177
  if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
162
- visible_fields[field_name] = f
178
+ visible_fields[field_name] = f.ensure_loaded
163
179
  end
164
180
  end
165
181
  end
166
182
  end
183
+ if !had_any_fields_at_all && !has_no_fields?
184
+ warn(GraphQL::Schema::Object::FieldsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
185
+ end
167
186
  visible_fields
168
187
  end
169
188
  end
@@ -186,6 +205,7 @@ module GraphQL
186
205
  subclass.class_eval do
187
206
  @own_fields ||= nil
188
207
  @field_class ||= nil
208
+ @has_no_fields ||= false
189
209
  end
190
210
  end
191
211
 
@@ -24,7 +24,7 @@ module GraphQL
24
24
  implements(next_interface)
25
25
  end
26
26
  elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType)
27
- if options.any?
27
+ if !options.empty?
28
28
  raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature."
29
29
  end
30
30
  new_memberships << int
@@ -73,13 +73,13 @@ module GraphQL
73
73
  def interfaces(context = GraphQL::Query::NullContext.instance)
74
74
  visible_interfaces = super
75
75
  inherited_interfaces = superclass.interfaces(context)
76
- if visible_interfaces.any?
77
- if inherited_interfaces.any?
76
+ if !visible_interfaces.empty?
77
+ if !inherited_interfaces.empty?
78
78
  visible_interfaces.concat(inherited_interfaces)
79
79
  visible_interfaces.uniq!
80
80
  end
81
81
  visible_interfaces
82
- elsif inherited_interfaces.any?
82
+ elsif !inherited_interfaces.empty?
83
83
  inherited_interfaces
84
84
  else
85
85
  EmptyObjects::EMPTY_ARRAY
@@ -7,7 +7,11 @@ module GraphQL
7
7
  module HasUnresolvedTypeError
8
8
  private
9
9
  def add_unresolved_type_error(child_class)
10
- child_class.const_set(:UnresolvedTypeError, Class.new(GraphQL::UnresolvedTypeError))
10
+ if child_class.name # Don't set this for anonymous classes
11
+ child_class.const_set(:UnresolvedTypeError, Class.new(GraphQL::UnresolvedTypeError))
12
+ else
13
+ child_class.const_set(:UnresolvedTypeError, UnresolvedTypeError)
14
+ end
11
15
  end
12
16
  end
13
17
  end
@@ -32,7 +32,7 @@ module GraphQL
32
32
 
33
33
  def validators
34
34
  inherited_validators = superclass.validators
35
- if inherited_validators.any?
35
+ if !inherited_validators.empty?
36
36
  if @own_validators.nil?
37
37
  inherited_validators
38
38
  else
@@ -8,6 +8,14 @@ module GraphQL
8
8
  extend GraphQL::Schema::Member::HasFields
9
9
  extend GraphQL::Schema::Member::HasInterfaces
10
10
 
11
+ # Raised when an Object doesn't have any field defined and hasn't explicitly opted out of this requirement
12
+ class FieldsAreRequiredError < GraphQL::Error
13
+ def initialize(object_type)
14
+ message = "Object types must have fields, but #{object_type.graphql_name} doesn't have any. Define a field for this type, remove it from your schema, or add `has_no_fields(true)` to its definition."
15
+ super(message)
16
+ end
17
+ end
18
+
11
19
  # @return [Object] the application object this type is wrapping
12
20
  attr_reader :object
13
21
 
@@ -58,6 +58,7 @@ module GraphQL
58
58
  end
59
59
  end
60
60
  schema = Class.new(GraphQL::Schema) {
61
+ use GraphQL::Schema::Visibility
61
62
  query(query_root)
62
63
  def self.visible?(member, _ctx)
63
64
  member.graphql_name != "Root"
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require "graphql/types/string"
3
2
 
4
3
  module GraphQL
5
4
  class Schema