graphql 2.0.21 → 2.0.32

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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
  3. data/lib/generators/graphql/mutation_update_generator.rb +1 -1
  4. data/lib/generators/graphql/relay.rb +18 -1
  5. data/lib/graphql/backtrace.rb +0 -4
  6. data/lib/graphql/dataloader/source.rb +69 -45
  7. data/lib/graphql/dataloader.rb +4 -4
  8. data/lib/graphql/execution/interpreter/arguments_cache.rb +31 -30
  9. data/lib/graphql/execution/interpreter/runtime.rb +122 -101
  10. data/lib/graphql/execution/interpreter.rb +1 -2
  11. data/lib/graphql/execution/lookahead.rb +1 -1
  12. data/lib/graphql/filter.rb +2 -1
  13. data/lib/graphql/introspection/entry_points.rb +1 -1
  14. data/lib/graphql/language/document_from_schema_definition.rb +16 -9
  15. data/lib/graphql/language/lexer.rb +89 -57
  16. data/lib/graphql/language/nodes.rb +3 -0
  17. data/lib/graphql/language/parser.rb +706 -691
  18. data/lib/graphql/language/parser.y +1 -0
  19. data/lib/graphql/language/printer.rb +28 -14
  20. data/lib/graphql/language/visitor.rb +64 -61
  21. data/lib/graphql/query/context.rb +16 -7
  22. data/lib/graphql/query/null_context.rb +8 -18
  23. data/lib/graphql/query/validation_pipeline.rb +2 -1
  24. data/lib/graphql/query.rb +37 -12
  25. data/lib/graphql/schema/addition.rb +38 -12
  26. data/lib/graphql/schema/always_visible.rb +10 -0
  27. data/lib/graphql/schema/argument.rb +8 -10
  28. data/lib/graphql/schema/build_from_definition.rb +8 -7
  29. data/lib/graphql/schema/directive.rb +1 -1
  30. data/lib/graphql/schema/enum_value.rb +2 -2
  31. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  32. data/lib/graphql/schema/field.rb +26 -15
  33. data/lib/graphql/schema/input_object.rb +9 -7
  34. data/lib/graphql/schema/interface.rb +5 -1
  35. data/lib/graphql/schema/introspection_system.rb +1 -1
  36. data/lib/graphql/schema/member/build_type.rb +10 -2
  37. data/lib/graphql/schema/member/has_arguments.rb +9 -7
  38. data/lib/graphql/schema/member/has_directives.rb +1 -1
  39. data/lib/graphql/schema/member/has_fields.rb +1 -1
  40. data/lib/graphql/schema/member/has_interfaces.rb +1 -1
  41. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  42. data/lib/graphql/schema/object.rb +6 -1
  43. data/lib/graphql/schema/printer.rb +3 -1
  44. data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
  45. data/lib/graphql/schema/resolver.rb +12 -10
  46. data/lib/graphql/schema/timeout.rb +1 -1
  47. data/lib/graphql/schema/validator.rb +1 -1
  48. data/lib/graphql/schema/warden.rb +37 -4
  49. data/lib/graphql/schema.rb +92 -23
  50. data/lib/graphql/tracing/appoptics_trace.rb +35 -11
  51. data/lib/graphql/tracing/appsignal_trace.rb +4 -0
  52. data/lib/graphql/tracing/data_dog_trace.rb +89 -50
  53. data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
  54. data/lib/graphql/tracing/legacy_trace.rb +5 -1
  55. data/lib/graphql/tracing/notifications_trace.rb +7 -0
  56. data/lib/graphql/tracing/platform_trace.rb +26 -12
  57. data/lib/graphql/tracing/prometheus_trace.rb +4 -0
  58. data/lib/graphql/tracing/scout_trace.rb +3 -0
  59. data/lib/graphql/tracing/statsd_trace.rb +4 -0
  60. data/lib/graphql/tracing.rb +1 -0
  61. data/lib/graphql/types/relay/connection_behaviors.rb +1 -1
  62. data/lib/graphql/types/relay/edge_behaviors.rb +1 -1
  63. data/lib/graphql/version.rb +1 -1
  64. data/readme.md +1 -1
  65. metadata +36 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43f1cc3ef36a54a31f8afa03ee3109b34f046e26c427acfb80599a2dd245cf14
