graphql 1.12.24 → 1.13.19

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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -8
  3. data/lib/generators/graphql/enum_generator.rb +4 -10
  4. data/lib/generators/graphql/field_extractor.rb +31 -0
  5. data/lib/generators/graphql/input_generator.rb +50 -0
  6. data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
  7. data/lib/generators/graphql/install_generator.rb +10 -3
  8. data/lib/generators/graphql/interface_generator.rb +7 -7
  9. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  10. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  11. data/lib/generators/graphql/mutation_generator.rb +5 -30
  12. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  13. data/lib/generators/graphql/object_generator.rb +8 -37
  14. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  15. data/lib/generators/graphql/scalar_generator.rb +4 -2
  16. data/lib/generators/graphql/templates/enum.erb +5 -1
  17. data/lib/generators/graphql/templates/input.erb +9 -0
  18. data/lib/generators/graphql/templates/interface.erb +4 -2
  19. data/lib/generators/graphql/templates/mutation.erb +1 -1
  20. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  21. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  22. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  23. data/lib/generators/graphql/templates/object.erb +4 -2
  24. data/lib/generators/graphql/templates/scalar.erb +3 -1
  25. data/lib/generators/graphql/templates/union.erb +4 -2
  26. data/lib/generators/graphql/type_generator.rb +46 -10
  27. data/lib/generators/graphql/union_generator.rb +5 -5
  28. data/lib/graphql/analysis/ast/field_usage.rb +2 -2
  29. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  30. data/lib/graphql/analysis/ast/visitor.rb +5 -4
  31. data/lib/graphql/argument.rb +1 -1
  32. data/lib/graphql/backtrace/table.rb +1 -1
  33. data/lib/graphql/base_type.rb +5 -3
  34. data/lib/graphql/boolean_type.rb +1 -1
  35. data/lib/graphql/dataloader/source.rb +2 -2
  36. data/lib/graphql/dataloader.rb +55 -22
  37. data/lib/graphql/date_encoding_error.rb +16 -0
  38. data/lib/graphql/define/instance_definable.rb +15 -0
  39. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  40. data/lib/graphql/directive/include_directive.rb +1 -1
  41. data/lib/graphql/directive/skip_directive.rb +1 -1
  42. data/lib/graphql/directive.rb +1 -5
  43. data/lib/graphql/enum_type.rb +7 -3
  44. data/lib/graphql/execution/errors.rb +1 -0
  45. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  46. data/lib/graphql/execution/interpreter/arguments_cache.rb +6 -4
  47. data/lib/graphql/execution/interpreter/runtime.rb +66 -38
  48. data/lib/graphql/execution/lookahead.rb +2 -2
  49. data/lib/graphql/execution/multiplex.rb +4 -1
  50. data/lib/graphql/field.rb +1 -1
  51. data/lib/graphql/float_type.rb +1 -1
  52. data/lib/graphql/id_type.rb +1 -1
  53. data/lib/graphql/input_object_type.rb +1 -1
  54. data/lib/graphql/int_type.rb +1 -1
  55. data/lib/graphql/interface_type.rb +1 -1
  56. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  57. data/lib/graphql/introspection/directive_type.rb +5 -3
  58. data/lib/graphql/introspection/entry_points.rb +2 -2
  59. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  60. data/lib/graphql/introspection/field_type.rb +3 -3
  61. data/lib/graphql/introspection/input_value_type.rb +4 -4
  62. data/lib/graphql/introspection/schema_type.rb +9 -4
  63. data/lib/graphql/introspection/type_type.rb +18 -12
  64. data/lib/graphql/introspection.rb +4 -1
  65. data/lib/graphql/language/block_string.rb +2 -6
  66. data/lib/graphql/language/document_from_schema_definition.rb +11 -4
  67. data/lib/graphql/language/lexer.rb +50 -28
  68. data/lib/graphql/language/lexer.rl +2 -4
  69. data/lib/graphql/language/nodes.rb +4 -3
  70. data/lib/graphql/language/parser.rb +841 -820
  71. data/lib/graphql/language/parser.y +13 -6
  72. data/lib/graphql/language/printer.rb +10 -1
  73. data/lib/graphql/language/sanitized_printer.rb +5 -5
  74. data/lib/graphql/language/token.rb +0 -4
  75. data/lib/graphql/name_validator.rb +0 -4
  76. data/lib/graphql/object_type.rb +2 -2
  77. data/lib/graphql/pagination/active_record_relation_connection.rb +43 -6
  78. data/lib/graphql/pagination/relation_connection.rb +59 -29
  79. data/lib/graphql/query/arguments.rb +1 -1
  80. data/lib/graphql/query/arguments_cache.rb +1 -1
  81. data/lib/graphql/query/context.rb +15 -2
  82. data/lib/graphql/query/input_validation_result.rb +9 -0
  83. data/lib/graphql/query/literal_input.rb +1 -1
  84. data/lib/graphql/query/null_context.rb +12 -7
  85. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  86. data/lib/graphql/query/validation_pipeline.rb +2 -3
  87. data/lib/graphql/query/variable_validation_error.rb +2 -2
  88. data/lib/graphql/query/variables.rb +35 -4
  89. data/lib/graphql/query.rb +0 -1
  90. data/lib/graphql/relay/connection_type.rb +15 -2
  91. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  92. data/lib/graphql/relay/global_id_resolve.rb +1 -2
  93. data/lib/graphql/relay/mutation.rb +1 -1
  94. data/lib/graphql/relay/page_info.rb +1 -1
  95. data/lib/graphql/relay/range_add.rb +4 -0
  96. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  97. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  98. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  99. data/lib/graphql/rubocop.rb +4 -0
  100. data/lib/graphql/scalar_type.rb +1 -1
  101. data/lib/graphql/schema/addition.rb +37 -28
  102. data/lib/graphql/schema/argument.rb +30 -15
  103. data/lib/graphql/schema/build_from_definition.rb +6 -5
  104. data/lib/graphql/schema/directive/feature.rb +1 -1
  105. data/lib/graphql/schema/directive/flagged.rb +2 -2
  106. data/lib/graphql/schema/directive/include.rb +1 -1
  107. data/lib/graphql/schema/directive/skip.rb +1 -1
  108. data/lib/graphql/schema/directive/transform.rb +1 -1
  109. data/lib/graphql/schema/directive.rb +23 -4
  110. data/lib/graphql/schema/enum.rb +61 -12
  111. data/lib/graphql/schema/enum_value.rb +6 -0
  112. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  113. data/lib/graphql/schema/field.rb +261 -83
  114. data/lib/graphql/schema/field_extension.rb +89 -2
  115. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  116. data/lib/graphql/schema/finder.rb +5 -5
  117. data/lib/graphql/schema/input_object.rb +24 -7
  118. data/lib/graphql/schema/interface.rb +11 -20
  119. data/lib/graphql/schema/introspection_system.rb +1 -1
  120. data/lib/graphql/schema/list.rb +21 -4
  121. data/lib/graphql/schema/loader.rb +3 -0
  122. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  123. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
  124. data/lib/graphql/schema/member/build_type.rb +0 -4
  125. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  126. data/lib/graphql/schema/member/has_arguments.rb +56 -14
  127. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  128. data/lib/graphql/schema/member/has_fields.rb +76 -18
  129. data/lib/graphql/schema/member/has_interfaces.rb +100 -0
  130. data/lib/graphql/schema/member/validates_input.rb +2 -2
  131. data/lib/graphql/schema/member.rb +1 -0
  132. data/lib/graphql/schema/non_null.rb +9 -3
  133. data/lib/graphql/schema/object.rb +10 -75
  134. data/lib/graphql/schema/printer.rb +1 -1
  135. data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
  136. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  137. data/lib/graphql/schema/resolver.rb +37 -17
  138. data/lib/graphql/schema/scalar.rb +15 -1
  139. data/lib/graphql/schema/subscription.rb +11 -1
  140. data/lib/graphql/schema/traversal.rb +1 -1
  141. data/lib/graphql/schema/type_expression.rb +1 -1
  142. data/lib/graphql/schema/type_membership.rb +18 -4
  143. data/lib/graphql/schema/union.rb +8 -1
  144. data/lib/graphql/schema/validator/format_validator.rb +0 -4
  145. data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
  146. data/lib/graphql/schema/validator/required_validator.rb +29 -15
  147. data/lib/graphql/schema/validator.rb +4 -7
  148. data/lib/graphql/schema/warden.rb +126 -53
  149. data/lib/graphql/schema.rb +120 -24
  150. data/lib/graphql/static_validation/all_rules.rb +1 -0
  151. data/lib/graphql/static_validation/base_visitor.rb +6 -6
  152. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  153. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  154. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  155. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  156. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  157. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  158. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  159. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  160. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
  161. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  162. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  163. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +13 -7
  164. data/lib/graphql/static_validation/validation_context.rb +4 -0
  165. data/lib/graphql/string_type.rb +1 -1
  166. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -4
  167. data/lib/graphql/subscriptions/event.rb +20 -12
  168. data/lib/graphql/subscriptions/serialize.rb +22 -2
  169. data/lib/graphql/subscriptions.rb +17 -19
  170. data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
  171. data/lib/graphql/tracing/data_dog_tracing.rb +24 -2
  172. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  173. data/lib/graphql/tracing/platform_tracing.rb +20 -10
  174. data/lib/graphql/types/iso_8601_date.rb +13 -5
  175. data/lib/graphql/types/iso_8601_date_time.rb +8 -1
  176. data/lib/graphql/types/relay/connection_behaviors.rb +28 -10
  177. data/lib/graphql/types/relay/default_relay.rb +5 -1
  178. data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
  179. data/lib/graphql/types/relay/has_node_field.rb +1 -1
  180. data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
  181. data/lib/graphql/types/relay/node_field.rb +2 -3
  182. data/lib/graphql/types/relay/nodes_field.rb +19 -3
  183. data/lib/graphql/types/string.rb +1 -1
  184. data/lib/graphql/union_type.rb +1 -1
  185. data/lib/graphql/version.rb +1 -1
  186. data/lib/graphql.rb +22 -32
  187. metadata +31 -11
  188. /data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +0 -0
  189. /data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +0 -0
