graphql 2.0.15 → 2.0.16

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: 46b43157a331eae41dd54897d24dbc3adac190910237c7c1c158fc2bc32b6616
4
- data.tar.gz: 118ac97b740ba1340d3e6b9aee5fa2f81ca25dc8769a98ec0d8e25d63e839711
3
+ metadata.gz: d626d72a5ae0bfb10f8fcdb9cbd781f04b5bb38b3f90b20de062a78679c254a8
4
+ data.tar.gz: 6793ba627f3f59a43426258e187567f391a44f8973816fb0b1e1eb7784ae19b0
5
5
  SHA512:
6
- metadata.gz: be38356ecdff7f8a9627bb021e83d13ea92e951b287386e76fd6e42ff05ef0a5a37ce9c98e6e4bf1834459989558f6c8b512c98e7642bef2c624578c1db24932
7
- data.tar.gz: b0b75f342dca7a731b1b59e6399d364184152ceb0c5deb9fb4338a3582b3b6e38bf789a53839f6def9eb4b9c079c45e74675fb608a184dff4e6aeaf0ce052dfd
6
+ metadata.gz: 5958721b08e45d886036f8afe0a5942e443117d9aa71d94450b727ec20d13b63a945a97c247f8605d4eb1ae8631ba301599e95b938355f18dc7c5bf64e2b1b44
7
+ data.tar.gz: 9d029e7d0288c87cc9c09e627538b5e93b3d1ec7e785914b135088ec7bafcf8172458fa1d68a95a6385786d472c7260f6bc82b719e86743b5dac1f2a87501f7c
@@ -93,7 +93,7 @@ module GraphQL
93
93
  class ScopedContext
94
94
  def initialize(query_context)
95
95
  @query_context = query_context
96
- @path_contexts = {}
96
+ @scoped_contexts = {}
97
97
  @no_path = [].freeze
98
98
  end
99
99
 
@@ -106,12 +106,17 @@ module GraphQL
106
106
  end
107
107
 
108
108
  def merge!(hash)
109
- current_ctx = @path_contexts[current_path] ||= {}
110
- current_ctx.merge!(hash)
109
+ ctx = @scoped_contexts
110
+ current_path.each do |path_part|
111
+ ctx = ctx[path_part] ||= { parent: ctx }
112
+ end
113
+ this_scoped_ctx = ctx[:scoped_context] ||= {}
114
+ this_scoped_ctx.merge!(hash)
111
115
  end
112
116
 
113
117
  def current_path
114
- @query_context[:current_path] || @no_path
118
+ thread_info = Thread.current[:__graphql_runtime_info]
119
+ (thread_info && thread_info[:current_path]) || @no_path
115
120
  end
116
121
 
117
122
  def key?(key)
@@ -151,16 +156,20 @@ module GraphQL
151
156
  # Start at the current location,
152
157
  # but look up the tree for previously-assigned scoped values
153
158
  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)
159
+ ctx = @scoped_contexts
160
+ current_path.each do |path_part|
161
+ if ctx.key?(path_part)
162
+ ctx = ctx[path_part]
163
+ else
164
+ break
165
+ end
157
166
  end
158
167
 
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)
168
+ while ctx
169
+ if (scoped_ctx = ctx[:scoped_context])
170
+ yield(scoped_ctx)
163
171
  end
172
+ ctx = ctx[:parent]
164
173
  end
165
174
  end
166
175
  end
@@ -195,13 +204,16 @@ module GraphQL
195
204
 
196
205
  # Lookup `key` from the hash passed to {Schema#execute} as `context:`
197
206
  def [](key)
198
- if RUNTIME_METADATA_KEYS.include?(key)
207
+ if @scoped_context.key?(key)
208
+ @scoped_context[key]
209
+ elsif @provided_values.key?(key)
210
+ @provided_values[key]
211
+ elsif RUNTIME_METADATA_KEYS.include?(key)
199
212
  thread_info = Thread.current[:__graphql_runtime_info]
200
213
  thread_info && thread_info[key]
201
- elsif @scoped_context.key?(key)
202
- @scoped_context[key]
203
214
  else
204
- @provided_values[key]
215
+ # not found
216
+ nil
205
217
  end
206
218
  end
207
219
 
