graphql 2.3.10 → 2.3.11

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: df4f4d50869f6ef14685ae04e9020a6b1c151d9fd287af7d86d139aadfc2f105
4
- data.tar.gz: 0415f6e0012b4a9517e84d5de66ebccbff41ce01b384b49c480cda7b5353d16d
3
+ metadata.gz: 7fd32c95924aac8818df81050ba408953617ee69edf0b1e20a16b4927e198230
4
+ data.tar.gz: a938a1fbde00c6ba934e63f40248b890bc7cbb6d0374d51dd138a88acbc05465
5
5
  SHA512:
6
- metadata.gz: 7921e2420473405956845345b75ba8c4768e9d90cf6d96b0249844524e7c53ff00ec11ed940635a3f12c264cb52095310be4e6a642c4806362ce2e9f5fe7d15f
7
- data.tar.gz: 3e33cd66559ec943b8b5e74124343918037176d1727d4ed425ccee384c3505708c848b5dac0dc04d2c68858ae9bc8717b204fe66dfee48adc981bf4a41fa284c
6
+ metadata.gz: 595f05e3c28c15e05e65543d48493e101d92ac1238deb0ec9682b2b853293041438652ccdab3dbb9c4242d9b93b29e10da82851f7985ac286916a77697850e0d
7
+ data.tar.gz: 90c3315b9d03b6c8eb4cee5e44578ec6b968cfe6de5feeaf160ecdbce2428a6a774edff7e06d7e2afaae4a5e33faeb809b629fae486ae1737bcf8bd9d298a0b1
@@ -45,6 +45,13 @@ module Graphql
45
45
  # post "/graphql", to: "graphql#execute"
46
46
  # ```
47
47
  #
48
+ # Add ActiveRecord::QueryLogs metadata:
49
+ # ```ruby
50
+ # current_graphql_operation: -> { GraphQL::Current.operation_name },
51
+ # current_graphql_field: -> { GraphQL::Current.field&.path },
52
+ # current_dataloader_source: -> { GraphQL::Current.dataloader_source_class },
53
+ # ```
54
+ #
48
55
  # Accept a `--batch` option which adds `GraphQL::Batch` setup.
49
56
  #
50
57
  # Use `--skip-graphiql` to skip `graphiql-rails` installation.
@@ -92,6 +99,11 @@ module Graphql
92
99
  default: false,
93
100
  desc: "Use GraphQL Playground over Graphiql as IDE"
94
101
 
102
+ class_option :skip_query_logs,
103
+ type: :boolean,
104
+ default: false,
105
+ desc: "Skip ActiveRecord::QueryLogs hooks in config/application.rb"
106
+
95
107
  # These two options are taken from Rails' own generators'
96
108
  class_option :api,
97
109
  type: :boolean,
@@ -180,6 +192,40 @@ RUBY
180
192
  install_relay
181
193
  end
182
194
 