@@ -103,10 +103,12 @@ module GraphQL
103
103
  # Call this method to provide a new subscription_scope; OR
104
104
  # call it without an argument to get the subscription_scope
105
105
  # @param new_scope [Symbol]
106
+ # @param optional [Boolean] If true, then don't require `scope:` to be provided to updates to this subscription.
106
107
  # @return [Symbol]
107
- def self.subscription_scope(new_scope = READING_SCOPE)
108
+ def self.subscription_scope(new_scope = READING_SCOPE, optional: false)
108
109
  if new_scope != READING_SCOPE
109
110
  @subscription_scope = new_scope
111
+ @subscription_scope_optional = optional
110
112
  elsif defined?(@subscription_scope)
111
113
  @subscription_scope
112
114
  else
@@ -114,6 +116,14 @@ module GraphQL
114
116
  end
115
117
  end
116
118
 
119
+ def self.subscription_scope_optional?
120
+ if defined?(@subscription_scope_optional)
121
+ @subscription_scope_optional
122
+ else
123
+ find_inherited_value(:subscription_scope_optional, false)
124
+ end
125
+ end
126
+
117
127
  # This is called during initial subscription to get a "name" for this subscription.
118
128
  # Later, when `.trigger` is called, this will be called again to build another "name".
119
129
  # Any subscribers with matching topic will begin the update flow.
