graphql 2.0.0 → 2.0.1

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c54b2619d5d111575839ba3e5d34794d993cb37a0b9aee4d41347551bb6be846
4
- data.tar.gz: f6d53678c268767478a2a86ec34c045ab8744f4ea6f97deeebed3708df634340
3
+ metadata.gz: f37881627f4e4ddcdb6f2f83f395982d624396949b7e26c1c7fa395f34b6c37a
4
+ data.tar.gz: c4840087ffff471824df26453876279b2e44014cdf99f9778b35481c917e18fa
5
5
  SHA512:
6
- metadata.gz: c7ccf09ae0a94e0cdce47f66555681391ac29af9cb544b662e8f9cd127905cda5cf5923dc64500dcab15899c7c51d5d2458ae78b95cb40ca47b1ce792e35f52c
7
- data.tar.gz: ba6d04f4fc107fe9e2daaf70bd06288f85a6ead011764ab1b975e1796e97fa8c289cc08186bbf23c6020670a585c1a48c22d2acbd28ed10c6ec09b97f07a2a47
6
+ metadata.gz: 34999652dad1730d8cf51a46b213dc62d34a00e429b734da857aadbe5d5497c1a70c5d6ebc9d0a316db65a2e8e40075ab56d8bb59eb1cdc45b88c9549149ff92
7
+ data.tar.gz: 8ff253848a6fcdaaaa831c87377b4b9d4ca494704bba0844336dc3c500b125df5f888f076f77cdf4fae61c03b2d8cb9383e18406bbf8b6ab2d7bd399dd54bf7d
@@ -11,7 +11,9 @@ module GraphQL
11
11
  # executed sychronously.
12
12
  def run; end
13
13
  def run_isolated; yield; end
14
- def yield; end
14
+ def yield
15
+ raise GraphQL::Error, "GraphQL::Dataloader is not running -- add `use GraphQL::Dataloader` to your schema to use Dataloader sources."
16
+ end
15
17
 
16
18
  def append_job
17
19
  yield
@@ -230,7 +230,6 @@ module GraphQL
230
230
  call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
