graphql 1.11.10 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +5 -5
  3. data/lib/generators/graphql/relay_generator.rb +63 -0
  4. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  5. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  6. data/lib/generators/graphql/templates/node_type.erb +9 -0
  7. data/lib/generators/graphql/templates/object.erb +1 -1
  8. data/lib/generators/graphql/templates/query_type.erb +1 -3
  9. data/lib/generators/graphql/templates/schema.erb +8 -35
  10. data/lib/graphql/analysis/analyze_query.rb +7 -0
  11. data/lib/graphql/analysis/ast/visitor.rb +9 -1
  12. data/lib/graphql/analysis/ast.rb +11 -2
  13. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  14. data/lib/graphql/backtrace/table.rb +22 -2
  15. data/lib/graphql/backtrace/tracer.rb +40 -9
  16. data/lib/graphql/backtrace.rb +28 -19
  17. data/lib/graphql/backwards_compatibility.rb +1 -0
  18. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  19. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  20. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  21. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  22. data/lib/graphql/dataloader/null_dataloader.rb +21 -0
  23. data/lib/graphql/dataloader/request.rb +24 -0
  24. data/lib/graphql/dataloader/request_all.rb +22 -0
  25. data/lib/graphql/dataloader/source.rb +93 -0
  26. data/lib/graphql/dataloader.rb +197 -0
  27. data/lib/graphql/define/assign_global_id_field.rb +1 -1
  28. data/lib/graphql/define/instance_definable.rb +32 -2
  29. data/lib/graphql/define/type_definer.rb +5 -5
  30. data/lib/graphql/deprecated_dsl.rb +5 -0
  31. data/lib/graphql/enum_type.rb +2 -0
  32. data/lib/graphql/execution/errors.rb +4 -0
  33. data/lib/graphql/execution/execute.rb +7 -0
  34. data/lib/graphql/execution/interpreter/arguments.rb +51 -14
  35. data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
  36. data/lib/graphql/execution/interpreter/runtime.rb +210 -124
  37. data/lib/graphql/execution/interpreter.rb +10 -6
  38. data/lib/graphql/execution/multiplex.rb +20 -6
  39. data/lib/graphql/function.rb +4 -0
  40. data/lib/graphql/input_object_type.rb +2 -0
  41. data/lib/graphql/interface_type.rb +3 -1
  42. data/lib/graphql/language/document_from_schema_definition.rb +50 -23
  43. data/lib/graphql/object_type.rb +2 -0
  44. data/lib/graphql/pagination/connection.rb +5 -1
  45. data/lib/graphql/pagination/connections.rb +6 -16
  46. data/lib/graphql/query/context.rb +4 -0
  47. data/lib/graphql/query/serial_execution.rb +1 -0
  48. data/lib/graphql/query/validation_pipeline.rb +1 -1
  49. data/lib/graphql/query.rb +2 -0
  50. data/lib/graphql/relay/base_connection.rb +7 -0
  51. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  52. data/lib/graphql/relay/connection_type.rb +1 -1
  53. data/lib/graphql/relay/mutation.rb +1 -0
  54. data/lib/graphql/relay/node.rb +3 -0
  55. data/lib/graphql/relay/type_extensions.rb +2 -0
  56. data/lib/graphql/scalar_type.rb +2 -0
  57. data/lib/graphql/schema/argument.rb +25 -7
  58. data/lib/graphql/schema/build_from_definition.rb +139 -51
  59. data/lib/graphql/schema/directive/flagged.rb +57 -0
  60. data/lib/graphql/schema/directive.rb +76 -0
  61. data/lib/graphql/schema/enum.rb +3 -0
  62. data/lib/graphql/schema/enum_value.rb +12 -6
  63. data/lib/graphql/schema/field/connection_extension.rb +3 -2
  64. data/lib/graphql/schema/field.rb +28 -9
  65. data/lib/graphql/schema/input_object.rb +33 -22
  66. data/lib/graphql/schema/interface.rb +1 -0
  67. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
  68. data/lib/graphql/schema/member/build_type.rb +3 -3
  69. data/lib/graphql/schema/member/has_arguments.rb +24 -6
  70. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  71. data/lib/graphql/schema/member/has_directives.rb +98 -0
  72. data/lib/graphql/schema/member/has_validators.rb +31 -0
  73. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  74. data/lib/graphql/schema/member.rb +4 -0
  75. data/lib/graphql/schema/object.rb +11 -0
  76. data/lib/graphql/schema/printer.rb +5 -4
  77. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
  78. data/lib/graphql/schema/resolver.rb +7 -0
  79. data/lib/graphql/schema/subscription.rb +19 -1
  80. data/lib/graphql/schema/timeout_middleware.rb +2 -0
  81. data/lib/graphql/schema/validation.rb +2 -0
  82. data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
  83. data/lib/graphql/schema/validator/format_validator.rb +49 -0
  84. data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
  85. data/lib/graphql/schema/validator/length_validator.rb +57 -0
  86. data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
  87. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  88. data/lib/graphql/schema/validator.rb +163 -0
  89. data/lib/graphql/schema.rb +72 -49
  90. data/lib/graphql/static_validation/base_visitor.rb +0 -3
  91. data/lib/graphql/static_validation/rules/fields_will_merge.rb +4 -4
  92. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  93. data/lib/graphql/static_validation/validation_context.rb +1 -6
  94. data/lib/graphql/static_validation/validator.rb +12 -14
  95. data/lib/graphql/subscriptions.rb +17 -20
  96. data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
  97. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  98. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  99. data/lib/graphql/tracing.rb +2 -2
  100. data/lib/graphql/types/relay/base_connection.rb +2 -92
  101. data/lib/graphql/types/relay/base_edge.rb +2 -35
  102. data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
  103. data/lib/graphql/types/relay/default_relay.rb +27 -0
  104. data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
  105. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  106. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  107. data/lib/graphql/types/relay/node.rb +2 -4
  108. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  109. data/lib/graphql/types/relay/node_field.rb +1 -19
  110. data/lib/graphql/types/relay/nodes_field.rb +1 -19
  111. data/lib/graphql/types/relay/page_info.rb +2 -14
  112. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  113. data/lib/graphql/types/relay.rb +11 -3
  114. data/lib/graphql/union_type.rb +2 -0
  115. data/lib/graphql/upgrader/member.rb +1 -0
  116. data/lib/graphql/upgrader/schema.rb +1 -0
  117. data/lib/graphql/version.rb +1 -1
  118. data/lib/graphql.rb +38 -4
  119. metadata +31 -6
  120. data/lib/graphql/types/relay/base_field.rb +0 -22
  121. data/lib/graphql/types/relay/base_interface.rb +0 -29
  122. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Dataloader