@@ -173,7 +173,7 @@ Some late-bound types couldn't be resolved:
173
173
  end
174
174
  when Class
175
175
  if member.respond_to?(:graphql_definition)
176
- graphql_member = member.graphql_definition
176
+ graphql_member = member.graphql_definition(silence_deprecation_warning: true)
177
177
  visit(schema, graphql_member, context_description)
178
178
  else
179
179
  raise GraphQL::Schema::InvalidTypeError.new("Unexpected traversal member: #{member} (#{member.class.name})")
@@ -11,7 +11,7 @@ module GraphQL
11
11
  def self.build_type(type_owner, ast_node)
12
12
  case ast_node
13
13
  when GraphQL::Language::Nodes::TypeName
14
- type_owner.get_type(ast_node.name)
14
+ type_owner.get_type(ast_node.name) # rubocop:disable Development/ContextIsPassedCop -- this is a `context` or `warden`, it's already query-aware
15
15
  when GraphQL::Language::Nodes::NonNullType
16
16
  ast_inner_type = ast_node.of_type
17
17
  inner_type = build_type(type_owner, ast_inner_type)
@@ -4,8 +4,6 @@ module GraphQL
4
4
  class Schema
5
5
  # This class joins an object type to an abstract type (interface or union) of which
6
6
  # it is a member.
