graphql 2.0.14 → 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 (118) 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/analysis/ast/visitor.rb +42 -35
  6. data/lib/graphql/analysis/ast.rb +2 -2
  7. data/lib/graphql/backtrace/table.rb +2 -2
  8. data/lib/graphql/backtrace/trace.rb +96 -0
  9. data/lib/graphql/backtrace/tracer.rb +1 -1
  10. data/lib/graphql/backtrace.rb +2 -1
  11. data/lib/graphql/dataloader/source.rb +69 -45
  12. data/lib/graphql/dataloader.rb +8 -5
  13. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  14. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
  15. data/lib/graphql/execution/interpreter/resolve.rb +19 -0
  16. data/lib/graphql/execution/interpreter/runtime.rb +355 -268
  17. data/lib/graphql/execution/interpreter.rb +19 -15
  18. data/lib/graphql/execution/lazy.rb +6 -12
  19. data/lib/graphql/execution/lookahead.rb +16 -5
  20. data/lib/graphql/execution/multiplex.rb +2 -1
  21. data/lib/graphql/filter.rb +8 -2
  22. data/lib/graphql/introspection/directive_type.rb +2 -2
  23. data/lib/graphql/introspection/entry_points.rb +1 -1
  24. data/lib/graphql/introspection/field_type.rb +1 -1
  25. data/lib/graphql/introspection/schema_type.rb +2 -2
  26. data/lib/graphql/introspection/type_type.rb +5 -5
  27. data/lib/graphql/introspection.rb +1 -1
  28. data/lib/graphql/language/document_from_schema_definition.rb +58 -35
  29. data/lib/graphql/language/lexer.rb +248 -1505
  30. data/lib/graphql/language/nodes.rb +69 -40
  31. data/lib/graphql/language/parser.rb +775 -742
  32. data/lib/graphql/language/parser.y +44 -38
  33. data/lib/graphql/language/printer.rb +48 -25
  34. data/lib/graphql/language/visitor.rb +192 -81
  35. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  36. data/lib/graphql/pagination/connection.rb +5 -5
  37. data/lib/graphql/query/context.rb +93 -27
  38. data/lib/graphql/query/null_context.rb +8 -18
  39. data/lib/graphql/query/validation_pipeline.rb +2 -1
  40. data/lib/graphql/query.rb +55 -13
  41. data/lib/graphql/rake_task.rb +28 -1
  42. data/lib/graphql/schema/addition.rb +38 -12
  43. data/lib/graphql/schema/always_visible.rb +10 -0
  44. data/lib/graphql/schema/argument.rb +15 -23
  45. data/lib/graphql/schema/build_from_definition.rb +54 -25
  46. data/lib/graphql/schema/directive/transform.rb +1 -1
  47. data/lib/graphql/schema/directive.rb +12 -2
  48. data/lib/graphql/schema/enum.rb +24 -17
  49. data/lib/graphql/schema/enum_value.rb +3 -4
  50. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  51. data/lib/graphql/schema/field.rb +95 -73
  52. data/lib/graphql/schema/field_extension.rb +1 -4
  53. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  54. data/lib/graphql/schema/input_object.rb +9 -7
  55. data/lib/graphql/schema/interface.rb +5 -11
  56. data/lib/graphql/schema/introspection_system.rb +1 -1
  57. data/lib/graphql/schema/late_bound_type.rb +2 -0
  58. data/lib/graphql/schema/member/base_dsl_methods.rb +17 -14
  59. data/lib/graphql/schema/member/build_type.rb +11 -3
  60. data/lib/graphql/schema/member/has_arguments.rb +114 -65
  61. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  62. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  63. data/lib/graphql/schema/member/has_directives.rb +81 -61
  64. data/lib/graphql/schema/member/has_fields.rb +95 -38
  65. data/lib/graphql/schema/member/has_interfaces.rb +49 -8
  66. data/lib/graphql/schema/member/has_validators.rb +32 -6
  67. data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
  68. data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
  69. data/lib/graphql/schema/object.rb +8 -5
  70. data/lib/graphql/schema/printer.rb +3 -1
  71. data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
  72. data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
  73. data/lib/graphql/schema/resolver.rb +16 -14
  74. data/lib/graphql/schema/timeout.rb +25 -29
  75. data/lib/graphql/schema/type_membership.rb +3 -0
  76. data/lib/graphql/schema/union.rb +10 -1
  77. data/lib/graphql/schema/validator.rb +2 -2
  78. data/lib/graphql/schema/warden.rb +64 -7
  79. data/lib/graphql/schema.rb +171 -28
  80. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  81. data/lib/graphql/static_validation/literal_validator.rb +15 -1
  82. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  83. data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
  84. data/lib/graphql/static_validation/validator.rb +1 -1
  85. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +7 -1
  86. data/lib/graphql/subscriptions/event.rb +2 -7
  87. data/lib/graphql/subscriptions.rb +5 -0
  88. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  89. data/lib/graphql/tracing/appoptics_trace.rb +255 -0
  90. data/lib/graphql/tracing/appsignal_trace.rb +81 -0
  91. data/lib/graphql/tracing/data_dog_trace.rb +187 -0
  92. data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
  93. data/lib/graphql/tracing/legacy_trace.rb +69 -0
  94. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  95. data/lib/graphql/tracing/notifications_trace.rb +49 -0
  96. data/lib/graphql/tracing/platform_trace.rb +123 -0
  97. data/lib/graphql/tracing/platform_tracing.rb +15 -3
  98. data/lib/graphql/tracing/prometheus_trace.rb +93 -0
  99. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +1 -1
  100. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  101. data/lib/graphql/tracing/scout_trace.rb +75 -0
  102. data/lib/graphql/tracing/statsd_trace.rb +60 -0
  103. data/lib/graphql/tracing/trace.rb +75 -0
  104. data/lib/graphql/tracing.rb +17 -39
  105. data/lib/graphql/type_kinds.rb +6 -3
  106. data/lib/graphql/types/relay/base_connection.rb +1 -1
  107. data/lib/graphql/types/relay/connection_behaviors.rb +28 -6
  108. data/lib/graphql/types/relay/edge_behaviors.rb +16 -5
  109. data/lib/graphql/types/relay/node_behaviors.rb +8 -2
  110. data/lib/graphql/types/relay/page_info_behaviors.rb +7 -2
  111. data/lib/graphql/types/relay.rb +0 -1
  112. data/lib/graphql/types/string.rb +1 -1
  113. data/lib/graphql/version.rb +1 -1
  114. data/lib/graphql.rb +16 -9
  115. data/readme.md +1 -1
  116. metadata +66 -29
  117. data/lib/graphql/language/lexer.rl +0 -280
  118. data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -14,7 +14,7 @@ module GraphQL