4
+ # @see Source#request_all which returns an instance of this.
5
+ class RequestAll < Request
6
+ def initialize(source, keys)
7
+ @source = source
8
+ @keys = keys
9
+ end
10
+
11
+ # Call this method to cause the current Fiber to wait for the results of this request.
12
+ #
13
+ # @return [Array<Object>] One object for each of `keys`
14
+ def load
15
+ if @keys.any? { |k| !@source.results.key?(k) }
16
+ @source.sync
17
+ end
18
+ @keys.map { |k| @source.results[k] }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Dataloader
5
+ class Source
6
+ # @api private
7
+ attr_reader :results
8
+
9
+ # Called by {Dataloader} to prepare the {Source}'s internal state
10
+ # @api private
11
+ def setup(dataloader)
12
+ @pending_keys = []
13
+ @results = {}
14
+ @dataloader = dataloader
15
+ end
16
+
17
+ attr_reader :dataloader
18
+
19
+ # @return [Dataloader::Request] a pending request for a value from `key`. Call `.load` on that object to wait for the result.
20
+ def request(key)
21
+ if !@results.key?(key)
22
+ @pending_keys << key
23
+ end
24
+ Dataloader::Request.new(self, key)
25
+ end
26
+
27
+ # @return [Dataloader::Request] a pending request for a values from `keys`. Call `.load` on that object to wait for the results.
28
+ def request_all(keys)
29
+ pending_keys = keys.select { |k| !@results.key?(k) }
30
+ @pending_keys.concat(pending_keys)
31
+ Dataloader::RequestAll.new(self, keys)
32
+ end
33
+
34
+ # @param key [Object] A loading key which will be passed to {#fetch} if it isn't already in the internal cache.
35
+ # @return [Object] The result from {#fetch} for `key`. If `key` hasn't been loaded yet, the Fiber will yield until it's loaded.
36
+ def load(key)
37
+ if @results.key?(key)
38
+ @results[key]
39
+ else
40
+ @pending_keys << key
41
+ sync
42
+ @results[key]
43
+ end
44
+ end
45
+
46
+ # @param keys [Array<Object>] Loading keys which will be passed to `#fetch` (or read from the internal cache).
47
+ # @return [Object] The result from {#fetch} for `keys`. If `keys` haven't been loaded yet, the Fiber will yield until they're loaded.
48
+ def load_all(keys)
49
+ if keys.any? { |k| !@results.key?(k) }
50
+ pending_keys = keys.select { |k| !@results.key?(k) }
51
+ @pending_keys.concat(pending_keys)
52
+ sync
53
+ end
54
+
55
+ keys.map { |k| @results[k] }
56
+ end
57
+
58
+ # Subclasses must implement this method to return a value for each of `keys`
59
+ # @param keys [Array<Object>] keys passed to {#load}, {#load_all}, {#request}, or {#request_all}
60
+ # @return [Array<Object>] A loaded value for each of `keys`. The array must match one-for-one to the list of `keys`.
61
+ def fetch(keys)
62
+ # somehow retrieve these from the backend
63
+ raise "Implement `#{self.class}#fetch(#{keys.inspect}) to return a record for each of the keys"
64
+ end
65
+
66
+ # Wait for a batch, if there's anything to batch.
67
+ # Then run the batch and update the cache.
68
+ # @return [void]
69
+ def sync
70
+ @dataloader.yield
71
+ end
72
+
73
+ # @return [Boolean] True if this source has any pending requests for data.
74
+ def pending?
75
+ @pending_keys.any?
76
+ end
77
+
78
+ # Called by {GraphQL::Dataloader} to resolve and pending requests to this source.
79
+ # @api private
80
+ # @return [void]
81
+ def run_pending_keys
82
+ return if @pending_keys.empty?
83
+ fetch_keys = @pending_keys.uniq
84
+ @pending_keys = []
85
+ results = fetch(fetch_keys)
86
+ fetch_keys.each_with_index do |key, idx|
87
+ @results[key] = results[idx]
88
+ end
89
+ nil
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphql/dataloader/null_dataloader"
4
+ require "graphql/dataloader/request"
5
+ require "graphql/dataloader/request_all"
6
+ require "graphql/dataloader/source"
7
+
8
+ module GraphQL
9
+ # This plugin supports Fiber-based concurrency, along with {GraphQL::Dataloader::Source}.
10
+ #
11
+ # @example Installing Dataloader
12
+ #
13
+ # class MySchema < GraphQL::Schema
14
+ # use GraphQL::Dataloader
15
+ # end
16
+ #
17
+ # @example Waiting for batch-loaded data in a GraphQL field
18
+ #
19
+ # field :team, Types::Team, null: true
20
+ #
21
+ # def team
22
+ # dataloader.with(Sources::Record, Team).load(object.team_id)
23
+ # end
24
+ #
25
+ class Dataloader
26
+ def self.use(schema)
27
+ schema.dataloader_class = self
28
+ end
29
+
30
+ def initialize(multiplex_context)
31
+ @context = multiplex_context
32
+ @source_cache = Hash.new { |h, source_class| h[source_class] = Hash.new { |h2, batch_parameters|
33
+ source = source_class.new(*batch_parameters)
34
+ source.setup(self)
35
+ h2[batch_parameters] = source
36
+ }
37
+ }
38
+ @waiting_fibers = []
39
+ @yielded_fibers = Set.new
40
+ end
41
+
42
+ # @return [Hash] the {Multiplex} context
43
+ attr_reader :context
44
+
45
+ # @api private
46
+ attr_reader :yielded_fibers
47
+
48
+ # Add some work to this dataloader to be scheduled later.
49
+ # @param block Some work to enqueue
50
+ # @return [void]
51
+ def enqueue(&block)
52
+ @waiting_fibers << Fiber.new {
53
+ begin
54
+ yield
55
+ rescue StandardError => exception
56
+ exception
57
+ end
58
+ }
59
+ nil
60
+ end
61
+
62
+ # Tell the dataloader that this fiber is waiting for data.
63
+ #
64
+ # Dataloader will resume the fiber after the requested data has been loaded (by another Fiber).
65
+ #
66
+ # @return [void]
67
+ def yield
68
+ Fiber.yield
69
+ nil
70
+ end
71
+
72
+ # @return [Boolean] Returns true if the current Fiber has yielded once via Dataloader
73
+ def yielded?
74
+ @yielded_fibers.include?(Fiber.current)
75
+ end
76
+
77
+ # Run all Fibers until they're all done
78
+ #
79
+ # Each cycle works like this:
80
+ #
81
+ # - Run each pending execution fiber (`@waiting_fibers`),
82
+ # - Then run each pending Source, preparing more data for those fibers.
83
+ # - Run each pending Source _again_ (if one Source requested more data from another Source)
84
+ # - Continue until there are no pending sources
85
+ # - Repeat: run execution fibers again ...
86
+ #
87
+ # @return [void]
88
+ def run
89
+ # Start executing Fibers. This will run until all the Fibers are done.
90
+ already_run_fibers = []
91
+ while (current_fiber = @waiting_fibers.pop)
92
+ # Run each execution fiber, enqueuing it in `already_run_fibers`
93
+ # if it's still `.alive?`.
94
+ # Any spin-off continuations will be enqueued in `@waiting_fibers` (via {#enqueue})
95
+ resume_fiber_and_enqueue_continuation(current_fiber, already_run_fibers)
96
+
97
+ if @waiting_fibers.empty?
98
+ # Now, run all Sources which have become pending _before_ resuming GraphQL execution.
99
+ # Sources might queue up other Sources, which is fine -- those will also run before resuming execution.
100
+ #
101
+ # This is where an evented approach would be even better -- can we tell which
102
+ # fibers are ready to continue, and continue execution there?
103
+ #
104
+ source_fiber_stack = if (first_source_fiber = create_source_fiber)
105
+ [first_source_fiber]
106
+ else
107
+ nil
108
+ end
109
+
110
+ if source_fiber_stack
111
+ while (outer_source_fiber = source_fiber_stack.pop)
112
+ resume_fiber_and_enqueue_continuation(outer_source_fiber, source_fiber_stack)
113
+
114
+ # If this source caused more sources to become pending, run those before running this one again:
115
+ next_source_fiber = create_source_fiber
116
+ if next_source_fiber
117
+ source_fiber_stack << next_source_fiber
118
+ end
119
+ end
120
+ end
121
+
122
+ # We ran all the first round of execution fibers,
123
+ # and we ran all the pending sources.
124
+ # So pick up any paused execution fibers and repeat.
125
+ @waiting_fibers.concat(already_run_fibers)
126
+ already_run_fibers.clear
127
+ end
128
+ end
129
+ nil
130
+ end
131
+
132
+ # Get a Source instance from this dataloader, for calling `.load(...)` or `.request(...)` on.
133
+ #
134
+ # @param source_class [Class<GraphQL::Dataloader::Source]
135
+ # @param batch_parameters [Array<Object>]
136
+ # @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`,
137
+ # and cached for the lifetime of this {Multiplex}.
138
+ def with(source_class, *batch_parameters)
139
+ @source_cache[source_class][batch_parameters]
140
+ end
141
+
142
+ # @api private
143
+ attr_accessor :current_runtime
144
+
145
+ private
146
+
147
+ # Check if this fiber is still alive.
148
+ # If it is, and it should continue, then enqueue a continuation.
149
+ # If it is, re-enqueue it in `fiber_queue`.
150
+ # Otherwise, clean it up from @yielded_fibers.
151
+ # @return [void]
152
+ def resume_fiber_and_enqueue_continuation(fiber, fiber_stack)
153
+ result = fiber.resume
154
+ if result.is_a?(StandardError)
155
+ raise result
156
+ end
157
+
158
+ # This fiber yielded; there's more to do here.
159
+ # (If `#alive?` is false, then the fiber concluded without yielding.)
160
+ if fiber.alive?
161
+ if !@yielded_fibers.include?(fiber)
162
+ # This fiber hasn't yielded yet, we should enqueue a continuation fiber
163
+ @yielded_fibers.add(fiber)
164
+ current_runtime.enqueue_selections_fiber
165
+ end
166
+ fiber_stack << fiber
167
+ else
168
+ # Keep this set clean so that fibers can be GC'ed during execution
169
+ @yielded_fibers.delete(fiber)
170
+ end
171
+ end
172
+
173
+ # If there are pending sources, return a fiber for running them.
174
+ # Otherwise, return `nil`.
175
+ #
176
+ # @return [Fiber, nil]
177
+ def create_source_fiber
178
+ pending_sources = nil
179
+ @source_cache.each_value do |source_by_batch_params|
180
+ source_by_batch_params.each_value do |source|
181
+ if source.pending?
182
+ pending_sources ||= []
183
+ pending_sources << source
184
+ end
185
+ end
186
+ end
187
+
188
+ if pending_sources
189
+ source_fiber = Fiber.new do
190
+ pending_sources.each(&:run_pending_keys)
191
+ end
192
+ end
193
+
194
+ source_fiber
195
+ end
196
+ end
197
+ end
@@ -4,7 +4,7 @@ module GraphQL
4
4
  module AssignGlobalIdField