7
- #
8
- # TODO: Not yet implemented for interfaces.
9
7
  class TypeMembership
10
8
  # @return [Class<GraphQL::Schema::Object>]
11
9
  attr_accessor :object_type
@@ -26,9 +24,25 @@ module GraphQL
26
24
  end
27
25
 
28
26
  # @return [Boolean] if false, {#object_type} will be treated as _not_ a member of {#abstract_type}
29
- def visible?(_ctx)
30
- true
27
+ def visible?(ctx)
28
+ warden = Warden.from_context(ctx)
29
+ (@object_type.respond_to?(:visible?) ? warden.visible_type?(@object_type, ctx) : true) &&
30
+ (@abstract_type.respond_to?(:visible?) ? warden.visible_type?(@abstract_type, ctx) : true)
31
31
  end
32
+
33
+ def graphql_name
34
+ "#{@object_type.graphql_name}.#{@abstract_type.kind.interface? ? "implements" : "belongsTo" }.#{@abstract_type.graphql_name}"
35
+ end
36
+
37
+ def path
38
+ graphql_name
39
+ end
40
+
41
+ def inspect
42
+ "#<#{self.class} #{@object_type.inspect} => #{@abstract_type.inspect}>"
43
+ end
44
+
45
+ alias :type_class :itself
32
46
  end
33
47
  end
34
48
  end
@@ -19,8 +19,9 @@ module GraphQL
19
19
  end
20
20
  else
21
21
  visible_types = []
22
+ warden = Warden.from_context(context)
22
23
  type_memberships.each do |type_membership|
23
- if type_membership.visible?(context)
24
+ if warden.visible_type_membership?(type_membership, context)
24
25
  visible_types << type_membership.object_type
25
26
  end
26
27
  end
@@ -28,6 +29,12 @@ module GraphQL
28
29
  end
29
30
  end
30
31
 
32
+ def all_possible_types
33
+ type_memberships.map(&:object_type)
34
+ end
35
+
36
+ prepend GraphQL::Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
37
+
31
38
  def to_graphql
32
39
  type_defn = GraphQL::UnionType.new
33
40
  type_defn.name = graphql_name
@@ -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]
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  class Schema
3
4
  class Validator
@@ -14,7 +14,7 @@ module GraphQL
14
14
  # argument :ingredient_id, ID, required: true
15
15
  # argument :cups, Integer, required: false
16
16
  # argument :tablespoons, Integer, required: false
17
- # argument :teaspoons, Integer, required: true
17
+ # argument :teaspoons, Integer, required: false
18
18
  # validates required: { one_of: [:cups, :tablespoons, :teaspoons] }
19
19
  # end
20
20
  #
@@ -28,11 +28,23 @@ module GraphQL
28
28
  # validates required: { one_of: [:node_id, [:object_type, :object_id]] }
29
29
  # end
30
30
  #
31
+ # @example require _some_ value for an argument, even if it's null
32
+ # field :update_settings, AccountSettings do
33
+ # # `required: :nullable` means this argument must be given, but may be `null`
34
+ # argument :age, Integer, required: :nullable
35
+ # end
36
+ #
31
37
  class RequiredValidator < Validator
32
38
  # @param one_of [Symbol, Array<Symbol>] An argument, or a list of arguments, that represents a valid set of inputs for this field
33
39
  # @param message [String]
34
- def initialize(one_of:, message: "%{validated} has the wrong arguments", **default_options)
35
- @one_of = one_of
40
+ def initialize(one_of: nil, argument: nil, message: "%{validated} has the wrong arguments", **default_options)
41
+ @one_of = if one_of
42
+ one_of
43
+ elsif argument
44
+ [argument]
45
+ else
46
+ raise ArgumentError, "`one_of:` or `argument:` must be given in `validates required: {...}`"
47
+ end
36
48
  @message = message
