graphql 1.12.16 → 1.13.2

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.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -1
  3. data/lib/generators/graphql/install_generator.rb +9 -2
  4. data/lib/generators/graphql/mutation_generator.rb +1 -1
  5. data/lib/generators/graphql/object_generator.rb +2 -1
  6. data/lib/generators/graphql/relay.rb +19 -11
  7. data/lib/generators/graphql/templates/schema.erb +14 -2
  8. data/lib/generators/graphql/type_generator.rb +0 -1
  9. data/lib/graphql/analysis/ast/field_usage.rb +3 -3
  10. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  11. data/lib/graphql/analysis/ast/visitor.rb +4 -4
  12. data/lib/graphql/backtrace/table.rb +1 -1
  13. data/lib/graphql/base_type.rb +4 -2
  14. data/lib/graphql/boolean_type.rb +1 -1
  15. data/lib/graphql/dataloader/source.rb +50 -2
  16. data/lib/graphql/dataloader.rb +93 -37
  17. data/lib/graphql/define/instance_definable.rb +1 -1
  18. data/lib/graphql/deprecated_dsl.rb +11 -3
  19. data/lib/graphql/deprecation.rb +1 -5
  20. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  21. data/lib/graphql/directive/include_directive.rb +1 -1
  22. data/lib/graphql/directive/skip_directive.rb +1 -1
  23. data/lib/graphql/directive.rb +0 -4
  24. data/lib/graphql/enum_type.rb +5 -1
  25. data/lib/graphql/execution/errors.rb +1 -0
  26. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  27. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
  28. data/lib/graphql/execution/interpreter/runtime.rb +39 -23
  29. data/lib/graphql/execution/lookahead.rb +2 -2
  30. data/lib/graphql/execution/multiplex.rb +4 -1
  31. data/lib/graphql/float_type.rb +1 -1
  32. data/lib/graphql/id_type.rb +1 -1
  33. data/lib/graphql/int_type.rb +1 -1
  34. data/lib/graphql/integer_encoding_error.rb +18 -2
  35. data/lib/graphql/introspection/directive_type.rb +1 -1
  36. data/lib/graphql/introspection/entry_points.rb +2 -2
  37. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  38. data/lib/graphql/introspection/field_type.rb +2 -2
  39. data/lib/graphql/introspection/input_value_type.rb +10 -4
  40. data/lib/graphql/introspection/schema_type.rb +2 -2
  41. data/lib/graphql/introspection/type_type.rb +10 -10
  42. data/lib/graphql/language/block_string.rb +2 -6
  43. data/lib/graphql/language/document_from_schema_definition.rb +4 -2
  44. data/lib/graphql/language/lexer.rb +0 -3
  45. data/lib/graphql/language/lexer.rl +0 -4
  46. data/lib/graphql/language/nodes.rb +12 -2
  47. data/lib/graphql/language/parser.rb +442 -434
  48. data/lib/graphql/language/parser.y +5 -4
  49. data/lib/graphql/language/printer.rb +6 -1
  50. data/lib/graphql/language/sanitized_printer.rb +5 -5
  51. data/lib/graphql/language/token.rb +0 -4
  52. data/lib/graphql/name_validator.rb +0 -4
  53. data/lib/graphql/pagination/connections.rb +35 -16
  54. data/lib/graphql/query/arguments.rb +1 -1
  55. data/lib/graphql/query/arguments_cache.rb +1 -1
  56. data/lib/graphql/query/context.rb +15 -2
  57. data/lib/graphql/query/literal_input.rb +1 -1
  58. data/lib/graphql/query/null_context.rb +12 -7
  59. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  60. data/lib/graphql/query/validation_pipeline.rb +1 -1
  61. data/lib/graphql/query/variables.rb +5 -1
  62. data/lib/graphql/query.rb +4 -0
  63. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  64. data/lib/graphql/relay/global_id_resolve.rb +1 -1
  65. data/lib/graphql/relay/page_info.rb +1 -1
  66. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  67. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  68. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  69. data/lib/graphql/rubocop.rb +4 -0
  70. data/lib/graphql/schema/addition.rb +37 -28
  71. data/lib/graphql/schema/argument.rb +79 -34
  72. data/lib/graphql/schema/build_from_definition.rb +5 -5
  73. data/lib/graphql/schema/directive/feature.rb +1 -1
  74. data/lib/graphql/schema/directive/flagged.rb +2 -2
  75. data/lib/graphql/schema/directive/include.rb +1 -1
  76. data/lib/graphql/schema/directive/skip.rb +1 -1
  77. data/lib/graphql/schema/directive/transform.rb +1 -1
  78. data/lib/graphql/schema/directive.rb +7 -3
  79. data/lib/graphql/schema/enum.rb +60 -10
  80. data/lib/graphql/schema/enum_value.rb +6 -0
  81. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  82. data/lib/graphql/schema/field.rb +140 -42
  83. data/lib/graphql/schema/field_extension.rb +52 -2
  84. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  85. data/lib/graphql/schema/finder.rb +5 -5
  86. data/lib/graphql/schema/input_object.rb +13 -14
  87. data/lib/graphql/schema/interface.rb +11 -20
  88. data/lib/graphql/schema/introspection_system.rb +1 -1
  89. data/lib/graphql/schema/list.rb +3 -1
  90. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  91. data/lib/graphql/schema/member/build_type.rb +0 -4
  92. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  93. data/lib/graphql/schema/member/has_arguments.rb +145 -57
  94. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  95. data/lib/graphql/schema/member/has_fields.rb +76 -18
  96. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  97. data/lib/graphql/schema/member.rb +1 -0
  98. data/lib/graphql/schema/non_null.rb +3 -1
  99. data/lib/graphql/schema/object.rb +10 -75
  100. data/lib/graphql/schema/printer.rb +1 -1
  101. data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
  102. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  103. data/lib/graphql/schema/resolver.rb +49 -64
  104. data/lib/graphql/schema/scalar.rb +2 -0
  105. data/lib/graphql/schema/subscription.rb +17 -9
  106. data/lib/graphql/schema/traversal.rb +1 -1
  107. data/lib/graphql/schema/type_expression.rb +1 -1
  108. data/lib/graphql/schema/type_membership.rb +18 -4
  109. data/lib/graphql/schema/union.rb +8 -1
  110. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  111. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  112. data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
  113. data/lib/graphql/schema/validator/format_validator.rb +4 -5
  114. data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
  115. data/lib/graphql/schema/validator/length_validator.rb +5 -3
  116. data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
  117. data/lib/graphql/schema/validator.rb +33 -25
  118. data/lib/graphql/schema/warden.rb +116 -52
  119. data/lib/graphql/schema.rb +124 -27
  120. data/lib/graphql/static_validation/base_visitor.rb +8 -5
  121. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  122. data/lib/graphql/static_validation/error.rb +3 -1
  123. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  124. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  125. data/lib/graphql/static_validation/rules/fields_will_merge.rb +52 -26
  126. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  127. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  128. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
  129. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  130. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
  131. data/lib/graphql/static_validation/validation_context.rb +8 -2
  132. data/lib/graphql/static_validation/validator.rb +15 -12
  133. data/lib/graphql/string_encoding_error.rb +13 -3
  134. data/lib/graphql/string_type.rb +1 -1
  135. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +15 -5
  136. data/lib/graphql/subscriptions/event.rb +66 -13
  137. data/lib/graphql/subscriptions/serialize.rb +1 -1
  138. data/lib/graphql/subscriptions.rb +17 -19
  139. data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
  140. data/lib/graphql/types/int.rb +1 -1
  141. data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
  142. data/lib/graphql/types/relay/default_relay.rb +5 -1
  143. data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
  144. data/lib/graphql/types/relay/has_node_field.rb +1 -1
  145. data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
  146. data/lib/graphql/types/string.rb +1 -1
  147. data/lib/graphql/unauthorized_error.rb +1 -1
  148. data/lib/graphql/version.rb +1 -1
  149. data/lib/graphql.rb +10 -32
  150. data/readme.md +1 -1
  151. metadata +13 -6
