graphql 2.0.27 → 2.2.6

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  3. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  4. data/lib/generators/graphql/install_generator.rb +3 -0
  5. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  6. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  7. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  8. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  9. data/lib/generators/graphql/templates/base_field.erb +2 -0
  10. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  11. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  12. data/lib/generators/graphql/templates/base_object.erb +2 -0
  13. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  14. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  15. data/lib/generators/graphql/templates/base_union.erb +2 -0
  16. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  17. data/lib/generators/graphql/templates/loader.erb +2 -0
  18. data/lib/generators/graphql/templates/mutation.erb +2 -0
  19. data/lib/generators/graphql/templates/node_type.erb +2 -0
  20. data/lib/generators/graphql/templates/query_type.erb +2 -0
  21. data/lib/generators/graphql/templates/schema.erb +2 -0
  22. data/lib/graphql/analysis/ast/analyzer.rb +7 -0
  23. data/lib/graphql/analysis/ast/field_usage.rb +32 -7
  24. data/lib/graphql/analysis/ast/query_complexity.rb +80 -128
  25. data/lib/graphql/analysis/ast/query_depth.rb +7 -2
  26. data/lib/graphql/analysis/ast/visitor.rb +2 -2
  27. data/lib/graphql/analysis/ast.rb +21 -11
  28. data/lib/graphql/backtrace/trace.rb +12 -15
  29. data/lib/graphql/coercion_error.rb +1 -9
  30. data/lib/graphql/dataloader/async_dataloader.rb +85 -0
  31. data/lib/graphql/dataloader/source.rb +11 -3
  32. data/lib/graphql/dataloader.rb +109 -142
  33. data/lib/graphql/duration_encoding_error.rb +16 -0
  34. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
  35. data/lib/graphql/execution/interpreter/runtime.rb +70 -248
  36. data/lib/graphql/execution/interpreter.rb +91 -157
  37. data/lib/graphql/execution/lookahead.rb +88 -21
  38. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  39. data/lib/graphql/introspection/entry_points.rb +11 -5
  40. data/lib/graphql/introspection/schema_type.rb +3 -1
  41. data/lib/graphql/language/block_string.rb +34 -18
  42. data/lib/graphql/language/definition_slice.rb +1 -1
  43. data/lib/graphql/language/document_from_schema_definition.rb +37 -37
  44. data/lib/graphql/language/lexer.rb +271 -177
  45. data/lib/graphql/language/nodes.rb +74 -56
  46. data/lib/graphql/language/parser.rb +697 -1986
  47. data/lib/graphql/language/printer.rb +299 -146
  48. data/lib/graphql/language/sanitized_printer.rb +20 -22
  49. data/lib/graphql/language/static_visitor.rb +167 -0
  50. data/lib/graphql/language/visitor.rb +20 -81
  51. data/lib/graphql/language.rb +1 -0
  52. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  53. data/lib/graphql/pagination/array_connection.rb +3 -3
  54. data/lib/graphql/pagination/connection.rb +28 -1
  55. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  56. data/lib/graphql/pagination/relation_connection.rb +3 -3
  57. data/lib/graphql/query/context/scoped_context.rb +101 -0
  58. data/lib/graphql/query/context.rb +36 -98
  59. data/lib/graphql/query/null_context.rb +4 -11
  60. data/lib/graphql/query/validation_pipeline.rb +2 -2
  61. data/lib/graphql/query/variables.rb +3 -3
  62. data/lib/graphql/query.rb +13 -22
  63. data/lib/graphql/railtie.rb +9 -6
  64. data/lib/graphql/rake_task.rb +3 -12
  65. data/lib/graphql/schema/argument.rb +6 -1
  66. data/lib/graphql/schema/build_from_definition.rb +0 -11
  67. data/lib/graphql/schema/directive/one_of.rb +12 -0
  68. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  69. data/lib/graphql/schema/directive.rb +1 -1
  70. data/lib/graphql/schema/enum.rb +3 -3
  71. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  72. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  73. data/lib/graphql/schema/field.rb +8 -5
  74. data/lib/graphql/schema/has_single_input_argument.rb +156 -0
  75. data/lib/graphql/schema/input_object.rb +2 -2
  76. data/lib/graphql/schema/interface.rb +10 -10
  77. data/lib/graphql/schema/introspection_system.rb +2 -0
  78. data/lib/graphql/schema/loader.rb +0 -2
  79. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  80. data/lib/graphql/schema/member/has_arguments.rb +61 -38
  81. data/lib/graphql/schema/member/has_fields.rb +8 -5
  82. data/lib/graphql/schema/member/has_interfaces.rb +23 -9
  83. data/lib/graphql/schema/member/scoped.rb +19 -0
  84. data/lib/graphql/schema/member/validates_input.rb +3 -3
  85. data/lib/graphql/schema/object.rb +8 -0
  86. data/lib/graphql/schema/printer.rb +8 -7
  87. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  88. data/lib/graphql/schema/resolver.rb +7 -3
  89. data/lib/graphql/schema/scalar.rb +3 -3
  90. data/lib/graphql/schema/subscription.rb +11 -4
  91. data/lib/graphql/schema/union.rb +1 -1
  92. data/lib/graphql/schema/warden.rb +96 -94
  93. data/lib/graphql/schema.rb +219 -72
  94. data/lib/graphql/static_validation/all_rules.rb +1 -1
  95. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  96. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  97. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  98. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  99. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  100. data/lib/graphql/static_validation/validation_context.rb +5 -5
  101. data/lib/graphql/static_validation/validator.rb +3 -0
  102. data/lib/graphql/static_validation.rb +0 -1
  103. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
  104. data/lib/graphql/subscriptions/event.rb +8 -2
  105. data/lib/graphql/subscriptions.rb +14 -12
  106. data/lib/graphql/testing/helpers.rb +125 -0
  107. data/lib/graphql/testing.rb +2 -0
  108. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  109. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  110. data/lib/graphql/tracing/data_dog_trace.rb +21 -34
  111. data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
  112. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  113. data/lib/graphql/tracing/platform_tracing.rb +2 -0
  114. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  115. data/lib/graphql/tracing/sentry_trace.rb +94 -0
  116. data/lib/graphql/tracing/trace.rb +1 -0
  117. data/lib/graphql/tracing.rb +3 -1
  118. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  119. data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
  120. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  121. data/lib/graphql/types.rb +1 -0
  122. data/lib/graphql/version.rb +1 -1
  123. data/lib/graphql.rb +3 -3
  124. data/readme.md +12 -2
  125. metadata +33 -25
  126. data/lib/graphql/deprecation.rb +0 -9
  127. data/lib/graphql/filter.rb +0 -59
  128. data/lib/graphql/language/parser.y +0 -560
  129. data/lib/graphql/static_validation/type_stack.rb +0 -216
  130. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/query/context/scoped_context"
