graphql 2.5.7 → 2.5.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7178e1b984bd4ab4488c7ff283d0efc2014a947e3b6901a0f97614e06d2ead6a
4
- data.tar.gz: bef137f0d523fc01840f9315c6af6703de75e63987fffd032920dbde909fba50
3
+ metadata.gz: 35ee0d9a05997b264c746c531a4ff8d86ae9c8a4990523319da5ffafc9c89246
4
+ data.tar.gz: e68bf5323cff52c65403b4ed34a90de85a1f8b660d95cd43c167aeba2bce2d51
5
5
  SHA512:
6
- metadata.gz: e6b4410aaef9503db55c6264bc9c6bcdbc8130522c84dda20837b8adec00ff33d9b897f0a1e63385979907e7c8144270858184692e4299a66603fdcff7ae616f
7
- data.tar.gz: 6db0fe8a27e99763dcf8f2f1478d6bbb2bc095ac19be34feaaf48f4bfe6c697a99e6e0d30ab37c095fa88ab0b71259b241b639f2efd8e83ea17d894baae21af5
6
+ metadata.gz: 7233e64a37086b088d22609117498fcc12992472bde573a0a08b31d579121e9062759deac9ca22394273d28f914c04042dbbe53ba94a90859d0e82d75cc3c25a
7
+ data.tar.gz: bdeefc1210bcac49daef9670cfb53047c1d048e07ca2c68d6b31936e737f11314ac4cb6dbb6dc3063200f472379980fca42dfa997b44202ee56e5e1a6c525a9e
@@ -123,14 +123,14 @@ module GraphQL
123
123
  case inner_type.kind.name
124
124
  when "SCALAR", "ENUM"
125
125
  result_name = ast_node.alias || ast_node.name