195
+ if !options[:skip_query_logs]
196
+ config_file = "config/application.rb"
197
+ current_app_rb = File.read(Rails.root.join(config_file))
198
+ existing_log_tags_pattern = /config.active_record.query_log_tags = \[\n?(\s*:[a-z_]+,?\s*\n?|\s*#[^\]]*\n)*/m
199
+ existing_log_tags = existing_log_tags_pattern.match(current_app_rb)
200
+ if existing_log_tags && behavior == :invoke
201
+ code = <<-RUBY
202
+ # GraphQL-Ruby query log tags:
203
+ current_graphql_operation: -> { GraphQL::Current.operation_name },
204
+ current_graphql_field: -> { GraphQL::Current.field&.path },
205
+ current_dataloader_source: -> { GraphQL::Current.dataloader_source_class },
206
+ RUBY
207
+ if !existing_log_tags.to_s.end_with?(",")
208
+ code = ",\n#{code} "
209
+ end
210
+ # Try to insert this code _after_ any plain symbol entries in the array of query log tags:
211
+ after_code = existing_log_tags_pattern
212
+ else
213
+ code = <<-RUBY
214
+ config.active_record.query_log_tags_enabled = true
215
+ config.active_record.query_log_tags = [
216
+ # Rails query log tags:
217
+ :application, :controller, :action, :job,
218
+ # GraphQL-Ruby query log tags:
219
+ current_graphql_operation: -> { GraphQL::Current.operation_name },
220
+ current_graphql_field: -> { GraphQL::Current.field&.path },
221
+ current_dataloader_source: -> { GraphQL::Current.dataloader_source_class },
222
+ ]
223
+ RUBY
224
+ after_code = "class Application < Rails::Application\n"
225
+ end
226
+ insert_into_file(config_file, code, after: after_code)
227
+ end
228
+
183
229
  if gemfile_modified?
184
230
  say "Gemfile has been modified, make sure you `bundle install`"
185
231
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ # This module exposes Fiber-level runtime information.
5
+ #
6
+ # It won't work across unrelated fibers, although it will work in child Fibers.
7
+ #
8
+ # @example Setting Up ActiveRecord::QueryLogs
9
+ #
10
+ # config.active_record.query_log_tags = [
11
+ # :namespaced_controller,
12
+ # :action,
13
+ # :job,
14
+ # # ...
15
+ # {
16
+ # # GraphQL runtime info:
17
+ # current_graphql_operation: -> { GraphQL::Current.operation_name },
18
+ # current_graphql_field: -> { GraphQL::Current.field&.path },
19
+ # current_dataloader_source: -> { GraphQL::Current.dataloader_source_class },
20
+ # # ...
21
+ # },
22
+ # ]
23
+ #
24
+ module Current
25
+ # @return [String, nil] Comma-joined operation names for the currently-running {Multiplex}. `nil` if all operations are anonymous.
26
+ def self.operation_name
27
+ if (m = Fiber[:__graphql_current_multiplex])
28
+ m.context[:__graphql_current_operation_name] ||= begin
29
+ names = m.queries.map { |q| q.selected_operation_name }
30
+ if names.all?(&:nil?)
31
+ nil
32
+ else
33
+ names.join(",")
34
+ end
35
+ end
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ # @see GraphQL::Field#path for a string identifying this field
42
+ # @return [GraphQL::Field, nil] The currently-running field, if there is one.
43
+ def self.field
44
+ Thread.current[:__graphql_runtime_info]&.values&.first&.current_field
45
+ end
46
+
47
+ # @return [Class, nil] The currently-running {Dataloader::Source} class, if there is one.
48
+ def self.dataloader_source_class
49
+ Fiber[:__graphql_current_dataloader_source]&.class
50
+ end
51
+ end
52
+ end
@@ -271,7 +271,10 @@ module GraphQL
271
271
 
272
272
  if pending_sources
273
273
  spawn_fiber do
274
- pending_sources.each(&:run_pending_keys)
274
+ pending_sources.each do |source|
275
+ Fiber[:__graphql_current_dataloader_source] = source
276
+ source.run_pending_keys
277
+ end
275
278
  end
276
279
  end
277
280
  end
@@ -8,22 +8,17 @@ module GraphQL
8
8
  @query = query
9
9
  @dataloader = query.context.dataloader
10
10
  @storage = Hash.new do |h, argument_owner|
11
- args_by_parent = if argument_owner.arguments_statically_coercible?
11
+ h[argument_owner] = if argument_owner.arguments_statically_coercible?
12
12
  shared_values_cache = {}
13
13
  Hash.new do |h2, ignored_parent_object|
14
14
  h2[ignored_parent_object] = shared_values_cache
15
- end
15
+ end.compare_by_identity
16
16
  else
17
17
  Hash.new do |h2, parent_object|
18
- args_by_node = {}
19
- args_by_node.compare_by_identity
20
- h2[parent_object] = args_by_node
21
- end
18
+ h2[parent_object] = {}.compare_by_identity
19
+ end.compare_by_identity
22
20
  end
23
- args_by_parent.compare_by_identity
24
- h[argument_owner] = args_by_parent
25
- end
26
- @storage.compare_by_identity
21
+ end.compare_by_identity
27
22
  end
28
23
 
29
24
  def fetch(ast_node, argument_owner, parent_object)
@@ -53,8 +53,7 @@ module GraphQL
53
53
  end
54
54
  end
55
55
  # { Class => Boolean }
56
- @lazy_cache = {}
57
- @lazy_cache.compare_by_identity
56
+ @lazy_cache = {}.compare_by_identity
58
57
  end
59
58
 
60
59
  def final_result
@@ -727,12 +726,7 @@ module GraphQL
727
726
  end
728
727
 
729
728
  def get_current_runtime_state
730
- current_state = Thread.current[:__graphql_runtime_info] ||= begin
731
- per_query_state = {}
732
- per_query_state.compare_by_identity
733
- per_query_state
734
- end
735
-
729
+ current_state = Thread.current[:__graphql_runtime_info] ||= {}.compare_by_identity
736
730
  current_state[@query] ||= CurrentState.new
737
731
  end
738
732
 
@@ -34,6 +34,7 @@ module GraphQL
34
34
  end
35
35
 
36
36
  multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
37
+ Fiber[:__graphql_current_multiplex] = multiplex
37
38
  multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
38
39
  schema = multiplex.schema
39
40
  queries = multiplex.queries
@@ -136,6 +137,7 @@ module GraphQL
136
137
  queries.map { |q| q.result_values ||= {} }
137
138
  raise
138
139
  ensure
140
+ Fiber[:__graphql_current_multiplex] = nil
139
141
  queries.map { |query|
140
142
  runtime = query.context.namespace(:interpreter_runtime)[:runtime]
141
143
  if runtime
@@ -15,8 +15,8 @@ module GraphQL
15
15
  end
16
16
 
17
17
  def __type(name:)
18
- if context.types.reachable_type?(name)
19
- context.types.type(name)
18
+ if context.types.reachable_type?(name) && (type = context.types.type(name))
19
+ type
20
20
  elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
21
21
  type
22
22
  else
@@ -39,7 +39,7 @@ module GraphQL
39
39
  end
40
40
 
41
41
  def directives
42
- @context.types.directives
42
+ @context.types.directives.sort_by(&:graphql_name)
43
43
  end
44
44
  end
45
45
  end
@@ -34,11 +34,11 @@ module GraphQL
34
34
  attr_reader :filename
35
35
 
36
36
  def line
37
- @line ||= @source.line_at(@pos)
37
+ @line ||= @source&.line_at(@pos)
38
38
  end
39
39
 
40
40
  def col
41
- @col ||= @source.column_at(@pos)
41
+ @col ||= @source&.column_at(@pos)
42
42
  end
43
43
 
44
44
  def definition_line
@@ -82,12 +82,14 @@ module GraphQL
82
82
  @provided_values[key] = value
83
83
  end
84
84
 
85
- def_delegators :@query, :trace, :interpreter?
85
+ def_delegators :@query, :trace
86
86
 
87
87
  def types
88
- @query.types
88
+ @types ||= @query.types
89
89
  end
90
90
 
91
+ attr_writer :types
92
+
91
93
  RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path])
92
94
  # @!method []=(key, value)
93
95
  # Reassign `key` to the hash passed to {Schema#execute} as `context:`
@@ -27,10 +27,6 @@ module GraphQL
27
27
  @warden = Schema::Warden::NullWarden.new(context: self, schema: @schema)
28
28
  end
29
29
 
30
- def interpreter?
31
- true
32
- end
33
-
34
30
  def types
35
31
  @types ||= GraphQL::Schema::Warden::SchemaSubset.new(@warden)
36
32
  end
data/lib/graphql/query.rb CHANGED
@@ -106,8 +106,8 @@ module GraphQL
106
106
  end
107
107
 
108
108
  if use_schema_subset
109
- @schema_subset = @schema.subset_class.new(self)
110
- @warden = Schema::Warden::NullWarden.new(context: self, schema: @schema)
109
+ @schema_subset = @schema.subset_class.new(context: @context, schema: @schema)
110
+ @warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema)
111
111
  else