5
5
  def self.call(type_defn, field_name, **field_kwargs)
6
6
  resolve = GraphQL::Relay::GlobalIdResolve.new(type: type_defn)
7
- GraphQL::Define::AssignObjectField.call(type_defn, field_name, **field_kwargs, type: GraphQL::ID_TYPE.to_non_null_type, resolve: resolve)
7
+ GraphQL::Define::AssignObjectField.call(type_defn, field_name, **field_kwargs, type: GraphQL::DEPRECATED_ID_TYPE.to_non_null_type, resolve: resolve)
8
8
  end
9
9
  end
10
10
  end
@@ -3,6 +3,23 @@ module GraphQL
3
3
  module Define
4
4
  # @api deprecated
5
5
  module InstanceDefinable
6
+ module DeprecatedDefine
7
+ def define(**kwargs, &block)
8
+ deprecated_caller = caller(1, 1).first
9
+ if deprecated_caller.include?("lib/graphql")
10
+ deprecated_caller = caller(2, 10).find { |c| !c.include?("lib/graphql") }
11
+ end
12
+
13
+ if deprecated_caller
14
+ warn <<-ERR
15
+ #{self}.define will be removed in GraphQL-Ruby 2.0; use a class-based definition instead. See https://graphql-ruby.org/schema/class_based_api.html.
16
+ -> called from #{deprecated_caller}
17
+ ERR
18
+ end
19
+ deprecated_define(**kwargs, &block)
20
+ end
21
+ end
22
+
6
23
  def self.included(base)