4
- data.tar.gz: 4e4a15c766650e36004175de15cfcb9f67c6b4e7d24e2f91712a6ad3f8681453
3
+ metadata.gz: c86c6f3920de2d67a9d8b23a2783ec33f15b70571236db184ca9a9e6b459b80b
4
+ data.tar.gz: 6f4659f46b99da5c6065ba7084b899253958bdb5627b65f469262898cb38205c
5
5
  SHA512:
6
- metadata.gz: 908f3790bcc773bb6c7fe3db1f5e2ccabf361ece7b462b80fc4e59911167eabb8baf544739f31ac425579bca61b56b46937ac60e7a54a03827228016457388de
7
- data.tar.gz: 62fb9dc90ab55aff66d3d1621fc28b18c0c9599e2f8a7f8521d372a6f549c85ac0a2e50a0d43cb77bde728cb44f85b178bb37388c6bd3d2aed5f8e91723a79ef
6
+ metadata.gz: 02bf27d69ec35ecdad618ed18a7731fe7d66cd1b5105fcc7625e7b68f534b31e7e5b6f458ba1efe22b5f6e9e6d819cc696e7cdeba6e80bbfa3259691d9ede441
7
+ data.tar.gz: 5b91453bbc8e01a72bcfbe5cef21e7f65704ff1976d945c01291e48667a4a1f36e35f690048ba2a029d5c9f019cfa9c72fa46b2f851b422117de48a4722152ec
@@ -6,7 +6,7 @@ module Graphql
6
6
  # TODO: What other options should be supported?
7
7
  #
8
8
  # @example Generate a `GraphQL::Schema::RelayClassicMutation` by name
9
- # rails g graphql:mutation CreatePostMutation
9
+ # rails g graphql:mutation DeletePostMutation
10
10
  class MutationDeleteGenerator < OrmMutationsBase
11
11
 
12
12
  desc "Scaffold a Relay Classic ORM delete mutation for the given model class"
@@ -6,7 +6,7 @@ module Graphql
6
6
  # TODO: What other options should be supported?
7
7
  #
8
8
  # @example Generate a `GraphQL::Schema::RelayClassicMutation` by name
9
- # rails g graphql:mutation CreatePostMutation
9
+ # rails g graphql:mutation UpdatePostMutation
10
10
  class MutationUpdateGenerator < OrmMutationsBase
11
11
 
12
12
  desc "Scaffold a Relay Classic ORM update mutation for the given model class"
@@ -6,7 +6,24 @@ module Graphql
6
6
  # Add Node, `node(id:)`, and `nodes(ids:)`
7
7
  template("node_type.erb", "#{options[:directory]}/types/node_type.rb")
8
8
  in_root do
9
- fields = " # Add `node(id: ID!) and `nodes(ids: [ID!]!)`\n include GraphQL::Types::Relay::HasNodeField\n include GraphQL::Types::Relay::HasNodesField\n\n"
9
+ fields = <<-RUBY
10
+ field :node, Types::NodeType, null: true, description: "Fetches an object given its ID." do
11
+ argument :id, ID, required: true, description: "ID of the object."
12
+ end
13
+
14
+ def node(id:)
15
+ context.schema.object_from_id(id, context)
16
+ end
17
+
18
+ field :nodes, [Types::NodeType, null: true], null: true, description: "Fetches a list of objects given a list of IDs." do
19
+ argument :ids, [ID], required: true, description: "IDs of the objects."
20
+ end
21
+
22
+ def nodes(ids:)
23
+ ids.map { |id| context.schema.object_from_id(id, context) }
24
+ end
25
+
26
+ RUBY
10
27
  inject_into_file "#{options[:directory]}/types/query_type.rb", fields, after: /class .*QueryType\s*<\s*[^\s]+?\n/m, force: false
11
28
  end
12
29
 
@@ -55,9 +55,5 @@ module GraphQL
55
55
  @parent_frame = parent_frame
56
56
  end
57
57
  end