112
112
  @schema_subset = nil
113
113
  @warden = warden
@@ -187,10 +187,6 @@ module GraphQL
187
187
  @query_string ||= (document ? document.to_query_string : nil)
188
188
  end
189
189
 
190
- def interpreter?
191
- true
192
- end
193
-
194
190
  attr_accessor :multiplex
195
191
 
196
192
  # @return [GraphQL::Tracing::Trace]
@@ -127,11 +127,12 @@ module GraphQL
127
127
  builder = self
128
128
 
129
129
  found_types = types.values
130
+ object_types = found_types.select { |t| t.respond_to?(:kind) && t.kind.object? }
130
131
  schema_class = Class.new(schema_superclass) do
131
132
  begin
132
133
  # Add these first so that there's some chance of resolving late-bound types
133
134
  add_type_and_traverse(found_types, root: false)
134
- orphan_types(found_types.select { |t| t.respond_to?(:kind) && t.kind.object? })
135
+ orphan_types(object_types)
135
136
  query query_root_type
136
137
  mutation mutation_root_type
137
138
  subscription subscription_root_type
@@ -141,6 +142,12 @@ module GraphQL
141
142
  raise InvalidDocumentError, "Type \"#{type_name}\" not found in document.", err_backtrace
142
143
  end
143
144
 