@@ -21,7 +21,9 @@ module GraphQL
21
21
  end
22
22
 
23
23
  def validate(_object, _context, value)
24
- if @in_list.include?(value)
24
+ if permitted_empty_value?(value)
25
+ # pass
26
+ elsif @in_list.include?(value)
25
27
  @message
26
28
  end
27
29
  end
@@ -18,10 +18,6 @@ module GraphQL
18
18
  # # It's pretty hard to come up with a legitimate use case for `without:`
19
19
  #
20
20
  class FormatValidator < Validator
21
- if !String.method_defined?(:match?)
22
- using GraphQL::StringMatchBackport
23
- end
24
-
25
21
  # @param with [RegExp, nil]
26
22
  # @param without [Regexp, nil]
27
23
  # @param message [String]
@@ -38,7 +34,10 @@ module GraphQL
38
34
  end
39
35
 
40
36
  def validate(_object, _context, value)
41
- if (@with_pattern && !value.match?(@with_pattern)) ||
37
+ if permitted_empty_value?(value)
38
+ # Do nothing
39
+ elsif value.nil? ||
40
+ (@with_pattern && !value.match?(@with_pattern)) ||
42
41
  (@without_pattern && value.match?(@without_pattern))
43
42
  @message
44
43
  end
@@ -23,7 +23,9 @@ module GraphQL
23
23
  end