37
49
  super(**default_options)
38
50
  end
@@ -40,19 +52,21 @@ module GraphQL
40
52
  def validate(_object, _context, value)
41
53
  matched_conditions = 0
42
54
 
43
- @one_of.each do |one_of_condition|
44
- case one_of_condition
45
- when Symbol
46
- if value.key?(one_of_condition)
47
- matched_conditions += 1
48
- end
49
- when Array
50
- if one_of_condition.all? { |k| value.key?(k) }
51
- matched_conditions += 1
52
- break
55
+ if !value.nil?
56
+ @one_of.each do |one_of_condition|
57
+ case one_of_condition
58
+ when Symbol
59
+ if value.key?(one_of_condition)
60
+ matched_conditions += 1
61
+ end
62
+ when Array
63
+ if one_of_condition.all? { |k| value.key?(k) }
64
+ matched_conditions += 1
65
+ break
66
+ end
67
+ else
68
+ raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}"
53
69
  end
54
- else
55
- raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}"
56
70
  end
57
71
  end
58
72
 
@@ -48,16 +48,13 @@ module GraphQL
48
48
  EMPTY_ARRAY
49
49
  else
50
50
  validates_hash = validates_hash.dup
51
- allow_null = validates_hash.delete(:allow_null)
52
- allow_blank = validates_hash.delete(:allow_blank)
53
51
 
54
- # This could be {...}.compact on Ruby 2.4+
55
52
  default_options = {}
56
- if !allow_null.nil?
57
- default_options[:allow_null] = allow_null
53
+ if validates_hash[:allow_null]
54
+ default_options[:allow_null] = validates_hash.delete(:allow_null)
58
55
  end
59
- if !allow_blank.nil?
60
- default_options[:allow_blank] = allow_blank
56
+ if validates_hash[:allow_blank]
57
+ default_options[:allow_blank] = validates_hash.delete(:allow_blank)
61
58
  end
62
59
 
63
60
  # allow_nil or allow_blank are the _only_ validations:
@@ -37,6 +37,52 @@ 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
+ def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
82
+ def arguments(owner, ctx); owner.arguments(ctx); end
83
+ end
84
+ end
85
+
40
86
  # @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
41
87
  # @param context [GraphQL::Query::Context]
42
88
  # @param schema [GraphQL::Schema]
@@ -54,8 +100,8 @@ module GraphQL
54
100
  def types
55
101
  @types ||= begin
56
102
  vis_types = {}
57
- @schema.types.each do |n, t|
58
- if visible_type?(t)
103
+ @schema.types(@context).each do |n, t|
104
+ if visible_and_reachable_type?(t)
59
105
  vis_types[n] = t
60
106
  end
61
107
  end
@@ -66,8 +112,8 @@ module GraphQL
66
112
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
67
113
  def get_type(type_name)
68
114
  @visible_types ||= read_through do |name|
69
- type_defn = @schema.get_type(name)
70
- if type_defn && visible_type?(type_defn)
115
+ type_defn = @schema.get_type(name, @context)
116
+ if type_defn && visible_and_reachable_type?(type_defn)
71
117
  type_defn
72
118
  else
73
119
  nil
@@ -84,7 +130,7 @@ module GraphQL
84
130
 
85
131
  # @return Boolean True if the type is visible and reachable in the schema
86
132
  def reachable_type?(type_name)