3
+
2
4
  module GraphQL
3
5
  class Query
4
6
  # Expose some query-specific info to field resolve functions.
@@ -90,104 +92,6 @@ module GraphQL
90
92
  @scoped_context = ScopedContext.new(self)
91
93
  end
92
94
 
93
- class ScopedContext
94
- NO_PATH = GraphQL::EmptyObjects::EMPTY_ARRAY
95
- NO_CONTEXT = GraphQL::EmptyObjects::EMPTY_HASH
96
-
97
- def initialize(query_context)
98
- @query_context = query_context
99
- @scoped_contexts = nil
100
- @all_keys = nil
101
- end
102
-
103
- def merged_context
104
- if @scoped_contexts.nil?
105
- NO_CONTEXT
106
- else
107
- merged_ctx = {}
108
- each_present_path_ctx do |path_ctx|
109
- merged_ctx = path_ctx.merge(merged_ctx)
110
- end
111
- merged_ctx
112
- end
113
- end
114
-
115
- def merge!(hash)
116
- @all_keys ||= Set.new
117
- @all_keys.merge(hash.keys)
118
- ctx = @scoped_contexts ||= {}
119
- current_path.each do |path_part|
120
- ctx = ctx[path_part] ||= { parent: ctx }
121
- end
122
- this_scoped_ctx = ctx[:scoped_context] ||= {}
123
- this_scoped_ctx.merge!(hash)
124
- end
125
-
126
- def key?(key)
127
- if @all_keys && @all_keys.include?(key)
128
- each_present_path_ctx do |path_ctx|
129
- if path_ctx.key?(key)
130
- return true
131
- end
132
- end
133
- end
134
- false
135
- end
136
-
137
- def [](key)
138
- each_present_path_ctx do |path_ctx|
139
- if path_ctx.key?(key)
140
- return path_ctx[key]
141
- end
142
- end
143
- nil
144
- end
145
-
146
- def current_path
147
- @query_context.current_path || NO_PATH
148
- end
149
-
150
- def dig(key, *other_keys)
151
- each_present_path_ctx do |path_ctx|
152
- if path_ctx.key?(key)
153
- found_value = path_ctx[key]
154
- if other_keys.any?
155
- return found_value.dig(*other_keys)
156
- else
157
- return found_value
158
- end
159
- end
160
- end
161
- nil
162
- end
163
-
164
- private
165
-
166
- # Start at the current location,
167
- # but look up the tree for previously-assigned scoped values
168
- def each_present_path_ctx
169
- ctx = @scoped_contexts
170
- if ctx.nil?
171
- # no-op
172
- else
173
- current_path.each do |path_part|
174
- if ctx.key?(path_part)
175
- ctx = ctx[path_part]
176
- else
177
- break
178
- end
179
- end
180
-
181
- while ctx
182
- if (scoped_ctx = ctx[:scoped_context])
183
- yield(scoped_ctx)
184
- end
185
- ctx = ctx[:parent]
186
- end
187
- end
188
- end
189
- end
190
-
191
95
  # @return [Hash] A hash that will be added verbatim to the result hash, as `"extensions" => { ... }`