231
231
  evaluate_selections(
232
232
  path,
233
- context.scoped_context,
234
233
  object_proxy,
235
234
  root_type,
236
235
  root_op_type == "mutation",
@@ -349,7 +348,7 @@ module GraphQL
349
348
  NO_ARGS = {}.freeze
350
349
 
351
350
  # @return [void]
352
- def evaluate_selections(path, scoped_context, owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
351
+ def evaluate_selections(path, owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
353
352
  set_all_interpreter_context(owner_object, nil, nil, path)
354
353
 
355
354
  finished_jobs = 0
@@ -357,7 +356,7 @@ module GraphQL
357
356
  gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
358
357
  @dataloader.append_job {
359
358
  evaluate_selection(
360
- path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_selection, selections_result, parent_object
359
+ path, result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
361
360
  )
362
361
  finished_jobs += 1
363
362
  if target_result && finished_jobs == enqueued_jobs
@@ -372,7 +371,7 @@ module GraphQL
372
371
  attr_reader :progress_path
373
372
 
374
373
  # @return [void]
375
- def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
374
+ def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
376
375
  return if dead_result?(selections_result)
377
376
  # As a performance optimization, the hash key will be a `Node` if
378
377
  # there's only one selection of the field. But if there are multiple
@@ -414,8 +413,6 @@ module GraphQL
414
413
  end
415
414
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
416
415
  set_all_interpreter_context(nil, field_defn, nil, next_path)
417
-
418
- context.scoped_context = scoped_context
419
416
  object = owner_object
420
417
 
421
418
  if is_introspection
@@ -425,19 +422,18 @@ module GraphQL
425
422
  total_args_count = field_defn.arguments(context).size
426
423
  if total_args_count == 0
427
424
  resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
428
- evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
425
+ evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
429
426
  else
430
427
  # TODO remove all arguments(...) usages?
431
428
  @query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
432
- evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
429
+ evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
433
430
  end
434
431
  end
435
432
  end
436
433
 
437
- def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result, parent_object) # rubocop:disable Metrics/ParameterLists
438
- context.scoped_context = scoped_context
434
+ def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object) # rubocop:disable Metrics/ParameterLists
439
435
  return_type = field_defn.type
440
- after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
436
+ after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
441
437
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
442
438
  continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
443
439
  next
@@ -510,7 +506,7 @@ module GraphQL
510
506
  rescue GraphQL::ExecutionError => err
511
507
  err
512
508
  end
513
- after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
509
+ after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
514
510
  continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
515
511
  if HALT != continue_value
516
512
  continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
@@ -688,7 +684,7 @@ module GraphQL
688
684
  resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
689
685
  resolved_value ||= value
690
686
 
691
- after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type|
687
+ after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type|
692
688
  possible_types = query.possible_types(current_type)
693
689
 
694
690
  if !possible_types.include?(resolved_type)
@@ -708,7 +704,7 @@ module GraphQL
708
704
  rescue GraphQL::ExecutionError => err
709
705
  err
710
706
  end
711
- after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
707
+ after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
712
708
  continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
713
709
  if HALT != continue_value
714
710
  response_hash = GraphQLResultHash.new(result_name, selection_result)
@@ -734,7 +730,6 @@ module GraphQL
734
730
  call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
735
731
  evaluate_selections(
736
732
  path,
737
- context.scoped_context,
738
733
  continue_value,
739
734
  current_type,
740
735
  false,
@@ -757,7 +752,6 @@ module GraphQL
757
752
  set_result(selection_result, result_name, response_list)
758
753
 
759
754
  idx = 0
760
- scoped_context = context.scoped_context
761
755
  begin
762
756
  value.each do |inner_value|
763
757
  break if dead_result?(response_list)
@@ -768,10 +762,10 @@ module GraphQL
768
762
  idx += 1
769
763
  if use_dataloader_job
770
764
  @dataloader.append_job do
771
- resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
765
+ resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
772
766
  end
773
767
  else
774
- resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
768
+ resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
775
769
  end
776
770
  end
777
771
  rescue NoMethodError => err
@@ -791,11 +785,11 @@ module GraphQL
791
785
  end
792
786
  end
793
787
 
794
- def resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
788
+ def resolve_list_item(inner_value, inner_type, next_path, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
795
789
  set_all_interpreter_context(nil, nil, nil, next_path)
796
790
  call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
797
791
  # This will update `response_list` with the lazy
798
- after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
792
+ after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
799
793
  continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
800
794
  if HALT != continue_value
801
795
  continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
@@ -870,11 +864,10 @@ module GraphQL
870
864
  # @param eager [Boolean] Set to `true` for mutation root fields only
871
865
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
872
866
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
873
- def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
867
+ def after_lazy(lazy_obj, owner:, field:, path:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
874
868
  if lazy?(lazy_obj)
875
869
  lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
876
870
  set_all_interpreter_context(owner_object, field, arguments, path)
877
- context.scoped_context = scoped_context
878
871
  # Wrap the execution of _this_ method with tracing,
879
872
  # but don't wrap the continuation below
880
873
  inner_obj = begin
@@ -86,8 +86,83 @@ module GraphQL
86
86
  @errors = []
87
87
  @path = []
88
88
  @value = nil
89
- @context = self # for SharedMethods
90
- @scoped_context = {}
89
+ @context = self # for SharedMethods TODO delete sharedmethods
90
+ @scoped_context = ScopedContext.new(self)
91
+ end
92
+
93
+ class ScopedContext
94
+ def initialize(query_context)
95
+ @query_context = query_context
96
+ @path_contexts = {}
97
+ @no_path = [].freeze
98
+ end
99
+
100
+ def merged_context
101
+ merged_ctx = {}
102
+ each_present_path_ctx do |path_ctx|
103
+ merged_ctx = path_ctx.merge(merged_ctx)
104
+ end
105
+ merged_ctx
106
+ end
107
+
108
+ def merge!(hash)
109
+ current_ctx = @path_contexts[current_path] ||= {}
110
+ current_ctx.merge!(hash)
111
+ end
112
+
113
+ def current_path
114
+ @query_context.namespace(:interpreter)[:current_path] || @no_path
115
+ end
116
+
117
+ def key?(key)
118
+ each_present_path_ctx do |path_ctx|
119
+ if path_ctx.key?(key)
120
+ return true
121
+ end
122
+ end
123
+ false
124
+ end
125
+
126
+ def [](key)
127
+ each_present_path_ctx do |path_ctx|
128
+ if path_ctx.key?(key)
129
+ return path_ctx[key]
130
+ end
131
+ end
132
+ nil
133
+ end
134
+
135
+ def dig(key, *other_keys)
136
+ each_present_path_ctx do |path_ctx|
137
+ if path_ctx.key?(key)
138
+ found_value = path_ctx[key]
139
+ if other_keys.any?
140
+ return found_value.dig(*other_keys)
141
+ else
142
+ return found_value
143
+ end
144
+ end
145
+ end
146
+ nil
147
+ end
148
+
149
+ private
150
+
151
+ # Start at the current location,
152
+ # but look up the tree for previously-assigned scoped values
153
+ def each_present_path_ctx
154
+ search_path = current_path.dup
155
+ if (current_path_ctx = @path_contexts[search_path])
156
+ yield(current_path_ctx)
157
+ end
158
+
159
+ while search_path.size > 0
160
+ search_path.pop # look one level higher
161
+ if (search_path_ctx = @path_contexts[search_path])
162
+ yield(search_path_ctx)
163
+ end
164
+ end
165
+ end
91
166
  end
92
167
 
93
168
  # @return [Hash] A hash that will be added verbatim to the result hash, as `"extensions" => { ... }`
@@ -106,7 +181,7 @@ module GraphQL
106
181
  attr_writer :value
107
182
 
108
183
  # @api private
109
- attr_accessor :scoped_context
184
+ attr_reader :scoped_context
110
185
 
111
186
  def []=(key, value)
112
187
  @provided_values[key] = value
@@ -119,8 +194,11 @@ module GraphQL
119
194
 
120
195
  # Lookup `key` from the hash passed to {Schema#execute} as `context:`
121
196
  def [](key)
122
- return @scoped_context[key] if @scoped_context.key?(key)
123
- @provided_values[key]
197
+ if @scoped_context.key?(key)
198
+ @scoped_context[key]
199
+ else
200
+ @provided_values[key]
201
+ end
124
202
  end
125
203
 
126
204
  def delete(key)
@@ -135,7 +213,7 @@ module GraphQL
135
213
 
136
214
  def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
137
215
  if @scoped_context.key?(key)
138
- @scoped_context[key]
216
+ scoped_context[key]
139
217
  elsif @provided_values.key?(key)
140
218
  @provided_values[key]
141
219
  elsif default != UNSPECIFIED_FETCH_DEFAULT
@@ -148,12 +226,21 @@ module GraphQL
148
226
  end
149
227
 
150
228
  def dig(key, *other_keys)
151
- @scoped_context.key?(key) ? @scoped_context.dig(key, *other_keys) : @provided_values.dig(key, *other_keys)
229
+ if @scoped_context.key?(key)
230
+ @scoped_context.dig(key, *other_keys)
231
+ else
232
+ @provided_values.dig(key, *other_keys)
233
+ end
152
234
  end
153
235
 
154
236
  def to_h
155
- @provided_values.merge(@scoped_context)
237
+ if (current_scoped_context = @scoped_context.merged_context)
238
+ @provided_values.merge(current_scoped_context)
239
+ else
240
+ @provided_values
241
+ end
156
242
  end
243
+
157
244
  alias :to_hash :to_h
158
245
 
159
246
  def key?(key)
@@ -185,7 +272,7 @@ module GraphQL
185
272
  end
186
273
 
187
274
  def scoped_merge!(hash)
188
- @scoped_context = @scoped_context.merge(hash)
275
+ @scoped_context.merge!(hash)
189
276
  end
190
277
 
191
278
  def scoped_set!(key, value)
@@ -4,6 +4,12 @@ module GraphQL
4
4
  class InputValidationResult
5
5
  attr_accessor :problems
6
6
 
7
+ def self.from_problem(explanation, path = nil, extensions: nil, message: nil)
8
+ result = self.new
9
+ result.add_problem(explanation, path, extensions: extensions, message: message)
10
+ result
11
+ end
12
+
7
13
  def initialize(valid: true, problems: nil)
8
14
  @valid = valid
9
15
  @problems = problems
@@ -27,7 +33,7 @@ module GraphQL
27
33
  end
28
34
 
29
35
  def merge_result!(path, inner_result)
30
- return if inner_result.valid?
36
+ return if inner_result.nil? || inner_result.valid?
31
37
 
32
38
  if inner_result.problems
33
39
  inner_result.problems.each do |p|
@@ -38,6 +44,9 @@ module GraphQL
38
44
  # It could have been explicitly set on inner_result (if it had no problems)
39
45
  @valid = false
40
46
  end
47
+
48
+ VALID = self.new
49
+ VALID.freeze
41
50
  end
42
51
  end
43
52
  end
data/lib/graphql/query.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/query/context"
3
3
  require "graphql/query/fingerprint"
4
- require "graphql/query/literal_input"
5
4
  require "graphql/query/null_context"
6
5
  require "graphql/query/result"
7
6
  require "graphql/query/variables"
@@ -123,16 +123,14 @@ module GraphQL
123
123
  end
124
124
 
125
125
  def validate_non_null_input(value_name, ctx)
126
- result = GraphQL::Query::InputValidationResult.new
127
-
128
126
  allowed_values = ctx.warden.enum_values(self)
129
127
  matching_value = allowed_values.find { |v| v.graphql_name == value_name }
130
128
 
131
129
  if matching_value.nil?
132
- result.add_problem("Expected #{GraphQL::Language.serialize(value_name)} to be one of: #{allowed_values.map(&:graphql_name).join(', ')}")
130
+ GraphQL::Query::InputValidationResult.from_problem("Expected #{GraphQL::Language.serialize(value_name)} to be one of: #{allowed_values.map(&:graphql_name).join(', ')}")
131
+ else
132
+ nil
133
133
  end
134
-
135
- result
136
134
  end
137
135
 
138
136
  def coerce_result(value, ctx)
@@ -29,7 +29,13 @@ module GraphQL
29
29
  attr_reader :method_str
30
30
 
31
31
  # @return [Symbol] The method on the type to look up
32
- attr_reader :resolver_method
32
+ def resolver_method
33
+ if @resolver_class
34
+ @resolver_class.resolver_method
35
+ else
36
+ @resolver_method
37
+ end
38
+ end
33
39
 
34
40
  # @return [Class] The thing this field was defined on (type, mutation, resolver)
35
41
  attr_accessor :owner
@@ -68,7 +74,10 @@ module GraphQL
68
74
  attr_reader :trace
69
75
 
70
76
  # @return [String, nil]
71
- attr_accessor :subscription_scope
77
+ def subscription_scope
78
+ @subscription_scope || (@resolver_class.respond_to?(:subscription_scope) ? @resolver_class.subscription_scope : nil)
79
+ end
80
+ attr_writer :subscription_scope
72
81
 
73
82
  # Create a field instance from a list of arguments, keyword arguments, and a block.
74
83
  #
@@ -82,11 +91,9 @@ module GraphQL
82
91
  # @return [GraphQL::Schema:Field] an instance of `self
83
92
  # @see {.initialize} for other options
84
93
  def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
85
- if (parent_config = resolver || mutation || subscription)
86
- # Get the parent config, merge in local overrides
87
- kwargs = parent_config.field_options.merge(kwargs)
94
+ if (resolver_class = resolver || mutation || subscription)
88
95
  # Add a reference to that parent class
89
- kwargs[:resolver_class] = parent_config
96
+ kwargs[:resolver_class] = resolver_class
90
97
  end
91
98
 
92
99
  if name
@@ -101,8 +108,8 @@ module GraphQL
101
108
 
102
109
  kwargs[:description] = desc
103
110
  kwargs[:type] = type
104
- elsif (kwargs[:field] || kwargs[:function] || resolver || mutation) && type.is_a?(String)
105
- # The return type should be copied from `field` or `function`, and the second positional argument is the description
111
+ elsif (resolver || mutation) && type.is_a?(String)
112
+ # The return type should be copied from the resolver, and the second positional argument is the description
106
113
  kwargs[:description] = type
107
114
  else
108
115
  kwargs[:type] = type
@@ -119,8 +126,8 @@ module GraphQL
119
126
  def connection?
120
127
  if @connection.nil?
121
128
  # Provide default based on type name
122
- return_type_name = if (contains_type = @field || @function)
123
- Member::BuildType.to_type_name(contains_type.type)
129
+ return_type_name = if @resolver_class && @resolver_class.type
130
+ Member::BuildType.to_type_name(@resolver_class.type)
124
131
  elsif @return_type_expr
125
132
  Member::BuildType.to_type_name(@return_type_expr)
126
133
  else
@@ -181,9 +188,6 @@ module GraphQL
181
188
  # @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
182
189
  # @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
183
190
  # @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
184
- # @param resolve [<#call(obj, args, ctx)>] **deprecated** for compatibility with <1.8.0
185
- # @param field [GraphQL::Field, GraphQL::Schema::Field] **deprecated** for compatibility with <1.8.0
186
- # @param function [GraphQL::Function] **deprecated** for compatibility with <1.8.0
187
191
  # @param resolver_class [Class] (Private) A {Schema::Resolver} which this field was derived from. Use `resolver:` to create a field with a resolver.
188
192
  # @param arguments [{String=>GraphQL::Schema::Argument, Hash}] Arguments for this field (may be added in the block, also)
189
193
  # @param camelize [Boolean] If true, the field name will be camelized when building the schema
@@ -197,30 +201,20 @@ module GraphQL
197
201
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
198
202
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
199
203
  # @param validates [Array<Hash>] Configurations for validating this field
200
- def initialize(type: nil, name: nil, owner: nil, null: true, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, &definition_block)
204
+ def initialize(type: nil, name: nil, owner: nil, null: true, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, &definition_block)
201
205
  if name.nil?
202
206
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
203
207
  end
204
- if !(field || function || resolver_class)
208
+ if !(resolver_class)
205
209
  if type.nil?
206
210
  raise ArgumentError, "missing second `type` argument or keyword `type:`"
207
211
  end
208
212
  end
209
- if (field || function || resolve) && extras.any?
210
- raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
211
- end
212
213
  @original_name = name
213
214
  name_s = -name.to_s
214
215
  @underscored_name = -Member::BuildType.underscore(name_s)
215
216
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
216
217
  @description = description
217
- if field.is_a?(GraphQL::Schema::Field)
218
- raise ArgumentError, "Instead of passing a field as `field:`, use `add_field(field)` to add an already-defined field."
219
- else
220
- @field = field
221
- end
222
- @function = function
223
- @resolve = resolve
224
218
  self.deprecation_reason = deprecation_reason
225
219
 
226
220
  if method && hash_key && dig
@@ -253,7 +247,9 @@ module GraphQL
253
247
  @max_page_size = max_page_size == :not_given ? nil : max_page_size
254
248
  @introspection = introspection
255
249
  @extras = extras
256
- @broadcastable = broadcastable
250
+ if !broadcastable.nil?
251
+ @broadcastable = broadcastable
252
+ end
257
253
  @resolver_class = resolver_class
258
254
  @scope = scope
259
255
  @trace = trace
@@ -297,6 +293,10 @@ module GraphQL
297
293
  self.extensions(extensions)
298
294
  end
299
295
 
296
+ if resolver_class && resolver_class.extensions.any?
297
+ self.extensions(resolver_class.extensions)
298
+ end
299
+
300
300
  if directives.any?
301
301
  directives.each do |(dir_class, options)|
302
302
  self.directive(dir_class, **options)
@@ -321,7 +321,13 @@ module GraphQL
321
321
  # @return [Boolean, nil]
322
322
  # @see GraphQL::Subscriptions::BroadcastAnalyzer
323
323
  def broadcastable?
324
- @broadcastable
324
+ if defined?(@broadcastable)
325
+ @broadcastable
326
+ elsif @resolver_class
327
+ @resolver_class.broadcastable?
328
+ else
329
+ nil
330
+ end
325
331
  end
326
332
 
327
333
  # @param text [String]
@@ -329,6 +335,8 @@ module GraphQL
329
335
  def description(text = nil)
330
336
  if text
331
337
  @description = text
338
+ elsif @resolver_class
339
+ @description || @resolver_class.description
332
340
  else
333
341
  @description
334
342
  end
@@ -394,7 +402,12 @@ module GraphQL
394
402
  def extras(new_extras = nil)
395
403
  if new_extras.nil?
396
404
  # Read the value
397
- @extras
405
+ field_extras = @extras
406
+ if @resolver_class && @resolver_class.extras.any?
407
+ field_extras + @resolver_class.extras
408
+ else
409
+ field_extras
410
+ end
398
411
  else
399
412
  if @extras.frozen?
400
413
  @extras = @extras.dup
@@ -477,7 +490,11 @@ module GraphQL
477
490
  when Numeric
478
491
  @complexity = new_complexity
479
492
  when nil
480
- @complexity
493
+ if @resolver_class
494
+ @complexity || @resolver_class.complexity || 1
495
+ else
496
+ @complexity || 1
497
+ end
481
498
  else
482
499
  raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
483
500
  end
@@ -485,29 +502,31 @@ module GraphQL
485
502
 
486
503
  # @return [Boolean] True if this field's {#max_page_size} should override the schema default.
487
504
  def has_max_page_size?
488
- @has_max_page_size
505
+ @has_max_page_size || (@resolver_class && @resolver_class.has_max_page_size?)
489
506
  end
490
507
 
491
508
  # @return [Integer, nil] Applied to connections if {#has_max_page_size?}
492
- attr_reader :max_page_size
509
+ def max_page_size
510
+ @max_page_size || (@resolver_class && @resolver_class.max_page_size)
511
+ end
493
512
 
494
513
  class MissingReturnTypeError < GraphQL::Error; end
495
514
  attr_writer :type
496
515
 
497
516
  def type
498
- @type ||= if @function
499
- Member::BuildType.parse_type(@function.type, null: false)
500
- elsif @field
501
- Member::BuildType.parse_type(@field.type, null: false)
502
- elsif @return_type_expr.nil?
503
- # Not enough info to determine type
504
- message = "Can't determine the return type for #{self.path}"
505
- if @resolver_class
506
- message += " (it has `resolver: #{@resolver_class}`, consider configuration a `type ...` for that class)"
507
- end
508
- raise MissingReturnTypeError, message
517
+ if @resolver_class && (t = @resolver_class.type)
518
+ t
509
519
  else
510
- Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
520
+ @type ||= if @return_type_expr.nil?
521
+ # Not enough info to determine type
522
+ message = "Can't determine the return type for #{self.path}"
523
+ if @resolver_class
524
+ message += " (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)"
525
+ end
526
+ raise MissingReturnTypeError, message
527
+ else
528
+ Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
529
+ end
511
530
  end
512
531
  rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
513
532
  # Let this propagate up
@@ -637,14 +656,14 @@ module GraphQL
637
656
  # - A method on the wrapped object;
638
657
  # - Or, raise not implemented.
639
658
  #
640
- if obj.respond_to?(@resolver_method)
641
- method_to_call = @resolver_method
659
+ if obj.respond_to?(resolver_method)
660
+ method_to_call = resolver_method
642
661
  method_receiver = obj
643
662
  # Call the method with kwargs, if there are any
644
663
  if ruby_kwargs.any?
645
- obj.public_send(@resolver_method, **ruby_kwargs)
664
+ obj.public_send(resolver_method, **ruby_kwargs)
646
665
  else
647
- obj.public_send(@resolver_method)
666
+ obj.public_send(resolver_method)
648
667
  end
649
668
  elsif obj.object.is_a?(Hash)
650
669
  inner_object = obj.object
@@ -667,7 +686,7 @@ module GraphQL
667
686
  raise <<-ERR
668
687
  Failed to implement #{@owner.graphql_name}.#{@name}, tried:
669
688
 
670
- - `#{obj.class}##{@resolver_method}`, which did not exist
689
+ - `#{obj.class}##{resolver_method}`, which did not exist
671
690
  - `#{obj.object.class}##{@method_sym}`, which did not exist
672
691
  - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
673
692
 
@@ -127,19 +127,15 @@ module GraphQL
127
127
  INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object responding to `to_h` or `to_unsafe_h`."
128
128
 
129
129
  def validate_non_null_input(input, ctx)
130
- result = GraphQL::Query::InputValidationResult.new
131
-
132
130
  warden = ctx.warden
133
131
 
134
132
  if input.is_a?(Array)
135
- result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
136
- return result
133
+ return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
137
134
  end
138
135
 
139
136
  if !(input.respond_to?(:to_h) || input.respond_to?(:to_unsafe_h))
140
137
  # We're not sure it'll act like a hash, so reject it:
141
- result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
142
- return result
138
+ return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
143
139
  end
144
140
 
145
141
  # Inject missing required arguments
@@ -151,18 +147,22 @@ module GraphQL
151
147
  m
152
148
  end
153
149
 
154
-
150
+ result = nil
155
151
  [input, missing_required_inputs].each do |args_to_validate|
156
152
  args_to_validate.each do |argument_name, value|
157
153
  argument = warden.get_argument(self, argument_name)
158
154
  # Items in the input that are unexpected
159
- unless argument
155
+ if argument.nil?
156
+ result ||= Query::InputValidationResult.new
160
157
  result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
161
- next
158
+ else
159
+ # Items in the input that are expected, but have invalid values
160
+ argument_result = argument.type.validate_input(value, ctx)
161
+ result ||= Query::InputValidationResult.new
162
+ if !argument_result.valid?
163
+ result.merge_result!(argument_name, argument_result)
164
+ end
162
165
  end
163
- # Items in the input that are expected, but have invalid values
164
- argument_result = argument.type.validate_input(value, ctx)
165
- result.merge_result!(argument_name, argument_result) unless argument_result.valid?
166
166
  end
167
167
  end
168
168
 
@@ -46,10 +46,11 @@ module GraphQL
46
46
  end
47
47
 
48
48
  def validate_non_null_input(value, ctx)
49
- result = GraphQL::Query::InputValidationResult.new
49
+ result = nil
50
50
  ensure_array(value).each_with_index do |item, index|
51
51
  item_result = of_type.validate_input(item, ctx)
52
52
  if !item_result.valid?
53
+ result ||= GraphQL::Query::InputValidationResult.new
53
54
  result.merge_result!(index, item_result)
54
55
  end
55
56
  end
@@ -78,23 +78,44 @@ module GraphQL
78
78
  # @return [GraphQL::Schema::Argument]
79
79
  def add_argument(arg_defn)
80
80
  @own_arguments ||= {}
81
- prev_defn = own_arguments[arg_defn.name]
81
+ prev_defn = @own_arguments[arg_defn.name]
82
82
  case prev_defn
83
83
  when nil
84
- own_arguments[arg_defn.name] = arg_defn
84
+ @own_arguments[arg_defn.name] = arg_defn
85
85
  when Array
86
86
  prev_defn << arg_defn
87
87
  when GraphQL::Schema::Argument
88
- own_arguments[arg_defn.name] = [prev_defn, arg_defn]
88
+ @own_arguments[arg_defn.name] = [prev_defn, arg_defn]
89
89
  else
90
90
  raise "Invariant: unexpected `@own_arguments[#{arg_defn.name.inspect}]`: #{prev_defn.inspect}"
91
91
  end
92
92
  arg_defn
93
93
  end
94
94
 
95
+ def remove_argument(arg_defn)
96
+ prev_defn = @own_arguments[arg_defn.name]
97
+ case prev_defn
98
+ when nil
99
+ # done
100
+ when Array
101
+ prev_defn.delete(arg_defn)
102
+ when GraphQL::Schema::Argument
103
+ @own_arguments.delete(arg_defn.name)
104
+ else
105
+ raise "Invariant: unexpected `@own_arguments[#{arg_defn.name.inspect}]`: #{prev_defn.inspect}"
106
+ end
107
+ nil
108
+ end
109
+
95
110
  # @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
96
111
  def arguments(context = GraphQL::Query::NullContext)
97
- inherited_arguments = ((self.is_a?(Class) && superclass.respond_to?(:arguments)) ? superclass.arguments(context) : nil)
112
+ inherited_arguments = if self.is_a?(Class) && superclass.respond_to?(:arguments)
113
+ superclass.arguments(context)
114
+ elsif defined?(@resolver_class) && @resolver_class
115
+ @resolver_class.field_arguments(context)
116
+ else
117
+ nil
118
+ end
98
119
  # Local definitions override inherited ones
99
120
  if own_arguments.any?
100
121
  own_arguments_that_apply = {}
@@ -125,6 +146,10 @@ module GraphQL
125
146
  all_defns.merge!(ancestor.own_arguments)
126
147
  end
127
148
  end
149
+ elsif defined?(@resolver_class) && @resolver_class
150
+ all_defns = {}
151
+ all_defns.merge!(@resolver_class.own_field_arguments)
152
+ all_defns.merge!(own_arguments)
128
153
  else
129
154
  all_defns = own_arguments
130
155
  end
@@ -137,8 +162,13 @@ module GraphQL
137
162
  def get_argument(argument_name, context = GraphQL::Query::NullContext)
138
163
  warden = Warden.from_context(context)
139
164
  if !self.is_a?(Class)
140
- a = own_arguments[argument_name]
141
- a && Warden.visible_entry?(:visible_argument?, a, context, warden)
165
+ if (arg_config = own_arguments[argument_name]) && (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden))
166
+ visible_arg
167
+ elsif defined?(@resolver_class) && @resolver_class
168
+ @resolver_class.get_field_argument(argument_name, context)
169
+ else
170
+ nil
171
+ end
142
172
  else
143
173
  for ancestor in ancestors
144
174
  if ancestor.respond_to?(:own_arguments) &&
@@ -10,9 +10,9 @@ module GraphQL
10
10
 
11
11
  def validate_input(val, ctx)
12
12
  if val.nil?
13
- GraphQL::Query::InputValidationResult.new
13
+ Query::InputValidationResult::VALID
14
14
  else
15
- validate_non_null_input(val, ctx)
15
+ validate_non_null_input(val, ctx) || Query::InputValidationResult::VALID
16
16
  end
17
17
  end
18
18
 
@@ -70,11 +70,32 @@ module GraphQL
70
70
  end
71
71
 
72
72
  class << self
73
+ def dummy
74
+ @dummy ||= begin
75
+ d = Class.new(GraphQL::Schema::Resolver)
76
+ d.argument_class(self.argument_class)
77
+ # TODO make this lazier?
78
+ d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
79
+ d
80
+ end
81
+ end
82
+
83
+ def field_arguments(context = GraphQL::Query::NullContext)
84
+ dummy.arguments(context)
85
+ end
86
+
87
+ def get_field_argument(name, context = GraphQL::Query::NullContext)
88
+ dummy.get_argument(name, context)
89
+ end
90
+
91
+ def own_field_arguments
92
+ dummy.own_arguments
93
+ end
73
94
 
74
95
  # Also apply this argument to the input type:
75
- def argument(*args, **kwargs, &block)
96
+ def argument(*args, own_argument: false, **kwargs, &block)
76
97
  it = input_type # make sure any inherited arguments are already added to it
77
- arg = super
98
+ arg = super(*args, **kwargs, &block)
78
99
 
79
100
  # This definition might be overriding something inherited;
80
101
  # if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
@@ -114,15 +135,6 @@ module GraphQL
114
135
  @input_type ||= generate_input_type
115
136
  end
116
137
 
117
- # Extend {Schema::Mutation.field_options} to add the `input` argument
118
- def field_options
119
- sig = super
120
- # Arguments were added at the root, but they should be nested
121
- sig[:arguments].clear
122
- sig[:arguments][:input] = { type: input_type, required: true, description: "Parameters for #{graphql_name}" }
123
- sig
124
- end
125
-
126
138
  private
127
139
 
128
140
  # Generate the input type for the `input:` argument
@@ -130,11 +142,17 @@ module GraphQL
130
142
  # @return [Class] a subclass of {.input_object_class}
131
143
  def generate_input_type
132
144
  mutation_args = all_argument_definitions
133
- mutation_name = graphql_name
134
145
  mutation_class = self
135
146
  Class.new(input_object_class) do
136
- graphql_name("#{mutation_name}Input")
137
- description("Autogenerated input type of #{mutation_name}")
147
+ class << self
148
+ def default_graphql_name
149
+ "#{self.mutation.graphql_name}Input"
150
+ end
151
+
152
+ def description(new_desc = nil)
153
+ super || "Autogenerated input type of #{self.mutation.graphql_name}"
154
+ end
155
+ end
138
156
  mutation(mutation_class)
139
157
  # these might be inherited:
140
158
  mutation_args.each do |arg|
@@ -20,7 +20,7 @@ module GraphQL
20
20
  @payload_type ||= generate_payload_type
21
21
  end
22
22
 
23
- alias :type :payload_type
23
+ # alias :type :payload_type
24
24
  alias :type_expr :payload_type
25
25
 
26
26
  def field_class(new_class = nil)
@@ -15,8 +15,6 @@ module GraphQL
15
15
  #
16
16
  # A resolver's configuration may be overridden with other keywords in the `field(...)` call.
17
17
  #
18
- # See the {.field_options} to see how a Resolver becomes a set of field configuration options.
19
- #
20
18
  # @see {GraphQL::Schema::Mutation} for a concrete subclass of `Resolver`.
21
19
  # @see {GraphQL::Function} `Resolver` is a replacement for `GraphQL::Function`
22
20
  class Resolver
@@ -210,6 +208,18 @@ module GraphQL
210
208
  end
211
209
 
212
210
  class << self
211
+ def field_arguments(context = GraphQL::Query::NullContext)
212
+ arguments(context)
213
+ end
214
+
215
+ def get_field_argument(name, context = GraphQL::Query::NullContext)
216
+ get_argument(name, context)
217
+ end
218
+
219
+ def own_field_arguments
220
+ own_arguments
221
+ end
222
+
213
223
  # Default `:resolve` set below.
214
224
  # @return [Symbol] The method to call on instances of this object to resolve the field
215
225
  def resolve_method(new_method = nil)
@@ -242,6 +252,14 @@ module GraphQL
242
252
  @null.nil? ? (superclass.respond_to?(:null) ? superclass.null : true) : @null
243
253
  end
244
254
 
255
+ def resolver_method(new_method_name = nil)
256
+ if new_method_name
257
+ @resolver_method = new_method_name
258
+ else
259
+ @resolver_method || :resolve_with_support
260
+ end
261
+ end
262
+
245
263
  # Call this method to get the return type of the field,
246
264
  # or use it as a configuration method to assign a return type
247
265
  # instead of generating one.
@@ -257,8 +275,8 @@ module GraphQL
257
275
  @type_expr = new_type
258
276
  @null = null
259
277
  else
260
- if @type_expr
261
- GraphQL::Schema::Member::BuildType.parse_type(@type_expr, null: @null)
278
+ if type_expr
279
+ GraphQL::Schema::Member::BuildType.parse_type(type_expr, null: self.null)
262
280
  elsif superclass.respond_to?(:type)
263
281
  superclass.type
264
282
  else
@@ -307,47 +325,7 @@ module GraphQL
307
325
 
308
326
  # @return [Boolean] `true` if this resolver or a superclass has an assigned `max_page_size`
309
327
  def has_max_page_size?
310
- defined?(@max_page_size) || (superclass.respond_to?(:has_max_page_size?) && superclass.has_max_page_size?)
311
- end
312
-
313
- def field_options
314
-
315
- all_args = {}
316
- all_argument_definitions.each do |arg|
317
- if (prev_entry = all_args[arg.graphql_name])
318
- if prev_entry.is_a?(Array)
319
- prev_entry << arg
320
- else
321
- all_args[arg.graphql_name] = [prev_entry, arg]
322
- end
323
- else
324
- all_args[arg.graphql_name] = arg
325
- end
326
- end
327
-
328
- field_opts = {
329
- type: type_expr,
330
- description: description,
331
- extras: extras,
332
- resolver_method: :resolve_with_support,
333
- resolver_class: self,
334
- arguments: all_args,
335
- null: null,
336
- complexity: complexity,
337
- broadcastable: broadcastable?,
338
- }
339
-
340
- # If there aren't any, then the returned array is `[].freeze`,
341
- # but passing that along breaks some user code.
342
- if (exts = extensions).any?
343
- field_opts[:extensions] = exts
344
- end
345
-
346
- if has_max_page_size?
347
- field_opts[:max_page_size] = max_page_size
348
- end
349
-
350
- field_opts
328
+ (!!defined?(@max_page_size)) || (superclass.respond_to?(:has_max_page_size?) && superclass.has_max_page_size?)
351
329
  end
352
330
 
353
331
  # A non-normalized type configuration, without `null` applied
@@ -41,7 +41,6 @@ module GraphQL
41
41
  end
42
42
 
43
43
  def validate_non_null_input(value, ctx)
44
- result = Query::InputValidationResult.new
45
44
  coerced_result = begin
46
45
  ctx.query.with_error_handling do
47
46
  coerce_input(value, ctx)
@@ -56,11 +55,12 @@ module GraphQL
56
55
  else
57
56
  " #{GraphQL::Language.serialize(value)}"
58
57
  end
59
- result.add_problem("Could not coerce value#{str_value} to #{graphql_name}")
58
+ Query::InputValidationResult.from_problem("Could not coerce value#{str_value} to #{graphql_name}")
60
59
  elsif coerced_result.is_a?(GraphQL::CoercionError)
61
- result.add_problem(coerced_result.message, message: coerced_result.message, extensions: coerced_result.extensions)
60
+ Query::InputValidationResult.from_problem(coerced_result.message, message: coerced_result.message, extensions: coerced_result.extensions)
61
+ else
62
+ nil
62
63
  end
63
- result
64
64
  end
65
65
  end
66
66
  end
@@ -143,13 +143,6 @@ module GraphQL
143
143
  def self.topic_for(arguments:, field:, scope:)
144
144
  Subscriptions::Serialize.dump_recursive([scope, field.graphql_name, arguments])
145
145
  end
146
-
147
- # Overriding Resolver#field_options to include subscription_scope
148
- def self.field_options
149
- super.merge(
150
- subscription_scope: subscription_scope
151
- )
152
- end
153
146
  end
154
147
  end
155
148
  end
@@ -10,7 +10,7 @@ module GraphQL
10
10
  # should go through a warden. If you access the schema directly,
11
11
  # you may show a client something that it shouldn't be allowed to see.
12
12
  #
13
- # @example Hidding private fields
13
+ # @example Hiding private fields
14
14
  # private_members = -> (member, ctx) { member.metadata[:private] }
15
15
  # result = Schema.execute(query_string, except: private_members)
16
16
  #
@@ -56,17 +56,24 @@ module GraphQL
56
56
  # @param args [Hash<String, Symbol => Object]
57
57
  # @param object [Object]
58
58
  # @param scope [Symbol, String]
59
+ # @param context [Hash]
59
60
  # @return [void]
60
- def trigger(event_name, args, object, scope: nil)
61
+ def trigger(event_name, args, object, scope: nil, context: {})
62
+ # Make something as context-like as possible, even though there isn't a current query:
63
+ context = @schema.context_class.new(
64
+ query: GraphQL::Query.new(@schema, "", validate: false),
65
+ object: nil,
66
+ values: context
67
+ )
61
68
  event_name = event_name.to_s
62
69
 
63
70
  # Try with the verbatim input first:
64
- field = @schema.get_field(@schema.subscription, event_name)
71
+ field = @schema.get_field(@schema.subscription, event_name, context)
65
72
 
66
73
  if field.nil?
67
74
  # And if it wasn't found, normalize it:
68
75
  normalized_event_name = normalize_name(event_name)
69
- field = @schema.get_field(@schema.subscription, normalized_event_name)
76
+ field = @schema.get_field(@schema.subscription, normalized_event_name, context)
70
77
  if field.nil?
71
78
  raise InvalidTriggerError, "No subscription matching trigger: #{event_name} (looked for #{@schema.subscription.graphql_name}.#{normalized_event_name})"
72
79
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.0.0"
3
+ VERSION = "2.0.1"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-09 00:00:00.000000000 Z
11
+ date: 2022-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -366,7 +366,6 @@ files:
366
366
  - lib/graphql/query/context.rb
367
367
  - lib/graphql/query/fingerprint.rb
368
368
  - lib/graphql/query/input_validation_result.rb
369
- - lib/graphql/query/literal_input.rb
370
369
  - lib/graphql/query/null_context.rb
371
370
  - lib/graphql/query/result.rb
372
371
  - lib/graphql/query/validation_pipeline.rb
@@ -1,131 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- class Query
4
- # Turn query string values into something useful for query execution
5
- class LiteralInput
6
- def self.coerce(type, ast_node, variables)
7
- case ast_node
8
- when nil
9
- nil
10
- when Language::Nodes::NullValue
11
- nil
12
- when Language::Nodes::VariableIdentifier
13
- variables[ast_node.name]
14
- else
15
- case type.kind.name
16
- when "SCALAR"
17
- # TODO smell
18
- # This gets used for plain values during subscriber.trigger
19
- if variables
20
- type.coerce_input(ast_node, variables.context)
21
- else
22
- type.coerce_isolated_input(ast_node)
23
- end
24
- when "ENUM"
25
- # TODO smell
26
- # This gets used for plain values sometimes
27
- v = ast_node.is_a?(GraphQL::Language::Nodes::Enum) ? ast_node.name : ast_node
28
- if variables
29
- type.coerce_input(v, variables.context)
30
- else
31
- type.coerce_isolated_input(v)
32
- end
33
- when "NON_NULL"
34
- LiteralInput.coerce(type.of_type, ast_node, variables)
35
- when "LIST"
36
- if ast_node.is_a?(Array)
37
- ast_node.map { |element_ast| LiteralInput.coerce(type.of_type, element_ast, variables) }
38
- else
39
- [LiteralInput.coerce(type.of_type, ast_node, variables)]
40
- end
41
- when "INPUT_OBJECT"
42
- # TODO smell: handling AST vs handling plain Ruby
43
- next_args = ast_node.is_a?(Hash) ? ast_node : ast_node.arguments
44
- from_arguments(next_args, type, variables)
45
- else
46
- raise "Invariant: unexpected type to coerce to: #{type}"
47
- end
48
- end
49
- end
50
-
51
- def self.from_arguments(ast_arguments, argument_owner, variables)
52
- context = variables ? variables.context : nil
53
- values_hash = {}
54
- defaults_used = Set.new
55
-
56
- indexed_arguments = case ast_arguments
57
- when Hash
58
- ast_arguments
59
- when Array
60
- ast_arguments.each_with_object({}) { |a, memo| memo[a.name] = a }
61
- else
62
- raise ArgumentError, "Unexpected ast_arguments: #{ast_arguments}"
63
- end
64
-
65
- argument_defns = argument_owner.arguments(context || GraphQL::Query::NullContext)
66
- argument_defns.each do |arg_name, arg_defn|
67
- ast_arg = indexed_arguments[arg_name]
68
- # First, check the argument in the AST.
69
- # If the value is a variable,
70
- # only add a value if the variable is actually present.
71
- # Otherwise, coerce the value in the AST, prepare the value and add it.
72
- #
73
- # TODO: since indexed_arguments can come from a plain Ruby hash,
74
- # have to check for `false` or `nil` as hash values. This is getting smelly :S
75
- if indexed_arguments.key?(arg_name)
76
- arg_value = ast_arg.is_a?(GraphQL::Language::Nodes::Argument) ? ast_arg.value : ast_arg
77
-
78
- value_is_a_variable = arg_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
79
-
80
- if (!value_is_a_variable || (value_is_a_variable && variables.key?(arg_value.name)))
81
-
82
- value = coerce(arg_defn.type, arg_value, variables)
83
- # Legacy `prepare` application
84
- if arg_defn.is_a?(GraphQL::Argument)
85
- value = arg_defn.prepare(value, context)
86
- end
87
-
88
- if value.is_a?(GraphQL::ExecutionError)
89
- value.ast_node = ast_arg
90
- raise value
91
- end
92
-
93
- values_hash[arg_name] = value
94
- end
95
- end
96
-
97
- # Then, the definition for a default value.
98
- # If the definition has a default value and
99
- # a value wasn't provided from the AST,
100
- # then add the default value.
101
- if arg_defn.default_value? && !values_hash.key?(arg_name)
102
- value = arg_defn.default_value
103
- defaults_used << arg_name
104
- # `context` isn't present when pre-calculating defaults
105
- if context
106
- if arg_defn.is_a?(GraphQL::Argument)
107
- value = arg_defn.prepare(value, context)
108
- end
109
- if value.is_a?(GraphQL::ExecutionError)
110
- value.ast_node = ast_arg
111
- raise value
112
- end
113
- end
114
- values_hash[arg_name] = value
115
- end
116
- end
117
-
118
- # A Schema::InputObject, Schema::GraphQL::Field, Schema::Directive, logic from Interpreter::Arguments
119
- ruby_kwargs = {}
120
- values_hash.each do |key, value|
121
- ruby_kwargs[Schema::Member::BuildType.underscore(key).to_sym] = value
122
- end
123
- if argument_owner.is_a?(Class) && argument_owner < GraphQL::Schema::InputObject
124
- argument_owner.new(ruby_kwargs: ruby_kwargs, context: context, defaults_used: defaults_used)
125
- else
126
- ruby_kwargs
127
- end
128
- end
129
- end
130
- end
131
- end