graphql 2.0.30 → 2.3.6

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 (157) 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 +3 -0
  6. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  7. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  8. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  9. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  10. data/lib/generators/graphql/templates/base_field.erb +2 -0
  11. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  12. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  13. data/lib/generators/graphql/templates/base_object.erb +2 -0
  14. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  15. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  16. data/lib/generators/graphql/templates/base_union.erb +2 -0
  17. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  18. data/lib/generators/graphql/templates/loader.erb +2 -0
  19. data/lib/generators/graphql/templates/mutation.erb +2 -0
  20. data/lib/generators/graphql/templates/node_type.erb +2 -0
  21. data/lib/generators/graphql/templates/query_type.erb +2 -0
  22. data/lib/generators/graphql/templates/schema.erb +5 -0
  23. data/lib/graphql/analysis/analyzer.rb +89 -0
  24. data/lib/graphql/analysis/field_usage.rb +82 -0
  25. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  26. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  27. data/lib/graphql/analysis/query_complexity.rb +183 -0
  28. data/lib/graphql/analysis/query_depth.rb +58 -0
  29. data/lib/graphql/analysis/visitor.rb +282 -0
  30. data/lib/graphql/analysis.rb +92 -1
  31. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  32. data/lib/graphql/backtrace/trace.rb +12 -15
  33. data/lib/graphql/coercion_error.rb +1 -9
  34. data/lib/graphql/dataloader/async_dataloader.rb +88 -0
  35. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  36. data/lib/graphql/dataloader/request.rb +5 -0
  37. data/lib/graphql/dataloader/source.rb +11 -3
  38. data/lib/graphql/dataloader.rb +112 -142
  39. data/lib/graphql/duration_encoding_error.rb +16 -0
  40. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  41. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  42. data/lib/graphql/execution/interpreter/runtime.rb +163 -365
  43. data/lib/graphql/execution/interpreter.rb +92 -158
  44. data/lib/graphql/execution/lookahead.rb +88 -21
  45. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  46. data/lib/graphql/introspection/entry_points.rb +11 -5
  47. data/lib/graphql/introspection/schema_type.rb +3 -1
  48. data/lib/graphql/language/block_string.rb +34 -18
  49. data/lib/graphql/language/definition_slice.rb +1 -1
  50. data/lib/graphql/language/document_from_schema_definition.rb +38 -38
  51. data/lib/graphql/language/lexer.rb +305 -193
  52. data/lib/graphql/language/nodes.rb +113 -66
  53. data/lib/graphql/language/parser.rb +787 -1986
  54. data/lib/graphql/language/printer.rb +303 -146
  55. data/lib/graphql/language/sanitized_printer.rb +20 -22
  56. data/lib/graphql/language/static_visitor.rb +167 -0
  57. data/lib/graphql/language/visitor.rb +20 -81
  58. data/lib/graphql/language.rb +61 -0
  59. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  60. data/lib/graphql/pagination/array_connection.rb +6 -6
  61. data/lib/graphql/pagination/connection.rb +28 -1
  62. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  63. data/lib/graphql/query/context/scoped_context.rb +101 -0
  64. data/lib/graphql/query/context.rb +66 -131
  65. data/lib/graphql/query/null_context.rb +4 -11
  66. data/lib/graphql/query/validation_pipeline.rb +4 -4
  67. data/lib/graphql/query/variables.rb +3 -3
  68. data/lib/graphql/query.rb +17 -26
  69. data/lib/graphql/railtie.rb +9 -6
  70. data/lib/graphql/rake_task.rb +3 -12
  71. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  72. data/lib/graphql/schema/addition.rb +21 -11
  73. data/lib/graphql/schema/argument.rb +43 -8
  74. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  75. data/lib/graphql/schema/build_from_definition.rb +9 -12
  76. data/lib/graphql/schema/directive/one_of.rb +12 -0
  77. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  78. data/lib/graphql/schema/directive.rb +3 -1
  79. data/lib/graphql/schema/enum.rb +3 -3
  80. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  81. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  82. data/lib/graphql/schema/field.rb +49 -35
  83. data/lib/graphql/schema/has_single_input_argument.rb +157 -0
  84. data/lib/graphql/schema/input_object.rb +4 -4
  85. data/lib/graphql/schema/interface.rb +10 -10
  86. data/lib/graphql/schema/introspection_system.rb +4 -2
  87. data/lib/graphql/schema/late_bound_type.rb +4 -0
  88. data/lib/graphql/schema/list.rb +2 -2
  89. data/lib/graphql/schema/loader.rb +2 -3
  90. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  91. data/lib/graphql/schema/member/has_arguments.rb +63 -73
  92. data/lib/graphql/schema/member/has_directives.rb +1 -1
  93. data/lib/graphql/schema/member/has_fields.rb +8 -5
  94. data/lib/graphql/schema/member/has_interfaces.rb +23 -9
  95. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  96. data/lib/graphql/schema/member/scoped.rb +19 -0
  97. data/lib/graphql/schema/member/type_system_helpers.rb +1 -2
  98. data/lib/graphql/schema/member/validates_input.rb +3 -3
  99. data/lib/graphql/schema/mutation.rb +7 -0
  100. data/lib/graphql/schema/object.rb +8 -0
  101. data/lib/graphql/schema/printer.rb +8 -7
  102. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  103. data/lib/graphql/schema/resolver.rb +27 -13
  104. data/lib/graphql/schema/scalar.rb +3 -3
  105. data/lib/graphql/schema/subscription.rb +11 -4
  106. data/lib/graphql/schema/union.rb +1 -1
  107. data/lib/graphql/schema/unique_within_type.rb +1 -1
  108. data/lib/graphql/schema/warden.rb +96 -95
  109. data/lib/graphql/schema.rb +323 -102
  110. data/lib/graphql/static_validation/all_rules.rb +1 -1
  111. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  112. data/lib/graphql/static_validation/literal_validator.rb +2 -3
  113. data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
  114. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  115. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +2 -2
  116. data/lib/graphql/static_validation/validation_context.rb +5 -5
  117. data/lib/graphql/static_validation/validator.rb +3 -0
  118. data/lib/graphql/static_validation.rb +0 -1
  119. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +4 -3
  120. data/lib/graphql/subscriptions/broadcast_analyzer.rb +1 -1
  121. data/lib/graphql/subscriptions/event.rb +8 -2
  122. data/lib/graphql/subscriptions/serialize.rb +2 -0
  123. data/lib/graphql/subscriptions.rb +15 -13
  124. data/lib/graphql/testing/helpers.rb +151 -0
  125. data/lib/graphql/testing.rb +2 -0
  126. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  127. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  128. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  129. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  130. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  131. data/lib/graphql/tracing/prometheus_trace.rb +9 -9
  132. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  133. data/lib/graphql/tracing/trace.rb +1 -0
  134. data/lib/graphql/tracing.rb +3 -1
  135. data/lib/graphql/type_kinds.rb +1 -1
  136. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  137. data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
  138. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  139. data/lib/graphql/types.rb +1 -0
  140. data/lib/graphql/version.rb +1 -1
  141. data/lib/graphql.rb +13 -13
  142. data/readme.md +12 -2
  143. metadata +33 -26
  144. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  145. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  146. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  147. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  148. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  149. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  150. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  151. data/lib/graphql/analysis/ast.rb +0 -81
  152. data/lib/graphql/deprecation.rb +0 -9
  153. data/lib/graphql/filter.rb +0 -59
  154. data/lib/graphql/language/parser.y +0 -560
  155. data/lib/graphql/schema/base_64_bp.rb +0 -26
  156. data/lib/graphql/static_validation/type_stack.rb +0 -216
  157. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -0,0 +1,157 @@
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.any?
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
+ mutation(mutation_class)
140
+ # these might be inherited:
141
+ mutation_args.each do |arg|
142
+ add_argument(arg)
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ private
149
+
150
+ def authorize_arguments(args, values)
151
+ # remove the `input` wrapper to match values
152
+ input_args = args["input"].type.unwrap.arguments(context)
153
+ super(input_args, values)
154
+ end
155
+ end
156
+ end
157
+ end
@@ -79,7 +79,7 @@ module GraphQL
79
79
  end