192
96
  def response_extensions
193
97
  namespace(:__query_result_extensions__)
@@ -333,6 +237,10 @@ module GraphQL
333
237
  @storage.key?(ns)
334
238
  end
335
239
 
240
+ def logger
241
+ @query && @query.logger
242
+ end
243
+
336
244
  def inspect
337
245
  "#<Query::Context ...>"
338
246
  end
@@ -345,6 +253,36 @@ module GraphQL
345
253
  scoped_merge!(key => value)
346
254
  nil
347
255
  end
256
+
257
+ # Use this when you need to do a scoped set _inside_ a lazy-loaded (or batch-loaded)
258
+ # block of code.
259
+ #
260
+ # @example using scoped context inside a promise
261
+ # scoped_ctx = context.scoped
262
+ # SomeBatchLoader.load(...).then do |thing|
263
+ # # use a scoped_ctx which was created _before_ dataloading:
264
+ # scoped_ctx.set!(:thing, thing)
265
+ # end
266
+ # @return [Context::Scoped]
267
+ def scoped
268
+ Scoped.new(@scoped_context, current_path)
269
+ end
270
+
271
+ class Scoped
272
+ def initialize(scoped_context, path)
273
+ @path = path
274
+ @scoped_context = scoped_context
275
+ end
276
+
277
+ def merge!(hash)
278
+ @scoped_context.merge!(hash, at: @path)
279
+ end
280
+
281
+ def set!(key, value)
282
+ @scoped_context.merge!({ key => value }, at: @path)
283
+ nil
284
+ end
285
+ end
348
286
  end
349
287
  end
350
288
  end
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/query/context"
2
3
  module GraphQL
3
4
  class Query
4
5
  # This object can be `ctx` in places where there is no query
5
- class NullContext
6
+ class NullContext < Context
7
+ include Singleton
8
+
6
9
  class NullQuery
7
10
  def after_lazy(value)
8
11
  yield(value)
@@ -27,16 +30,6 @@ module GraphQL
27
30
  def interpreter?
28
31
  true
29
32
  end
30
-
31
- class << self
32
- extend Forwardable
33
-
34
- def instance
35
- @instance ||= self.new
36
- end
37
-
38
- def_delegators :instance, :query, :warden, :schema, :interpreter?, :dataloader, :[], :fetch, :dig, :key?
39
- end
40
33
  end
