graphql 1.11.1 → 1.11.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.

Potentially problematic release.


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

Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +8 -0
  3. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  4. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  5. data/lib/generators/graphql/templates/base_field.erb +2 -0
  6. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  7. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  8. data/lib/generators/graphql/templates/base_mutation.erb +2 -0
  9. data/lib/generators/graphql/templates/base_object.erb +2 -0
  10. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  11. data/lib/generators/graphql/templates/base_union.erb +2 -0
  12. data/lib/generators/graphql/templates/enum.erb +2 -0
  13. data/lib/generators/graphql/templates/graphql_controller.erb +13 -9
  14. data/lib/generators/graphql/templates/interface.erb +2 -0
  15. data/lib/generators/graphql/templates/loader.erb +2 -0
  16. data/lib/generators/graphql/templates/mutation.erb +2 -0
  17. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  18. data/lib/generators/graphql/templates/object.erb +2 -0
  19. data/lib/generators/graphql/templates/query_type.erb +2 -0
  20. data/lib/generators/graphql/templates/scalar.erb +2 -0
  21. data/lib/generators/graphql/templates/schema.erb +2 -0
  22. data/lib/generators/graphql/templates/union.erb +3 -1
  23. data/lib/graphql.rb +16 -0
  24. data/lib/graphql/argument.rb +3 -3
  25. data/lib/graphql/backtrace/tracer.rb +2 -1
  26. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  27. data/lib/graphql/directive.rb +4 -0
  28. data/lib/graphql/execution/interpreter.rb +10 -0
  29. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  30. data/lib/graphql/execution/interpreter/runtime.rb +59 -45
  31. data/lib/graphql/field.rb +4 -0
  32. data/lib/graphql/input_object_type.rb +4 -0
  33. data/lib/graphql/introspection.rb +96 -0
  34. data/lib/graphql/introspection/field_type.rb +7 -3
  35. data/lib/graphql/introspection/input_value_type.rb +6 -0
  36. data/lib/graphql/introspection/introspection_query.rb +6 -92
  37. data/lib/graphql/introspection/type_type.rb +7 -3
  38. data/lib/graphql/language/block_string.rb +24 -5
  39. data/lib/graphql/language/lexer.rb +7 -3
  40. data/lib/graphql/language/lexer.rl +7 -3
  41. data/lib/graphql/language/nodes.rb +2 -1
  42. data/lib/graphql/language/parser.rb +107 -103
  43. data/lib/graphql/language/parser.y +4 -0
  44. data/lib/graphql/language/sanitized_printer.rb +59 -26
  45. data/lib/graphql/language/visitor.rb +2 -2
  46. data/lib/graphql/name_validator.rb +6 -7
  47. data/lib/graphql/pagination/connection.rb +6 -8
  48. data/lib/graphql/pagination/connections.rb +23 -3
  49. data/lib/graphql/query.rb +2 -2
  50. data/lib/graphql/query/context.rb +30 -3
  51. data/lib/graphql/query/fingerprint.rb +2 -0
  52. data/lib/graphql/query/validation_pipeline.rb +3 -0
  53. data/lib/graphql/relay/range_add.rb +14 -5
  54. data/lib/graphql/schema.rb +40 -31
  55. data/lib/graphql/schema/argument.rb +56 -5
  56. data/lib/graphql/schema/build_from_definition.rb +67 -38
  57. data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
  58. data/lib/graphql/schema/directive/deprecated.rb +1 -1
  59. data/lib/graphql/schema/enum_value.rb +1 -0
  60. data/lib/graphql/schema/field.rb +17 -10
  61. data/lib/graphql/schema/field/connection_extension.rb +44 -34
  62. data/lib/graphql/schema/input_object.rb +21 -18
  63. data/lib/graphql/schema/interface.rb +1 -1
  64. data/lib/graphql/schema/late_bound_type.rb +2 -2
  65. data/lib/graphql/schema/loader.rb +20 -1
  66. data/lib/graphql/schema/member/build_type.rb +14 -4
  67. data/lib/graphql/schema/member/has_arguments.rb +19 -1
  68. data/lib/graphql/schema/member/has_fields.rb +17 -7
  69. data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
  70. data/lib/graphql/schema/mutation.rb +4 -0
  71. data/lib/graphql/schema/relay_classic_mutation.rb +3 -1
  72. data/lib/graphql/schema/resolver.rb +6 -0
  73. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -1
  74. data/lib/graphql/schema/subscription.rb +2 -12
  75. data/lib/graphql/schema/timeout.rb +29 -15
  76. data/lib/graphql/schema/union.rb +29 -0
  77. data/lib/graphql/schema/unique_within_type.rb +1 -2
  78. data/lib/graphql/schema/validation.rb +8 -0
  79. data/lib/graphql/schema/warden.rb +8 -3
  80. data/lib/graphql/static_validation/literal_validator.rb +7 -7
  81. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  82. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -2
  83. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -2
  84. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +4 -2
  85. data/lib/graphql/static_validation/validator.rb +7 -4
  86. data/lib/graphql/subscriptions.rb +32 -22
  87. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +45 -20
  88. data/lib/graphql/subscriptions/serialize.rb +22 -4
  89. data/lib/graphql/tracing/appoptics_tracing.rb +10 -2
  90. data/lib/graphql/types/iso_8601_date_time.rb +2 -1
  91. data/lib/graphql/types/relay/base_connection.rb +6 -5
  92. data/lib/graphql/unauthorized_error.rb +1 -1
  93. data/lib/graphql/version.rb +1 -1
  94. metadata +3 -3