@@ -235,7 +247,7 @@ module GraphQL
235
247
  def dig(key, *other_keys)
236
248
  if RUNTIME_METADATA_KEYS.include?(key)
237
249
  (thread_info = Thread.current[:__graphql_runtime_info]).key?(key) &&
238
- thread_info.dig(key)
250
+ thread_info.dig(key, *other_keys)
239
251
  elsif @scoped_context.key?(key)
240
252
  @scoped_context.dig(key, *other_keys)
241
253
  else
@@ -23,6 +23,10 @@ module GraphQL
23
23
  # @example Invoking the task from Ruby
24
24
  # require "rake"
25
25
  # Rake::Task["graphql:schema:dump"].invoke
26
+ #
27
+ # @example Providing arguments to build the introspection query
28
+ # require "graphql/rake_task"
29
+ # GraphQL::RakeTask.new(schema_name: "MySchema", include_is_one_of: true)
26
30
  class RakeTask
27
31
  include Rake::DSL
28
32
 
@@ -37,6 +41,11 @@ module GraphQL
37
41
  directory: ".",
38
42
  idl_outfile: "schema.graphql",
39
43
  json_outfile: "schema.json",
44
+ include_deprecated_args: true,
45
+ include_schema_description: false,
46
+ include_is_repeatable: false,
47
+ include_specified_by_url: false,
48
+ include_is_one_of: false
40
49
  }
41
50
 
42
51
  # @return [String] Namespace for generated tasks
@@ -74,6 +83,10 @@ module GraphQL
74
83
  # @return [String] directory for IDL & JSON files
75
84
  attr_accessor :directory
76
85
 
86
+ # @return [Boolean] Options for additional fields in the introspection query JSON response
87
+ # @see GraphQL::Schema.as_json
88
+ attr_accessor :include_deprecated_args, :include_schema_description, :include_is_repeatable, :include_specified_by_url, :include_is_one_of
89
+
77
90
  # Set the parameters of this task by passing keyword arguments
78
91
  # or assigning attributes inside the block
79
92
  def initialize(options = {})
@@ -96,7 +109,21 @@ module GraphQL
96
109
  def write_outfile(method_name, file)
97
110
  schema = @load_schema.call(self)
98
111
  context = @load_context.call(self)
99
- result = schema.public_send(method_name, only: @only, except: @except, context: context)
112
+ result = case method_name
113
+ when :to_json
114
+ schema.to_json(
115
+ include_is_one_of: include_is_one_of,
116
+ include_deprecated_args: include_deprecated_args,
117
+ include_is_repeatable: include_is_repeatable,
118
+ include_specified_by_url: include_specified_by_url,
119
+ include_schema_description: include_schema_description,
120
+ only: @only, except: @except, context: context
121
+ )
122
+ when :to_definition
123
+ schema.to_definition(only: @only, except: @except, context: context)
124
+ else
125
+ raise ArgumentError, "Unexpected schema dump method: #{method_name.inspect}"
126
+ end
100
127
  dir = File.dirname(file)
101
128
  FileUtils.mkdir_p(dir)
102
129
  if !result.end_with?("\n")
@@ -70,9 +70,18 @@ module GraphQL
70
70
  private
71
71
 
72
72
  def assert_valid_union_member(type_defn)
73
- if type_defn.is_a?(Module) && !type_defn.is_a?(Class)
73
+ case type_defn
74
+ when Class
75
+ if !type_defn.kind.object?
76
+ raise ArgumentError, "Union possible_types can only be object types (not #{type_defn.kind.name}, #{type_defn.inspect})"
77
+ end
78
+ when Module
74
79
  # it's an interface type, defined as a module
75
80
  raise ArgumentError, "Union possible_types can only be object types (not interface types), remove #{type_defn.graphql_name} (#{type_defn.inspect})"
81
+ when String, GraphQL::Schema::LateBoundType
82
+ # Ok - assume it will get checked later
83
+ else
84
+ raise ArgumentError, "Union possible_types can only be class-based GraphQL types (not #{type_defn.inspect} (#{type_defn.class.name}))."
76
85
  end
77
86
  end
78
87
  end
@@ -154,9 +154,22 @@ module GraphQL
154
154
  # @param context [Hash]
155
155
  # @param only [<#call(member, ctx)>]
