graphql 2.0.15 → 2.0.16

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: 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