58
-
59
- class DefaultBacktraceTrace < GraphQL::Tracing::Trace
60
- include GraphQL::Backtrace::Trace
61
- end
62
58
  end
63
59
  end
@@ -7,9 +7,9 @@ module GraphQL
7
7
  # @api private
8
8
  def setup(dataloader)
9
9
  # These keys have been requested but haven't been fetched yet
10
- @pending_keys = []
10
+ @pending = {}
11
11
  # These keys have been passed to `fetch` but haven't been finished yet
12
- @fetching_keys = []
12
+ @fetching = {}
13
13
  # { key => result }
14
14
  @results = {}
15
15
  @dataloader = dataloader
@@ -18,42 +18,66 @@ module GraphQL
18
18
  attr_reader :dataloader
19
19
 
20
20
  # @return [Dataloader::Request] a pending request for a value from `key`. Call `.load` on that object to wait for the result.
21
- def request(key)
22
- if !@results.key?(key)
23
- @pending_keys << key
21
+ def request(value)
22
+ res_key = result_key_for(value)
23
+ if !@results.key?(res_key)
24
+ @pending[res_key] ||= value
24
25
  end
25
- Dataloader::Request.new(self, key)
26
+ Dataloader::Request.new(self, value)
27
+ end
28
+
29
+ # Implement this method to return a stable identifier if different
30
+ # key objects should load the same data value.
31
+ #
32
+ # @param value [Object] A value passed to `.request` or `.load`, for which a value will be loaded
33
+ # @return [Object] The key for tracking this pending data
34
+ def result_key_for(value)
35
+ value
26
36
  end
27
37
 
28
38
  # @return [Dataloader::Request] a pending request for a values from `keys`. Call `.load` on that object to wait for the results.
29
- def request_all(keys)
30
- pending_keys = keys.select { |k| !@results.key?(k) }
31
- @pending_keys.concat(pending_keys)
32
- Dataloader::RequestAll.new(self, keys)
39
+ def request_all(values)
40
+ values.each do |v|
41
+ res_key = result_key_for(v)
42
+ if !@results.key?(res_key)
43
+ @pending[res_key] ||= v
44
+ end
45
+ end
46
+ Dataloader::RequestAll.new(self, values)
33
47
  end
34
48
 
35
- # @param key [Object] A loading key which will be passed to {#fetch} if it isn't already in the internal cache.
49
+ # @param value [Object] A loading value which will be passed to {#fetch} if it isn't already in the internal cache.
36
50
  # @return [Object] The result from {#fetch} for `key`. If `key` hasn't been loaded yet, the Fiber will yield until it's loaded.
37
- def load(key)
38
- if @results.key?(key)
39
- result_for(key)
51
+ def load(value)
52
+ result_key = result_key_for(value)
53
+ if @results.key?(result_key)
54
+ result_for(result_key)
40
55
  else
41
- @pending_keys << key
42
- sync
43
- result_for(key)
56
+ @pending[result_key] ||= value
57
+ sync([result_key])
58
+ result_for(result_key)
44
59
  end
45
60
  end
46
61
 
47
- # @param keys [Array<Object>] Loading keys which will be passed to `#fetch` (or read from the internal cache).
62
+ # @param values [Array<Object>] Loading keys which will be passed to `#fetch` (or read from the internal cache).
48
63
  # @return [Object] The result from {#fetch} for `keys`. If `keys` haven't been loaded yet, the Fiber will yield until they're loaded.
49
- def load_all(keys)
50
- if keys.any? { |k| !@results.key?(k) }
51
- pending_keys = keys.select { |k| !@results.key?(k) }
52
- @pending_keys.concat(pending_keys)
53
- sync
64
+ def load_all(values)
65
+ result_keys = []
66
+ pending_keys = []
67
+ values.each { |v|
68
+ k = result_key_for(v)
69
+ result_keys << k
70
+ if !@results.key?(k)
71
+ @pending[k] ||= v
72
+ pending_keys << k
73
+ end
74
+ }
75
+
76
+ if pending_keys.any?
77
+ sync(pending_keys)
54
78
  end
55
79
 
56
- keys.map { |k| result_for(k) }
80
+ result_keys.map { |k| result_for(k) }
57
81
  end