80
80
 
81
81
  def self.one_of?
82
- directives.any? { |d| d.is_a?(GraphQL::Schema::Directive::OneOf) }
82
+ false # Re-defined when `OneOf` is added
83
83
  end
84
84
 
85
85
  def unwrap_value(value)
@@ -136,6 +136,7 @@ module GraphQL
136
136
  def #{method_name}
137
137
  self[#{method_name.inspect}]
138
138
  end
139
+ alias_method :#{method_name}, :#{method_name}
139
140
  RUBY
140
141
  argument_defn
141
142
  end
@@ -145,7 +146,7 @@ module GraphQL
145
146
  end
146
147
 
147
148
  # @api private
148
- INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object responding to `to_h` or `to_unsafe_h`."
149
+ INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object."
149
150
 
150
151
  def validate_non_null_input(input, ctx, max_errors: nil)
151
152
  warden = ctx.warden
@@ -215,8 +216,7 @@ module GraphQL
215
216
  if resolved_arguments.is_a?(GraphQL::Error)
216
217
  raise resolved_arguments
217
218
  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
219
+ self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
220
220
  end
221
221
  end
222
222
  end
@@ -20,6 +20,15 @@ module GraphQL
20
20
  # - Added as class methods to this interface
21
21
  # - Added as class methods to all child interfaces
22
22
  def definition_methods(&block)
23
+ # Use an instance variable to tell whether it's been included previously or not;
24
+ # You can't use constant detection because constants are brought into scope
25
+ # by `include`, which has already happened at this point.
26
+ if !defined?(@_definition_methods)
27
+ defn_methods_module = Module.new
28
+ @_definition_methods = defn_methods_module
29
+ const_set(:DefinitionMethods, defn_methods_module)
30
+ extend(self::DefinitionMethods)
31
+ end
23
32
  self::DefinitionMethods.module_eval(&block)
24
33
  end
25
34
 
@@ -47,20 +56,11 @@ module GraphQL
47
56
 
48
57
  child_class.type_membership_class(self.type_membership_class)
49
58
  child_class.ancestors.reverse_each do |ancestor|
50
- if ancestor.const_defined?(:DefinitionMethods)
59
+ if ancestor.const_defined?(:DefinitionMethods) && ancestor != child_class
51
60
  child_class.extend(ancestor::DefinitionMethods)
52
61
  end
53
62
  end
54
63
 
55
- # Use an instance variable to tell whether it's been included previously or not;
56
- # You can't use constant detection because constants are brought into scope
57
- # by `include`, which has already happened at this point.
58
- if !child_class.instance_variable_defined?(:@_definition_methods)
59
- defn_methods_module = Module.new
60
- child_class.instance_variable_set(:@_definition_methods, defn_methods_module)
61
- child_class.const_set(:DefinitionMethods, defn_methods_module)
62
- child_class.extend(child_class::DefinitionMethods)
63
- end
64
64
  child_class.introspection(introspection)
65
65
  child_class.description(description)
66
66
  # If interfaces are mixed into each other, only define this class once
@@ -25,10 +25,10 @@ module GraphQL
25
25
  load_constant(:DirectiveLocationEnum)
26
26
  ]