41
34
  end
42
35
  end
@@ -14,7 +14,7 @@ module GraphQL
14
14
  #
15
15
  # @api private
16
16
  class ValidationPipeline
17
- attr_reader :max_depth, :max_complexity
17
+ attr_reader :max_depth, :max_complexity, :validate_timeout_remaining
18
18
 
19
19
  def initialize(query:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
20
20
  @validation_errors = []
@@ -71,7 +71,7 @@ module GraphQL
71
71
  validator = @query.static_validator || @schema.static_validator
72
72
  validation_result = validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
73
73
  @validation_errors.concat(validation_result[:errors])
74
-
74
+ @validate_timeout_remaining = validation_result[:remaining_timeout]
75
75
  if @validation_errors.empty?
76
76
  @validation_errors.concat(@query.variables.errors)
77
77
  end
@@ -26,7 +26,7 @@ module GraphQL
26
26
  # - Then, fall back to the default value from the query string
27
27
  # If it's still nil, raise an error if it's required.
28
28
  variable_type = schema.type_from_ast(ast_variable.type, context: ctx)
29
- if variable_type.nil?
29
+ if variable_type.nil? || !variable_type.unwrap.kind.input?
30
30
  # Pass -- it will get handled by a validator
31
31
  else
32
32
  variable_name = ast_variable.name
@@ -80,12 +80,12 @@ module GraphQL
80
80
  else
81
81
  val
82
82
  end
83
- end
83
+ end
84
84
 
85
85
  def add_max_errors_reached_message
86
86
  message = "Too many errors processing variables, max validation error limit reached. Execution aborted"
87
87
  validation_result = GraphQL::Query::InputValidationResult.from_problem(message)
88
- errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
88
+ errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
89
89
  end
90
90
  end
91
91
  end
data/lib/graphql/query.rb CHANGED
@@ -95,15 +95,10 @@ module GraphQL
95
95
  # @param root_value [Object] the object used to resolve fields on the root type
96
96
  # @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
97
97
  # @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
98
- # @param except [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns truthy
99
- # @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
100
- def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil, warden: nil)
98
+ def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil)
101
99
  # Even if `variables: nil` is passed, use an empty hash for simpler logic
102
100
  variables ||= {}
103
101
  @schema = schema
104
- if only || except
105
- merge_filters(except: except, only: only)
106
- end
107
102
  @context = schema.context_class.new(query: self, object: root_value, values: context)
108
103
  @warden = warden
109
104
  @subscription_topic = subscription_topic
@@ -120,8 +115,6 @@ module GraphQL
120
115
  if schema.trace_class <= GraphQL::Tracing::CallLegacyTracers
121
116
  context_tracers += [GraphQL::Backtrace::Tracer]
122
117
  @tracers << GraphQL::Backtrace::Tracer
123
- elsif !(current_trace.class <= GraphQL::Backtrace::Trace)
124
- raise "Invariant: `backtrace: true` should have provided a trace class with Backtrace mixed in, but it didnt. (Found: #{current_trace.class.ancestors}). This is a bug in GraphQL-Ruby, please report it on GitHub."
125
118
  end
126
119
  end
127
120
 
@@ -129,7 +122,6 @@ module GraphQL
129
122
  raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
130
123
  end
131
124
 
132
-
133
125
  @analysis_errors = []
134
126
  if variables.is_a?(String)
135
127
  raise ArgumentError, "Query variables should be a Hash, not a String. Try JSON.parse to prepare variables."
@@ -168,6 +160,14 @@ module GraphQL
168
160
 
169
161
  @result_values = nil
170
162
  @executed = false
163
+
164
+ @logger = if context && context[:logger] == false
165
+ Logger.new(IO::NULL)
166
+ elsif context && (l = context[:logger])
167
+ l
168
+ else
169
+ schema.default_logger
170
+ end
171
171
  end
172
172
 
173
173
  # If a document was provided to `GraphQL::Schema#execute` instead of the raw query string, we will need to get it from the document
@@ -317,7 +317,7 @@ module GraphQL
317
317
  end
318
318
 
319
319
  def_delegators :validation_pipeline, :validation_errors,