58
82
 
59
83
  # Subclasses must implement this method to return a value for each of `keys`
@@ -67,14 +91,13 @@ module GraphQL
67
91
  # Wait for a batch, if there's anything to batch.
68
92
  # Then run the batch and update the cache.
69
93
  # @return [void]
70
- def sync
71
- pending_keys = @pending_keys.dup
94
+ def sync(pending_result_keys)
72
95
  @dataloader.yield
73
96
  iterations = 0
74
- while pending_keys.any? { |k| !@results.key?(k) }
97
+ while pending_result_keys.any? { |key| !@results.key?(key) }
75
98
  iterations += 1
76
99
  if iterations > 1000
77
- raise "#{self.class}#sync tried 1000 times to load pending keys (#{pending_keys}), but they still weren't loaded. There is likely a circular dependency."
100
+ raise "#{self.class}#sync tried 1000 times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency."
78
101
  end
79
102
  @dataloader.yield
80
103
  end
@@ -83,15 +106,18 @@ module GraphQL
83
106
 
84
107
  # @return [Boolean] True if this source has any pending requests for data.
85
108
  def pending?
86
- !@pending_keys.empty?
109
+ !@pending.empty?
87
110
  end
88
111
 
89
112
  # Add these key-value pairs to this source's cache
90
113
  # (future loads will use these merged values).
91
- # @param results [Hash<Object => Object>] key-value pairs to cache in this source
114
+ # @param new_results [Hash<Object => Object>] key-value pairs to cache in this source
92
115
  # @return [void]
93
- def merge(results)
94
- @results.merge!(results)
116
+ def merge(new_results)
117
+ new_results.each do |new_k, new_v|
118
+ key = result_key_for(new_k)
119
+ @results[key] = new_v
120
+ end
95
121
  nil
96
122
  end
97
123
 
@@ -99,24 +125,22 @@ module GraphQL
99
125
  # @api private
100
126
  # @return [void]
101
127
  def run_pending_keys
102
- if !@fetching_keys.empty?
103
- @pending_keys -= @fetching_keys
128
+ if !@fetching.empty?
129
+ @fetching.each_key { |k| @pending.delete(k) }
104
130
  end
105
- return if @pending_keys.empty?
106
- fetch_keys = @pending_keys.uniq
107
- @fetching_keys.concat(fetch_keys)
108
- @pending_keys = []
109
- results = fetch(fetch_keys)
110
- fetch_keys.each_with_index do |key, idx|
131
+ return if @pending.empty?
132
+ fetch_h = @pending
133
+ @pending = {}
134
+ @fetching.merge!(fetch_h)
135
+ results = fetch(fetch_h.values)
136
+ fetch_h.each_with_index do |(key, _value), idx|
111
137
  @results[key] = results[idx]
112
138
  end
113
139
  nil
114
140
  rescue StandardError => error
115
- fetch_keys.each { |key| @results[key] = error }
141
+ fetch_h.each_key { |key| @results[key] = error }
116
142
  ensure
117
- if fetch_keys
118
- @fetching_keys -= fetch_keys
119
- end
143
+ fetch_h && fetch_h.each_key { |k| @fetching.delete(k) }
120
144
  end
121
145
 
122
146
  # These arguments are given to `dataloader.with(source_class, ...)`. The object
@@ -137,7 +161,7 @@ module GraphQL
137
161
  [*batch_args, **batch_kwargs]
138
162
  end
139
163
 
140
- attr_reader :pending_keys
164
+ attr_reader :pending
141
165
 
142
166
  private
143
167
 
@@ -111,8 +111,8 @@ module GraphQL
111
111
  @source_cache.each do |source_class, batched_sources|
112
112
  batched_sources.each do |batch_args, batched_source_instance|
113
113
  if batched_source_instance.pending?
114
- prev_pending_keys[batched_source_instance] = batched_source_instance.pending_keys.dup
115
- batched_source_instance.pending_keys.clear
114
+ prev_pending_keys[batched_source_instance] = batched_source_instance.pending.dup
115
+ batched_source_instance.pending.clear
116
116
  end