126
- owner_type = query.field_definition.owner
126
+ field_defn = query.field_definition
127
+ owner_type = field_defn.owner
127
128
  selection_result = GraphQLResultHash.new(nil, owner_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
128
129
  selection_result.base_path = base_path
129
130
  selection_result.ordered_result_keys = [result_name]
130
131
  runtime_state = get_current_runtime_state
131
132
  runtime_state.current_result = selection_result
132
133
  runtime_state.current_result_name = result_name
133
- field_defn = query.field_definition
134
134
  continue_value = continue_value(object, field_defn, false, ast_node, result_name, selection_result)
135
135
  if HALT != continue_value
136
136
  continue_field(continue_value, owner_type, field_defn, root_type, ast_node, nil, false, nil, nil, result_name, selection_result, false, runtime_state) # rubocop:disable Metrics/ParameterLists
@@ -156,14 +156,14 @@ module GraphQL
156
156
  end
157
157
  when "SCALAR", "ENUM"
158
158
  result_name = ast_node.alias || ast_node.name
159
- owner_type = query.field_definition.owner
160
- selection_result = GraphQLResultHash.new(nil, query.parent_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
159
+ field_defn = query.field_definition
160
+ owner_type = field_defn.owner
161
+ selection_result = GraphQLResultHash.new(nil, owner_type, nil, nil, false, EmptyObjects::EMPTY_ARRAY, false, ast_node, nil, nil)
161
162
  selection_result.ordered_result_keys = [result_name]
162
163
  selection_result.base_path = base_path
163
164
  runtime_state = get_current_runtime_state
164
165
  runtime_state.current_result = selection_result
165
166
  runtime_state.current_result_name = result_name
166
- field_defn = query.field_definition
167
167
  continue_value = continue_value(object, field_defn, false, ast_node, result_name, selection_result)
168
168
  if HALT != continue_value
169
169
  continue_field(continue_value, owner_type, field_defn, query.root_type, ast_node, nil, false, nil, nil, result_name, selection_result, false, runtime_state) # rubocop:disable Metrics/ParameterLists
@@ -826,6 +826,13 @@ module GraphQL
826
826
  else
827
827
  dir_defn = @schema_directives.fetch(dir_node.name)
828
828
  raw_dir_args = arguments(nil, dir_defn, dir_node)
829
+ if !raw_dir_args.is_a?(GraphQL::ExecutionError)
830
+ begin
831
+ dir_defn.validate!(raw_dir_args, context)
832
+ rescue GraphQL::ExecutionError => err
833
+ raw_dir_args = err
834
+ end
835
+ end
829
836
  dir_args = continue_value(
830
837
  raw_dir_args, # value
831
838
  nil, # field
@@ -18,7 +18,8 @@ module GraphQL
18
18
  # @param object [Object] A starting object for execution
19
19
  # @param query [GraphQL::Query] A full query instance that this partial is based on. Caches are shared.
20
20
  # @param context [Hash] Extra context values to merge into `query.context`, if provided
21
- def initialize(path:, object:, query:, context: nil)
21
+ # @param fragment_node [GraphQL::Language::Nodes::InlineFragment, GraphQL::Language::Nodes::FragmentDefinition]
22
+ def initialize(path: nil, object:, query:, context: nil, fragment_node: nil, type: nil)
22
23
  @path = path
23
24
  @object = object
24
25
  @query = query
@@ -31,57 +32,18 @@ module GraphQL
31
32
  @multiplex = nil
32
33
  @result_values = nil
33
34
  @result = nil
34
- selections = [@query.selected_operation]
35
- type = @query.root_type
36
- parent_type = nil
37
- field_defn = nil
38
- @path.each do |name_in_doc|
39
- if name_in_doc.is_a?(Integer)
40
- if type.list?
41
- type = type.unwrap
42
- next
43
- else
44
- raise ArgumentError, "Received path with index `#{name_in_doc}`, but type wasn't a list. Type: #{type.to_type_signature}, path: #{@path}"
45
- end
46
- end
47
-
48
- next_selections = []
49
- selections.each do |selection|
50
- selections_to_check = []
51
- selections_to_check.concat(selection.selections)
52
- while (sel = selections_to_check.shift)
53
- case sel
54
- when GraphQL::Language::Nodes::InlineFragment
55
- selections_to_check.concat(sel.selections)
56
- when GraphQL::Language::Nodes::FragmentSpread
57
- fragment = @query.fragments[sel.name]
58
- selections_to_check.concat(fragment.selections)
59
- when GraphQL::Language::Nodes::Field
60
- if sel.alias == name_in_doc || sel.name == name_in_doc
61
- next_selections << sel
62
- end
63
- else
64
- raise "Unexpected selection in partial path: #{sel.class}, #{sel.inspect}"
65
- end
66
- end
67
- end
68
35
 
69
- if next_selections.empty?
70
- raise ArgumentError, "Path `#{@path.inspect}` is not present in this query. `#{name_in_doc.inspect}` was not found. Try a different path or rewrite the query to include it."
71
- end
72
- field_name = next_selections.first.name
73
- field_defn = @schema.get_field(type, field_name, @query.context) || raise("Invariant: no field called #{field_name} on #{type.graphql_name}")
74
- parent_type = type
75
- type = field_defn.type
76
- if type.non_null?
77
- type = type.of_type
78
- end
79
- selections = next_selections
36
+ if fragment_node
37
+ @ast_nodes = [fragment_node]
38
+ @root_type = type || raise(ArgumentError, "Pass `type:` when using `node:`")
39
+ # This is only used when `@leaf`
40
+ @field_definition = nil
41
+ elsif path.nil?
42
+ raise ArgumentError, "`path:` is required if `node:` is not given; add `path:`"
43
+ else
44
+ set_type_info_from_path
80
45
  end
81
- @parent_type = parent_type
82
- @ast_nodes = selections
83
- @root_type = type
84
- @field_definition = field_defn
46
+
85
47
  @leaf = @root_type.unwrap.kind.leaf?
86
48
  end
87
49
 
@@ -89,7 +51,7 @@ module GraphQL
89
51
  @leaf
90
52
  end
91
53
 
92
- attr_reader :context, :query, :ast_nodes, :root_type, :object, :field_definition, :path, :parent_type, :schema
54
+ attr_reader :context, :query, :ast_nodes, :root_type, :object, :field_definition, :path, :schema
93
55
 
94
56
  attr_accessor :multiplex, :result_values
95
57
 
@@ -155,6 +117,63 @@ module GraphQL
155
117
  def selected_operation_name
156
118
  @query.selected_operation_name
157
119
  end
120
+
121
+ private
122
+
123
+ def set_type_info_from_path
124
+ selections = [@query.selected_operation]
125
+ type = @query.root_type
126
+ parent_type = nil
127
+ field_defn = nil
128
+
129
+ @path.each do |name_in_doc|
130
+ if name_in_doc.is_a?(Integer)
131
+ if type.list?
132
+ type = type.unwrap
133
+ next
134
+ else
135
+ raise ArgumentError, "Received path with index `#{name_in_doc}`, but type wasn't a list. Type: #{type.to_type_signature}, path: #{@path}"
136
+ end
137
+ end
138
+
139
+ next_selections = []
140
+ selections.each do |selection|
141
+ selections_to_check = []
142
+ selections_to_check.concat(selection.selections)
143
+ while (sel = selections_to_check.shift)
144
+ case sel
145
+ when GraphQL::Language::Nodes::InlineFragment
146
+ selections_to_check.concat(sel.selections)
147
+ when GraphQL::Language::Nodes::FragmentSpread
148
+ fragment = @query.fragments[sel.name]
149
+ selections_to_check.concat(fragment.selections)
150
+ when GraphQL::Language::Nodes::Field
151
+ if sel.alias == name_in_doc || sel.name == name_in_doc
152
+ next_selections << sel
153
+ end
154
+ else
155
+ raise "Unexpected selection in partial path: #{sel.class}, #{sel.inspect}"
156
+ end
157
+ end
158
+ end
159
+
160
+ if next_selections.empty?
161
+ raise ArgumentError, "Path `#{@path.inspect}` is not present in this query. `#{name_in_doc.inspect}` was not found. Try a different path or rewrite the query to include it."
162
+ end
163
+ field_name = next_selections.first.name
164
+ field_defn = @schema.get_field(type, field_name, @query.context) || raise("Invariant: no field called #{field_name} on #{type.graphql_name}")
165
+ parent_type = type
166
+ type = field_defn.type
167
+ if type.non_null?
168
+ type = type.of_type
169
+ end
170
+ selections = next_selections
171
+ end
172
+
173
+ @ast_nodes = selections
174
+ @root_type = type
175
+ @field_definition = field_defn
176
+ end
158
177
  end
159
178
  end
160
179
  end
@@ -9,6 +9,7 @@ module GraphQL
9
9
  class Directive < GraphQL::Schema::Member
10
10
  extend GraphQL::Schema::Member::HasArguments
11
11
  extend GraphQL::Schema::Member::HasArguments::HasDirectiveArguments
12
+ extend GraphQL::Schema::Member::HasValidators
12
13
 
13
14
  class << self
14
15
  # Directives aren't types, they don't have kinds.
@@ -75,6 +76,10 @@ module GraphQL
75
76
  yield
76
77
  end
77
78
 
79
+ def validate!(arguments, context)
80
+ Schema::Validator.validate!(validators, self, context, arguments)
81
+ end
82
+
78
83
  def on_field?
79
84
  locations.include?(FIELD)
80
85
  end
@@ -111,6 +116,9 @@ module GraphQL
111
116
  # @return [GraphQL::Interpreter::Arguments]
112
117
  attr_reader :arguments
113
118
 
119
+ class InvalidArgumentError < GraphQL::Error
120
+ end
121
+
114
122
  def initialize(owner, **arguments)
115
123
  @owner = owner
116
124
  assert_valid_owner
@@ -119,7 +127,19 @@ module GraphQL
119
127
  # - lazy resolution
120
128
  # Probably, those won't be needed here, since these are configuration arguments,
121
129
  # not runtime arguments.
122
- @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
130
+ context = Query::NullContext.instance
131
+ self.class.all_argument_definitions.each do |arg_defn|
132
+ value = arguments[arg_defn.keyword]
133
+ result = arg_defn.type.validate_input(value, context)
134
+ if !result.valid?
135
+ raise InvalidArgumentError, "@#{graphql_name}.#{arg_defn.graphql_name} on #{owner.path} is invalid (#{value.inspect}): #{result.problems.first["explanation"]}"
136
+ end
137
+ end
138
+ self.class.validate!(arguments, context)
139
+ @arguments = self.class.coerce_arguments(nil, arguments, context)
140
+ if @arguments.is_a?(GraphQL::ExecutionError)
141
+ raise @arguments
142
+ end
123
143
  end
124
144
 
125
145
  def graphql_name
@@ -182,6 +182,10 @@ module GraphQL
182
182
 
183
183
  input.each do |argument_name, value|
184
184
  argument = types.argument(self, argument_name)
185
+ if argument.nil? && ctx.is_a?(Query::NullContext) && argument_name.is_a?(Symbol)
186
+ # Validating definition directive arguments which come in as Symbols
187
+ argument = types.arguments(self).find { |arg| arg.keyword == argument_name }
188
+ end
185
189
  # Items in the input that are unexpected
186
190
  if argument.nil?
187
191
  result ||= Query::InputValidationResult.new
@@ -50,12 +50,7 @@ module GraphQL
50
50
  end
51
51
 
52
52
  if coerced_result.nil?
53
- str_value = if value == Float::INFINITY
54
- ""
55
- else
56
- " #{GraphQL::Language.serialize(value)}"
57
- end
58
- Query::InputValidationResult.from_problem("Could not coerce value#{str_value} to #{graphql_name}")
53
+ Query::InputValidationResult.from_problem("Could not coerce value #{GraphQL::Language.serialize(value)} to #{graphql_name}")
59
54
  elsif coerced_result.is_a?(GraphQL::CoercionError)
60
55
  Query::InputValidationResult.from_problem(coerced_result.message, message: coerced_result.message, extensions: coerced_result.extensions)
61
56
  else
@@ -71,15 +71,23 @@ module GraphQL
71
71
  def execute_field(query:, field:, **_rest)
72
72
  timeout_state = query.context.namespace(@timeout).fetch(:state)
73
73
  # If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query.
74
- if timeout_state != false && Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
74
+ if timeout_state == false
75
+ super
76
+ elsif Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
75
77
  error = GraphQL::Schema::Timeout::TimeoutError.new(field)
76
78
  # Only invoke the timeout callback for the first timeout
77
79
  if !timeout_state[:timed_out]
78
80
  timeout_state[:timed_out] = true
79
81
  @timeout.handle_timeout(error, query)
82
+ timeout_state = query.context.namespace(@timeout).fetch(:state)
80
83
  end
81
84
 
82
- error
85
+ # `handle_timeout` may have set this to be `false`
86
+ if timeout_state != false
87
+ error
88
+ else
89
+ super
90
+ end
83
91
  else
84
92
  super
85
93
  end
@@ -102,6 +110,15 @@ module GraphQL
102
110
  # override to do something interesting
103
111
  end
104
112
 
113
+ # Call this method (eg, from {#handle_timeout}) to disable timeout tracking
114
+ # for the given query.
115
+ # @param query [GraphQL::Query]
116
+ # @return [void]
117
+ def disable_timeout(query)
118
+ query.context.namespace(self)[:state] = false
119
+ nil
120
+ end
121
+
105
122
  # This error is raised when a query exceeds `max_seconds`.
106
123
  # Since it's a child of {GraphQL::ExecutionError},
107
124
  # its message will be added to the response's `errors` key.
@@ -99,10 +99,10 @@ module GraphQL
99
99
  argument_definitions = @validated.arguments(context).values
100
100
  required_names = @one_of.map do |arg_keyword|
101
101
  if arg_keyword.is_a?(Array)
102
- names = arg_keyword.map { |arg| arg_keyword_to_grapqhl_name(argument_definitions, arg) }
102
+ names = arg_keyword.map { |arg| arg_keyword_to_graphql_name(argument_definitions, arg) }
103
103
  "(" + names.join(" and ") + ")"
104
104
  else
105
- arg_keyword_to_grapqhl_name(argument_definitions, arg_keyword)
105
+ arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
106
106
  end
107
107
  end
108
108
 
@@ -113,7 +113,7 @@ module GraphQL
113
113
  end
114
114
  end
115
115
 
116
- def arg_keyword_to_grapqhl_name(argument_definitions, arg_keyword)
116
+ def arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
117
117
  argument_definition = argument_definitions.find { |defn| defn.keyword == arg_keyword }
118
118
  argument_definition.graphql_name
119
119
  end
@@ -256,8 +256,7 @@ module GraphQL
256
256
  end
257
257
 
258
258
  def loadable?(t, _ctx)
259
- load_all_types
260
- !@all_types[t.graphql_name] && @cached_visible[t]
259
+ @cached_visible[t] && !referenced?(t)
261
260
  end
262
261
 
263
262
  def loadable_possible_types(t, _ctx)
@@ -232,7 +232,9 @@ module GraphQL
232
232
 
233
233
  # @return [Boolean] True if this type is used for `loads:` but not in the schema otherwise and not _explicitly_ hidden.
234
234
  def loadable?(type, _ctx)
235
- !reachable_type_set.include?(type) && visible_type?(type)
235
+ visible_type?(type) &&
236
+ !referenced?(type) &&
237
+ (type.respond_to?(:interfaces) ? interfaces(type).all? { |i| loadable?(i, _ctx) } : true)
236
238
  end
237
239
 
238
240
  def loadable_possible_types(union_type, _ctx)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.5.7"
3
+ VERSION = "2.5.8"
4
4
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.7
4
+ version: 2.5.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-05-15 00:00:00.000000000 Z
10
+ date: 2025-05-28 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64