7
24
  base.extend(ClassMethods)
8
25
  base.ensure_defined(:metadata)
@@ -14,7 +31,7 @@ module GraphQL
14
31
  end
15
32
 
16
33
  # @api deprecated
17
- def define(**kwargs, &block)
34
+ def deprecated_define(**kwargs, &block)
18
35
  # make sure the previous definition_proc was executed:
19
36
  ensure_defined
20
37
  stash_dependent_methods
@@ -22,11 +39,16 @@ module GraphQL
22
39
  nil
23
40
  end
24
41
 
42
+ # @api deprecated
43
+ def define(**kwargs, &block)
44
+ deprecated_define(**kwargs, &block)
45
+ end
46
+
25
47
  # @api deprecated
26
48
  def redefine(**kwargs, &block)
27
49
  ensure_defined
28
50
  new_inst = self.dup
29
- new_inst.define(**kwargs, &block)
51
+ new_inst.deprecated_define(**kwargs, &block)
30
52
  new_inst
31
53
  end
32
54
 
@@ -125,8 +147,16 @@ module GraphQL
125
147
  module ClassMethods
126
148
  # Create a new instance
127
149
  # and prepare a definition using its {.definitions}.
150
+ # @api deprecated
128
151
  # @param kwargs [Hash] Key-value pairs corresponding to defininitions from `accepts_definitions`