320
- :analyzers, :ast_analyzers, :max_depth, :max_complexity
320
+ :analyzers, :ast_analyzers, :max_depth, :max_complexity, :validate_timeout_remaining
321
321
 
322
322
  attr_accessor :analysis_errors
323
323
  def valid?
@@ -354,17 +354,6 @@ module GraphQL
354
354
  with_prepared_ast { @query }
355
355
  end
356
356
 
357
- # @return [void]
358
- def merge_filters(only: nil, except: nil)
359
- if @prepared_ast
360
- raise "Can't add filters after preparing the query"
361
- else
362
- @filter ||= @schema.default_filter
363
- @filter = @filter.merge(only: only, except: except)
364
- end
365
- nil
366
- end
367
-
368
357
  def subscription?
369
358
  with_prepared_ast { @subscription }
370
359
  end
@@ -386,6 +375,8 @@ module GraphQL
386
375
  end
387
376
  end
388
377
 
378
+ attr_reader :logger
379
+
389
380
  private
390
381
 
391
382
  def find_operation(operations, operation_name)
@@ -400,7 +391,7 @@ module GraphQL
400
391
 
401
392
  def prepare_ast
402
393
  @prepared_ast = true
403
- @warden ||= @schema.warden_class.new(@filter, schema: @schema, context: @context)
394
+ @warden ||= @schema.warden_class.new(schema: @schema, context: @context)
404
395
  parse_error = nil
405
396
  @document ||= begin
406
397
  if query_string
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module GraphQL
3
4
  class Railtie < Rails::Railtie
4
- config.before_configuration do
5
- # Bootsnap compile cache has similar expiration properties,
6
- # so we assume that if the user has bootsnap setup it's ok
7
- # to piggy back on it.
8
- if ::Object.const_defined?("Bootsnap::CompileCache::ISeq") && Bootsnap::CompileCache::ISeq.cache_dir
9
- Language::Parser.cache ||= Language::Cache.new(Pathname.new(Bootsnap::CompileCache::ISeq.cache_dir).join('graphql'))
5
+ config.graphql = ActiveSupport::OrderedOptions.new
6
+ config.graphql.parser_cache = false
7
+
8
+ initializer("graphql.cache") do |app|
9
+ if config.graphql.parser_cache
10
+ Language::Parser.cache ||= Language::Cache.new(
11
+ app.root.join("tmp/cache/graphql")
12
+ )
10
13
  end
11
14
  end
12
15
  end
@@ -9,8 +9,7 @@ module GraphQL
9
9
  # By default, schemas are looked up by name as constants using `schema_name:`.
10
10
  # You can provide a `load_schema` function to return your schema another way.
11
11
  #
12
- # `load_context:`, `only:` and `except:` are supported so that
13
- # you can keep an eye on how filters affect your schema.
12
+ # Use `load_context:` and `visible?` to dump schemas under certain visibility constraints.
14
13
  #
15
14
  # @example Dump a Schema to .graphql + .json files
16
15
  # require "graphql/rake_task"
@@ -36,8 +35,6 @@ module GraphQL
36
35
  schema_name: nil,
37
36
  load_schema: ->(task) { Object.const_get(task.schema_name) },
38
37
  load_context: ->(task) { {} },
39
- only: nil,
40
- except: nil,
41
38
  directory: ".",
42
39
  idl_outfile: "schema.graphql",
43
40
  json_outfile: "schema.json",
@@ -68,12 +65,6 @@ module GraphQL
68
65
  # @return [<#call(task)>] A callable for loading the query context
69
66
  attr_accessor :load_context
70
67
 
71
- # @return [<#call(member, ctx)>, nil] A filter for this task
72
- attr_accessor :only
73
-
74
- # @return [<#call(member, ctx)>, nil] A filter for this task
75
- attr_accessor :except
76
-
77
68
  # @return [String] target for IDL task
78
69
  attr_accessor :idl_outfile
79
70
 
@@ -117,10 +108,10 @@ module GraphQL
117
108
  include_is_repeatable: include_is_repeatable,
118
109
  include_specified_by_url: include_specified_by_url,