27
27
  @types = {}
28
- @possible_types = {}
28
+ @possible_types = {}.tap(&: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?
@@ -39,7 +39,9 @@ module GraphQL
39
39
  entry_point_fields.delete('__type') if schema.disable_type_introspection_entry_point?
40
40
  entry_point_fields
41
41
  end
42
+ @entry_point_fields.each { |k, v| v.dynamic_introspection = true }
42
43
  @dynamic_fields = get_fields_from_class(class_sym: :DynamicFields)
44
+ @dynamic_fields.each { |k, v| v.dynamic_introspection = true }
43
45
  end
44
46
 
45
47
  def entry_points
@@ -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
@@ -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
 
@@ -176,7 +177,6 @@ module GraphQL
176
177
  while (of_type = unwrapped_field_hash["ofType"])
177
178
  unwrapped_field_hash = of_type
178
179
  end
179
- type_name = unwrapped_field_hash["name"]
180
180
 
181
181
  type_defn.field(
182
182
  field_hash["name"],
@@ -186,7 +186,6 @@ module GraphQL
186
186
  null: true,
187
187
  camelize: false,
188
188
  connection_extension: nil,
189
- connection: type_name.end_with?("Connection"),
190
189
  ) do
191
190
  if field_hash["args"].any?
192
191
  loader.build_arguments(self, field_hash["args"], type_resolver)
@@ -102,7 +102,8 @@ module GraphQL
102
102
  def default_graphql_name
103
103
  @default_graphql_name ||= begin
104
104
  raise GraphQL::RequiredImplementationMissingError, 'Anonymous class should declare a `graphql_name`' if name.nil?
105
- -name.split("::").last.sub(/Type\Z/, "")
105
+ g_name = -name.split("::").last
106
+ g_name.end_with?("Type") ? g_name.sub(/Type\Z/, "") : g_name
106
107
  end
107
108
  end
108
109
 
@@ -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}")
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}")
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,7 +76,7 @@ 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)
79
+ def arguments(context = GraphQL::Query::NullContext.instance)
113
80
  if own_arguments.any?