129
152
  # @param block [Proc] Block which calls helper methods from `accepts_definitions`
153
+ def deprecated_define(**kwargs, &block)
154
+ instance = self.new
155
+ instance.deprecated_define(**kwargs, &block)
156
+ instance
157
+ end
158
+
159
+ # @api deprecated
130
160
  def define(**kwargs, &block)
131
161
  instance = self.new
132
162
  instance.define(**kwargs, &block)
@@ -7,11 +7,11 @@ module GraphQL
7
7
  class TypeDefiner
8
8
  include Singleton
9
9
  # rubocop:disable Naming/MethodName
10
- def Int; GraphQL::INT_TYPE; end
11
- def String; GraphQL::STRING_TYPE; end
12
- def Float; GraphQL::FLOAT_TYPE; end
13
- def Boolean; GraphQL::BOOLEAN_TYPE; end
14
- def ID; GraphQL::ID_TYPE; end
10
+ def Int; GraphQL::DEPRECATED_INT_TYPE; end
11
+ def String; GraphQL::DEPRECATED_STRING_TYPE; end
12
+ def Float; GraphQL::DEPRECATED_FLOAT_TYPE; end
13
+ def Boolean; GraphQL::DEPRECATED_BOOLEAN_TYPE; end
14
+ def ID; GraphQL::DEPRECATED_ID_TYPE; end
15
15
  # rubocop:enable Naming/MethodName
