graphql 1.12.8 → 1.12.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10ea46e2edf136b445de343ee1a58dd98b0a6c5800ef31923d7d184198ed58fd
4
- data.tar.gz: f2f6dff0bf020bfef04769deb3fd548317121f0e636d7a47a94baea862f341ac
3
+ metadata.gz: bcd53f90295f4a4444841bee25b0329ba993ea2ae3d10bb9ba807f262b9354ca
4
+ data.tar.gz: af7807ae411d7f50252758b64c2248fc3c640cc2930c22c0740bae25d567e7ef
5
5
  SHA512:
6
- metadata.gz: a79fde55cedcd391b7dfe7c141e22c51563aeefceb0fda494dd50f4281fefb0104f8663e688395268c95361ee10e01c7bfb69250368441ec8b8c9ad0d00ac279
7
- data.tar.gz: 49a7b0f5cdf9d3a64ff05b0898c7ef93c6cdfd210baf93affa25afe3f3d7b59187fe3e7727f467af9411a085c5f6e8e281fafd27352fe97128f98bd82ffde330
6
+ metadata.gz: 770c7bde4c17619f645d77829bb9ca78bed5f944a7609a31095d9e8e859adbf0728988a640ad98a3a10fbccc73f658a0906296d1d6bed5f0f6d77b53a9bf9c79
7
+ data.tar.gz: 9fee4b7cb3e63c07a702462c1b6ebc0a0bd2d1342a4d22803839b63a03d8a910ebd6189a748695ba75e2732e1af30e9a6b9587d72be602eee6b1db43822bb779
@@ -122,7 +122,7 @@ module Graphql
122
122
  if options.api?
123
123
  say("Skipped graphiql, as this rails project is API only")
124
124
  say(" You may wish to use GraphiQL.app for development: https://github.com/skevy/graphiql-app")
125
- elsif !options[:skip_graphiql]
125
+ elsif !options[:skip_graphiql] && !File.read(Rails.root.join("Gemfile")).include?("graphiql-rails")
126
126
  gem("graphiql-rails", group: :development)
127
127
 
128
128
  # This is a little cheat just to get cleaner shell output:
@@ -15,9 +15,9 @@ class GraphqlController < ApplicationController
15
15
  }
16
16
  result = <%= schema_name %>.execute(query, variables: variables, context: context, operation_name: operation_name)
17
17
  render json: result
18
- rescue => e
18
+ rescue StandardError => e
19
19
  raise e unless Rails.env.development?
20
- handle_error_in_development e
20
+ handle_error_in_development(e)
21
21
  end
22
22
 
23
23
  private
data/lib/graphql.rb CHANGED
@@ -81,10 +81,19 @@ end
81
81
  # Order matters for these:
82
82
 
83
83
  require "graphql/execution_error"
84
+ require "graphql/runtime_type_error"
85
+ require "graphql/unresolved_type_error"
86
+ require "graphql/invalid_null_error"
87
+ require "graphql/analysis_error"
88
+ require "graphql/coercion_error"
89
+ require "graphql/invalid_name_error"
90
+ require "graphql/integer_decoding_error"
91
+ require "graphql/integer_encoding_error"
92
+ require "graphql/string_encoding_error"
93
+
84
94
  require "graphql/define"
85
95
  require "graphql/base_type"
86
96
  require "graphql/object_type"
87
-
88
97
  require "graphql/enum_type"
89
98
  require "graphql/input_object_type"
90
99
  require "graphql/interface_type"
@@ -109,9 +118,6 @@ require "graphql/analysis"
109
118
  require "graphql/tracing"
110
119
  require "graphql/dig"
111
120
  require "graphql/execution"
112
- require "graphql/runtime_type_error"
113
- require "graphql/unresolved_type_error"
114
- require "graphql/invalid_null_error"
115
121
  require "graphql/pagination"
116
122
  require "graphql/schema"
117
123
  require "graphql/query"