24
24
 
25
25
  def validate(_object, _context, value)
26
- if !@in_list.include?(value)
26
+ if permitted_empty_value?(value)
27
+ # pass
28
+ elsif !@in_list.include?(value)
27
29
  @message
28
30
  end
29
31
  end
@@ -43,11 +43,13 @@ module GraphQL
43
43
  end
44
44
 
45
45
  def validate(_object, _context, value)
46
- if @maximum && value.length > @maximum
46
+ return if permitted_empty_value?(value) # pass in this case
47
+ length = value.nil? ? 0 : value.length
48
+ if @maximum && length > @maximum
47
49
  partial_format(@too_long, { count: @maximum })
48
- elsif @minimum && value.length < @minimum
50
+ elsif @minimum && length < @minimum
49
51
  partial_format(@too_short, { count: @minimum })
50
- elsif @is && value.length != @is
52
+ elsif @is && length != @is
51
53
  partial_format(@wrong_length, { count: @is })
52
54
  end
53
55
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  class Schema
3
4
  class Validator
@@ -24,13 +25,15 @@ module GraphQL
24
25
  # @param other_than [Integer]
25
26
  # @param odd [Boolean]
26
27
  # @param even [Boolean]
28
+ # @param within [Range]
27
29
  # @param message [String] used for all validation failures
28
30
  def initialize(
29
31
  greater_than: nil, greater_than_or_equal_to: nil,
30
32
  less_than: nil, less_than_or_equal_to: nil,
31
33
  equal_to: nil, other_than: nil,
32
- odd: nil, even: nil,
34
+ odd: nil, even: nil, within: nil,
33
35
  message: "%{validated} must be %{comparison} %{target}",
36
+ null_message: Validator::AllowNullValidator::MESSAGE,
34
37
  **default_options
35
38
  )
36
39
 
@@ -42,12 +45,18 @@ module GraphQL
42
45
  @other_than = other_than
43
46
  @odd = odd
44
47
  @even = even
48
+ @within = within
45
49
  @message = message
50
+ @null_message = null_message
46
51
  super(**default_options)
47
52
  end
48
53
 
49
54
  def validate(object, context, value)
50
- if @greater_than && value <= @greater_than
55
+ if permitted_empty_value?(value)
56
+ # pass in this case
57
+ elsif value.nil? # @allow_null is handled in the parent class
58
+ @null_message
59
+ elsif @greater_than && value <= @greater_than
51
60
  partial_format(@message, { comparison: "greater than", target: @greater_than })
52
61
  elsif @greater_than_or_equal_to && value < @greater_than_or_equal_to
53
62
  partial_format(@message, { comparison: "greater than or equal to", target: @greater_than_or_equal_to })
@@ -63,6 +72,8 @@ module GraphQL
63
72
  (partial_format(@message, { comparison: "even", target: "" })).strip
64
73
  elsif @odd && !value.odd?
65
74
  (partial_format(@message, { comparison: "odd", target: "" })).strip
75
+ elsif @within && !@within.include?(value)
76
+ partial_format(@message, { comparison: "within", target: @within })
66
77
  end
67
78
  end
68
79
  end
@@ -7,7 +7,6 @@ module GraphQL
7
7
  # @return [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class<GraphQL::Schema::InputObject>]
8
8
  attr_reader :validated
9
9
 
10
- # TODO should this implement `if:` and `unless:` ?
11
10
  # @param validated [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class<GraphQL::Schema::InputObject>] The argument or argument owner this validator is attached to
12
11
  # @param allow_blank [Boolean] if `true`, then objects that respond to `.blank?` and return true for `.blank?` will skip this validation
13
12
  # @param allow_null [Boolean] if `true`, then incoming `null`s will skip this validation