119
110
  include_schema_description: include_schema_description,
120
- only: @only, except: @except, context: context
111
+ context: context
121
112
  )
122
113
  when :to_definition
123
- schema.to_definition(only: @only, except: @except, context: context)
114
+ schema.to_definition(context: context)
124
115
  else
125
116
  raise ArgumentError, "Unexpected schema dump method: #{method_name.inspect}"
126
117
  end
@@ -221,8 +221,13 @@ module GraphQL
221
221
  #
222
222
  # This will have to be called later, when the runtime object _is_ available.
223
223
  value
224
- else
224
+ elsif obj.respond_to?(@prepare)
225
225
  obj.public_send(@prepare, value)
226
+ elsif owner.respond_to?(@prepare)
227
+ owner.public_send(@prepare, value, context || obj.context)
228
+ else
229
+ raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}. "\
230
+ "Could not find prepare method #{@prepare} on #{obj.class} or #{owner}."
226
231
  end
227
232
  elsif @prepare.respond_to?(:call)
228
233
  @prepare.call(value, context || obj.context)
@@ -432,14 +432,12 @@ module GraphQL
432
432
  builder = self
433
433
 
434
434
  field_definitions.each do |field_definition|
435
- type_name = resolve_type_name(field_definition.type)
436
435
  resolve_method_name = -"resolve_field_#{field_definition.name}"
437
436
  schema_field_defn = owner.field(
438
437
  field_definition.name,
439
438
  description: field_definition.description,
440
439
  type: type_resolver.call(field_definition.type),
441
440
  null: true,
442
- connection: type_name.end_with?("Connection"),
443
441
  connection_extension: nil,
444
442
  deprecation_reason: build_deprecation_reason(field_definition.directives),
445
443
  ast_node: field_definition,
@@ -487,15 +485,6 @@ module GraphQL
487
485
  }
488
486
  resolve_type_proc
489
487
  end
490
-
491
- def resolve_type_name(type)
492
- case type
493
- when GraphQL::Language::Nodes::TypeName
494
- return type.name
495
- else
496
- resolve_type_name(type.of_type)
497
- end
498
- end
499
488
  end
500
489
 
501
490
  private_constant :Builder
@@ -6,6 +6,18 @@ module GraphQL
6
6
  description "Requires that exactly one field must be supplied and that field must not be `null`."
7
7
  locations(GraphQL::Schema::Directive::INPUT_OBJECT)
8
8
  default_directive true
9
+
10
+ def initialize(...)
11
+ super
12
+
13
+ owner.extend(IsOneOf)
14
+ end
15
+
16
+ module IsOneOf
17
+ def one_of?
18
+ true
19
+ end
20
+ end
9
21
  end
10
22
  end
11
23
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Directive < GraphQL::Schema::Member
5
+ class SpecifiedBy < GraphQL::Schema::Directive
6
+ description "Exposes a URL that specifies the behavior of this scalar."
7
+ locations(GraphQL::Schema::Directive::SCALAR)
8
+ default_directive true
9
+
10
+ argument :url, String, description: "The URL that specifies the behavior of this scalar."
11
+ end
12
+ end
13
+ end
14
+ end
@@ -119,7 +119,7 @@ module GraphQL
119
119
  # - lazy resolution
120
120
  # Probably, those won't be needed here, since these are configuration arguments,
121
121
  # not runtime arguments.
122
- @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext)
122
+ @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
123
123
  end
124
124
 
125
125
  def graphql_name
@@ -14,7 +14,7 @@ module GraphQL
14
14
  # # ONIONS
15
15
  # # PEPPERS
16
16
  # # }
17
- # class PizzaTopping < GraphQL::Enum
17
+ # class PizzaTopping < GraphQL::Schema::Enum
18
18
  # value :MUSHROOMS
19
19
  # value :ONIONS
20
20
  # value :PEPPERS
@@ -68,7 +68,7 @@ module GraphQL
68
68
  end
69
69
 
70
70
  # @return [Array<GraphQL::Schema::EnumValue>] Possible values of this enum
71
- def enum_values(context = GraphQL::Query::NullContext)
71
+ def enum_values(context = GraphQL::Query::NullContext.instance)
72
72
  inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