14
14
  class << self
15
15
  # Used internally to signal that the query shouldn't be executed
16
16
  # @api private
17
- NO_OPERATION = {}.freeze
17
+ NO_OPERATION = GraphQL::EmptyObjects::EMPTY_HASH
18
18
 
19
19
  # @param schema [GraphQL::Schema]
20
20
  # @param queries [Array<GraphQL::Query, Hash>]
@@ -34,11 +34,12 @@ module GraphQL
34
34
  end
35
35
 
36
36
  multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
37
- multiplex.trace("execute_multiplex", { multiplex: multiplex }) do
37
+ multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
38
38
  schema = multiplex.schema
39
39
  queries = multiplex.queries
40
40
  query_instrumenters = schema.instrumenters[:query]
41
41
  multiplex_instrumenters = schema.instrumenters[:multiplex]
42
+ lazies_at_depth = Hash.new { |h, k| h[k] = [] }
42
43
 
43
44
  # First, run multiplex instrumentation, then query instrumentation for each query
44
45
  call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do
@@ -67,10 +68,10 @@ module GraphQL
67
68
  # Although queries in a multiplex _share_ an Interpreter instance,
68
69
  # they also have another item of state, which is private to that query
69
70
  # in particular, assign it here:
70
- runtime = Runtime.new(query: query)
71
- query.context.namespace(:interpreter)[:runtime] = runtime
71
+ runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
72
+ query.context.namespace(:interpreter_runtime)[:runtime] = runtime
72
73
 
73
- query.trace("execute_query", {query: query}) do
74
+ query.current_trace.execute_query(query: query) do
74
75
  runtime.run_eager
75
76
  end
76
77
  rescue GraphQL::ExecutionError => err
@@ -86,25 +87,21 @@ module GraphQL
86
87
 
87
88
  # Then, work through lazy results in a breadth-first way