87
- type = get_type(type_name)
133
+ type = get_type(type_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
88
134
  type && reachable_type_set.include?(type)
89
135
  end
90
136
 
@@ -92,8 +138,8 @@ module GraphQL
92
138
  def get_field(parent_type, field_name)
93
139
  @visible_parent_fields ||= read_through do |type|
94
140
  read_through do |f_name|
95
- field_defn = @schema.get_field(type, f_name)
96
- if field_defn && visible_field?(type, field_defn)
141
+ field_defn = @schema.get_field(type, f_name, @context)
142
+ if field_defn && visible_field?(field_defn, nil, type)
97
143
  field_defn
98
144
  else
99
145
  nil
@@ -106,15 +152,15 @@ module GraphQL
106
152
 
107
153
  # @return [GraphQL::Argument, nil] The argument named `argument_name` on `parent_type`, if it exists and is visible
108
154
  def get_argument(parent_type, argument_name)
109
- argument = parent_type.get_argument(argument_name)
110
- return argument if argument && visible_argument?(argument)
155
+ argument = parent_type.get_argument(argument_name, @context)
156
+ return argument if argument && visible_argument?(argument, @context)
111
157
  end
112
158
 
113
159
  # @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
114
160
  def possible_types(type_defn)
115
161
  @visible_possible_types ||= read_through { |type_defn|
116
162
  pt = @schema.possible_types(type_defn, @context)
117
- pt.select { |t| visible_type?(t) }
163
+ pt.select { |t| visible_and_reachable_type?(t) }
118
164
  }
119
165
  @visible_possible_types[type_defn]
120
166
  end
@@ -122,26 +168,31 @@ module GraphQL
122
168
  # @param type_defn [GraphQL::ObjectType, GraphQL::InterfaceType]
123
169
  # @return [Array<GraphQL::Field>] Fields on `type_defn`
124
170
  def fields(type_defn)
125
- @visible_fields ||= read_through { |t| @schema.get_fields(t).each_value.select { |f| visible_field?(t, f) } }
171
+ @visible_fields ||= read_through { |t| @schema.get_fields(t, @context).values }
126
172
  @visible_fields[type_defn]
127
173
  end
128
174
 
129
175
  # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
130
176
  # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
131
- def arguments(argument_owner)
132
- @visible_arguments ||= read_through { |o| o.arguments.each_value.select { |a| visible_argument?(a) } }
177
+ def arguments(argument_owner, ctx = nil)
178
+ @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a, @context) } }
133
179
  @visible_arguments[argument_owner]
134
180
  end
135
181
 
136
182
  # @return [Array<GraphQL::EnumType::EnumValue>] Visible members of `enum_defn`
137
183
  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]
184
+ @visible_enum_arrays ||= read_through { |e| e.enum_values(@context) }
185
+ @visible_enum_arrays[enum_defn]
186
+ end
187
+
188
+ def visible_enum_value?(enum_value, _ctx = nil)
189
+ @visible_enum_values ||= read_through { |ev| visible?(ev) }
190
+ @visible_enum_values[enum_value]
140
191
  end
141
192
 
142
193
  # @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
143
194
  def interfaces(obj_type)
144
- @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible?(i) } }
195
+ @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible_type?(i) } }
145
196
  @visible_interfaces[obj_type]
146
197
  end
147
198
 
@@ -158,25 +209,59 @@ module GraphQL
158
209
  end
159
210
  end
160
211
 
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)
212
+ # @param owner [Class, Module] If provided, confirm that field has the given owner.
213
+ def visible_field?(field_defn, _ctx = nil, owner = field_defn.owner)
173
214
  # This field is visible in its own right
174
215
  visible?(field_defn) &&
175
216
  # This field's return type is visible
176
- visible_type?(field_defn.type.unwrap) &&
217
+ visible_and_reachable_type?(field_defn.type.unwrap) &&
177
218
  # This field is either defined on this object type,
178
219
  # 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))