117
117
  end
118
118
  end
@@ -127,8 +127,8 @@ module GraphQL
127
127
  res
128
128
  ensure
129
129
  @pending_jobs = prev_queue
130
- prev_pending_keys.each do |source_instance, pending_keys|
131
- source_instance.pending_keys.concat(pending_keys)
130
+ prev_pending_keys.each do |source_instance, pending|
131
+ source_instance.pending.merge!(pending)
132
132
  end
133
133
  end
134
134
 
@@ -7,49 +7,50 @@ module GraphQL
7
7
  def initialize(query)
8
8
  @query = query
9
9
  @dataloader = query.context.dataloader
10
- @storage = Hash.new do |h, ast_node|
11
- h[ast_node] = Hash.new do |h2, arg_owner|
12
- h2[arg_owner] = Hash.new do |h3, parent_object|
13
- dataload_for(ast_node, arg_owner, parent_object) do |kwarg_arguments|
14
- h3[parent_object] = @query.schema.after_lazy(kwarg_arguments) do |resolved_args|
15
- h3[parent_object] = resolved_args
16
- end
17
- end
18
-
19
- if !h3.key?(parent_object)
20
- # TODO should i bother putting anything here?
21
- h3[parent_object] = NO_ARGUMENTS
22
- else
23
- h3[parent_object]
24
- end
10
+ @storage = Hash.new do |h, argument_owner|
11
+ args_by_parent = if argument_owner.arguments_statically_coercible?
12
+ shared_values_cache = {}
13
+ Hash.new do |h2, ignored_parent_object|
14
+ h2[ignored_parent_object] = shared_values_cache
15
+ end
16
+ else
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
25
21
  end
26
22
  end
23
+ args_by_parent.compare_by_identity
24
+ h[argument_owner] = args_by_parent
27
25
  end
26
+ @storage.compare_by_identity
28
27
  end
29
28
 
30
29
  def fetch(ast_node, argument_owner, parent_object)
31
- # If any jobs were enqueued, run them now,
32
- # since this might have been called outside of execution.
33
- # (The jobs are responsible for updating `result` in-place.)
34
- if !@storage.key?(ast_node) || !@storage[ast_node].key?(argument_owner)
35
- @dataloader.run_isolated do
36
- @storage[ast_node][argument_owner][parent_object]
30
+ # This runs eagerly if no block is given
31
+ @storage[argument_owner][parent_object][ast_node] ||= begin
32
+ args_hash = self.class.prepare_args_hash(@query, ast_node)
33
+ kwarg_arguments = argument_owner.coerce_arguments(parent_object, args_hash, @query.context)
34
+ @query.after_lazy(kwarg_arguments) do |resolved_args|
35
+ @storage[argument_owner][parent_object][ast_node] = resolved_args
37
36
  end
38
37
  end
39
- # Ack, the _hash_ is updated, but the key is eventually
40
- # overridden with an immutable arguments instance.
41
- # The first call queues up the job,
42
- # then this call fetches the result.
43
- # TODO this should be better, find a solution
44
- # that works with merging the runtime.rb code
45
- @storage[ast_node][argument_owner][parent_object]
38
+
46
39
  end
47
40
 
48
41
  # @yield [Interpreter::Arguments, Lazy<Interpreter::Arguments>] The finally-loaded arguments
49
42
  def dataload_for(ast_node, argument_owner, parent_object, &block)
50
43
  # First, normalize all AST or Ruby values to a plain Ruby hash
51
- args_hash = self.class.prepare_args_hash(@query, ast_node)
52
- argument_owner.coerce_arguments(parent_object, args_hash, @query.context, &block)
44
+ arg_storage = @storage[argument_owner][parent_object]
45
+ if (args = arg_storage[ast_node])
46
+ yield(args)
47
+ else
48
+ args_hash = self.class.prepare_args_hash(@query, ast_node)
49
+ argument_owner.coerce_arguments(parent_object, args_hash, @query.context) do |resolved_args|
50
+ arg_storage[ast_node] = resolved_args
51
+ yield(resolved_args)
52
+ end
53
+ end
53
54
  nil
54
55
  end
55
56