88
89
  multiplex.dataloader.append_job {
89
- tracer = multiplex
90
90
  query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
91
91
  queries = multiplex ? multiplex.queries : [query]
92
92
  final_values = queries.map do |query|
93
- runtime = query.context.namespace(:interpreter)[:runtime]
93
+ runtime = query.context.namespace(:interpreter_runtime)[:runtime]
94
94
  # it might not be present if the query has an error
95
95
  runtime ? runtime.final_result : nil
96
96
  end
97
97
  final_values.compact!
98
- tracer.trace("execute_query_lazy", {multiplex: multiplex, query: query}) do
99
- Interpreter::Resolve.resolve_all(final_values, multiplex.dataloader)
98
+ multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
99
+ Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
100
100
  end
101
101
  queries.each do |query|
102
- runtime = query.context.namespace(:interpreter)[:runtime]
102
+ runtime = query.context.namespace(:interpreter_runtime)[:runtime]
103
103
  if runtime
104
- runtime.delete_interpreter_context(:current_path)
105
- runtime.delete_interpreter_context(:current_field)
106
- runtime.delete_interpreter_context(:current_object)
107
- runtime.delete_interpreter_context(:current_arguments)
104
+ runtime.delete_all_interpreter_context
108
105
  end
109
106
  end
110
107
  }
@@ -123,7 +120,7 @@ module GraphQL
123
120
  end
124
121
  else
125
122
  result = {
126
- "data" => query.context.namespace(:interpreter)[:runtime].final_result
123
+ "data" => query.context.namespace(:interpreter_runtime)[:runtime].final_result
127
124
  }
128
125
 
129
126
  if query.context.errors.any?
@@ -146,6 +143,13 @@ module GraphQL
146
143
  # Assign values here so that the query's `@executed` becomes true
147
144
  queries.map { |q| q.result_values ||= {} }
148
145
  raise
146
+ ensure
147
+ queries.map { |query|
148
+ runtime = query.context.namespace(:interpreter_runtime)[:runtime]
149
+ if runtime
150
+ runtime.delete_all_interpreter_context
151
+ end
152
+ }
149
153
  end
150
154
  end
151
155
  end
@@ -12,16 +12,14 @@ module GraphQL
12
12
  # - It has no error-catching functionality
13
13
  # @api private
14
14
  class Lazy
15
- attr_reader :path, :field
15
+ attr_reader :field
16
16
 
17
17
  # Create a {Lazy} which will get its inner value by calling the block
18
- # @param path [Array<String, Integer>]
19
18
  # @param field [GraphQL::Schema::Field]
20
19
  # @param get_value_func [Proc] a block to get the inner value (later)
21
- def initialize(path: nil, field: nil, &get_value_func)
20
+ def initialize(field: nil, &get_value_func)
22
21
  @get_value_func = get_value_func
23
22
  @resolved = false
24
- @path = path
25
23
  @field = field
26
24
  end
27
25
 
@@ -29,15 +27,11 @@ module GraphQL
29
27
  def value
30
28
  if !@resolved
31
29
  @resolved = true
32
- @value = begin
33
- v = @get_value_func.call
34
- if v.is_a?(Lazy)
35
- v = v.value
36
- end
37
- v
38
- rescue GraphQL::ExecutionError => err
39
- err
30
+ v = @get_value_func.call
31
+ if v.is_a?(Lazy)
32
+ v = v.value
40
33
  end
34
+ @value = v
41
35
  end
42
36
 
43
37
  # `SKIP` was made into a subclass of `GraphQL::Error` to improve runtime performance
@@ -55,7 +55,7 @@ module GraphQL
55
55
  @arguments
56
56
  else
57
57
  @arguments = if @field
58
- @query.schema.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
58
+ @query.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
59
59
  args.is_a?(Execution::Interpreter::Arguments) ? args.keyword_arguments : args
60
60
  end
61
61
  else
@@ -76,8 +76,8 @@ module GraphQL
76
76
  # @param field_name [String, Symbol]
77
77
  # @param arguments [Hash] Arguments which must match in the selection
78
78
  # @return [Boolean]
79
- def selects?(field_name, arguments: nil)
80
- selection(field_name, arguments: arguments).selected?
79
+ def selects?(field_name, selected_type: @selected_type, arguments: nil)
80
+ selection(field_name, selected_type: selected_type, arguments: arguments).selected?
81
81
  end
82
82
 
83
83
  # @return [Boolean] True if this lookahead represents a field that was requested
@@ -95,11 +95,22 @@ module GraphQL
95
95
  @query.get_field(selected_type, field_name)
96
96
  when Symbol
97
97
  # Try to avoid the `.to_s` below, if possible