16
16
 
17
17
  # Make a {ListType} which wraps the input type
@@ -23,12 +23,17 @@ module GraphQL
23
23
  ]
24
24
 
25
25
  def self.activate
26
+ deprecated_caller = caller(1, 1).first
27
+ warn "DeprecatedDSL will be removed from GraphQL-Ruby 2.0, use `.to_non_null_type` instead of `!` and remove `.activate` from #{deprecated_caller}"
26
28
  TYPE_CLASSES.each { |c| c.extend(Methods) }
27
29
  GraphQL::Schema::List.include(Methods)
28
30
  GraphQL::Schema::NonNull.include(Methods)
29
31
  end
32
+
30
33
  module Methods
31
34
  def !
35
+ deprecated_caller = caller(1, 1).first
36
+ warn "DeprecatedDSL will be removed from GraphQL-Ruby 2.0, use `.to_non_null_type` instead of `!` at #{deprecated_caller}"
32
37
  to_non_null_type
33
38
  end
34
39
  end
@@ -2,6 +2,8 @@
2
2
  module GraphQL
3
3
  # @api deprecated
4
4
  class EnumType < GraphQL::BaseType
5
+ extend Define::InstanceDefinable::DeprecatedDefine
6
+
5
7
  accepts_definitions :values, value: GraphQL::Define::AssignEnumValue
6
8
  ensure_defined(:values, :validate_non_null_input, :coerce_non_null_input, :coerce_result)
7
9
  attr_accessor :ast_node
@@ -18,6 +18,10 @@ module GraphQL
18
18
  #
19
19
  class Errors
20
20
  def self.use(schema)
21
+ if schema.plugins.any? { |(plugin, kwargs)| plugin == self }
22
+ definition_line = caller(2, 1).first
23
+ warn("GraphQL::Execution::Errors is now installed by default, remove `use GraphQL::Execution::Errors` from #{definition_line}")
24
+ end
21
25
  schema.error_handler = self.new(schema)
22
26
  end
23
27
 
@@ -18,7 +18,14 @@ module GraphQL
18
18
  # @api private
19
19
  PROPAGATE_NULL = PropagateNull.new
20
20
 
21
+ def self.use(schema_class)
22
+ schema_class.query_execution_strategy(self)
23
+ schema_class.mutation_execution_strategy(self)
24
+ schema_class.subscription_execution_strategy(self)
25
+ end
26
+
21
27
  def execute(ast_operation, root_type, query)
28
+ warn "#{self.class} will be removed in GraphQL-Ruby 2.0, please upgrade to the Interpreter: https://graphql-ruby.org/queries/interpreter.html"
22
29
  result = resolve_root_selection(query)
23
30
  lazy_resolve_root_selection(result, **{query: query})
24
31
  GraphQL::Execution::Flatten.call(query.context)