@@ -133,12 +139,6 @@ require "graphql/static_validation"
133
139
  require "graphql/dataloader"
134
140
  require "graphql/introspection"
135
141
 
136
- require "graphql/analysis_error"
137
- require "graphql/coercion_error"
138
- require "graphql/invalid_name_error"
139
- require "graphql/integer_decoding_error"
140
- require "graphql/integer_encoding_error"
141
- require "graphql/string_encoding_error"
142
142
  require "graphql/version"
143
143
  require "graphql/compatibility"
144
144
  require "graphql/function"
@@ -29,7 +29,12 @@ module GraphQL
29
29
 
30
30
  def initialize
31
31
  @source_cache = Hash.new { |h, source_class| h[source_class] = Hash.new { |h2, batch_parameters|
32
- source = source_class.new(*batch_parameters)
32
+ source = if RUBY_VERSION < "3"
33
+ source_class.new(*batch_parameters)
34
+ else
35
+ batch_args, batch_kwargs = batch_parameters
36
+ source_class.new(*batch_args, **batch_kwargs)
37
+ end
33
38
  source.setup(self)
34
39
  h2[batch_parameters] = source
35
40
  }
@@ -43,8 +48,15 @@ module GraphQL
43
48
  # @param batch_parameters [Array<Object>]
44
49
  # @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`,
45
50
  # and cached for the lifetime of this {Multiplex}.
46
- def with(source_class, *batch_parameters)
47
- @source_cache[source_class][batch_parameters]
51
+ if RUBY_VERSION < "3"
52
+ def with(source_class, *batch_parameters)
53
+ @source_cache[source_class][batch_parameters]
54
+ end
55
+ else
56
+ def with(source_class, *batch_args, **batch_kwargs)
57
+ batch_parameters = [batch_args, batch_kwargs]
58
+ @source_cache[source_class][batch_parameters]
59
+ end
48
60
  end
49
61
 
50
62
  # Tell the dataloader that this fiber is waiting for data.
@@ -104,7 +116,7 @@ module GraphQL
104
116
  while @pending_jobs.any?
105
117
  # Create a Fiber to consume jobs until one of the jobs yields
106
118
  # or jobs run out
107
- f = Fiber.new {
119
+ f = spawn_fiber {
108
120
  while (job = @pending_jobs.shift)
109
121
  job.call
110
122
  end
@@ -191,7 +203,7 @@ module GraphQL
191
203
  #
192
204
  # This design could probably be improved by maintaining a `@pending_sources` queue which is shared by the fibers,
193
205
  # similar to `@pending_jobs`. That way, when a fiber is resumed, it would never pick up work that was finished by a different fiber.
194
- source_fiber = Fiber.new do
206
+ source_fiber = spawn_fiber do
195
207
  pending_sources.each(&:run_pending_keys)
196
208
  end
197
209
  end
@@ -204,5 +216,24 @@ module GraphQL
204
216
  rescue UncaughtThrowError => e
205
217
  throw e.tag, e.value
206
218
  end
219
+
220
+ # Copies the thread local vars into the fiber thread local vars. Many
221
+ # gems (such as RequestStore, MiniRacer, etc.) rely on thread local vars
222
+ # to keep track of execution context, and without this they do not
223
+ # behave as expected.
224
+ #
225
+ # @see https://github.com/rmosolgo/graphql-ruby/issues/3449
226
+ def spawn_fiber
227
+ fiber_locals = {}
228
+
229
+ Thread.current.keys.each do |fiber_var_key|
230
+ fiber_locals[fiber_var_key] = Thread.current[fiber_var_key]
231
+ end
232
+
233
+ Fiber.new do
234
+ fiber_locals.each { |k, v| Thread.current[k] = v }
235
+ yield
236
+ end
237
+ end
207
238
  end
208
239
  end
@@ -50,7 +50,7 @@ module GraphQL
50
50
  root_type = schema.root_type_for_operation(root_op_type)
51
51
  path = []
52
52
  set_all_interpreter_context(query.root_value, nil, nil, path)
53
- object_proxy = authorized_new(root_type, query.root_value, context, path)
53
+ object_proxy = authorized_new(root_type, query.root_value, context)
54
54
  object_proxy = schema.sync_lazy(object_proxy)
55
55
  if object_proxy.nil?
56
56
  # Root .authorized? returned false.
@@ -193,7 +193,7 @@ module GraphQL
193
193
  object = owner_object
194
194
 
195
195
  if is_introspection
196
- object = authorized_new(field_defn.owner, object, context, next_path)
196
+ object = authorized_new(field_defn.owner, object, context)
197
197
  end
198
198
 
199
199
  total_args_count = field_defn.arguments.size
@@ -246,11 +246,17 @@ module GraphQL
246
246
  # Use this flag to tell Interpreter::Arguments to add itself
247
247
  # to the keyword args hash _before_ freezing everything.
248
248
  extra_args[:argument_details] = :__arguments_add_self
249
+ when :irep_node
250
+ # This is used by `__typename` in order to support the legacy runtime,
251
+ # but it has no use here (and it's always `nil`).
252
+ # Stop adding it here to avoid the overhead of `.merge_extras` below.
249
253
  else
250
254
  extra_args[extra] = field_defn.fetch_extra(extra, context)
251
255
  end
252
256
  end
253
- resolved_arguments = resolved_arguments.merge_extras(extra_args)
257
+ if extra_args.any?
258
+ resolved_arguments = resolved_arguments.merge_extras(extra_args)
259
+ end
254
260
  resolved_arguments.keyword_arguments
255
261
  end
256
262
 
@@ -372,7 +378,7 @@ module GraphQL
372
378
  end
373
379
  when "OBJECT"
374
380
  object_proxy = begin
375
- authorized_new(current_type, value, context, path)
381
+ authorized_new(current_type, value, context)
376
382
  rescue GraphQL::ExecutionError => err
377
383
  err
378
384
  end
@@ -635,22 +641,8 @@ module GraphQL
635
641
  end
636
642
  end
637
643
 
638
- def authorized_new(type, value, context, path)
639
- trace_payload = { context: context, type: type, object: value, path: path }
640
-
641
- auth_val = context.query.trace("authorized", trace_payload) do
642
- type.authorized_new(value, context)
643
- end
644
-
645
- if context.schema.lazy?(auth_val)
646
- GraphQL::Execution::Lazy.new do
647
- context.query.trace("authorized_lazy", trace_payload) do
648
- context.schema.sync_lazy(auth_val)
649
- end
650
- end
651
- else
652
- auth_val
653
- end
644
+ def authorized_new(type, value, context)
645
+ type.authorized_new(value, context)
654
646
  end
655
647
  end
656
648
  end
@@ -79,7 +79,7 @@ module GraphQL
79
79
  context: context,
80
80
  parent: parent,
81
81
  field: field,
82
- max_page_size: field.max_page_size || context.schema.default_max_page_size,
82
+ max_page_size: field.has_max_page_size? ? field.max_page_size : context.schema.default_max_page_size,
83
83
  first: arguments[:first],
84
84
  after: arguments[:after],
85
85
  last: arguments[:last],
@@ -355,23 +355,6 @@ module GraphQL
355
355
  # For forwards-compatibility with Schema classes
356
356
  alias :graphql_definition :itself
357
357
 
358
- # Validate a query string according to this schema.
359
- # @param string_or_document [String, GraphQL::Language::Nodes::Document]
360
- # @return [Array<GraphQL::StaticValidation::Error >]
361
- def validate(string_or_document, rules: nil, context: nil)
362
- doc = if string_or_document.is_a?(String)
363
- GraphQL.parse(string_or_document)
364
- else
365
- string_or_document
366
- end
367
- query = GraphQL::Query.new(self, document: doc, context: context)
368
- validator_opts = { schema: self }
369
- rules && (validator_opts[:rules] = rules)
370
- validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
371
- res = validator.validate(query, timeout: validate_timeout)
372
- res[:errors]
373
- end
374
-
375
358
  def deprecated_define(**kwargs, &block)
376
359
  super
377
360
  ensure_defined
@@ -712,6 +695,7 @@ module GraphQL
712
695
  def_delegators :_schema_class, :unauthorized_object, :unauthorized_field, :inaccessible_fields
713
696
  def_delegators :_schema_class, :directive
714
697
  def_delegators :_schema_class, :error_handler
698
+ def_delegators :_schema_class, :validate
715
699
 
716
700
 
717
701
  # Given this schema member, find the class-based definition object
@@ -861,7 +845,6 @@ module GraphQL
861
845
  def_delegators :graphql_definition,
862
846
  # Execution
863
847
  :execution_strategy_for_operation,
864
- :validate,
865
848
  # Configuration
866
849
  :metadata, :redefine,
867
850
  :id_from_object_proc, :object_from_id_proc,
@@ -1293,6 +1276,23 @@ module GraphQL
1293
1276
  end
1294
1277
  end
1295
1278
 
1279
+ # Validate a query string according to this schema.
1280
+ # @param string_or_document [String, GraphQL::Language::Nodes::Document]
1281
+ # @return [Array<GraphQL::StaticValidation::Error >]
1282
+ def validate(string_or_document, rules: nil, context: nil)
1283
+ doc = if string_or_document.is_a?(String)
1284
+ GraphQL.parse(string_or_document)
1285
+ else
1286
+ string_or_document
1287
+ end
1288
+ query = GraphQL::Query.new(self, document: doc, context: context)
1289
+ validator_opts = { schema: self }
1290
+ rules && (validator_opts[:rules] = rules)
1291
+ validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
1292
+ res = validator.validate(query, timeout: validate_timeout)
1293
+ res[:errors]
1294
+ end
1295
+
1296
1296
  attr_writer :max_complexity
1297
1297
 
1298
1298
  def max_complexity(max_complexity = nil)
@@ -226,8 +226,8 @@ module GraphQL
226
226
  # It's funny to think of a _result_ of an input object.
227
227
  # This is used for rendering the default value in introspection responses.
228
228
  def coerce_result(value, ctx)
229
- # Allow the application to provide values as :symbols, and convert them to the strings
230
- value = value.reduce({}) { |memo, (k, v)| memo[k.to_s] = v; memo }
229
+ # Allow the application to provide values as :snake_symbols, and convert them to the camelStrings
230
+ value = value.reduce({}) { |memo, (k, v)| memo[Member::BuildType.camelize(k.to_s)] = v; memo }
231
231
 
232
232
  result = {}
233
233
 
@@ -113,27 +113,15 @@ module GraphQL
113
113
  end
114
114
 
115
115
  def visible?(context)
116
- if @mutation
117
- @mutation.visible?(context)
118
- else
119
- true
120
- end
116
+ true
121
117
  end
122
118
 
123
119
  def accessible?(context)
124
- if @mutation
125
- @mutation.accessible?(context)
126
- else
127
- true
128
- end
120
+ true
129
121
  end
130
122
 
131
123
  def authorized?(object, context)
132
- if @mutation
133
- @mutation.authorized?(object, context)
134
- else
135
- true
136
- end
124
+ true
137
125
  end
138
126
  end
139
127
  end
@@ -48,12 +48,26 @@ module GraphQL
48
48
  # @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy]
49
49
  # @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false`
50
50
  def authorized_new(object, context)
51
- auth_val = context.query.with_error_handling do
52
- begin
53
- authorized?(object, context)
54
- rescue GraphQL::UnauthorizedError => err
55
- context.schema.unauthorized_object(err)
51
+ trace_payload = { context: context, type: self, object: object, path: context[:current_path] }
52
+
53
+ maybe_lazy_auth_val = context.query.trace("authorized", trace_payload) do
54
+ context.query.with_error_handling do
55
+ begin
56
+ authorized?(object, context)
57
+ rescue GraphQL::UnauthorizedError => err
58
+ context.schema.unauthorized_object(err)
59
+ end
60
+ end
61
+ end
62
+
63
+ auth_val = if context.schema.lazy?(maybe_lazy_auth_val)
64
+ GraphQL::Execution::Lazy.new do
65
+ context.query.trace("authorized_lazy", trace_payload) do
66
+ context.schema.sync_lazy(maybe_lazy_auth_val)
67
+ end
56
68
  end
69
+ else
70
+ maybe_lazy_auth_val
57
71
  end
58
72
 
59
73
  context.schema.after_lazy(auth_val) do |is_authorized|
@@ -4,7 +4,7 @@ module GraphQL
4
4
  module DirectivesAreDefined
5
5
  def initialize(*)
6
6
  super
7
- @directive_names = context.schema.directives.keys
7
+ @directive_names = context.warden.directives.map(&:graphql_name)
8
8
  end
9
9
 
10
10
  def on_directive(node, parent)
@@ -373,17 +373,26 @@ module GraphQL
373
373
  # In this context, `parents` represends the "self scope" of the field,
374
374
  # what types may be found at this point in the query.
375
375
  def mutually_exclusive?(parents1, parents2)
376
- parents1.each do |type1|
377
- parents2.each do |type2|
378
- # If the types we're comparing are both different object types,
379
- # they have to be mutually exclusive.
380
- if type1 != type2 && type1.kind.object? && type2.kind.object?
381
- return true
376
+ if parents1.empty? || parents2.empty?
377
+ false
378
+ elsif parents1.length == parents2.length
379
+ parents1.length.times.any? do |i|
380
+ type1 = parents1[i - 1]
381
+ type2 = parents2[i - 1]
382
+ if type1 == type2
383
+ # If the types we're comparing are the same type,
384
+ # then they aren't mutually exclusive
385
+ false
386
+ else
387
+ # Check if these two scopes have _any_ types in common.
388
+ possible_right_types = context.query.possible_types(type1)
389
+ possible_left_types = context.query.possible_types(type2)
390
+ (possible_right_types & possible_left_types).empty?
382
391
  end
383
392
  end
393
+ else
394
+ true
384
395
  end
385
-
386
- false
387
396
  end
388
397
  end
389
398
  end
@@ -34,12 +34,12 @@ module GraphQL
34
34
  # channel: self,
35
35
  # }
36
36
  #
37
- # result = MySchema.execute({
37
+ # result = MySchema.execute(
38
38
  # query: query,
39
39
  # context: context,
40
40
  # variables: variables,
41
41
  # operation_name: operation_name
42
- # })
42
+ # )
43
43
  #
44
44
  # payload = {
45
45
  # result: result.to_h,
@@ -146,14 +146,15 @@ module GraphQL
146
146
  def setup_stream(channel, initial_event)
147
147
  topic = initial_event.topic
148
148
  channel.stream_from(stream_event_name(initial_event), coder: @action_cable_coder) do |message|
149
- object = @serializer.load(message)
150
149
  events_by_fingerprint = @events[topic]
150
+ object = nil
151
151
  events_by_fingerprint.each do |_fingerprint, events|
152
152
  if events.any? && events.first == initial_event
153
153
  # The fingerprint has told us that this response should be shared by all subscribers,
154
154
  # so just run it once, then deliver the result to every subscriber
155
155
  first_event = events.first
156
156
  first_subscription_id = first_event.context.fetch(:subscription_id)
157
+ object ||= @serializer.load(message)
157
158
  result = execute_update(first_subscription_id, first_event, object)
158
159
  # Having calculated the result _once_, send the same payload to all subscribers
159
160
  events.each do |event|
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.12.8"
3
+ VERSION = "1.12.9"
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: 1.12.8
4
+ version: 1.12.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-12 00:00:00.000000000 Z
11
+ date: 2021-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips