graphql 2.0.13 → 2.3.10

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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  4. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  5. data/lib/generators/graphql/install_generator.rb +3 -0
  6. data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
  7. data/lib/generators/graphql/mutation_update_generator.rb +1 -1
  8. data/lib/generators/graphql/relay.rb +18 -1
  9. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  10. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  11. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  12. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  13. data/lib/generators/graphql/templates/base_field.erb +2 -0
  14. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  16. data/lib/generators/graphql/templates/base_object.erb +2 -0
  17. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  18. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  19. data/lib/generators/graphql/templates/base_union.erb +2 -0
  20. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  21. data/lib/generators/graphql/templates/loader.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +2 -0
  24. data/lib/generators/graphql/templates/query_type.erb +2 -0
  25. data/lib/generators/graphql/templates/schema.erb +8 -0
  26. data/lib/graphql/analysis/analyzer.rb +89 -0
  27. data/lib/graphql/analysis/field_usage.rb +82 -0
  28. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  29. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  30. data/lib/graphql/analysis/query_complexity.rb +183 -0
  31. data/lib/graphql/analysis/query_depth.rb +58 -0
  32. data/lib/graphql/analysis/visitor.rb +283 -0
  33. data/lib/graphql/analysis.rb +92 -1
  34. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  35. data/lib/graphql/backtrace/table.rb +2 -2
  36. data/lib/graphql/backtrace/trace.rb +93 -0
  37. data/lib/graphql/backtrace/tracer.rb +1 -1
  38. data/lib/graphql/backtrace.rb +2 -1
  39. data/lib/graphql/coercion_error.rb +1 -9
  40. data/lib/graphql/dataloader/async_dataloader.rb +88 -0
  41. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  42. data/lib/graphql/dataloader/request.rb +5 -0
  43. data/lib/graphql/dataloader/source.rb +89 -45
  44. data/lib/graphql/dataloader.rb +115 -142
  45. data/lib/graphql/duration_encoding_error.rb +16 -0
  46. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  47. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  48. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
  49. data/lib/graphql/execution/interpreter/resolve.rb +19 -0
  50. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  51. data/lib/graphql/execution/interpreter/runtime.rb +331 -455
  52. data/lib/graphql/execution/interpreter.rb +125 -61
  53. data/lib/graphql/execution/lazy.rb +6 -12
  54. data/lib/graphql/execution/lookahead.rb +124 -46
  55. data/lib/graphql/execution/multiplex.rb +3 -117
  56. data/lib/graphql/execution.rb +0 -1
  57. data/lib/graphql/introspection/directive_type.rb +3 -3
  58. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  59. data/lib/graphql/introspection/entry_points.rb +11 -5
  60. data/lib/graphql/introspection/field_type.rb +2 -2
  61. data/lib/graphql/introspection/schema_type.rb +10 -13
  62. data/lib/graphql/introspection/type_type.rb +17 -10
  63. data/lib/graphql/introspection.rb +3 -2
  64. data/lib/graphql/language/block_string.rb +34 -18
  65. data/lib/graphql/language/definition_slice.rb +1 -1
  66. data/lib/graphql/language/document_from_schema_definition.rb +75 -59
  67. data/lib/graphql/language/lexer.rb +358 -1506
  68. data/lib/graphql/language/nodes.rb +166 -93
  69. data/lib/graphql/language/parser.rb +795 -1953
  70. data/lib/graphql/language/printer.rb +340 -160
  71. data/lib/graphql/language/sanitized_printer.rb +21 -23
  72. data/lib/graphql/language/static_visitor.rb +167 -0
  73. data/lib/graphql/language/visitor.rb +188 -141
  74. data/lib/graphql/language.rb +61 -1
  75. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  76. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  77. data/lib/graphql/pagination/array_connection.rb +6 -6
  78. data/lib/graphql/pagination/connection.rb +33 -6
  79. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  80. data/lib/graphql/query/context/scoped_context.rb +101 -0
  81. data/lib/graphql/query/context.rb +117 -112
  82. data/lib/graphql/query/null_context.rb +12 -25
  83. data/lib/graphql/query/validation_pipeline.rb +6 -5
  84. data/lib/graphql/query/variables.rb +3 -3
  85. data/lib/graphql/query.rb +86 -30
  86. data/lib/graphql/railtie.rb +9 -6
  87. data/lib/graphql/rake_task.rb +29 -11
  88. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  89. data/lib/graphql/schema/addition.rb +59 -23
  90. data/lib/graphql/schema/always_visible.rb +11 -0
  91. data/lib/graphql/schema/argument.rb +55 -26
  92. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  93. data/lib/graphql/schema/build_from_definition.rb +56 -32
  94. data/lib/graphql/schema/directive/one_of.rb +24 -0
  95. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  96. data/lib/graphql/schema/directive/transform.rb +1 -1
  97. data/lib/graphql/schema/directive.rb +15 -3
  98. data/lib/graphql/schema/enum.rb +35 -24
  99. data/lib/graphql/schema/enum_value.rb +2 -3
  100. data/lib/graphql/schema/field/connection_extension.rb +2 -16
  101. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  102. data/lib/graphql/schema/field.rb +147 -107
  103. data/lib/graphql/schema/field_extension.rb +1 -4
  104. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  105. data/lib/graphql/schema/has_single_input_argument.rb +158 -0
  106. data/lib/graphql/schema/input_object.rb +47 -11
  107. data/lib/graphql/schema/interface.rb +15 -21
  108. data/lib/graphql/schema/introspection_system.rb +7 -17
  109. data/lib/graphql/schema/late_bound_type.rb +10 -0
  110. data/lib/graphql/schema/list.rb +2 -2
  111. data/lib/graphql/schema/loader.rb +2 -3
  112. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -14
  113. data/lib/graphql/schema/member/build_type.rb +11 -3
  114. data/lib/graphql/schema/member/has_arguments.rb +170 -130
  115. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  116. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  117. data/lib/graphql/schema/member/has_directives.rb +81 -61
  118. data/lib/graphql/schema/member/has_fields.rb +100 -38
  119. data/lib/graphql/schema/member/has_interfaces.rb +65 -10
  120. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  121. data/lib/graphql/schema/member/has_validators.rb +32 -6
  122. data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
  123. data/lib/graphql/schema/member/scoped.rb +19 -0
  124. data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
  125. data/lib/graphql/schema/member/validates_input.rb +3 -3
  126. data/lib/graphql/schema/mutation.rb +7 -0
  127. data/lib/graphql/schema/object.rb +16 -5
  128. data/lib/graphql/schema/printer.rb +11 -8
  129. data/lib/graphql/schema/relay_classic_mutation.rb +7 -129
  130. data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
  131. data/lib/graphql/schema/resolver.rb +47 -32
  132. data/lib/graphql/schema/scalar.rb +3 -3
  133. data/lib/graphql/schema/subscription.rb +11 -4
  134. data/lib/graphql/schema/subset.rb +397 -0
  135. data/lib/graphql/schema/timeout.rb +25 -29
  136. data/lib/graphql/schema/type_expression.rb +2 -2
  137. data/lib/graphql/schema/type_membership.rb +3 -0
  138. data/lib/graphql/schema/union.rb +11 -2
  139. data/lib/graphql/schema/unique_within_type.rb +1 -1
  140. data/lib/graphql/schema/validator/all_validator.rb +60 -0
  141. data/lib/graphql/schema/validator.rb +4 -2
  142. data/lib/graphql/schema/warden.rb +238 -93
  143. data/lib/graphql/schema.rb +498 -103
  144. data/lib/graphql/static_validation/all_rules.rb +2 -1
  145. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  146. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  147. data/lib/graphql/static_validation/literal_validator.rb +24 -7
  148. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  149. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  150. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
  151. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  152. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  153. data/lib/graphql/static_validation/rules/fields_will_merge.rb +10 -10
  154. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  155. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  156. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  157. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  158. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  159. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  160. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  161. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  162. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  163. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  164. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  165. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  166. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  167. data/lib/graphql/static_validation/validation_context.rb +5 -5
  168. data/lib/graphql/static_validation/validator.rb +4 -1
  169. data/lib/graphql/static_validation.rb +0 -1
  170. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +11 -4
  171. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  172. data/lib/graphql/subscriptions/event.rb +11 -10
  173. data/lib/graphql/subscriptions/serialize.rb +2 -0
  174. data/lib/graphql/subscriptions.rb +20 -13
  175. data/lib/graphql/testing/helpers.rb +151 -0
  176. data/lib/graphql/testing.rb +2 -0
  177. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  178. data/lib/graphql/tracing/appoptics_trace.rb +251 -0
  179. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  180. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  181. data/lib/graphql/tracing/data_dog_trace.rb +183 -0
  182. data/lib/graphql/tracing/data_dog_tracing.rb +9 -21
  183. data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
  184. data/lib/graphql/tracing/legacy_trace.rb +69 -0
  185. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  186. data/lib/graphql/tracing/notifications_trace.rb +45 -0
  187. data/lib/graphql/tracing/platform_trace.rb +118 -0
  188. data/lib/graphql/tracing/platform_tracing.rb +17 -3
  189. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
  190. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  191. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  192. data/lib/graphql/tracing/scout_trace.rb +72 -0
  193. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  194. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  195. data/lib/graphql/tracing/trace.rb +76 -0
  196. data/lib/graphql/tracing.rb +20 -40
  197. data/lib/graphql/type_kinds.rb +7 -4
  198. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  199. data/lib/graphql/types/relay/base_connection.rb +1 -1
  200. data/lib/graphql/types/relay/connection_behaviors.rb +68 -6
  201. data/lib/graphql/types/relay/edge_behaviors.rb +33 -5
  202. data/lib/graphql/types/relay/node_behaviors.rb +8 -2
  203. data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
  204. data/lib/graphql/types/relay.rb +0 -1
  205. data/lib/graphql/types/string.rb +1 -1
  206. data/lib/graphql/types.rb +1 -0
  207. data/lib/graphql/version.rb +1 -1
  208. data/lib/graphql.rb +27 -20
  209. data/readme.md +13 -3
  210. metadata +96 -47
  211. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  212. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  213. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  214. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  215. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  216. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  217. data/lib/graphql/analysis/ast/visitor.rb +0 -269
  218. data/lib/graphql/analysis/ast.rb +0 -81
  219. data/lib/graphql/deprecation.rb +0 -9
  220. data/lib/graphql/filter.rb +0 -53
  221. data/lib/graphql/language/lexer.rl +0 -280
  222. data/lib/graphql/language/parser.y +0 -554
  223. data/lib/graphql/language/token.rb +0 -34
  224. data/lib/graphql/schema/base_64_bp.rb +0 -26
  225. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  226. data/lib/graphql/static_validation/type_stack.rb +0 -216
  227. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
  228. data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -4,41 +4,19 @@ require 'set'
4
4
 
5
5
  module GraphQL
6
6
  class Schema
7
- # Restrict access to a {GraphQL::Schema} with a user-defined filter.
7
+ # Restrict access to a {GraphQL::Schema} with a user-defined `visible?` implementations.
8
8
  #
9
9
  # When validating and executing a query, all access to schema members
10
10
  # should go through a warden. If you access the schema directly,
11
11
  # you may show a client something that it shouldn't be allowed to see.
12
12
  #
13
- # @example Hiding private fields
14
- # private_members = -> (member, ctx) { member.metadata[:private] }
15
- # result = Schema.execute(query_string, except: private_members)
16
- #
17
- # @example Custom filter implementation
18
- # # It must respond to `#call(member)`.
19
- # class MissingRequiredFlags
20
- # def initialize(user)
21
- # @user = user
22
- # end
23
- #
24
- # # Return `false` if any required flags are missing
25
- # def call(member, ctx)
26
- # member.metadata[:required_flags].any? do |flag|
27
- # !@user.has_flag?(flag)
28
- # end
29
- # end
30
- # end
31
- #
32
- # # Then, use the custom filter in query:
33
- # missing_required_flags = MissingRequiredFlags.new(current_user)
34
- #
35
- # # This query can only access members which match the user's flags
36
- # result = Schema.execute(query_string, except: missing_required_flags)
37
- #
38
13
  # @api private