114
81
  own_arguments_that_apply = {}
115
82
  own_arguments.each do |name, args_entry|
@@ -122,6 +89,10 @@ module GraphQL
122
89
  own_arguments_that_apply || own_arguments
123
90
  end
124
91
 
92
+ def any_arguments?
93
+ own_arguments.any?
94
+ end
95
+
125
96
  module ClassConfigured
126
97
  def inherited(child_class)
127
98
  super
@@ -129,7 +100,7 @@ module GraphQL
129
100
  end
130
101
 
131
102
  module InheritedArguments
132
- def arguments(context = GraphQL::Query::NullContext)
103
+ def arguments(context = GraphQL::Query::NullContext.instance)
133
104
  own_arguments = super
134
105
  inherited_arguments = superclass.arguments(context)
135
106
 
@@ -145,6 +116,10 @@ module GraphQL
145
116
  end
146
117
  end
147
118
 
119
+ def any_arguments?
120
+ super || superclass.any_arguments?
121
+ end
122
+
148
123
  def all_argument_definitions
149
124
  all_defns = {}
150
125
  ancestors.reverse_each do |ancestor|
@@ -158,7 +133,7 @@ module GraphQL
158
133
  end
159
134
 
160
135
 
161
- def get_argument(argument_name, context = GraphQL::Query::NullContext)
136
+ def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
162
137
  warden = Warden.from_context(context)
163
138
  for ancestor in ancestors
164
139
  if ancestor.respond_to?(:own_arguments) &&
@@ -173,9 +148,9 @@ module GraphQL
173
148
  end
174
149
 
175
150
  module FieldConfigured
176
- def arguments(context = GraphQL::Query::NullContext)
151
+ def arguments(context = GraphQL::Query::NullContext.instance)
177
152
  own_arguments = super
178
- if defined?(@resolver_class) && @resolver_class
153
+ if @resolver_class
179
154
  inherited_arguments = @resolver_class.field_arguments(context)
180
155
  if own_arguments.any?
181
156
  if inherited_arguments.any?
@@ -191,8 +166,12 @@ module GraphQL
191
166
  end
192
167
  end
193
168
 
169
+ def any_arguments?
170
+ super || (@resolver_class && @resolver_class.any_field_arguments?)
171
+ end
172
+
194
173
  def all_argument_definitions
195
- if defined?(@resolver_class) && @resolver_class
174
+ if @resolver_class
196
175
  all_defns = {}
197
176
  @resolver_class.all_field_argument_definitions.each do |arg_defn|
198
177
  key = arg_defn.graphql_name
@@ -224,7 +203,7 @@ module GraphQL
224
203
  end
225
204
 
226
205
  # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
227
- def get_argument(argument_name, context = GraphQL::Query::NullContext)
206
+ def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
228
207
  warden = Warden.from_context(context)
229
208
  if (arg_config = own_arguments[argument_name]) && (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden))
230
209
  visible_arg
@@ -247,8 +226,8 @@ module GraphQL
247
226
  #
248
227
  # @param values [Hash<String, Object>]
249
228
  # @param context [GraphQL::Query::Context]
250
- # @yield [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
251
- # @return [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
229
+ # @yield [Interpreter::Arguments, Execution::Lazy<Interpreter::Arguments>]
230
+ # @return [Interpreter::Arguments, Execution::Lazy<Interpreter::Arguments>]
252
231
  def coerce_arguments(parent_object, values, context, &block)