73
73
  visible_values = []
74
74
  warden = Warden.from_context(context)
@@ -110,7 +110,7 @@ module GraphQL
110
110
  end
111
111
 
112
112
  # @return [Hash<String => GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name.
113
- def values(context = GraphQL::Query::NullContext)
113
+ def values(context = GraphQL::Query::NullContext.instance)
114
114
  enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
115
115
  end
116
116
 
@@ -54,23 +54,9 @@ module GraphQL
54
54
  value.edge_class = custom_t
55
55
  end
56
56
  value
57
- elsif context.schema.new_connections?
57
+ else
58
58
  context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
59
59
  context.schema.connections.wrap(field, object.object, value, original_arguments, context)
60
- else
61
- if object.is_a?(GraphQL::Schema::Object)
62
- object = object.object
63
- end
64
- connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(value)
65
- connection_class.new(
66
- value,
67
- original_arguments,
68
- field: field,
69
- max_page_size: field.max_page_size,
70
- default_page_size: field.default_page_size,
71
- parent: object,
72
- context: context,
73
- )
74
60
  end
75
61
  end
76
62
  end
@@ -10,7 +10,14 @@ module GraphQL
10
10
  else
11
11
  ret_type = @field.type.unwrap
12
12
  if ret_type.respond_to?(:scope_items)
13
- ret_type.scope_items(value, context)
13
+ scoped_items = ret_type.scope_items(value, context)
14
+ if !scoped_items.equal?(value) && !ret_type.reauthorize_scoped_objects
15
+ if (current_runtime_state = Thread.current[:__graphql_runtime_info]) &&
16
+ (query_runtime_state = current_runtime_state[context.query])
17
+ query_runtime_state.was_authorized_by_scope_items = true
18
+ end
19
+ end
20
+ scoped_items
14
21
  else
15
22
  value
16
23
  end
@@ -138,7 +138,7 @@ module GraphQL
138
138
  # As a last ditch, try to force loading the return type:
139
139
  type.unwrap.name
140
140
  end
141
- @connection = return_type_name.end_with?("Connection")
141
+ @connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
142
142
  else
143
143
  @connection
144
144
  end
@@ -218,8 +218,8 @@ module GraphQL
218
218
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
219
219
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
220
220
  # @param validates [Array<Hash>] Configurations for validating this field
221
- # @fallback_value [Object] A fallback value if the method is not defined
222
- def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, &definition_block)
221
+ # @param fallback_value [Object] A fallback value if the method is not defined
222
+ def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
223
223
  if name.nil?
224
224
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
225
225
  end
@@ -267,6 +267,7 @@ module GraphQL
267
267
  @method_sym = method_name.to_sym
268
268
  @resolver_method = (resolver_method || name_s).to_sym
269
269
  @complexity = complexity
270
+ @dynamic_introspection = dynamic_introspection
270
271
  @return_type_expr = type
271
272
  @return_type_null = if !null.nil?
272
273
  null
@@ -351,6 +352,8 @@ module GraphQL
351
352
  @call_after_define = true
352
353
  end
353
354
 
355
+ attr_accessor :dynamic_introspection
356
+
354
357
  # If true, subscription updates with this field can be shared between viewers
355
358
  # @return [Boolean, nil]
356
359
  # @see GraphQL::Subscriptions::BroadcastAnalyzer
@@ -659,7 +662,7 @@ module GraphQL
659
662
  method_to_call = nil
660
663
  method_args = nil
661
664
 
662
- Schema::Validator.validate!(validators, application_object, query_ctx, args)
665
+ @own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args)
663
666
 
664
667
  query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
665
668
  if is_authorized
@@ -701,7 +704,7 @@ module GraphQL
701
704
  inner_object.dig(*@dig_keys)
702
705
  elsif inner_object.key?(@method_sym)
703
706
  inner_object[@method_sym]
704
- elsif inner_object.key?(@method_str)
707
+ elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
705
708
  inner_object[@method_str]
706
709
  elsif @fallback_value != NOT_CONFIGURED
707
710
  @fallback_value