@@ -466,6 +466,10 @@ def self.parse(query_string, filename: nil, tracer: GraphQL::Tracing::NullTracer
466
466
  self.new(query_string, filename: filename, tracer: tracer).parse_document
467
467
  end
468
468
 
469
+ def self.parse_file(filename, tracer: GraphQL::Tracing::NullTracer)
470
+ self.parse(File.read(filename), filename: filename, tracer: tracer)
471
+ end
472
+
469
473
  private
470
474
 
471
475
  def next_token
@@ -19,11 +19,12 @@ module GraphQL
19
19
 
20
20
  REDACTED = "\"<REDACTED>\""
21
21
 
22
- def initialize(query)
22
+ def initialize(query, inline_variables: true)
23
23
  @query = query
24
24
  @current_type = nil
25
25
  @current_field = nil
26
26
  @current_input_type = nil
27
+ @inline_variables = inline_variables
27
28
  end
28
29
 
29
30
  # @return [String, nil] A scrubbed query string, if the query was valid.
@@ -36,15 +37,14 @@ module GraphQL
36
37
  end
37
38
 
38
39
  def print_node(node, indent: "")
39
- if node.is_a?(String)
40
- type = @current_input_type.unwrap
41
- # Replace any strings that aren't IDs or Enum values with REDACTED
42
- if type.kind.enum? || type.graphql_name == "ID"
43
- super
40
+ case node
41
+ when FalseClass, Float, Integer, String, TrueClass
42
+ if @current_argument && redact_argument_value?(@current_argument, node)
43
+ redacted_argument_value(@current_argument)
44
44
  else
45
- REDACTED
45
+ super
46
46
  end
47
- elsif node.is_a?(Array)
47
+ when Array
48
48
  old_input_type = @current_input_type
49
49
  if @current_input_type && @current_input_type.list?
50
50
  @current_input_type = @current_input_type.of_type
@@ -59,28 +59,57 @@ module GraphQL
59
59
  end
60
60
  end
61
61
 
62
+ # Indicates whether or not to redact non-null values for the given argument. Defaults to redacting all strings
63
+ # arguments but this can be customized by subclasses.
64
+ def redact_argument_value?(argument, value)
65
+ # Default to redacting any strings or custom scalars encoded as strings
66
+ type = argument.type.unwrap
67
+ value.is_a?(String) && type.kind.scalar? && (type.graphql_name == "String" || !type.default_scalar?)
68
+ end
69
+
70
+ # Returns the value to use for redacted versions of the given argument. Defaults to the
71
+ # string "<REDACTED>".
72
+ def redacted_argument_value(argument)
73
+ REDACTED
74
+ end
75
+
62
76
  def print_argument(argument)
77
+ # We won't have type information if we're recursing into a custom scalar
78
+ return super if @current_input_type && @current_input_type.kind.scalar?
79
+
63
80
  arg_owner = @current_input_type || @current_directive || @current_field
64
- arg_def = arg_owner.arguments[argument.name]
81
+ old_current_argument = @current_argument
82
+ @current_argument = arg_owner.arguments[argument.name]
65
83
 
66
84
  old_input_type = @current_input_type
67
- @current_input_type = arg_def.type.non_null? ? arg_def.type.of_type : arg_def.type
68
- res = super
85
+ @current_input_type = @current_argument.type.non_null? ? @current_argument.type.of_type : @current_argument.type
86
+
87
+ argument_value = if coerce_argument_value_to_list?(@current_input_type, argument.value)
88
+ [argument.value]
89
+ else
90
+ argument.value
91
+ end
92
+ res = "#{argument.name}: #{print_node(argument_value)}".dup
93
+
69
94
  @current_input_type = old_input_type
95
+ @current_argument = old_current_argument
70
96
  res
71
97
  end
72
98
 
73
- def print_list_type(list_type)
74
- old_input_type = @current_input_type
75
- @current_input_type = old_input_type.of_type
76
- res = super
77
- @current_input_type = old_input_type
78
- res
99
+ def coerce_argument_value_to_list?(type, value)
100
+ type.list? &&
101
+ !value.is_a?(Array) &&
102
+ !value.nil? &&
103
+ !value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
79
104
  end
80
105
 
81
106
  def print_variable_identifier(variable_id)
82
- variable_value = query.variables[variable_id.name]
83
- print_node(value_to_ast(variable_value, @current_input_type))
107
+ if @inline_variables
108
+ variable_value = query.variables[variable_id.name]
109
+ print_node(value_to_ast(variable_value, @current_input_type))
110
+ else
111
+ super
112
+ end
84
113
  end
85
114
 
86
115
  def print_field(field, indent: "")
@@ -132,10 +161,14 @@ module GraphQL
132
161
  old_type = @current_type
133
162
  @current_type = query.schema.public_send(operation_definition.operation_type)
134
163
 
135
- out = "#{indent}#{operation_definition.operation_type}".dup
136
- out << " #{operation_definition.name}" if operation_definition.name
137
- out << print_directives(operation_definition.directives)
138
- out << print_selections(operation_definition.selections, indent: indent)
164
+ if @inline_variables
165
+ out = "#{indent}#{operation_definition.operation_type}".dup
166
+ out << " #{operation_definition.name}" if operation_definition.name
167
+ out << print_directives(operation_definition.directives)
168
+ out << print_selections(operation_definition.selections, indent: indent)
169
+ else
170
+ out = super
171
+ end
139
172
 
140
173
  @current_type = old_type
141
174
  out
@@ -171,10 +204,10 @@ module GraphQL
171
204
  arguments: arguments
172
205
  )