@@ -25,26 +24,6 @@ module GraphQL
25
24
  raise GraphQL::RequiredImplementationMissingError, "Validator classes should implement #validate"
26
25
  end
27
26
 
28
- # This is called by the validation system and eventually calls {#validate}.
29
- # @api private
30
- def apply(object, context, value)
31
- if value.nil?
32
- if @allow_null
33
- nil # skip this
34
- else
35
- "%{validated} can't be null"
36
- end
37
- elsif value.respond_to?(:blank?) && value.blank?
38
- if @allow_blank
39
- nil # skip this
40
- else
41
- "%{validated} can't be blank"
42
- end
43
- else
44
- validate(object, context, value)
45
- end
46
- end
47
-
48
27
  # This is like `String#%`, but it supports the case that only some of `string`'s
49
28
  # values are present in `substitutions`
50
29
  def partial_format(string, substitutions)
@@ -55,6 +34,12 @@ module GraphQL
55
34
  string
56
35
  end
57
36
 
37
+ # @return [Boolean] `true` if `value` is `nil` and this validator has `allow_null: true` or if value is `.blank?` and this validator has `allow_blank: true`
38
+ def permitted_empty_value?(value)
39
+ (value.nil? && @allow_null) ||
40
+ (@allow_blank && value.respond_to?(:blank?) && value.blank?)
41
+ end
42
+
58
43
  # @param schema_member [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class<GraphQL::Schema::InputObject>]
59
44
  # @param validates_hash [Hash{Symbol => Hash}, Hash{Class => Hash} nil] A configuration passed as `validates:`
60
45
  # @return [Array<Validator>]
@@ -62,6 +47,21 @@ module GraphQL
62
47
  if validates_hash.nil? || validates_hash.empty?
63
48
  EMPTY_ARRAY
64
49
  else
50
+ validates_hash = validates_hash.dup
51
+
52
+ default_options = {}
53
+ if validates_hash[:allow_null]
54
+ default_options[:allow_null] = validates_hash.delete(:allow_null)
55
+ end
56
+ if validates_hash[:allow_blank]
57
+ default_options[:allow_blank] = validates_hash.delete(:allow_blank)
58
+ end
59
+
60
+ # allow_nil or allow_blank are the _only_ validations:
61
+ if validates_hash.empty?
62
+ validates_hash = default_options
63
+ end
64
+
65
65
  validates_hash.map do |validator_name, options|
66
66
  validator_class = case validator_name
67
67
  when Class
@@ -69,7 +69,11 @@ module GraphQL
69
69
  else
70
70
  all_validators[validator_name] || raise(ArgumentError, "unknown validation: #{validator_name.inspect}")
71
71
  end
72
- validator_class.new(validated: schema_member, **options)
72
+ if options.is_a?(Hash)
73
+ validator_class.new(validated: schema_member, **(default_options.merge(options)))
74
+ else
75
+ validator_class.new(options, validated: schema_member, **default_options)
76
+ end
73
77
  end
74
78
  end
75
79
  end
@@ -122,10 +126,10 @@ module GraphQL
122
126
 
123
127
  validators.each do |validator|
124
128
  validated = as || validator.validated
125
- errors = validator.apply(object, context, value)
129
+ errors = validator.validate(object, context, value)
126
130
  if errors &&