253
232
  # Cache this hash to avoid re-merging it
254
233
  arg_defns = context.warden.arguments(self)
@@ -367,41 +346,52 @@ module GraphQL
367
346
  def authorize_application_object(argument, id, context, loaded_application_object)
368
347
  context.query.after_lazy(loaded_application_object) do |application_object|
369
348
  if application_object.nil?
370
- err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
371
- load_application_object_failed(err)
349
+ err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
350
+ application_object = load_application_object_failed(err)
372
351
  end
373
352
  # Double-check that the located object is actually of this type
374
353
  # (Don't want to allow arbitrary access to objects this way)
375
- maybe_lazy_resolve_type = context.schema.resolve_type(argument.loads, application_object, context)
376
- context.query.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result|
377
- if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
378
- application_object_type, application_object = resolve_type_result
379
- else
380
- application_object_type = resolve_type_result
381
- # application_object is already assigned
382
- end
383
- possible_object_types = context.warden.possible_types(argument.loads)
384
- if !possible_object_types.include?(application_object_type)
385
- err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
386
- load_application_object_failed(err)
387
- else
388
- # This object was loaded successfully
389
- # and resolved to the right type,
390
- # now apply the `.authorized?` class method if there is one
391
- context.query.after_lazy(application_object_type.authorized?(application_object, context)) do |authed|
392
- if authed
393
- application_object
394
- else
395
- err = GraphQL::UnauthorizedError.new(
396
- object: application_object,
397
- type: application_object_type,
398
- context: context,
399
- )
400
- if self.respond_to?(:unauthorized_object)
401
- err.set_backtrace(caller)
402
- unauthorized_object(err)
354
+ if application_object.nil?
355
+ nil
356
+ else
357
+ maybe_lazy_resolve_type = context.schema.resolve_type(argument.loads, application_object, context)
358
+ context.query.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result|
359
+ if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
360
+ application_object_type, application_object = resolve_type_result
361
+ else
362
+ application_object_type = resolve_type_result
363
+ # application_object is already assigned
364
+ end
365
+
366
+ if !(
367
+ context.warden.possible_types(argument.loads).include?(application_object_type) ||
368
+ context.warden.loadable?(argument.loads, context)
369
+ )
370
+ err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
371
+ application_object = load_application_object_failed(err)
372
+ end
373
+
374
+ if application_object.nil?
375
+ nil
376
+ else
377
+ # This object was loaded successfully
378
+ # and resolved to the right type,
379
+ # now apply the `.authorized?` class method if there is one
380
+ context.query.after_lazy(application_object_type.authorized?(application_object, context)) do |authed|
381
+ if authed
382
+ application_object
403
383
  else
404
- raise err
384
+ err = GraphQL::UnauthorizedError.new(
385
+ object: application_object,
386
+ type: application_object_type,
387
+ context: context,
388
+ )
389
+ if self.respond_to?(:unauthorized_object)
390
+ err.set_backtrace(caller)
391
+ unauthorized_object(err)
392
+ else
393
+ raise err
394
+ end
405
395
  end
406
396
  end
407
397
  end
@@ -91,7 +91,7 @@ module GraphQL
91
91
  private
92
92
 
93
93
  # Modify `target` by adding items from `dirs` such that:
94
- # - Any name conflict is overriden by the incoming member of `dirs`
94
+ # - Any name conflict is overridden by the incoming member of `dirs`
95
95
  # - Any other member of `dirs` is appended
96
96
  # @param target [Array<GraphQL::Schema::Directive>]
97
97
  # @param dirs [Array<GraphQL::Schema::Directive>]
@@ -97,7 +97,7 @@ module GraphQL
97
97
  end
98
98
 
99
99
  module InterfaceMethods
100
- def get_field(field_name, context = GraphQL::Query::NullContext)
100
+ def get_field(field_name, context = GraphQL::Query::NullContext.instance)
101
101
  warden = Warden.from_context(context)
102
102
  for ancestor in ancestors
103
103
  if ancestor.respond_to?(:own_fields) &&
@@ -110,7 +110,7 @@ module GraphQL
110
110
  end
111
111
 
112
112
  # @return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields
113
- def fields(context = GraphQL::Query::NullContext)
113
+ def fields(context = GraphQL::Query::NullContext.instance)
114
114
  warden = Warden.from_context(context)