39
14
  class Warden
40
15
  def self.from_context(context)
41
- (context.respond_to?(:warden) && context.warden) || PassThruWarden
16
+ context.warden || PassThruWarden
17
+ rescue NoMethodError
18
+ # this might be a hash which won't respond to #warden
19
+ PassThruWarden
42
20
  end
43
21
 
44
22
  # @param visibility_method [Symbol] a Warden method to call for this entry
@@ -80,20 +58,144 @@ module GraphQL
80
58
  def visible_type?(type, ctx); type.visible?(ctx); end
81
59
  def visible_enum_value?(ev, ctx); ev.visible?(ctx); end
82
60
  def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
61
+ def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
62
+ def arguments(owner, ctx); owner.arguments(ctx); end
63
+ def loadable?(type, ctx); type.visible?(ctx); end
64
+ def schema_subset
65
+ @schema_subset ||= Warden::SchemaSubset.new(self)
66
+ end
67
+ end
68
+ end
69
+
70
+ class NullWarden
71
+ def initialize(_filter = nil, context:, schema:)
72
+ @schema = schema
73
+ @schema_subset = Warden::SchemaSubset.new(self)
74
+ end
75
+
76
+ # @api private
77
+ module NullSubset
78
+ def self.new(query)
79
+ NullWarden.new(context: query.context, schema: query.schema).schema_subset
80
+ end
81
+ end
82
+
83
+ attr_reader :schema_subset
84
+
85
+ def visible_field?(field_defn, _ctx = nil, owner = nil); true; end
86
+ def visible_argument?(arg_defn, _ctx = nil); true; end
87
+ def visible_type?(type_defn, _ctx = nil); true; end
88
+ def visible_enum_value?(enum_value, _ctx = nil); true; end
89
+ def visible_type_membership?(type_membership, _ctx = nil); true; end
90
+ def interface_type_memberships(obj_type, _ctx = nil); obj_type.interface_type_memberships; end
91
+ def get_type(type_name); @schema.get_type(type_name); end # rubocop:disable Development/ContextIsPassedCop
92
+ def arguments(argument_owner, ctx = nil); argument_owner.all_argument_definitions; end
93
+ def enum_values(enum_defn); enum_defn.enum_values; end # rubocop:disable Development/ContextIsPassedCop
94
+ def get_argument(parent_type, argument_name); parent_type.get_argument(argument_name); end # rubocop:disable Development/ContextIsPassedCop
95
+ def types; @schema.types; end # rubocop:disable Development/ContextIsPassedCop
96
+ def root_type_for_operation(op_name); @schema.root_type_for_operation(op_name); end
97
+ def directives; @schema.directives.values; end
98
+ def fields(type_defn); type_defn.all_field_definitions; end # rubocop:disable Development/ContextIsPassedCop
99
+ def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end
100
+ def reachable_type?(type_name); true; end
101
+ def loadable?(type, _ctx); true; end
102
+ def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
103
+ def possible_types(type_defn); @schema.possible_types(type_defn); end
104
+ def interfaces(obj_type); obj_type.interfaces; end
105
+ end
106
+
107
+ def schema_subset
108
+ @schema_subset ||= SchemaSubset.new(self)
109
+ end
110
+
111
+ class SchemaSubset
112
+ def initialize(warden)
113
+ @warden = warden
114
+ end
115
+
116
+ def directives
117
+ @warden.directives
118
+ end
119
+
120
+ def directive_exists?(dir_name)
121
+ @warden.directives.any? { |d| d.graphql_name == dir_name }
122
+ end
123
+
124
+ def type(name)
125
+ @warden.get_type(name)
126
+ end
127
+
128
+ def field(owner, field_name)
129
+ @warden.get_field(owner, field_name)
130
+ end
131
+
132
+ def argument(owner, arg_name)
133
+ @warden.get_argument(owner, arg_name)
134
+ end
135
+
136
+ def query_root
137
+ @warden.root_type_for_operation("query")
138
+ end
139
+
140
+ def mutation_root
141
+ @warden.root_type_for_operation("mutation")
142
+ end
143
+
144
+ def subscription_root
145
+ @warden.root_type_for_operation("subscription")
146
+ end
147
+
148
+ def arguments(owner)
149
+ @warden.arguments(owner)
150
+ end
151
+
152
+ def fields(owner)
153
+ @warden.fields(owner)
154
+ end
155
+
156
+ def possible_types(type)
157
+ @warden.possible_types(type)
158
+ end
159
+
160
+ def enum_values(enum_type)
161
+ @warden.enum_values(enum_type)
162
+ end
163
+
164
+ def all_types
165
+ @warden.reachable_types
166
+ end
167
+
168
+ def interfaces(obj_type)
169
+ @warden.interfaces(obj_type)
170
+ end
171
+
172
+ def loadable?(t, ctx) # TODO remove ctx here?
173
+ @warden.loadable?(t, ctx)
174
+ end
175
+
176
+ def reachable_type?(type_name)
177
+ @warden.reachable_type?(type_name)
83
178
  end