173
206
  when "LIST"
174
- if value.respond_to?(:each)
175
- value.each { |v| value_to_ast(v, type.of_type) }
207
+ if value.is_a?(Array)
208
+ value.map { |v| value_to_ast(v, type.of_type) }
176
209
  else
177
- [value].each { |v| value_to_ast(v, type.of_type) }
210
+ [value].map { |v| value_to_ast(v, type.of_type) }
178
211
  end
179
212
  when "ENUM"
180
213
  GraphQL::Language::Nodes::Enum.new(name: value)
@@ -89,7 +89,7 @@ module GraphQL
89
89
  # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
90
90
  # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
91
91
  def on_abstract_node(node, parent)
92
- if node == DELETE_NODE
92
+ if node.equal?(DELETE_NODE)
93
93
  # This might be passed to `super(DELETE_NODE, ...)`
94
94
  # by a user hook, don't want to keep visiting in that case.
95
95
  nil
@@ -179,7 +179,7 @@ module GraphQL
179
179
  # The user-provided hook returned a new node.
180
180
  new_parent = new_parent && new_parent.replace_child(node, new_node)
181
181
  return new_node, new_parent
182
- elsif new_node == DELETE_NODE
182
+ elsif new_node.equal?(DELETE_NODE)
183
183
  # The user-provided hook requested to remove this node