98
- all_fields = @query.warden.fields(selected_type)
98
+ all_fields = if selected_type.kind.fields?
99
+ @query.warden.fields(selected_type)
100
+ else
101
+ # Handle unions by checking possible
102
+ @query.warden
103
+ .possible_types(selected_type)
104
+ .map { |t| @query.warden.fields(t) }
105
+ .flatten
106
+ end
107
+
99
108
  if (match_by_orig_name = all_fields.find { |f| f.original_name == field_name })
100
109
  match_by_orig_name
101
110
  else
102
- guessed_name = Schema::Member::BuildType.camelize(field_name.to_s)
111
+ # Symbol#name is only present on 3.0+
112
+ sym_s = field_name.respond_to?(:name) ? field_name.name : field_name.to_s
113
+ guessed_name = Schema::Member::BuildType.camelize(sym_s)
103
114
  @query.get_field(selected_type, guessed_name)
104
115
  end
105
116
  end
@@ -25,13 +25,14 @@ module GraphQL
25
25
  class Multiplex
26
26
  include Tracing::Traceable
27
27
 
28
- attr_reader :context, :queries, :schema, :max_complexity, :dataloader
28
+ attr_reader :context, :queries, :schema, :max_complexity, :dataloader, :current_trace
29
29
 
30
30
  def initialize(schema:, queries:, context:, max_complexity:)
31
31
  @schema = schema
32
32
  @queries = queries
33
33
  @queries.each { |q| q.multiplex = self }
34
34
  @context = context
35
+ @current_trace = @context[:trace] || schema.new_trace(multiplex: self)
35
36
  @dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
36
37
  @tracers = schema.tracers + (context[:tracers] || [])
37
38
  # Support `context: {backtrace: true}`
@@ -1,8 +1,14 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/deprecation"
3
+
2
4
  module GraphQL
3
5
  # @api private
4
6
  class Filter
5
- def initialize(only: nil, except: nil)
7
+ def initialize(only: nil, except: nil, silence_deprecation_warning: false)
8
+ if !silence_deprecation_warning
9
+ line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
10
+ GraphQL::Deprecation.warn("GraphQL::Filter, `only:`, `except:`, and `.merge_filters` are deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).\n #{line}")
11
+ end
6
12
  @only = only
7
13
  @except = except
8
14
  end
@@ -17,7 +23,7 @@ module GraphQL
17
23
  onlies = [self].concat(Array(only))
18
24
  merged_only = MergedOnly.build(onlies)
19
25
  merged_except = MergedExcept.build(Array(except))
20
- self.class.new(only: merged_only, except: merged_except)
26
+ self.class.new(only: merged_only, except: merged_except, silence_deprecation_warning: true)
21
27
  end
22
28
 
23
29
  private
@@ -11,8 +11,8 @@ module GraphQL
11
11
  "to the executor."
12
12
  field :name, String, null: false, method: :graphql_name
13
13
  field :description, String
14
- field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false
15
- field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false do
14
+ field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false, scope: false
15
+ field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do
16
16
  argument :include_deprecated, Boolean, required: false, default_value: false
17
17
  end
18
18
  field :on_operation, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_operation?
@@ -11,7 +11,7 @@ module GraphQL
11
11
  # Apply wrapping manually since this field isn't wrapped by instrumentation
12
12
  schema = @context.query.schema
13
13
  schema_type = schema.introspection_system.types["__Schema"]
14
- schema_type.authorized_new(schema, @context)
14
+ schema_type.wrap(schema, @context)
15
15
  end
16
16
 
17
17
  def __type(name:)
@@ -7,7 +7,7 @@ module GraphQL
7
7
  "a name, potentially a list of arguments, and a return type."
8
8
  field :name, String, null: false
9
9
  field :description, String
10
- field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false do
10
+ field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do
11
11
  argument :include_deprecated, Boolean, required: false, default_value: false
12
12
  end
13
13
  field :type, GraphQL::Schema::LateBoundType.new("__Type"), null: false
@@ -8,11 +8,11 @@ module GraphQL
8
8
  "available types and directives on the server, as well as the entry points for "\
9
9
  "query, mutation, and subscription operations."
10
10
 
11
- field :types, [GraphQL::Schema::LateBoundType.new("__Type")], "A list of all types supported by this server.", null: false
11
+ field :types, [GraphQL::Schema::LateBoundType.new("__Type")], "A list of all types supported by this server.", null: false, scope: false
12
12
  field :query_type, GraphQL::Schema::LateBoundType.new("__Type"), "The type that query operations will be rooted at.", null: false