127
- (errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
128
- (errors.is_a?(String))
131
+ (errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
132
+ (errors.is_a?(String))
129
133
  if all_errors.frozen? # It's empty
130
134
  all_errors = []
131
135
  end
@@ -161,3 +165,7 @@ require "graphql/schema/validator/exclusion_validator"
161
165
  GraphQL::Schema::Validator.install(:exclusion, GraphQL::Schema::Validator::ExclusionValidator)
162
166
  require "graphql/schema/validator/required_validator"
163
167
  GraphQL::Schema::Validator.install(:required, GraphQL::Schema::Validator::RequiredValidator)
168
+ require "graphql/schema/validator/allow_null_validator"
169
+ GraphQL::Schema::Validator.install(:allow_null, GraphQL::Schema::Validator::AllowNullValidator)
170
+ require "graphql/schema/validator/allow_blank_validator"
171
+ GraphQL::Schema::Validator.install(:allow_blank, GraphQL::Schema::Validator::AllowBlankValidator)
@@ -37,6 +37,50 @@ module GraphQL
37
37
  #
38
38
  # @api private
39
39
  class Warden
40
+ def self.from_context(context)
41
+ (context.respond_to?(:warden) && context.warden) || PassThruWarden
42
+ end
43
+
44
+ # @param visibility_method [Symbol] a Warden method to call for this entry
45
+ # @param entry [Object, Array<Object>] One or more definitions for a given name in a GraphQL Schema
46
+ # @param context [GraphQL::Query::Context]
47
+ # @param warden [Warden]
48
+ # @return [Object] `entry` or one of `entry`'s items if exactly one of them is visible for this context
49
+ # @return [nil] If neither `entry` nor any of `entry`'s items are visible for this context
50
+ def self.visible_entry?(visibility_method, entry, context, warden = Warden.from_context(context))
51
+ if entry.is_a?(Array)
52
+ visible_item = nil
53
+ entry.each do |item|
54
+ if warden.public_send(visibility_method, item, context)
55
+ if visible_item.nil?
56
+ visible_item = item
57
+ else
58
+ raise Schema::DuplicateNamesError, "Found two visible definitions for `#{item.path}`: #{visible_item.inspect}, #{item.inspect}"
59
+ end
60
+ end
61
+ end
62
+ visible_item
63
+ elsif warden.public_send(visibility_method, entry, context)
64
+ entry
65
+ else
66
+ nil
67
+ end
68
+ end
69
+
70
+ # This is used when a caller provides a Hash for context.
71
+ # We want to call the schema's hooks, but we don't have a full-blown warden.
72
+ # The `context` arguments to these methods exist purely to simplify the code that
73
+ # calls methods on this object, so it will have everything it needs.
74
+ class PassThruWarden
75
+ class << self
76
+ def visible_field?(field, ctx); field.visible?(ctx); end
77
+ def visible_argument?(arg, ctx); arg.visible?(ctx); end
78
+ def visible_type?(type, ctx); type.visible?(ctx); end
79
+ def visible_enum_value?(ev, ctx); ev.visible?(ctx); end
80
+ def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
81
+ end
82
+ end
83
+
40
84
  # @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
41
85
  # @param context [GraphQL::Query::Context]
42
86
  # @param schema [GraphQL::Schema]
@@ -54,8 +98,8 @@ module GraphQL
54
98
  def types
55
99
  @types ||= begin
56
100
  vis_types = {}
57
- @schema.types.each do |n, t|
58
- if visible_type?(t)
101
+ @schema.types(@context).each do |n, t|
102
+ if visible_and_reachable_type?(t)
59
103
  vis_types[n] = t
60
104
  end
61
105
  end
@@ -66,8 +110,8 @@ module GraphQL
66
110
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
67
111
  def get_type(type_name)
68
112
  @visible_types ||= read_through do |name|
69
- type_defn = @schema.get_type(name)
70
- if type_defn && visible_type?(type_defn)
113
+ type_defn = @schema.get_type(name, @context)
114
+ if type_defn && visible_and_reachable_type?(type_defn)
71
115
  type_defn
72
116
  else
73
117
  nil
@@ -84,7 +128,7 @@ module GraphQL
84
128
 
85
129
  # @return Boolean True if the type is visible and reachable in the schema
86
130
  def reachable_type?(type_name)
87
- type = get_type(type_name)
131
+ type = get_type(type_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
88
132
  type && reachable_type_set.include?(type)
89
133
  end
90
134
 
@@ -92,8 +136,8 @@ module GraphQL
92
136
  def get_field(parent_type, field_name)
93
137
  @visible_parent_fields ||= read_through do |type|
94
138
  read_through do |f_name|
95
- field_defn = @schema.get_field(type, f_name)
96
- if field_defn && visible_field?(type, field_defn)
139
+ field_defn = @schema.get_field(type, f_name, @context)
140
+ if field_defn && visible_field?(field_defn, nil, type)
97
141
  field_defn
98
142
  else
99
143
  nil
@@ -106,15 +150,15 @@ module GraphQL
106
150
 
107
151
  # @return [GraphQL::Argument, nil] The argument named `argument_name` on `parent_type`, if it exists and is visible
108
152
  def get_argument(parent_type, argument_name)
109
- argument = parent_type.get_argument(argument_name)
110
- return argument if argument && visible_argument?(argument)
153
+ argument = parent_type.get_argument(argument_name, @context)
154
+ return argument if argument && visible_argument?(argument, @context)
111
155
  end
112
156
 
113
157
  # @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
114
158
  def possible_types(type_defn)
115
159
  @visible_possible_types ||= read_through { |type_defn|
116
160
  pt = @schema.possible_types(type_defn, @context)
117
- pt.select { |t| visible_type?(t) }
161
+ pt.select { |t| visible_and_reachable_type?(t) }
118
162
  }
119
163
  @visible_possible_types[type_defn]
120
164
  end
@@ -122,26 +166,31 @@ module GraphQL
122
166
  # @param type_defn [GraphQL::ObjectType, GraphQL::InterfaceType]
123
167
  # @return [Array<GraphQL::Field>] Fields on `type_defn`
124
168
  def fields(type_defn)
125
- @visible_fields ||= read_through { |t| @schema.get_fields(t).each_value.select { |f| visible_field?(t, f) } }
169
+ @visible_fields ||= read_through { |t| @schema.get_fields(t, @context).values }
126
170
  @visible_fields[type_defn]
127
171
  end
128
172
 
129
173
  # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
130
174
  # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
131
175
  def arguments(argument_owner)
132
- @visible_arguments ||= read_through { |o| o.arguments.each_value.select { |a| visible_argument?(a) } }
176
+ @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a) } }
133
177
  @visible_arguments[argument_owner]