145
+ object_types.each do |t|
146
+ t.interfaces.each do |int_t|
147
+ int_t.orphan_types(t)
148
+ end
149
+ end
150
+
144
151
  if default_resolve.respond_to?(:resolve_type)
145
152
  def self.resolve_type(*args)
146
153
  self.definition_default_resolve.resolve_type(*args)
@@ -7,7 +7,7 @@ module GraphQL
7
7
  # In this case, the server hides types and fields _entirely_, unless the current context has certain `:flags` present.
8
8
  class Flagged < GraphQL::Schema::Directive
9
9
  def initialize(target, **options)
10
- if target.is_a?(Module) && !target.ancestors.include?(VisibleByFlag)
10
+ if target.is_a?(Module)
11
11
  # This is type class of some kind, `include` will put this module
12
12
  # in between the type class itself and its super class, so `super` will work fine
13
13
  target.include(VisibleByFlag)
@@ -50,7 +50,7 @@ module GraphQL
50
50
  if field.has_default_page_size? && !value.has_default_page_size_override?
51
51
  value.default_page_size = field.default_page_size
52
52
  end
53
- if context.schema.new_connections? && (custom_t = context.schema.connections.edge_class_for_field(@field))
53
+ if (custom_t = context.schema.connections.edge_class_for_field(@field))
54
54
  value.edge_class = custom_t
55
55
  end
56
56
  value
@@ -82,13 +82,29 @@ module GraphQL
82
82
  super
83
83
  end
84
84
 
85
+ # Register other Interface or Object types as implementers of this Interface.
86
+ #
87
+ # When those Interfaces or Objects aren't used as the return values of fields,
88
+ # they may have to be registered using this method so that GraphQL-Ruby can find them.
89
+ # @param types [Class, Module]
90
+ # @return [Array<Module, Class>] Implementers of this interface, if they're registered
85
91
  def orphan_types(*types)
86
92
  if types.any?
87
- @orphan_types = types
93
+ @orphan_types ||= []
94
+ @orphan_types.concat(types)
88
95
  else
89
- all_orphan_types = @orphan_types || []
90
- all_orphan_types += super if defined?(super)
91
- all_orphan_types.uniq
96
+ if defined?(@orphan_types)
97
+ all_orphan_types = @orphan_types.dup
98
+ if defined?(super)
99
+ all_orphan_types += super
100
+ all_orphan_types.uniq!
101
+ end
102
+ all_orphan_types
103
+ elsif defined?(super)
104
+ super
105
+ else
106
+ EmptyObjects::EMPTY_ARRAY
107
+ end
92
108
  end
93
109
  end
94
110
 
@@ -25,7 +25,7 @@ module GraphQL
25
25
  load_constant(:DirectiveLocationEnum)
26
26
  ]
27
27
  @types = {}
28
- @possible_types = {}.tap(&:compare_by_identity)
28
+ @possible_types = {}.compare_by_identity
29
29
  type_defns.each do |t|
30
30
  @types[t.graphql_name] = t
31
31
  @possible_types[t] = [t]
@@ -90,7 +90,8 @@ module GraphQL
90
90
  def resolve_late_binding(late_bound_type)
91
91
  case late_bound_type
92
92
  when GraphQL::Schema::LateBoundType
93
- @schema.get_type(late_bound_type.name)
93
+ type_name = late_bound_type.name
94
+ @types[type_name] || @schema.get_type(type_name)
94
95
  when GraphQL::Schema::List
95
96
  resolve_late_binding(late_bound_type.of_type).to_list_type
96
97
  when GraphQL::Schema::NonNull
@@ -198,9 +198,13 @@ module GraphQL
198
198
  end
199
199
 
200
200
  def all_argument_definitions
201
- all_defns = own_arguments.values
202
- all_defns.flatten!
203
- all_defns
201
+ if own_arguments.any?
202
+ all_defns = own_arguments.values
203
+ all_defns.flatten!
204
+ all_defns
205
+ else
206
+ EmptyObjects::EMPTY_ARRAY
207
+ end
204
208
  end
205
209
 
206
210
  # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.