220
+ ((field_defn.respond_to?(:owner) && field_defn.owner == owner) || field_on_visible_interface?(field_defn, owner))
221
+ end
222
+
223
+ def visible_argument?(arg_defn, _ctx = nil)
224
+ visible?(arg_defn) && visible_and_reachable_type?(arg_defn.type.unwrap)
225
+ end
226
+
227
+ def visible_type?(type_defn, _ctx = nil)
228
+ @type_visibility ||= read_through { |type_defn| visible?(type_defn) }
229
+ @type_visibility[type_defn]
230
+ end
231
+
232
+ def visible_type_membership?(type_membership, _ctx = nil)
233
+ visible?(type_membership)
234
+ end
235
+
236
+ def interface_type_memberships(obj_type, _ctx = nil)
237
+ @type_memberships ||= read_through do |obj_t|
238
+ obj_t.interface_type_memberships
239
+ end
240
+ @type_memberships[obj_type]
241
+ end
242
+
243
+ private
244
+
245
+ def visible_and_reachable_type?(type_defn)
246
+ @visible_and_reachable_type ||= read_through do |type_defn|
247
+ next false unless visible_type?(type_defn)
248
+ next true if root_type?(type_defn) || type_defn.introspection?
249
+
250
+ if type_defn.kind.union?
251
+ visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
252
+ elsif type_defn.kind.interface?
253
+ visible_possible_types?(type_defn)
254
+ else
255
+ referenced?(type_defn) || visible_abstract_type?(type_defn)
256
+ end
257
+ end
258
+
259
+ @visible_and_reachable_type[type_defn]
260
+ end
261
+
262
+ def union_memberships(obj_type)
263
+ @unions ||= read_through { |obj_type| @schema.union_memberships(obj_type).select { |u| visible?(u) } }
264
+ @unions[obj_type]
180
265
  end
181
266
 
182
267
  # We need this to tell whether a field was inherited by an interface
@@ -195,10 +280,10 @@ module GraphQL
195
280
  any_interface_has_visible_field = false
196
281
  ints = unfiltered_interfaces(type_defn)
197
282
  ints.each do |interface_type|
198
- if (iface_field_defn = interface_type.get_field(field_defn.graphql_name))
283
+ if (iface_field_defn = interface_type.get_field(field_defn.graphql_name, @context))
199
284
  any_interface_has_field = true
200
285
 
201
- if interfaces(type_defn).include?(interface_type) && visible_field?(interface_type, iface_field_defn)
286
+ if interfaces(type_defn).include?(interface_type) && visible_field?(iface_field_defn, nil, interface_type)
202
287
  any_interface_has_visible_field = true
203
288
  end
204
289
  end
@@ -215,23 +300,6 @@ module GraphQL
215
300
  end
216
301
  end
217
302
 
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
303
  def root_type?(type_defn)
236
304
  @query == type_defn ||
237
305
  @mutation == type_defn ||
@@ -259,7 +327,7 @@ module GraphQL
259
327
  end
260
328
 
261
329
  def visible_possible_types?(type_defn)
262
- possible_types(type_defn).any? { |t| visible_type?(t) }
330
+ possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
263
331
  end
264
332
 
265
333
  def visible?(member)
@@ -274,6 +342,7 @@ module GraphQL
274
342
  return @reachable_type_set if defined?(@reachable_type_set)
275
343
 
276
344
  @reachable_type_set = Set.new
345
+ rt_hash = {}
277
346
 
278
347
  unvisited_types = []
279
348
  ['query', 'mutation', 'subscription'].each do |op_name|
@@ -283,16 +352,16 @@ module GraphQL
283
352
  unvisited_types.concat(@schema.introspection_system.types.values)
284
353
 
285
354
  directives.each do |dir_class|
286
- dir_class.arguments.values.each do |arg_defn|
355
+ arguments(dir_class).each do |arg_defn|
287
356
  arg_t = arg_defn.type.unwrap
288
- if get_type(arg_t.graphql_name)
357
+ if get_type(arg_t.graphql_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
289
358
  unvisited_types << arg_t
290
359
  end
291
360
  end
292
361
  end
293
362
 
294
363
  @schema.orphan_types.each do |orphan_type|
295
- if get_type(orphan_type.graphql_name)
364
+ if get_type(orphan_type.graphql_name) == orphan_type # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
296
365
  unvisited_types << orphan_type
297
366
  end
298
367
  end
@@ -300,6 +369,10 @@ module GraphQL
300
369
  until unvisited_types.empty?
301
370
  type = unvisited_types.pop
302
371
  if @reachable_type_set.add?(type)
372
+ type_by_name = rt_hash[type.graphql_name] ||= type
373
+ if type_by_name != type
374
+ raise DuplicateNamesError, "Found two visible type definitions for `#{type.graphql_name}`: #{type.inspect}, #{type_by_name.inspect}"
375
+ end
303
376
  if type.kind.input_object?
304
377
  # recurse into visible arguments
305
378
  arguments(type).each do |argument|