134
178
  end
135
179
 
136
180
  # @return [Array<GraphQL::EnumType::EnumValue>] Visible members of `enum_defn`
137
181
  def enum_values(enum_defn)
138
- @visible_enum_values ||= read_through { |e| e.values.each_value.select { |enum_value_defn| visible?(enum_value_defn) } }
139
- @visible_enum_values[enum_defn]
182
+ @visible_enum_arrays ||= read_through { |e| e.enum_values(@context) }
183
+ @visible_enum_arrays[enum_defn]
184
+ end
185
+
186
+ def visible_enum_value?(enum_value, _ctx = nil)
187
+ @visible_enum_values ||= read_through { |ev| visible?(ev) }
188
+ @visible_enum_values[enum_value]
140
189
  end
141
190
 
142
191
  # @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
143
192
  def interfaces(obj_type)
144
- @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible?(i) } }
193
+ @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible_type?(i) } }
145
194
  @visible_interfaces[obj_type]
146
195
  end
147
196
 
@@ -158,25 +207,52 @@ module GraphQL
158
207
  end
159
208
  end
160
209
 
161
- private
162
-
163
- def union_memberships(obj_type)
164
- @unions ||= read_through { |obj_type| @schema.union_memberships(obj_type).select { |u| visible?(u) } }
165
- @unions[obj_type]
166
- end
167
-
168
- def visible_argument?(arg_defn)
169
- visible?(arg_defn) && visible_type?(arg_defn.type.unwrap)
170
- end
171
-
172
- def visible_field?(owner_type, field_defn)
210
+ # @param owner [Class, Module] If provided, confirm that field has the given owner.
211
+ def visible_field?(field_defn, _ctx = nil, owner = field_defn.owner)
173
212
  # This field is visible in its own right
174
213
  visible?(field_defn) &&
175
214
  # This field's return type is visible
176
- visible_type?(field_defn.type.unwrap) &&
215
+ visible_and_reachable_type?(field_defn.type.unwrap) &&
177
216
  # This field is either defined on this object type,
178
217
  # or the interface it's inherited from is also visible
179
- ((field_defn.respond_to?(:owner) && field_defn.owner == owner_type) || field_on_visible_interface?(field_defn, owner_type))
218
+ ((field_defn.respond_to?(:owner) && field_defn.owner == owner) || field_on_visible_interface?(field_defn, owner))
219
+ end
220
+
221
+ def visible_argument?(arg_defn, _ctx = nil)
222
+ visible?(arg_defn) && visible_and_reachable_type?(arg_defn.type.unwrap)
223
+ end
224
+
225
+ def visible_type?(type_defn, _ctx = nil)
226
+ @type_visibility ||= read_through { |type_defn| visible?(type_defn) }
227
+ @type_visibility[type_defn]
228
+ end
229
+
230
+ def visible_type_membership?(type_membership, _ctx = nil)
231
+ visible?(type_membership)
232
+ end
233
+
234
+ private
235
+
236
+ def visible_and_reachable_type?(type_defn)
237
+ @visible_and_reachable_type ||= read_through do |type_defn|
238
+ next false unless visible_type?(type_defn)
239
+ next true if root_type?(type_defn) || type_defn.introspection?
240
+
241
+ if type_defn.kind.union?
242
+ visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
243
+ elsif type_defn.kind.interface?
244
+ visible_possible_types?(type_defn)
245
+ else
246
+ referenced?(type_defn) || visible_abstract_type?(type_defn)
247
+ end
248
+ end
249
+
250
+ @visible_and_reachable_type[type_defn]
251
+ end
252
+
253
+ def union_memberships(obj_type)
254
+ @unions ||= read_through { |obj_type| @schema.union_memberships(obj_type).select { |u| visible?(u) } }
255
+ @unions[obj_type]
180
256
  end