115
115
  # Local overrides take precedence over inherited fields
116
116
  visible_fields = {}
@@ -130,22 +130,25 @@ module GraphQL
130
130
  end
131
131
 
132
132
  module ObjectMethods
133
- def get_field(field_name, context = GraphQL::Query::NullContext)
133
+ def get_field(field_name, context = GraphQL::Query::NullContext.instance)
134
134
  # Objects need to check that the interface implementation is visible, too
135
135
  warden = Warden.from_context(context)
136
- for ancestor in ancestors
136
+ ancs = ancestors
137
+ i = 0
138
+ while (ancestor = ancs[i])
137
139
  if ancestor.respond_to?(:own_fields) &&
138
140
  visible_interface_implementation?(ancestor, context, warden) &&
139
141
  (f_entry = ancestor.own_fields[field_name]) &&
140
142
  (f = Warden.visible_entry?(:visible_field?, f_entry, context, warden))
141
143
  return f
142
144
  end
145
+ i += 1
143
146
  end
144
147
  nil
145
148
  end
146
149
 
147
150
  # @return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields
148
- def fields(context = GraphQL::Query::NullContext)
151
+ def fields(context = GraphQL::Query::NullContext.instance)
149
152
  # Objects need to check that the interface implementation is visible, too
150
153
  warden = Warden.from_context(context)
151
154
  # Local overrides take precedence over inherited fields
@@ -70,11 +70,20 @@ module GraphQL
70
70
  end
71
71
 
72
72
  module InheritedInterfaces
73
- def interfaces(context = GraphQL::Query::NullContext)
73
+ def interfaces(context = GraphQL::Query::NullContext.instance)
74
74
  visible_interfaces = super
75
- visible_interfaces.concat(superclass.interfaces(context))
76
- visible_interfaces.uniq!
77
- visible_interfaces
75
+ inherited_interfaces = superclass.interfaces(context)
76
+ if visible_interfaces.any?
77
+ if inherited_interfaces.any?
78
+ visible_interfaces.concat(inherited_interfaces)
79
+ visible_interfaces.uniq!
80
+ end
81
+ visible_interfaces
82
+ elsif inherited_interfaces.any?
83
+ inherited_interfaces
84
+ else
85
+ EmptyObjects::EMPTY_ARRAY
86
+ end
78
87
  end
79
88
 
80
89
  def interface_type_memberships
@@ -90,25 +99,30 @@ module GraphQL
90
99
  end
91
100
 
92
101
  # param context [Query::Context] If omitted, skip filtering.
93
- def interfaces(context = GraphQL::Query::NullContext)
102
+ def interfaces(context = GraphQL::Query::NullContext.instance)
94
103
  warden = Warden.from_context(context)
95
- visible_interfaces = []
104
+ visible_interfaces = nil
96
105
  own_interface_type_memberships.each do |type_membership|
97
106
  case type_membership
98
107
  when Schema::TypeMembership
99
108
  if warden.visible_type_membership?(type_membership, context)
109
+ visible_interfaces ||= []
100
110
  visible_interfaces << type_membership.abstract_type
101
111
  end
102
112
  when String, Schema::LateBoundType
103
113
  # During initialization, `type_memberships` can hold late-bound types
114
+ visible_interfaces ||= []
104
115
  visible_interfaces << type_membership
105
116
  else
106
117
  raise "Invariant: Unexpected type_membership #{type_membership.class}: #{type_membership.inspect}"
107
118
  end
108
119
  end
109
- visible_interfaces.uniq!
110
-
111
- visible_interfaces
120
+ if visible_interfaces
121
+ visible_interfaces.uniq!
122
+ visible_interfaces
123
+ else
124
+ EmptyObjects::EMPTY_ARRAY
125
+ end
112
126
  end
113
127
 
114
128
  private
@@ -76,7 +76,7 @@ module GraphQL
76
76
 
77
77
  private
78
78
 
79
- # If one of thse values is accessed, initialize all the instance variables to retain
79
+ # If one of these values is accessed, initialize all the instance variables to retain
80
80
  # a consistent object shape.
81
81
  def initialize_relay_metadata
82
82
  if !defined?(@connection_type)