@@ -5,6 +5,9 @@ module GraphQL
5
5
  class Interpreter
6
6
  # A wrapper for argument hashes in GraphQL queries.
7
7
  #
8
+ # This object is immutable so that the runtime code can be sure that
9
+ # modifications don't leak from one use to another
10
+ #
8
11
  # @see GraphQL::Query#arguments_for to get access to these objects.
9
12
  class Arguments
10
13
  extend Forwardable
@@ -14,26 +17,43 @@ module GraphQL
14
17
  # This hash is the one used at runtime.
15
18
  #
16
19
  # @return [Hash<Symbol, Object>]
17
- def keyword_arguments
18
- @keyword_arguments ||= begin
19
- kwargs = {}
20
- argument_values.each do |name, arg_val|
21
- kwargs[name] = arg_val.value
22
- end
23
- kwargs
24
- end
25
- end
20
+ attr_reader :keyword_arguments
26
21
 
27
22
  # @param argument_values [nil, Hash{Symbol => ArgumentValue}]
28
- def initialize(argument_values:)
29
- @argument_values = argument_values
23
+ # @param keyword_arguments [nil, Hash{Symbol => Object}]
24
+ def initialize(keyword_arguments: nil, argument_values:)
30
25
  @empty = argument_values.nil? || argument_values.empty?
26
+ # This is only present when `extras` have been merged in:
27
+ if keyword_arguments
28
+ # This is a little crazy. We expect the `:argument_details` extra to _include extras_,
29
+ # but the object isn't created until _after_ extras are put together.
30
+ # So, we have to use a special flag here to say, "at the last minute, add yourself to the keyword args."
31
+ #
32
+ # Otherwise:
33
+ # - We can't access the final Arguments instance _while_ we're preparing extras
34
+ # - After we _can_ access it, it's frozen, so we can't add anything.
35
+ #
36
+ # So, this flag gives us a chance to sneak it in before freezing, _and_ while we have access
37
+ # to the new Arguments instance itself.
38
+ if keyword_arguments[:argument_details] == :__arguments_add_self
39
+ keyword_arguments[:argument_details] = self
40
+ end
41
+ @keyword_arguments = keyword_arguments.freeze
42
+ elsif !@empty
43
+ @keyword_arguments = {}
44
+ argument_values.each do |name, arg_val|
45
+ @keyword_arguments[name] = arg_val.value
46
+ end
47
+ @keyword_arguments.freeze
48
+ else
49
+ @keyword_arguments = NO_ARGS
50
+ end
51
+ @argument_values = argument_values ? argument_values.freeze : NO_ARGS
52
+ freeze
31
53
  end
32
54
 
33
55
  # @return [Hash{Symbol => ArgumentValue}]
34
- def argument_values
35
- @argument_values ||= {}
36
- end
56
+ attr_reader :argument_values
37
57
 
38
58
  def empty?
39
59
  @empty
@@ -45,6 +65,23 @@ module GraphQL
45
65
  def inspect
46
66
  "#<#{self.class} @keyword_arguments=#{keyword_arguments.inspect}>"
47
67
  end
68
+
69
+ # Create a new arguments instance which includes these extras.
70
+ #
71
+ # This is called by the runtime to implement field `extras: [...]`
72
+ #
73
+ # @param extra_args [Hash<Symbol => Object>]
74
+ # @return [Interpreter::Arguments]
75
+ # @api private
76
+ def merge_extras(extra_args)
77
+ self.class.new(
78
+ argument_values: argument_values,
79
+ keyword_arguments: keyword_arguments.merge(extra_args)
80
+ )
81
+ end
82
+
83
+ NO_ARGS = {}.freeze
84
+ EMPTY = self.new(argument_values: nil, keyword_arguments: NO_ARGS).freeze
48
85
  end
49
86
  end
50
87
  end
@@ -13,13 +13,6 @@ module GraphQL
13
13
  @object
14
14
  end
15
15
  end
16
-
17
- # Allows to return "raw" value from the resolver
18
- module HandlesRawValue
19
- def raw_value(obj)
20
- RawValue.new(obj)
21
- end
22
- end
23
16
  end
24
17
  end
25
18
  end