181
257
 
182
258
  # We need this to tell whether a field was inherited by an interface
@@ -195,10 +271,10 @@ module GraphQL
195
271
  any_interface_has_visible_field = false
196
272
  ints = unfiltered_interfaces(type_defn)
197
273
  ints.each do |interface_type|
198
- if (iface_field_defn = interface_type.get_field(field_defn.graphql_name))
274
+ if (iface_field_defn = interface_type.get_field(field_defn.graphql_name, @context))
199
275
  any_interface_has_field = true
200
276
 
201
- if interfaces(type_defn).include?(interface_type) && visible_field?(interface_type, iface_field_defn)
277
+ if interfaces(type_defn).include?(interface_type) && visible_field?(iface_field_defn, nil, interface_type)
202
278
  any_interface_has_visible_field = true
203
279
  end
204
280
  end
@@ -215,23 +291,6 @@ module GraphQL
215
291
  end
216
292
  end
217
293
 
218
- def visible_type?(type_defn)
219
- @type_visibility ||= read_through do |type_defn|
220
- next false unless visible?(type_defn)
221
- next true if root_type?(type_defn) || type_defn.introspection?
222
-
223
- if type_defn.kind.union?
224
- visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
225
- elsif type_defn.kind.interface?
226
- visible_possible_types?(type_defn)
227
- else
228
- referenced?(type_defn) || visible_abstract_type?(type_defn)
229
- end
230
- end
231
-
232
- @type_visibility[type_defn]
233
- end
234
-
235
294
  def root_type?(type_defn)
236
295
  @query == type_defn ||
237
296
  @mutation == type_defn ||
@@ -259,7 +318,7 @@ module GraphQL
259
318
  end
260
319
 
261
320
  def visible_possible_types?(type_defn)
262
- possible_types(type_defn).any? { |t| visible_type?(t) }
321
+ possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
263
322
  end
264
323
 
265
324
  def visible?(member)
@@ -274,6 +333,7 @@ module GraphQL
274
333
  return @reachable_type_set if defined?(@reachable_type_set)
275
334
 
276
335
  @reachable_type_set = Set.new
336
+ rt_hash = {}
277
337
 
278
338
  unvisited_types = []
279
339
  ['query', 'mutation', 'subscription'].each do |op_name|
@@ -283,16 +343,16 @@ module GraphQL
283
343
  unvisited_types.concat(@schema.introspection_system.types.values)
284
344
 
285
345
  directives.each do |dir_class|
286
- dir_class.arguments.values.each do |arg_defn|
346
+ arguments(dir_class).each do |arg_defn|
287
347
  arg_t = arg_defn.type.unwrap
288
- if get_type(arg_t.graphql_name)
348
+ if get_type(arg_t.graphql_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
289
349
  unvisited_types << arg_t
290
350
  end
291
351
  end
292
352
  end
293
353
 
294
354
  @schema.orphan_types.each do |orphan_type|
295
- if get_type(orphan_type.graphql_name)
355
+ if get_type(orphan_type.graphql_name) == orphan_type # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
296
356
  unvisited_types << orphan_type
297
357
  end
298
358
  end
@@ -300,6 +360,10 @@ module GraphQL
300
360
  until unvisited_types.empty?
301
361
  type = unvisited_types.pop
302
362
  if @reachable_type_set.add?(type)
363
+ type_by_name = rt_hash[type.graphql_name] ||= type
364
+ if type_by_name != type
365
+ raise DuplicateNamesError, "Found two visible type definitions for `#{type.graphql_name}`: #{type.inspect}, #{type_by_name.inspect}"
366
+ end
303
367
  if type.kind.input_object?
304
368
  # recurse into visible arguments
305
369
  arguments(type).each do |argument|