84
179
  end
85
180
 
86
- # @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
87
181
  # @param context [GraphQL::Query::Context]
88
182
  # @param schema [GraphQL::Schema]
89
- def initialize(filter, context:, schema:)
183
+ def initialize(context:, schema:)
90
184
  @schema = schema
91
185
  # Cache these to avoid repeated hits to the inheritance chain when one isn't present
92
186
  @query = @schema.query
93
187
  @mutation = @schema.mutation
94
188
  @subscription = @schema.subscription
95
189
  @context = context
96
- @visibility_cache = read_through { |m| filter.call(m, context) }
190
+ @visibility_cache = read_through { |m| schema.visible?(m, context) }
191
+ @visibility_cache.compare_by_identity
192
+ # Initialize all ivars to improve object shape consistency:
193
+ @types = @visible_types = @reachable_types = @visible_parent_fields =
194
+ @visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
195
+ @visible_enum_values = @visible_interfaces = @type_visibility = @type_memberships =
196
+ @visible_and_reachable_type = @unions = @unfiltered_interfaces =
197
+ @reachable_type_set = @schema_subset =
198
+ nil
97
199
  end
98
200
 
99
201
  # @return [Hash<String, GraphQL::BaseType>] Visible types in the schema
@@ -109,6 +211,11 @@ module GraphQL
109
211
  end
110
212
  end
111
213
 
214
+ # @return [Boolean] True if this type is used for `loads:` but not in the schema otherwise and not _explicitly_ hidden.
215
+ def loadable?(type, _ctx)
216
+ !reachable_type_set.include?(type) && visible_type?(type)
217
+ end
218
+
112
219
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
113
220
  def get_type(type_name)
114
221
  @visible_types ||= read_through do |name|
@@ -174,14 +281,29 @@ module GraphQL
174
281
 
175
282
  # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
176
283
  # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
177
- def arguments(argument_owner)
178
- @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a) } }
284
+ def arguments(argument_owner, ctx = nil)
285
+ @visible_arguments ||= read_through { |o|
286
+ args = o.arguments(@context)
287
+ if args.any?
288
+ args = args.values
289
+ args.select! { |a| visible_argument?(a, @context) }
290
+ args
291
+ else
292
+ EmptyObjects::EMPTY_ARRAY
293
+ end
294
+ }
179
295
  @visible_arguments[argument_owner]
180
296
  end
181
297
 
182
298
  # @return [Array<GraphQL::EnumType::EnumValue>] Visible members of `enum_defn`
183
299
  def enum_values(enum_defn)
184
- @visible_enum_arrays ||= read_through { |e| e.enum_values(@context) }
300
+ @visible_enum_arrays ||= read_through { |e|
301
+ values = e.enum_values(@context)
302
+ if values.size == 0
303
+ raise GraphQL::Schema::Enum::MissingValuesError.new(e)
304
+ end
305
+ values
306
+ }
185
307
  @visible_enum_arrays[enum_defn]
186
308
  end
187
309
 
@@ -192,7 +314,13 @@ module GraphQL
192
314
 
193
315
  # @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
194
316
  def interfaces(obj_type)
195
- @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible_type?(i) } }
317
+ @visible_interfaces ||= read_through { |t|
318
+ ints = t.interfaces(@context)
319
+ if ints.any?
320
+ ints.select! { |i| visible_type?(i) }
321
+ end
322
+ ints
323
+ }
196
324
  @visible_interfaces[obj_type]
197
325
  end
198
326
 
@@ -233,6 +361,13 @@ module GraphQL
233
361
  visible?(type_membership)
234
362
  end
235
363
 
364
+ def interface_type_memberships(obj_type, _ctx = nil)
365
+ @type_memberships ||= read_through do |obj_t|
366
+ obj_t.interface_type_memberships
367
+ end
368
+ @type_memberships[obj_type]
369
+ end
370
+
236
371
  private
237
372
 
238
373
  def visible_and_reachable_type?(type_defn)
@@ -241,11 +376,26 @@ module GraphQL
241
376
  next true if root_type?(type_defn) || type_defn.introspection?
242
377
 
243
378
  if type_defn.kind.union?
244
- visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
379
+ possible_types(type_defn).any? && (referenced?(type_defn) || orphan_type?(type_defn))
245
380
  elsif type_defn.kind.interface?
246
- visible_possible_types?(type_defn)
381
+ if possible_types(type_defn).any?
382
+ true
383
+ else
384
+ if @context.respond_to?(:logger) && (logger = @context.logger)
385
+ logger.debug { "Interface `#{type_defn.graphql_name}` hidden because it has no visible implementers" }
386
+ end
387
+ false
388
+ end
247
389
  else
248
- referenced?(type_defn) || visible_abstract_type?(type_defn)
390
+ if referenced?(type_defn)
391
+ true
392
+ elsif type_defn.kind.object?
393
+ # Show this object if it belongs to ...
394
+ interfaces(type_defn).any? { |t| referenced?(t) } || # an interface which is referenced in the schema
395
+ union_memberships(type_defn).any? { |t| referenced?(t) || orphan_type?(t) } # or a union which is referenced or added via orphan_types
396
+ else
397
+ false
398
+ end
249
399
  end
250
400
  end
251
401
 
@@ -300,39 +450,26 @@ module GraphQL
300
450
  end
301
451
 
302
452
  def referenced?(type_defn)
303
- @references_to ||= @schema.references_to
304
- graphql_name = type_defn.unwrap.graphql_name
305
- members = @references_to[graphql_name] || NO_REFERENCES
453
+ members = @schema.references_to(type_defn)
306
454
  members.any? { |m| visible?(m) }
307
455
  end
308
456
 
309
- NO_REFERENCES = [].freeze
310
-
311
457
  def orphan_type?(type_defn)
312
458
  @schema.orphan_types.include?(type_defn)
313
459
  end
314
460
 
315
- def visible_abstract_type?(type_defn)
316
- type_defn.kind.object? && (
317
- interfaces(type_defn).any? ||
318
- union_memberships(type_defn).any?
319
- )
320
- end
321
-
322
- def visible_possible_types?(type_defn)
323
- possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
324
- end
325
-
326
461
  def visible?(member)
327
462
  @visibility_cache[member]
328
463
  end
329
464
 
330
465
  def read_through
331
- Hash.new { |h, k| h[k] = yield(k) }
466
+ h = Hash.new { |h, k| h[k] = yield(k) }
467
+ h.compare_by_identity
468
+ h
332
469
  end
333
470
 
334
471
  def reachable_type_set
335
- return @reachable_type_set if defined?(@reachable_type_set)
472
+ return @reachable_type_set if @reachable_type_set
336
473
 
337
474
  @reachable_type_set = Set.new
338
475
  rt_hash = {}
@@ -359,54 +496,62 @@ module GraphQL
359
496
  end
360
497
  end
361
498
 
499
+ included_interface_possible_types_set = Set.new
500
+
362
501
  until unvisited_types.empty?
363
502
  type = unvisited_types.pop
364
- if @reachable_type_set.add?(type)
365
- type_by_name = rt_hash[type.graphql_name] ||= type
366
- if type_by_name != type
367
- raise DuplicateNamesError.new(
368
- duplicated_name: type.graphql_name, duplicated_definition_1: type.inspect, duplicated_definition_2: type_by_name.inspect
369
- )
503
+ visit_type(type, unvisited_types, @reachable_type_set, rt_hash, included_interface_possible_types_set, include_interface_possible_types: false)
504
+ end
505
+
506
+ @reachable_type_set
507
+ end
508
+
509
+ def visit_type(type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types:)
510
+ if visited_type_set.add?(type) || (include_interface_possible_types && type.kind.interface? && included_interface_possible_types_set.add?(type))
511
+ type_by_name = type_by_name_hash[type.graphql_name] ||= type
512
+ if type_by_name != type
513
+ name_1, name_2 = [type.inspect, type_by_name.inspect].sort
514
+ raise DuplicateNamesError.new(
515
+ duplicated_name: type.graphql_name, duplicated_definition_1: name_1, duplicated_definition_2: name_2
516
+ )
517
+ end
518
+ if type.kind.input_object?
519
+ # recurse into visible arguments
520
+ arguments(type).each do |argument|
521
+ argument_type = argument.type.unwrap
522
+ unvisited_types << argument_type
370
523
  end
371
- if type.kind.input_object?
372
- # recurse into visible arguments
373
- arguments(type).each do |argument|
374
- argument_type = argument.type.unwrap
375
- unvisited_types << argument_type
376
- end
377
- elsif type.kind.union?
378
- # recurse into visible possible types
379
- possible_types(type).each do |possible_type|
380
- unvisited_types << possible_type
524
+ elsif type.kind.union?
525
+ # recurse into visible possible types
526
+ possible_types(type).each do |possible_type|
527
+ unvisited_types << possible_type
528
+ end
529
+ elsif type.kind.fields?
530
+ if type.kind.object?
531
+ # recurse into visible implemented interfaces
532
+ interfaces(type).each do |interface|
533
+ unvisited_types << interface
381
534
  end
382
- elsif type.kind.fields?
383
- if type.kind.interface?
384
- # recurse into visible possible types
385
- possible_types(type).each do |possible_type|
386
- unvisited_types << possible_type
387
- end
388
- elsif type.kind.object?
389
- # recurse into visible implemented interfaces
390
- interfaces(type).each do |interface|
391
- unvisited_types << interface
392
- end
535
+ elsif include_interface_possible_types
536
+ possible_types(type).each do |pt|
537
+ unvisited_types << pt
393
538
  end
539
+ end
540
+ # Don't visit interface possible types -- it's not enough to justify visibility
394
541
 
395
- # recurse into visible fields
396
- fields(type).each do |field|
397
- field_type = field.type.unwrap
398
- unvisited_types << field_type
399
- # recurse into visible arguments
400
- arguments(field).each do |argument|
401
- argument_type = argument.type.unwrap
402
- unvisited_types << argument_type
403
- end
542
+ # recurse into visible fields
543
+ fields(type).each do |field|
544
+ field_type = field.type.unwrap
545
+ # In this case, if it's an interface, we want to include
546
+ visit_type(field_type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types: true)
547
+ # recurse into visible arguments
548
+ arguments(field).each do |argument|
549
+ argument_type = argument.type.unwrap
550
+ unvisited_types << argument_type
404
551
  end
405
552
  end
406
553
  end
407
554
  end
408
-
409
- @reachable_type_set
410
555
  end
411
556
  end
412
557
  end