graphql 1.10.0.pre1 → 1.10.0.pre2

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +1 -0
  3. data/lib/generators/graphql/install_generator.rb +1 -0
  4. data/lib/generators/graphql/mutation_generator.rb +1 -1
  5. data/lib/generators/graphql/templates/base_field.erb +0 -4
  6. data/lib/generators/graphql/templates/base_mutation.erb +8 -0
  7. data/lib/generators/graphql/templates/graphql_controller.erb +5 -0
  8. data/lib/generators/graphql/templates/mutation.erb +1 -1
  9. data/lib/generators/graphql/templates/schema.erb +1 -1
  10. data/lib/graphql.rb +4 -1
  11. data/lib/graphql/analysis/ast.rb +14 -13
  12. data/lib/graphql/analysis/ast/analyzer.rb +23 -4
  13. data/lib/graphql/analysis/ast/field_usage.rb +1 -1
  14. data/lib/graphql/analysis/ast/max_query_complexity.rb +3 -3
  15. data/lib/graphql/analysis/ast/max_query_depth.rb +7 -3
  16. data/lib/graphql/analysis/ast/query_complexity.rb +2 -2
  17. data/lib/graphql/analysis/ast/visitor.rb +3 -3
  18. data/lib/graphql/base_type.rb +1 -1
  19. data/lib/graphql/directive.rb +0 -1
  20. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  21. data/lib/graphql/execution/errors.rb +4 -8
  22. data/lib/graphql/execution/interpreter.rb +5 -11
  23. data/lib/graphql/execution/interpreter/runtime.rb +56 -48
  24. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  25. data/lib/graphql/execution/lookahead.rb +5 -5
  26. data/lib/graphql/execution/multiplex.rb +10 -0
  27. data/lib/graphql/function.rb +1 -1
  28. data/lib/graphql/input_object_type.rb +3 -2
  29. data/lib/graphql/interface_type.rb +1 -1
  30. data/lib/graphql/introspection/base_object.rb +2 -5
  31. data/lib/graphql/introspection/directive_type.rb +1 -1
  32. data/lib/graphql/introspection/entry_points.rb +6 -6
  33. data/lib/graphql/introspection/schema_type.rb +1 -6
  34. data/lib/graphql/introspection/type_type.rb +5 -5
  35. data/lib/graphql/language.rb +1 -1
  36. data/lib/graphql/language/block_string.rb +2 -2
  37. data/lib/graphql/language/definition_slice.rb +21 -10
  38. data/lib/graphql/language/document_from_schema_definition.rb +42 -42
  39. data/lib/graphql/language/lexer.rb +49 -48
  40. data/lib/graphql/language/lexer.rl +49 -48
  41. data/lib/graphql/language/nodes.rb +11 -8
  42. data/lib/graphql/language/parser.rb +4 -1
  43. data/lib/graphql/language/parser.y +4 -1
  44. data/lib/graphql/language/token.rb +1 -1
  45. data/lib/graphql/pagination/array_connection.rb +0 -1
  46. data/lib/graphql/pagination/connection.rb +31 -10
  47. data/lib/graphql/pagination/connections.rb +7 -2
  48. data/lib/graphql/pagination/relation_connection.rb +1 -7
  49. data/lib/graphql/query.rb +9 -4
  50. data/lib/graphql/query/arguments.rb +8 -1
  51. data/lib/graphql/query/literal_input.rb +2 -1
  52. data/lib/graphql/query/variables.rb +5 -1
  53. data/lib/graphql/relay/base_connection.rb +3 -3
  54. data/lib/graphql/relay/relation_connection.rb +9 -5
  55. data/lib/graphql/schema.rb +699 -153
  56. data/lib/graphql/schema/argument.rb +20 -4
  57. data/lib/graphql/schema/build_from_definition.rb +64 -31
  58. data/lib/graphql/schema/built_in_types.rb +5 -5
  59. data/lib/graphql/schema/directive.rb +16 -1
  60. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  61. data/lib/graphql/schema/directive/feature.rb +1 -1
  62. data/lib/graphql/schema/enum.rb +39 -3
  63. data/lib/graphql/schema/field.rb +39 -9
  64. data/lib/graphql/schema/field/connection_extension.rb +4 -4
  65. data/lib/graphql/schema/find_inherited_value.rb +13 -0
  66. data/lib/graphql/schema/finder.rb +13 -11
  67. data/lib/graphql/schema/input_object.rb +109 -1
  68. data/lib/graphql/schema/interface.rb +8 -7
  69. data/lib/graphql/schema/introspection_system.rb +104 -36
  70. data/lib/graphql/schema/late_bound_type.rb +1 -0
  71. data/lib/graphql/schema/list.rb +26 -0
  72. data/lib/graphql/schema/loader.rb +10 -4
  73. data/lib/graphql/schema/member.rb +3 -0
  74. data/lib/graphql/schema/member/base_dsl_methods.rb +23 -13
  75. data/lib/graphql/schema/member/build_type.rb +1 -1
  76. data/lib/graphql/schema/member/has_arguments.rb +2 -2
  77. data/lib/graphql/schema/member/has_fields.rb +15 -6
  78. data/lib/graphql/schema/member/instrumentation.rb +6 -1
  79. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  80. data/lib/graphql/schema/member/validates_input.rb +33 -0
  81. data/lib/graphql/schema/non_null.rb +25 -0
  82. data/lib/graphql/schema/object.rb +14 -1
  83. data/lib/graphql/schema/printer.rb +4 -3
  84. data/lib/graphql/schema/relay_classic_mutation.rb +5 -1
  85. data/lib/graphql/schema/resolver.rb +20 -2
  86. data/lib/graphql/schema/scalar.rb +18 -3
  87. data/lib/graphql/schema/subscription.rb +1 -1
  88. data/lib/graphql/schema/timeout_middleware.rb +3 -2
  89. data/lib/graphql/schema/traversal.rb +1 -1
  90. data/lib/graphql/schema/type_expression.rb +22 -24
  91. data/lib/graphql/schema/validation.rb +17 -1
  92. data/lib/graphql/schema/warden.rb +46 -17
  93. data/lib/graphql/schema/wrapper.rb +1 -1
  94. data/lib/graphql/static_validation/base_visitor.rb +10 -6
  95. data/lib/graphql/static_validation/definition_dependencies.rb +21 -12
  96. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  97. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  98. data/lib/graphql/static_validation/type_stack.rb +2 -2
  99. data/lib/graphql/static_validation/validator.rb +1 -1
  100. data/lib/graphql/subscriptions.rb +38 -13
  101. data/lib/graphql/subscriptions/event.rb +24 -7
  102. data/lib/graphql/subscriptions/instrumentation.rb +1 -1
  103. data/lib/graphql/subscriptions/subscription_root.rb +0 -1
  104. data/lib/graphql/tracing/active_support_notifications_tracing.rb +10 -10
  105. data/lib/graphql/tracing/platform_tracing.rb +1 -2
  106. data/lib/graphql/tracing/skylight_tracing.rb +1 -0
  107. data/lib/graphql/unresolved_type_error.rb +2 -2
  108. data/lib/graphql/upgrader/member.rb +1 -1
  109. data/lib/graphql/version.rb +1 -1
  110. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d8cd9472c04900ca59a46d06eedf501e09d1f0561a5df1c902faada7c9536df
4
- data.tar.gz: 409d5ef50405fe5f6ca2f1c197e9c98696c3b89bbf35323e325e7a4bc157419c
3
+ metadata.gz: 0b0dea1cf870243e0c1ca99c9aa276b3ab6dca48c94048d99033c7e868d6edb7
4
+ data.tar.gz: a9067feb798b969d50e6cd3b436432adba4216ad8b66d58cd8cde2d76f9d5589
5
5
  SHA512:
6
- metadata.gz: '0511959efa6a890699def5ea1d681ed9000435903f078955ecdf0a4f4c1be409ea542cd8e8b9cced7f22e22df49115038bbb544d611b9f66a6fdb5ae75c92566'
7
- data.tar.gz: cb29aa9f21e13c65d4b700720cd2079968f6f6b709712e464133f78c383841c219c24bce7a5ffe6b6dd6577c6e7c03f282f259dca73896b669d2cfcaaa072abe
6
+ metadata.gz: d2effe2d2074b17026c7c6e3ddc71db82b1bd17ce8624dbdd9a88345ff39b2177ae48981deabdda227fb4bcf965469a5e874faeb76c0830d598fdb5501673c98
7
+ data.tar.gz: 810111555e1713e4d0d6afabba7102a03f458349d135d92f573236feedd116d81481ba7119c364d9533d39f1d98aa940ce32dc7d452008f6c00827796e5794ef
@@ -25,6 +25,7 @@ module Graphql
25
25
 
26
26
  def create_mutation_root_type
27
27
  create_dir("#{options[:directory]}/mutations")
28
+ template("base_mutation.erb", "#{options[:directory]}/mutations/base_mutation.rb", { skip: true })
28
29
  template("mutation_type.erb", "#{options[:directory]}/types/mutation_type.rb", { skip: true })
29
30
  insert_root_type('mutation', 'MutationType')
30
31
  end
@@ -24,6 +24,7 @@ module Graphql
24
24
  # - query_type.rb
25
25
  # - loaders/
26
26
  # - mutations/
27
+ # - base_mutation.rb
27
28
  # - {app_name}_schema.rb
28
29
  # ```
29
30
  #
@@ -45,7 +45,7 @@ module Graphql
45
45
  private
46
46
 
47
47
  def assign_names!(name)
48
- @field_name = name.camelize(:lower)
48
+ @field_name = name.camelize.underscore
49
49
  @mutation_name = name.camelize(:upper)
50
50
  @file_name = name.camelize.underscore
51
51
  end
@@ -1,9 +1,5 @@
1
1
  module Types
2
2
  class BaseField < GraphQL::Schema::Field
3
3
  argument_class Types::BaseArgument
4
-
5
- def resolve_field(obj, args, ctx)
6
- resolve(obj, args, ctx)
7
- end
8
4
  end
9
5
  end
@@ -0,0 +1,8 @@
1
+ module Mutations
2
+ class BaseMutation < GraphQL::Schema::RelayClassicMutation
3
+ argument_class Types::BaseArgument
4
+ field_class Types::BaseField
5
+ input_object_class Types::BaseInputObject
6
+ object_class Types::BaseObject
7
+ end
8
+ end
@@ -1,4 +1,9 @@
1
1
  class GraphqlController < ApplicationController
2
+ # If accessing from outside this domain, nullify the session
3
+ # This allows for outside API access while preventing CSRF attacks,
4
+ # but you'll have to authenticate your user separately
5
+ # protect_from_forgery with: :null_session
6
+
2
7
  def execute
3
8
  variables = ensure_hash(params[:variables])
4
9
  query = params[:query]
@@ -1,5 +1,5 @@
1
1
  module Mutations
2
- class <%= mutation_name %> < GraphQL::Schema::RelayClassicMutation
2
+ class <%= mutation_name %> < BaseMutation
3
3
  # TODO: define return fields
4
4
  # field :post, Types::PostType, null: false
5
5
 
@@ -24,7 +24,7 @@ class <%= schema_name %> < GraphQL::Schema
24
24
  def self.resolve_type(type, obj, ctx)
25
25
  # TODO: Implement this function
26
26
  # to return the correct type for `obj`
27
- raise(NotImplementedError)
27
+ raise(GraphQL::RequiredImplementationMissingError)
28
28
  end
29
29
  <% end %><% if options[:batch] %>
30
30
  # GraphQL::Batch setup:
@@ -10,6 +10,9 @@ module GraphQL
10
10
  class Error < StandardError
11
11
  end
12
12
 
13
+ class RequiredImplementationMissingError < Error
14
+ end
15
+
13
16
  # Turn a query string or schema definition into an AST
14
17
  # @param graphql_string [String] a GraphQL query string or schema definition
15
18
  # @return [GraphQL::Language::Nodes::Document]
@@ -81,6 +84,7 @@ require "graphql/tracing"
81
84
  require "graphql/execution"
82
85
  require "graphql/dig"
83
86
  require "graphql/schema"
87
+ require "graphql/query"
84
88
  require "graphql/directive"
85
89
  require "graphql/execution"
86
90
  require "graphql/types"
@@ -104,7 +108,6 @@ require "graphql/invalid_name_error"
104
108
  require "graphql/unresolved_type_error"
105
109
  require "graphql/integer_encoding_error"
106
110
  require "graphql/string_encoding_error"
107
- require "graphql/query"
108
111
  require "graphql/internal_representation"
109
112
  require "graphql/static_validation"
110
113
  require "graphql/version"
@@ -12,9 +12,8 @@ module GraphQL
12
12
  module AST
13
13
  module_function
14
14
 
15
- def use(schema_defn)
16
- schema = schema_defn.target
17
- schema.analysis_engine = GraphQL::Analysis::AST
15
+ def use(schema_class)
16
+ schema_class.analysis_engine = GraphQL::Analysis::AST
18
17
  end
19
18
 
20
19
  # Analyze a multiplex, and all queries within.
@@ -23,7 +22,7 @@ module GraphQL
23
22
  #
24
23
  # @param multiplex [GraphQL::Execution::Multiplex]
25
24
  # @param analyzers [Array<GraphQL::Analysis::AST::Analyzer>]
26
- # @return [void]
25
+ # @return [Array<Any>] Results from multiplex analyzers
27
26
  def analyze_multiplex(multiplex, analyzers)
28
27
  multiplex_analyzers = analyzers.map { |analyzer| analyzer.new(multiplex) }
29
28
 
@@ -46,8 +45,8 @@ module GraphQL
46
45
  multiplex.queries.each_with_index do |query, idx|
47
46
  query.analysis_errors = multiplex_errors + analysis_errors(query_results[idx])
48
47
  end
48
+ multiplex_results
49
49
  end
50
- nil
51
50
  end
52
51
 
53
52
  # @param query [GraphQL::Query]
@@ -60,16 +59,18 @@ module GraphQL
60
59
  .select { |analyzer| analyzer.analyze? }
61
60
 
62
61
  analyzers_to_run = query_analyzers + multiplex_analyzers
63
- return [] unless analyzers_to_run.any?
62
+ if analyzers_to_run.any?
63
+ visitor = GraphQL::Analysis::AST::Visitor.new(
64
+ query: query,
65
+ analyzers: analyzers_to_run
66
+ )
64
67
 
65
- visitor = GraphQL::Analysis::AST::Visitor.new(
66
- query: query,
67
- analyzers: analyzers_to_run
68
- )
68
+ visitor.visit
69
69
 
70
- visitor.visit
71
-
72
- query_analyzers.map(&:result)
70
+ query_analyzers.map(&:result)
71
+ else
72
+ []
73
+ end
73
74
  end
74
75
  end
75
76
 
@@ -5,10 +5,21 @@ module GraphQL
5
5
  # Query analyzer for query ASTs. Query analyzers respond to visitor style methods
6
6
  # but are prefixed by `enter` and `leave`.
7
7
  #
8
- # @param [GraphQL::Query] The query to analyze
8
+ # When an analyzer is initialized with a Multiplex, you can always get the current query from
9
+ # `visitor.query` in the visit methods.
10
+ #
11
+ # @param [GraphQL::Query, GraphQL::Execution::Multiplex] The query or multiplex to analyze
9
12
  class Analyzer
10
- def initialize(query)
11
- @query = query
13
+ def initialize(subject)
14
+ @subject = subject
15
+
16
+ if subject.is_a?(GraphQL::Query)
17
+ @query = subject
18
+ @multiplex = nil
19
+ else
20
+ @multiplex = subject
21
+ @query = nil
22
+ end
12
23
  end
13
24
 
14
25
  # Analyzer hook to decide at analysis time whether a query should
@@ -22,7 +33,7 @@ module GraphQL
22
33
  # in a query error.
23
34
  # @return [Any] The analyzer result
24
35
  def result
25
- raise NotImplementedError
36
+ raise GraphQL::RequiredImplementationMissingError
26
37
  end
27
38
 
28
39
  class << self
@@ -58,7 +69,15 @@ module GraphQL
58
69
 
59
70
  protected
60
71
 
72
+ # @return [GraphQL::Query, GraphQL::Execution::Multiplex] Whatever this analyzer is analyzing
73
+ attr_reader :subject
74
+
75
+ # @return [GraphQL::Query, nil] `nil` if this analyzer is visiting a multiplex
76
+ # (When this is `nil`, use `visitor.query` inside visit methods to get the current query)
61
77
  attr_reader :query
78
+
79
+ # @return [GraphQL::Execution::Multiplex, nil] `nil` if this analyzer is visiting a query
80
+ attr_reader :multiplex
62
81
  end
63
82
  end
64
83
  end
@@ -11,7 +11,7 @@ module GraphQL
11
11
 
12
12
  def on_leave_field(node, parent, visitor)
13
13
  field_defn = visitor.field_definition
14
- field = "#{visitor.parent_type_definition.name}.#{field_defn.name}"
14
+ field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}"
15
15
  @used_fields << field
16
16
  @used_deprecated_fields << field if field_defn.deprecation_reason
17
17
  end
@@ -7,12 +7,12 @@ module GraphQL
7
7
  # see {Schema#max_complexity} and {Query#max_complexity}
8
8
  class MaxQueryComplexity < QueryComplexity
9
9
  def result
10
- return if query.max_complexity.nil?
10
+ return if subject.max_complexity.nil?
11
11
 
12
12
  total_complexity = max_possible_complexity
13
13
 
14
- if total_complexity > query.max_complexity
15
- GraphQL::AnalysisError.new("Query has complexity of #{total_complexity}, which exceeds max complexity of #{query.max_complexity}")
14
+ if total_complexity > subject.max_complexity
15
+ GraphQL::AnalysisError.new("Query has complexity of #{total_complexity}, which exceeds max complexity of #{subject.max_complexity}")
16
16
  else
17
17
  nil
18
18
  end
@@ -4,10 +4,14 @@ module GraphQL
4
4
  module AST
5
5
  class MaxQueryDepth < QueryDepth
6
6
  def result
7
- return unless query.max_depth
7
+ configured_max_depth = if query
8
+ query.max_depth
9
+ else
10
+ multiplex.schema.max_depth
11
+ end
8
12
 
9
- if @max_depth > query.max_depth
10
- GraphQL::AnalysisError.new("Query has depth of #{@max_depth}, which exceeds max depth of #{query.max_depth}")
13
+ if configured_max_depth && @max_depth > configured_max_depth
14
+ GraphQL::AnalysisError.new("Query has depth of #{@max_depth}, which exceeds max depth of #{configured_max_depth}")
11
15
  else
12
16
  nil
13
17
  end
@@ -42,7 +42,7 @@ module GraphQL
42
42
  if @complexities_on_type.last.is_a?(AbstractTypeComplexity)
43
43
  key = selection_key(visitor.response_path, visitor.query)
44
44
  parent_type = visitor.parent_type_definition
45
- query.possible_types(parent_type).each do |type|
45
+ visitor.query.possible_types(parent_type).each do |type|
46
46
  @complexities_on_type.last.merge(type, key, own_complexity)
47
47
  end
48
48
  else
@@ -74,7 +74,7 @@ module GraphQL
74
74
 
75
75
  case defined_complexity
76
76
  when Proc
77
- defined_complexity.call(query.context, arguments, child_complexity)
77
+ defined_complexity.call(visitor.query.context, arguments, child_complexity)
78
78
  when Numeric
79
79
  defined_complexity + (child_complexity || 0)
80
80
  else
@@ -134,7 +134,7 @@ module GraphQL
134
134
  argument_defn = if (arg = @argument_definitions.last)
135
135
  arg_type = arg.type.unwrap
136
136
  if arg_type.kind.input_object?
137
- arg_type.input_fields[node.name]
137
+ arg_type.arguments[node.name]
138
138
  else
139
139
  nil
140
140
  end
@@ -214,7 +214,7 @@ module GraphQL
214
214
  fragment_def = query.fragments[fragment_spread.name]
215
215
 
216
216
  object_type = if fragment_def.type
217
- query.schema.types.fetch(fragment_def.type.name, nil)
217
+ @query.warden.get_type(fragment_def.type.name)
218
218
  else
219
219
  object_types.last
220
220
  end
@@ -245,7 +245,7 @@ module GraphQL
245
245
 
246
246
  def on_fragment_with_type(node)
247
247
  object_type = if node.type
248
- @schema.types.fetch(node.type.name, nil)
248
+ @query.warden.get_type(node.type.name)
249
249
  else
250
250
  @object_types.last
251
251
  end
@@ -171,7 +171,7 @@ module GraphQL
171
171
  end
172
172
 
173
173
  def coerce_result(value, ctx)
174
- raise NotImplementedError
174
+ raise GraphQL::RequiredImplementationMissingError
175
175
  end
176
176
 
177
177
  # Types with fields may override this
@@ -45,7 +45,6 @@ module GraphQL
45
45
  INPUT_FIELD_DEFINITION = :INPUT_FIELD_DEFINITION,
46
46
  ]
47
47
 
48
- DEFAULT_DEPRECATION_REASON = 'No longer supported'
49
48
  LOCATION_DESCRIPTIONS = {
50
49
  QUERY: 'Location adjacent to a query operation.',
51
50
  MUTATION: 'Location adjacent to a mutation operation.',
@@ -1,13 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::Directive::DeprecatedDirective = GraphQL::Directive.define do
3
- name "deprecated"
4
- description "Marks an element of a GraphQL schema as no longer supported."
5
- locations([GraphQL::Directive::FIELD_DEFINITION, GraphQL::Directive::ENUM_VALUE])
6
-
7
- reason_description = "Explains why this element was deprecated, usually also including a "\
8
- "suggestion for how to access supported similar data. Formatted "\
9
- "in [Markdown](https://daringfireball.net/projects/markdown/)."
10
-
11
- argument :reason, GraphQL::STRING_TYPE, reason_description, default_value: GraphQL::Directive::DEFAULT_DEPRECATION_REASON
12
- default_directive true
13
- end
2
+ GraphQL::Directive::DeprecatedDirective = GraphQL::Schema::Directive::Deprecated.graphql_definition
@@ -18,12 +18,7 @@ module GraphQL
18
18
  #
19
19
  class Errors
20
20
  def self.use(schema)
21
- schema_class = schema.is_a?(Class) ? schema : schema.target.class
22
- schema.tracer(self.new(schema_class))
23
- end
24
-
25
- def initialize(schema)
26
- @schema = schema
21
+ schema.tracer(self.new)
27
22
  end
28
23
 
29
24
  def trace(event, data)
@@ -40,12 +35,13 @@ module GraphQL
40
35
  def with_error_handling(trace_data)
41
36
  yield
42
37
  rescue StandardError => err
43
- rescues = @schema.rescues
38
+ ctx = trace_data[:query].context
39
+ schema = ctx.schema
40
+ rescues = schema.rescues
44
41
  _err_class, handler = rescues.find { |err_class, handler| err.is_a?(err_class) }
45
42
  if handler
46
43
  obj = trace_data[:object]
47
44
  args = trace_data[:arguments]
48
- ctx = trace_data[:query].context
49
45
  field = trace_data[:field]
50
46
  if obj.is_a?(GraphQL::Schema::Object)
51
47
  obj = obj.object
@@ -17,17 +17,11 @@ module GraphQL
17
17
  runtime.final_value
18
18
  end
19
19
 
20
- def self.use(schema_defn)
21
- schema_defn.target.interpreter = true
22
- # Reach through the legacy objects for the actual class defn
23
- schema_class = schema_defn.target.class
24
- # This is not good, since both of these are holding state now,
25
- # we have to update both :(
26
- [schema_class, schema_defn].each do |schema_config|
27
- schema_config.query_execution_strategy(GraphQL::Execution::Interpreter)
28
- schema_config.mutation_execution_strategy(GraphQL::Execution::Interpreter)
29
- schema_config.subscription_execution_strategy(GraphQL::Execution::Interpreter)
30
- end
20
+ def self.use(schema_class)
21
+ schema_class.interpreter = true
22
+ schema_class.query_execution_strategy(GraphQL::Execution::Interpreter)
23
+ schema_class.mutation_execution_strategy(GraphQL::Execution::Interpreter)
24
+ schema_class.subscription_execution_strategy(GraphQL::Execution::Interpreter)
31
25
  end
32
26
 
33
27
  def self.begin_multiplex(multiplex)
@@ -45,8 +45,7 @@ module GraphQL
45
45
  def run_eager
46
46
  root_operation = query.selected_operation
47
47
  root_op_type = root_operation.operation_type || "query"
48
- legacy_root_type = schema.root_type_for_operation(root_op_type)
49
- root_type = legacy_root_type.metadata[:type_class] || raise("Invariant: type must be class-based: #{legacy_root_type}")
48
+ root_type = schema.root_type_for_operation(root_op_type)
50
49
  object_proxy = root_type.authorized_new(query.root_value, context)
51
50
  object_proxy = schema.sync_lazy(object_proxy)
52
51
  if object_proxy.nil?
@@ -86,11 +85,10 @@ module GraphQL
86
85
  end
87
86
  when GraphQL::Language::Nodes::InlineFragment
88
87
  if node.type
89
- type_defn = schema.types[node.type.name]
90
- type_defn = type_defn.metadata[:type_class]
88
+ type_defn = schema.get_type(node.type.name)
91
89
  # Faster than .map{}.include?()
92
90
  query.warden.possible_types(type_defn).each do |t|
93
- if t.metadata[:type_class] == owner_type
91
+ if t == owner_type
94
92
  gather_selections(owner_object, owner_type, node.selections, selections_by_name)
95
93
  break
96
94
  end
@@ -101,10 +99,9 @@ module GraphQL
101
99
  end
102
100
  when GraphQL::Language::Nodes::FragmentSpread
103
101
  fragment_def = query.fragments[node.name]
104
- type_defn = schema.types[fragment_def.type.name]
105
- type_defn = type_defn.metadata[:type_class]
106
- schema.possible_types(type_defn).each do |t|
107
- if t.metadata[:type_class] == owner_type
102
+ type_defn = schema.get_type(fragment_def.type.name)
103
+ query.warden.possible_types(type_defn).each do |t|
104
+ if t == owner_type
108
105
  gather_selections(owner_object, owner_type, fragment_def.selections, selections_by_name)
109
106
  break
110
107
  end
@@ -133,18 +130,18 @@ module GraphQL
133
130
  field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name)
134
131
  is_introspection = false
135
132
  if field_defn.nil?
136
- field_defn = if owner_type == schema.query.metadata[:type_class] && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
133
+ field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
137
134
  is_introspection = true
138
- entry_point_field.metadata[:type_class]
135
+ entry_point_field
139
136
  elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
140
137
  is_introspection = true
141
- dynamic_field.metadata[:type_class]
138
+ dynamic_field
142
139
  else
143
140
  raise "Invariant: no field for #{owner_type}.#{field_name}"
144
141
  end
145
142
  end
146
143
 
147
- return_type = resolve_if_late_bound_type(field_defn.type)
144
+ return_type = field_defn.type
148
145
 
149
146
  next_path = path.dup
150
147
  next_path << result_name
@@ -296,7 +293,6 @@ module GraphQL
296
293
  write_in_response(path, nil)
297
294
  nil
298
295
  else
299
- resolved_type = resolved_type.metadata[:type_class]
300
296
  continue_field(path, value, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
301
297
  end
302
298
  end
@@ -338,8 +334,6 @@ module GraphQL
338
334
  response_list
339
335
  when "NON_NULL"
340
336
  inner_type = type.of_type
341
- # For fields like `__schema: __Schema!`
342
- inner_type = resolve_if_late_bound_type(inner_type)
343
337
  # Don't `set_type_at_path` because we want the static type,
344
338
  # we're going to use that to determine whether a `nil` should be propagated or not.
345
339
  continue_field(path, value, field, inner_type, ast_node, next_selections, true, owner_object, arguments)
@@ -359,7 +353,7 @@ module GraphQL
359
353
  else
360
354
  dir_defn = schema.directives.fetch(dir_node.name)
361
355
  if !dir_defn.is_a?(Class)
362
- dir_defn = dir_defn.metadata[:type_class] || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
356
+ dir_defn = dir_defn.type_class || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
363
357
  end
364
358
  dir_args = arguments(nil, dir_defn, dir_node)
365
359
  dir_defn.resolve(object, dir_args, context) do
@@ -371,7 +365,7 @@ module GraphQL
371
365
  # Check {Schema::Directive.include?} for each directive that's present
372
366
  def directives_include?(node, graphql_object, parent_type)
373
367
  node.directives.each do |dir_node|
374
- dir_defn = schema.directives.fetch(dir_node.name).metadata[:type_class] || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
368
+ dir_defn = schema.directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
375
369
  args = arguments(graphql_object, dir_defn, dir_node)
376
370
  if !dir_defn.include?(graphql_object, args, context)
377
371
  return false
@@ -380,14 +374,6 @@ module GraphQL
380
374
  true
381
375
  end
382
376
 
383
- def resolve_if_late_bound_type(type)
384
- if type.is_a?(GraphQL::Schema::LateBoundType)
385
- query.warden.get_type(type.name).metadata[:type_class]
386
- else
387
- type
388
- end
389
- end
390
-
391
377
  # @param obj [Object] Some user-returned value that may want to be batched
392
378
  # @param path [Array<String>]
393
379
  # @param field [GraphQL::Schema::Field]
@@ -448,7 +434,7 @@ module GraphQL
448
434
  arg_defn = arg_defns[arg_name]
449
435
  # Need to distinguish between client-provided `nil`
450
436
  # and nothing-at-all
451
- is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_value)
437
+ is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_value, already_arguments: false)
452
438
  if is_present
453
439
  # This doesn't apply to directives, which are legacy
454
440
  # Can remove this when Skip and Include use classes or something.
@@ -460,50 +446,72 @@ module GraphQL
460
446
  end
461
447
  arg_defns.each do |name, arg_defn|
462
448
  if arg_defn.default_value? && !kwarg_arguments.key?(arg_defn.keyword)
463
- _is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_defn.default_value)
449
+ _is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_defn.default_value, already_arguments: false)
464
450
  kwarg_arguments[arg_defn.keyword] = value
465
451
  end
466
452
  end
467
453
  kwarg_arguments
468
454
  end
469
455
 
456
+ # TODO CAN THIS USE `.coerce_input` ???
457
+
470
458
  # Get a Ruby-ready value from a client query.
471
459
  # @param graphql_object [Object] The owner of the field whose argument this is
472
460
  # @param arg_type [Class, GraphQL::Schema::NonNull, GraphQL::Schema::List]
473
461
  # @param ast_value [GraphQL::Language::Nodes::VariableIdentifier, String, Integer, Float, Boolean]
462
+ # @param already_arguments [Boolean] if true, don't re-coerce these with `arguments(...)`
474
463
  # @return [Array(is_present, value)]
475
- def arg_to_value(graphql_object, arg_type, ast_value)
464
+ def arg_to_value(graphql_object, arg_type, ast_value, already_arguments:)
476
465
  if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
477
466
  # If it's not here, it will get added later
478
467
  if query.variables.key?(ast_value.name)
479
- return true, query.variables[ast_value.name]
468
+ variable_value = query.variables[ast_value.name]
469
+ arg_to_value(graphql_object, arg_type, variable_value, already_arguments: true)
480
470
  else
481
471
  return false, nil
482
472
  end
483
473
  elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
484
474
  return true, nil
485
475
  elsif arg_type.is_a?(GraphQL::Schema::NonNull)
486
- arg_to_value(graphql_object, arg_type.of_type, ast_value)
476
+ arg_to_value(graphql_object, arg_type.of_type, ast_value, already_arguments: already_arguments)
487
477
  elsif arg_type.is_a?(GraphQL::Schema::List)
488
- # Treat a single value like a list
489
- arg_value = Array(ast_value)
490
- list = []
491
- arg_value.map do |inner_v|
492
- _present, value = arg_to_value(graphql_object, arg_type.of_type, inner_v)
493
- list << value
494
- end
495
- return true, list
478
+ if ast_value.nil?
479
+ return true, nil
480
+ else
481
+ # Treat a single value like a list
482
+ arg_value = Array(ast_value)
483
+ list = []
484
+ arg_value.map do |inner_v|
485
+ _present, value = arg_to_value(graphql_object, arg_type.of_type, inner_v, already_arguments: already_arguments)
486
+ list << value
487
+ end
488
+ return true, list
489
+ end
496
490
  elsif arg_type.is_a?(Class) && arg_type < GraphQL::Schema::InputObject
497
- # For these, `prepare` is applied during `#initialize`.
498
- # Pass `nil` so it will be skipped in `#arguments`.
499
- # What a mess.
500
- args = arguments(nil, arg_type, ast_value)
501
- # We're not tracking defaults_used, but for our purposes
502
- # we compare the value to the default value.
503
- return true, arg_type.new(ruby_kwargs: args, context: context, defaults_used: nil)
491
+ if already_arguments
492
+ # This came from a variable, already prepared.
493
+ # But replace `nil` with `{}` like we would for `arg_type`
494
+ if ast_value.nil?
495
+ return false, nil
496
+ else
497
+ return true, arg_type.new(ruby_kwargs: ast_value, context: context, defaults_used: nil)
498
+ end
499
+ else
500
+ # For these, `prepare` is applied during `#initialize`.
501
+ # Pass `nil` so it will be skipped in `#arguments`.
502
+ # What a mess.
503
+ args = arguments(nil, arg_type, ast_value)
504
+ return true, arg_type.new(ruby_kwargs: args, context: context, defaults_used: nil)
505
+ end
504
506
  else
505
- flat_value = flatten_ast_value(ast_value)
506
- return true, arg_type.coerce_input(flat_value, context)
507
+ flat_value = if already_arguments
508
+ # It was coerced by variable handling
509
+ ast_value
510
+ else
511
+ v = flatten_ast_value(ast_value)
512
+ arg_type.coerce_input(v, context)
513
+ end
514
+ return true, flat_value
507
515
  end
508
516
  end
509
517