13
13
  field :mutation_type, GraphQL::Schema::LateBoundType.new("__Type"), "If this server supports mutation, the type that mutation operations will be rooted at."
14
14
  field :subscription_type, GraphQL::Schema::LateBoundType.new("__Type"), "If this server support subscription, the type that subscription operations will be rooted at."
15
- field :directives, [GraphQL::Schema::LateBoundType.new("__Directive")], "A list of all directives supported by this server.", null: false
15
+ field :directives, [GraphQL::Schema::LateBoundType.new("__Directive")], "A list of all directives supported by this server.", null: false, scope: false
16
16
  field :description, String, resolver_method: :schema_description
17
17
 
18
18
  def schema_description
@@ -14,15 +14,15 @@ module GraphQL
14
14
  field :kind, GraphQL::Schema::LateBoundType.new("__TypeKind"), null: false
15
15
  field :name, String, method: :graphql_name
16
16
  field :description, String
17
- field :fields, [GraphQL::Schema::LateBoundType.new("__Field")] do
17
+ field :fields, [GraphQL::Schema::LateBoundType.new("__Field")], scope: false do
18
18
  argument :include_deprecated, Boolean, required: false, default_value: false
19
19
  end
20
- field :interfaces, [GraphQL::Schema::LateBoundType.new("__Type")]
21
- field :possible_types, [GraphQL::Schema::LateBoundType.new("__Type")]
22
- field :enum_values, [GraphQL::Schema::LateBoundType.new("__EnumValue")] do
20
+ field :interfaces, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false
21
+ field :possible_types, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false
22
+ field :enum_values, [GraphQL::Schema::LateBoundType.new("__EnumValue")], scope: false do
23
23
  argument :include_deprecated, Boolean, required: false, default_value: false
24
24
  end
25
- field :input_fields, [GraphQL::Schema::LateBoundType.new("__InputValue")] do
25
+ field :input_fields, [GraphQL::Schema::LateBoundType.new("__InputValue")], scope: false do
26
26
  argument :include_deprecated, Boolean, required: false, default_value: false
27
27
  end
28
28
  field :of_type, GraphQL::Schema::LateBoundType.new("__Type")
@@ -4,7 +4,7 @@ module GraphQL
4
4
  def self.query(include_deprecated_args: false, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
5
5
  # The introspection query to end all introspection queries, copied from
6
6
  # https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js
7
- <<-QUERY
7
+ <<-QUERY.gsub(/\n{2,}/, "\n")
8
8
  query IntrospectionQuery {
9
9
  __schema {
10
10
  #{include_schema_description ? "description" : ""}
@@ -22,18 +22,26 @@ module GraphQL
22
22
  @include_introspection_types = include_introspection_types
23
23
  @include_built_in_scalars = include_built_in_scalars
24
24
  @include_built_in_directives = include_built_in_directives
25
+ @include_one_of = false
25
26
 
26
- filter = GraphQL::Filter.new(only: only, except: except)
27
- if @schema.respond_to?(:visible?)
28
- filter = filter.merge(only: @schema.method(:visible?))
27
+ schema_context = schema.context_class.new(query: nil, object: nil, schema: schema, values: context)
28
+
29
+ @warden = if only || except
30
+ filter = GraphQL::Filter
31
+ .new(only: only, except: except)
32
+ .merge(only: @schema.method(:visible?))
33
+ GraphQL::Schema::Warden.new(
34
+ filter,
35
+ schema: @schema,
36
+ context: schema_context,
37
+ )
38
+ else
39
+ @schema.warden_class.new(
40
+ schema: @schema,
41
+ context: schema_context,
42
+ )
29
43
  end
30
44
 
31
- schema_context = schema.context_class.new(query: nil, object: nil, schema: schema, values: context)
32
- @warden = GraphQL::Schema::Warden.new(
33
- filter,
34
- schema: @schema,
35
- context: schema_context,
36
- )
37
45
  schema_context.warden = @warden
38
46
  end
39
47
 
@@ -44,16 +52,18 @@ module GraphQL
44
52
  end
45
53
 
46
54
  def build_schema_node
47
- GraphQL::Language::Nodes::SchemaDefinition.new(
48
- query: (q = warden.root_type_for_operation("query")) && q.graphql_name,
49
- mutation: (m = warden.root_type_for_operation("mutation")) && m.graphql_name,
50
- subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
51
- # This only supports directives from parsing,
52
- # use a custom printer to add to this list.
53
- #
55
+ schema_options = {
54
56
  # `@schema.directives` is covered by `build_definition_nodes`
55
- directives: ast_directives(@schema),
56
- )
57
+ directives: definition_directives(@schema, :schema_directives),
58
+ }
59
+ if !schema_respects_root_name_conventions?(@schema)
60
+ schema_options.merge!({
61
+ query: (q = warden.root_type_for_operation("query")) && q.graphql_name,
62
+ mutation: (m = warden.root_type_for_operation("mutation")) && m.graphql_name,
63
+ subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
64
+ })
65
+ end
66
+ GraphQL::Language::Nodes::SchemaDefinition.new(schema_options)
57
67
  end
58
68
 
59
69
  def build_object_type_node(object_type)
@@ -243,20 +253,30 @@ module GraphQL
243
253
  end
244
254
 
245
255
  def build_directive_nodes(directives)
246
- if !include_built_in_directives
247
- directives = directives.reject { |directive| directive.default_directive? }
248
- end
249
-
250
256
  directives
251
257
  .map { |directive| build_directive_node(directive) }
252
258
  .sort_by(&:name)
253
259
  end
254
260
 
255
261
  def build_definition_nodes
256
- definitions = []
257
- definitions << build_schema_node if include_schema_node?
258
- definitions += build_directive_nodes(warden.directives)
259
- definitions += build_type_definition_nodes(warden.reachable_types)
262
+ dirs_to_build = warden.directives
263
+ if !include_built_in_directives
264
+ dirs_to_build = dirs_to_build.reject { |directive| directive.default_directive? }
265
+ end
266
+ dir_nodes = build_directive_nodes(dirs_to_build)
267
+
268
+ type_nodes = build_type_definition_nodes(warden.reachable_types)
269
+
270
+ if @include_one_of
271
+ # This may have been set to true when iterating over all types
272
+ dir_nodes.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))
273
+ end
274
+
275
+ definitions = [*dir_nodes, *type_nodes]
276
+ if include_schema_node?
277
+ definitions.unshift(build_schema_node)
278
+ end
279
+
260
280
  definitions
261
281
  end
262
282
 
@@ -283,7 +303,9 @@ module GraphQL
283
303
  private
284
304
 
285
305
  def include_schema_node?
286
- always_include_schema || !schema_respects_root_name_conventions?(schema)
306
+ always_include_schema ||
307
+ !schema_respects_root_name_conventions?(schema) ||
308
+ !schema.schema_directives.empty?
287
309
  end
288
310
 
289
311
  def schema_respects_root_name_conventions?(schema)
@@ -293,14 +315,14 @@ module GraphQL
293
315
  end
294
316
 
295
317
  def directives(member)
296
- definition_directives(member)
318
+ definition_directives(member, :directives)
297
319
  end
298
320
 
299
- def definition_directives(member)
300
- dirs = if !member.respond_to?(:directives) || member.directives.empty?
321
+ def definition_directives(member, directives_method)
322
+ dirs = if !member.respond_to?(directives_method) || member.directives.empty?
301
323
  []
302
324
  else
303
- member.directives.map do |dir|
325
+ member.public_send(directives_method).map do |dir|
304
326
  args = []
305
327
  dir.arguments.argument_values.each_value do |arg_value| # rubocop:disable Development/ContextIsPassedCop -- directive instance method
306
328
  arg_defn = arg_value.definition
@@ -314,6 +336,11 @@ module GraphQL
314
336
  )
315
337
  end
316
338
  end
339
+
340
+ # If this schema uses this built-in directive definition,
341
+ # include it in the print-out since it's not part of the spec yet.
342
+ @include_one_of ||= dir.class == GraphQL::Schema::Directive::OneOf
343
+
317
344
  GraphQL::Language::Nodes::Directive.new(
318
345
  name: dir.class.graphql_name,
319
346
  arguments: args
@@ -324,10 +351,6 @@ module GraphQL
324
351
  dirs
325
352
  end
326
353
 
327
- def ast_directives(member)
328
- member.ast_node ? member.ast_node.directives : []
329
- end
330
-
331
354
  attr_reader :schema, :warden, :always_include_schema,
332
355
  :include_introspection_types, :include_built_in_directives, :include_built_in_scalars
333
356
  end