184
184
  new_parent = new_parent && new_parent.delete_child(node)
185
185
  return nil, new_parent
@@ -1,16 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  class NameValidator
4
- VALID_NAME_REGEX = /^[_a-zA-Z][_a-zA-Z0-9]*$/
5
-
6
- def self.validate!(name)
7
- raise GraphQL::InvalidNameError.new(name, VALID_NAME_REGEX) unless valid?(name)
4
+ if !String.method_defined?(:match?)
5
+ using GraphQL::StringMatchBackport
8
6
  end
9
7
 
10
- private
8
+ VALID_NAME_REGEX = /^[_a-zA-Z][_a-zA-Z0-9]*$/
11
9
 
12
- def self.valid?(name)
13
- name =~ VALID_NAME_REGEX
10
+ def self.validate!(name)
11
+ name = name.is_a?(String) ? name : name.to_s
12
+ raise GraphQL::InvalidNameError.new(name, VALID_NAME_REGEX) unless name.match?(VALID_NAME_REGEX)
14
13
  end
15
14
  end
16
15
  end
@@ -15,11 +15,6 @@ module GraphQL
15
15
  class PaginationImplementationMissingError < GraphQL::Error
16
16
  end
17
17
 
18
- # @return [Class] The class to use for wrapping items as `edges { ... }`. Defaults to `Connection::Edge`
19
- def self.edge_class
20
- self::Edge
21
- end
22
-
23
18
  # @return [Object] A list object, from the application. This is the unpaginated value passed into the connection.
24
19
  attr_reader :items
25
20
 
@@ -58,7 +53,7 @@ module GraphQL
58
53
  # @param last [Integer, nil] Limit parameter from the client, if provided
59
54
  # @param before [String, nil] A cursor for pagination, if the client provided one.
60
55
  # @param max_page_size [Integer, nil] A configured value to cap the result size. Applied as `first` if neither first or last are given.
61
- def initialize(items, parent: nil, context: nil, first: nil, after: nil, max_page_size: :not_given, last: nil, before: nil)
56
+ def initialize(items, parent: nil, context: nil, first: nil, after: nil, max_page_size: :not_given, last: nil, before: nil, edge_class: nil)
62
57
  @items = items
63
58
  @parent = parent
64
59
  @context = context
@@ -66,7 +61,7 @@ module GraphQL
66
61
  @after_value = after
67
62
  @last_value = last
68
63
  @before_value = before
69
-
64
+ @edge_class = edge_class || self.class::Edge
70
65
  # This is only true if the object was _initialized_ with an override
71
66
  # or if one is assigned later.
72
67
  @has_max_page_size_override = max_page_size != :not_given
@@ -117,9 +112,12 @@ module GraphQL
117
112
 
118
113
  # @return [Array<Edge>] {nodes}, but wrapped with Edge instances
119
114
  def edges
120
- @edges ||= nodes.map { |n| self.class.edge_class.new(n, self) }
115
+ @edges ||= nodes.map { |n| @edge_class.new(n, self) }
121
116
  end
122
117
 
118
+ # @return [Class] A wrapper class for edges of this connection
119
+ attr_accessor :edge_class
120
+
123
121
  # @return [Array<Object>] A slice of {items}, constrained by {@first_value}/{@after_value}/{@last_value}/{@before_value}
124
122
  def nodes
125
123
  raise PaginationImplementationMissingError, "Implement #{self.class}#nodes to paginate `@items`"
@@ -63,9 +63,7 @@ module GraphQL
63
63
  all_wrappers
64
64
  end
65
65
 
66
- # Used by the runtime to wrap values in connection wrappers.
67
- # @api Private
68
- def wrap(field, parent, items, arguments, context, wrappers: all_wrappers)
66
+ def wrapper_for(items, wrappers: all_wrappers)
69
67
  impl = nil
70
68
 
71
69
  items.class.ancestors.each { |cls|
@@ -73,6 +71,16 @@ module GraphQL
73
71
  break if impl
74
72
  }
75
73
 
74
+ impl
75
+ end
76
+
77
+ # Used by the runtime to wrap values in connection wrappers.
78
+ # @api Private
79
+ def wrap(field, parent, items, arguments, context, wrappers: all_wrappers)
80
+ return items if GraphQL::Execution::Interpreter::RawValue === items
81
+
82
+ impl = wrapper_for(items, wrappers: wrappers)
83
+
76
84
  if impl.nil?
77
85
  raise ImplementationMissingError, "Couldn't find a connection wrapper for #{items.class} during #{field.path} (#{items.inspect})"
78
86
  end
@@ -86,9 +94,21 @@ module GraphQL
86
94
  after: arguments[:after],
87
95
  last: arguments[:last],
88
96
  before: arguments[:before],
97
+ edge_class: edge_class_for_field(field),
89
98
  )
90
99
  end
91
100
 
101
+ # use an override if there is one
102
+ # @api private
103
+ def edge_class_for_field(field)
104
+ conn_type = field.type.unwrap
105
+ conn_type_edge_type = conn_type.respond_to?(:edge_class) && conn_type.edge_class
106
+ if conn_type_edge_type && conn_type_edge_type != Relay::Edge
107
+ conn_type_edge_type
108
+ else
109
+ nil
110
+ end
111
+ end
92
112
  protected
93
113
 
94
114
  attr_reader :wrappers
@@ -259,9 +259,9 @@ module GraphQL
259
259
  # - Variables inlined to the query
260
260
  # - Strings replaced with `<REDACTED>`
261
261
  # @return [String, nil] Returns nil if the query is invalid.
262
- def sanitized_query_string
262
+ def sanitized_query_string(inline_variables: true)
263
263
  with_prepared_ast {
264
- GraphQL::Language::SanitizedPrinter.new(self).sanitized_query_string
264
+ GraphQL::Language::SanitizedPrinter.new(self, inline_variables: inline_variables).sanitized_query_string
265
265
  }
266
266
  end
267
267
 
@@ -34,7 +34,7 @@ module GraphQL
34
34
  # Remove this child from the result value
35
35
  # (used for null propagation and skip)
36
36
  # @api private
37
- def delete(child_ctx)
37
+ def delete_child(child_ctx)
38
38
  @value.delete(child_ctx.key)
39
39
  end
40
40
 
@@ -168,7 +168,6 @@ module GraphQL
168
168
  attr_accessor :scoped_context
169
169
 
170
170
  def_delegators :@provided_values, :[]=
171
- def_delegators :to_h, :fetch, :dig
172
171
  def_delegators :@query, :trace, :interpreter?
173
172
 
174
173
  # @!method []=(key, value)
@@ -180,6 +179,34 @@ module GraphQL
180
179
  @provided_values[key]
181
180
  end
182
181
 
182
+ def delete(key)
183
+ if @scoped_context.key?(key)
184
+ @scoped_context.delete(key)
185
+ else
186
+ @provided_values.delete(key)
187
+ end
188
+ end
189
+
190
+ UNSPECIFIED_FETCH_DEFAULT = Object.new
191
+
192
+ def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
193
+ if @scoped_context.key?(key)
194
+ @scoped_context[key]
195
+ elsif @provided_values.key?(key)
196
+ @provided_values[key]
197
+ elsif default != UNSPECIFIED_FETCH_DEFAULT
198
+ default
199
+ elsif block_given?
200
+ yield(self, key)
201
+ else
202
+ raise KeyError.new(key: key)
203
+ end
204
+ end
205
+
206
+ def dig(key, *other_keys)
207
+ @scoped_context.key?(key) ? @scoped_context.dig(key, *other_keys) : @provided_values.dig(key, *other_keys)
208
+ end
209
+
183
210
  def to_h
184
211
  @provided_values.merge(@scoped_context)
185
212
  end
@@ -293,7 +320,7 @@ module GraphQL
293
320
  end
294
321
  when GraphQL::Execution::Execute::SKIP
295
322
  @parent.skipped = true