156
156
  # @param except [<#call(member, ctx)>]
157
+ # @param include_deprecated_args [Boolean] If true, deprecated arguments will be included in the JSON response
158
+ # @param include_schema_description [Boolean] If true, the schema's description will be queried and included in the response
159
+ # @param include_is_repeatable [Boolean] If true, `isRepeatable: true|false` will be included with the schema's directives
160
+ # @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response
161
+ # @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects
157
162
  # @return [Hash] GraphQL result
158
- def as_json(only: nil, except: nil, context: {})
159
- execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
163
+ def as_json(only: nil, except: nil, context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
164
+ introspection_query = Introspection.query(
165
+ include_deprecated_args: include_deprecated_args,
166
+ include_schema_description: include_schema_description,
167
+ include_is_repeatable: include_is_repeatable,
168
+ include_is_one_of: include_is_one_of,
169
+ include_specified_by_url: include_specified_by_url,
170
+ )
171
+
172
+ execute(introspection_query, only: only, except: except, context: context).to_h
160
173
  end
161
174
 
162
175
  # Return the GraphQL IDL for the schema
@@ -127,8 +127,14 @@ module GraphQL
127
127
  # same name as if they were the same name. If _any_ of the fragments
128
128
  # with that name has a dependency, we record it.
129
129
  independent_fragment_nodes = @defdep_fragment_definitions.values.flatten - @defdep_immediate_dependencies.keys
130
-
130
+ visited_fragment_names = Set.new
131
131
  while fragment_node = independent_fragment_nodes.pop
132
+ if visited_fragment_names.add?(fragment_node.name)
133
+ # this is a new fragment name
134
+ else
135
+ # this is a duplicate fragment name
136
+ next
137
+ end
132
138
  loops += 1
133
139
  if loops > max_loops
134
140
  raise("Resolution loops exceeded the number of definitions; infinite loop detected. (Max: #{max_loops}, Current: #{loops})")
@@ -91,7 +91,13 @@ module GraphQL
91
91
  # A per-process map of subscriptions to deliver.
92
92
  # This is provided by Rails, so let's use it
93
93
  @subscriptions = Concurrent::Map.new
94
- @events = Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new { |h2, k2| h2[k2] = Concurrent::Array.new } }
94
+ @events = Concurrent::Map.new do |h, k|
95
+ h.compute_if_absent(k) do
96
+ Concurrent::Map.new do |h2, k2|
97
+ h2.compute_if_absent(k2) { Concurrent::Array.new }
98
+ end
99
+ end
100
+ end
95
101
  @action_cable = action_cable
96
102
  @action_cable_coder = action_cable_coder
97
103
  @serializer = serializer
@@ -90,6 +90,7 @@ module GraphQL
90
90
  arguments: normalized_args,
91
91
  field: field,
92
92
  scope: scope,
93
+ context: context,
93
94
  )
94
95
  execute_all(event, object)
95
96
  end
@@ -124,6 +125,10 @@ module GraphQL
124
125
  variables: variables,
125
126
  root_value: object,
126
127
  }
128
+
129
+ # merge event's and query's context together
130
+ context.merge!(event.context) unless event.context.nil? || context.nil?
131
+
127
132
  execute_options[:validate] = validate_update?(**execute_options)
128
133
  result = @schema.execute(**execute_options)
129
134
  subscriptions_context = result.context.namespace(:subscriptions)
@@ -50,6 +50,8 @@ module GraphQL
50
50
  null: edges_nullable,
51
51
  description: "A list of edges.",
52
52
  connection: false,
53
+ # Assume that the connection was scoped before this step:
54
+ scope: false,
53
55
  }
54
56
 
55
57
  if field_options
@@ -135,6 +137,8 @@ module GraphQL
135
137
  null: nullable,
136
138
  description: "A list of nodes.",
137
139
  connection: false,
140
+ # Assume that the connection was scoped before this step:
141
+ scope: false,
138
142
  }
139
143
  if field_options
140
144
  base_field_options.merge!(field_options)
@@ -13,7 +13,13 @@ module GraphQL
13
13
  end
14
14
 
15
15
  def default_relay?
16
- !!@default_relay
16
+ if defined?(@default_relay)
17
+ @default_relay
18
+ elsif self.is_a?(Class)
19
+ superclass.respond_to?(:default_relay?) && superclass.default_relay?
20
+ else
21
+ false
22
+ end
17
23
  end