296
- @parent.delete(self)
323
+ @parent.delete_child(self)
297
324
  else
298
325
  @value = new_value
299
326
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'digest/sha2'
4
+
3
5
  module GraphQL
4
6
  class Query
5
7
  # @api private
@@ -90,6 +90,9 @@ module GraphQL
90
90
  end
91
91
 
92
92
  @valid = @validation_errors.empty?
93
+ rescue SystemStackError => err
94
+ @valid = false
95
+ @schema.query_stack_error(@query, err)
93
96
  end
94
97
 
95
98
  # If there are max_* values, add them,
@@ -33,12 +33,21 @@ module GraphQL
33
33
  # @param item [Object] The newly-added item (will be wrapped in `edge_class`)
34
34
  # @param parent [Object] The owner of `collection`, will be passed to the connection if provided
35
35
  # @param context [GraphQL::Query::Context] The surrounding `ctx`, will be passed to the connection if provided (this is required for cursor encoders)
36
- # @param edge_class [Class] The class to wrap `item` with
37
- def initialize(collection:, item:, parent: nil, context: nil, edge_class: Relay::Edge)
38
- connection_class = BaseConnection.connection_for_nodes(collection)
36
+ # @param edge_class [Class] The class to wrap `item` with (defaults to the connection's edge class)
37
+ def initialize(collection:, item:, parent: nil, context: nil, edge_class: nil)
38
+ if context && context.schema.new_connections?
39
+ conn_class = context.schema.connections.wrapper_for(collection)
40
+ # The rest will be added by ConnectionExtension
41
+ @connection = conn_class.new(collection, parent: parent, context: context, edge_class: edge_class)
42
+ @edge = @connection.edge_class.new(item, @connection)
43
+ else
44
+ connection_class = BaseConnection.connection_for_nodes(collection)
45
+ @connection = connection_class.new(collection, {}, parent: parent, context: context)
46
+ edge_class ||= Relay::Edge
47
+ @edge = edge_class.new(item, @connection)
48
+ end
49
+
39
50
  @parent = parent
40
- @connection = connection_class.new(collection, {}, parent: parent, context: context)
41
- @edge = edge_class.new(item, @connection)
42
51
  end
43
52
  end
44
53
  end
@@ -127,7 +127,7 @@ module GraphQL
127
127
  end
128
128
  end
129
129
 
130
- # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered wtih {#lazy_resolve}.
130
+ # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {#lazy_resolve}.
131
131
  def lazy_method_name(obj)
132
132
  lazy_methods.get(obj)
133
133
  end
@@ -789,20 +789,25 @@ module GraphQL
789
789
  # @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
790
790
  # @param interpreter [Boolean] If false, the legacy {Execution::Execute} runtime will be used
791
791
  # @return [Class] the schema described by `document`
792
- def self.from_definition(definition_or_path, default_resolve: nil, interpreter: true, parser: BuildFromDefinition::DefaultParser, using: {})
792
+ def self.from_definition(definition_or_path, default_resolve: nil, interpreter: true, parser: GraphQL.default_parser, using: {})
793
793
  # If the file ends in `.graphql`, treat it like a filepath
794
- definition = if definition_or_path.end_with?(".graphql")
795
- File.read(definition_or_path)
794
+ if definition_or_path.end_with?(".graphql")
795
+ GraphQL::Schema::BuildFromDefinition.from_definition_path(
796
+ definition_or_path,
797
+ default_resolve: default_resolve,
798
+ parser: parser,
799
+ using: using,
800
+ interpreter: interpreter,
801
+ )
796
802
  else
797
- definition_or_path
798
- end
799
- GraphQL::Schema::BuildFromDefinition.from_definition(
800
- definition,
801
- default_resolve: default_resolve,
802
- parser: parser,
803
- using: using,
804
- interpreter: interpreter,
805
- )
803
+ GraphQL::Schema::BuildFromDefinition.from_definition(
804
+ definition_or_path,
805
+ default_resolve: default_resolve,
806
+ parser: parser,
807
+ using: using,
808
+ interpreter: interpreter,
809
+ )
810
+ end
806
811
  end
807
812
 
808
813
  # Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string.
@@ -832,7 +837,7 @@ module GraphQL
832
837
  # @param except [<#call(member, ctx)>]
833
838
  # @return [Hash] GraphQL result
834
839
  def as_json(only: nil, except: nil, context: {})
835
- execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context).to_h
840
+ execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
836
841
  end
837
842
 
838
843
  # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
@@ -880,7 +885,7 @@ module GraphQL
880
885
  # @param except [<#call(member, ctx)>]
881
886
  # @return [Hash] GraphQL result
882
887
  def as_json(only: nil, except: nil, context: {})
883
- execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context).to_h
888
+ execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
884
889
  end
885
890
 
886
891
  # Return the GraphQL IDL for the schema
@@ -1112,7 +1117,16 @@ module GraphQL
1112
1117
  if type.kind.union?
1113
1118
  type.possible_types(context: context)
1114
1119
  else
1115
- own_possible_types[type.graphql_name] ||
1120
+ stored_possible_types = own_possible_types[type.graphql_name]
1121
+ visible_possible_types = stored_possible_types.select do |possible_type|
1122
+ next true unless type.kind.interface?
1123
+ next true unless possible_type.kind.object?
1124
+
1125
+ # Use `.graphql_name` comparison to match legacy vs class-based types.
1126
+ # When we don't need to support legacy `.define` types, use `.include?(type)` instead.
1127
+ possible_type.interfaces(context).any? { |interface| interface.graphql_name == type.graphql_name }
1128
+ end if stored_possible_types
1129
+ visible_possible_types ||
1116
1130
  introspection_system.possible_types[type.graphql_name] ||
1117
1131
  (
1118
1132
  superclass.respond_to?(:possible_types) ?
@@ -1534,9 +1548,9 @@ module GraphQL
1534
1548
 
1535
1549
  # Add several directives at once
1536
1550
  # @param new_directives [Class]
1537
- def directives(new_directives = nil)
1538
- if new_directives
1539
- new_directives.each { |d| directive(d) }
1551
+ def directives(*new_directives)
1552
+ if new_directives.any?
1553
+ new_directives.flatten.each { |d| directive(d) }
1540
1554
  end
1541
1555
 
1542
1556
  find_inherited_value(:directives, default_directives).merge(own_directives)
@@ -1550,11 +1564,11 @@ module GraphQL
1550
1564
  end
1551
1565
 
1552
1566
  def default_directives
1553
- {
1567
+ @default_directives ||= {
1554
1568
  "include" => GraphQL::Schema::Directive::Include,
1555
1569
  "skip" => GraphQL::Schema::Directive::Skip,
1556
1570
  "deprecated" => GraphQL::Schema::Directive::Deprecated,
1557
- }
1571
+ }.freeze
1558
1572
  end
1559
1573
 
1560
1574
  def tracer(new_tracer)
@@ -1663,6 +1677,10 @@ module GraphQL
1663
1677
  end
1664
1678
  end
1665
1679
 
1680
+ def query_stack_error(query, err)
1681
+ query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
1682
+ end
1683
+
1666
1684
  private
1667
1685
 
1668
1686
  def lazy_methods
@@ -1783,16 +1801,7 @@ module GraphQL
1783
1801
  if owner.kind.union?
1784
1802
  # It's a union with possible_types
1785
1803
  # Replace the item by class name
1786
- owner.type_memberships.each { |tm|
1787
- possible_type = tm.object_type
1788
- if possible_type.is_a?(String) && (possible_type == type.name)
1789
- # This is a match of Ruby class names, not graphql names,
1790
- # since strings are used to refer to constants.
1791
- tm.object_type = type
1792
- elsif possible_type.is_a?(LateBoundType) && possible_type.graphql_name == type.graphql_name
1793
- tm.object_type = type
1794
- end
1795
- }
1804
+ owner.assign_type_membership_object_type(type)
1796
1805
  own_possible_types[owner.graphql_name] = owner.possible_types
1797
1806
  elsif type.kind.interface? && owner.kind.object?
1798
1807
  new_interfaces = []