18
24
  end
19
25
  end
@@ -8,6 +8,7 @@ module GraphQL
8
8
  child_class.description("An edge in a connection.")
9
9
  child_class.field(:cursor, String, null: false, description: "A cursor for use in pagination.")
10
10
  child_class.extend(ClassMethods)
11
+ child_class.extend(GraphQL::Types::Relay::DefaultRelay)
11
12
  child_class.node_nullable(true)
12
13
  end
13
14
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.0.15"
3
+ VERSION = "2.0.16"
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.15
4
+ version: 2.0.16
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-10-21 00:00:00.000000000 Z
11
+ date: 2022-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -538,7 +538,6 @@ files:
538
538
  - lib/graphql/tracing/appoptics_tracing.rb
539
539
  - lib/graphql/tracing/appsignal_tracing.rb
540
540
  - lib/graphql/tracing/data_dog_tracing.rb
541
- - lib/graphql/tracing/instrumentation_tracing.rb
542
541
  - lib/graphql/tracing/new_relic_tracing.rb
543
542
  - lib/graphql/tracing/notifications_tracing.rb
544
543
  - lib/graphql/tracing/platform_tracing.rb
@@ -598,7 +597,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
598
597
  - !ruby/object:Gem::Version
599
598
  version: '0'
600
599
  requirements: []
601
- rubygems_version: 3.2.33
600
+ rubygems_version: 3.3.3
602
601
  signing_key:
603
602
  specification_version: 4
604
603
  summary: A GraphQL language and runtime for Ruby
@@ -1,83 +0,0 @@
1
- module GraphQL
2
- module Tracing
3
- class InstrumentationTracing
4
- def initialize(schema)
5
- @schema = schema
6
- end
7
-
8
- def trace(event, data)
9
- instrumenters = nil
10
- query_instrumenters = nil
11
- case event
12
- when "execute_multiplex"
13
- instrumenters = @schema.instrumenters
14
- multiplex_instrumenters = instrumenters[:multiplex]
15
- query_instrumenters = instrumenters[:query]
16
- call_hooks(multiplex_instrumenters, data[:multiplex], :before_multiplex, :after_multiplex) {
17
- each_query_call_hooks(query_instrumenters, data[:multiplex].queries) {
18
- yield
19
- }
20
- }
21
- else
22
- yield
23
- end
24
- end
25
-
26
- private
27
-
28
- # Call the before_ hooks of each query,
29
- # Then yield if no errors.
30
- # `call_hooks` takes care of appropriate cleanup.
31
- def each_query_call_hooks(instrumenters, queries, i = 0)
32
- if i >= queries.length
33
- yield
34
- else
35
- query = queries[i]
36
- call_hooks(instrumenters, query, :before_query, :after_query) {
37
- each_query_call_hooks(instrumenters, queries, i + 1) {
38
- yield
39
- }
40
- }
41
- end
42
- end
43
-
44
- # Call each before hook, and if they all succeed, yield.
45
- # If they don't all succeed, call after_ for each one that succeeded.
46
- def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
47
- begin
48
- successful = []
49
- instrumenters.each do |instrumenter|
50
- instrumenter.public_send(before_hook_name, object)
51
- successful << instrumenter
52
- end
53
-
54
- # if any before hooks raise an exception, quit calling before hooks,
55
- # but call the after hooks on anything that succeeded but also
56
- # raise the exception that came from the before hook.
57
- rescue GraphQL::ExecutionError => err
58
- object.context.errors << err
59
- rescue => e
60
- raise call_after_hooks(successful, object, after_hook_name, e)
61
- end
62
-
63
- begin
64
- yield # Call the user code
65
- ensure
66
- ex = call_after_hooks(successful, object, after_hook_name, nil)
67
- raise ex if ex
68
- end
69
- end
70
-
71
- def call_after_hooks(instrumenters, object, after_hook_name, ex)
72
- instrumenters.reverse_each do |instrumenter|
73
- begin
74
- instrumenter.public_send(after_hook_name, object)
75
- rescue => e
76
- ex = e
77
- end
78
- end
79
- ex
80
- end
81
- end
82
- end
83
- end