graphql 1.10.1 → 1.13.0

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 (292) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +18 -2
  3. data/lib/generators/graphql/install_generator.rb +36 -6
  4. data/lib/generators/graphql/loader_generator.rb +1 -0
  5. data/lib/generators/graphql/mutation_generator.rb +2 -1
  6. data/lib/generators/graphql/object_generator.rb +54 -9
  7. data/lib/generators/graphql/relay.rb +63 -0
  8. data/lib/generators/graphql/relay_generator.rb +21 -0
  9. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  10. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  11. data/lib/generators/graphql/templates/base_edge.erb +8 -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_mutation.erb +2 -0
  17. data/lib/generators/graphql/templates/base_object.erb +2 -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/enum.erb +2 -0
  21. data/lib/generators/graphql/templates/graphql_controller.erb +16 -12
  22. data/lib/generators/graphql/templates/interface.erb +2 -0
  23. data/lib/generators/graphql/templates/loader.erb +2 -0
  24. data/lib/generators/graphql/templates/mutation.erb +2 -0
  25. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  26. data/lib/generators/graphql/templates/node_type.erb +9 -0
  27. data/lib/generators/graphql/templates/object.erb +3 -1
  28. data/lib/generators/graphql/templates/query_type.erb +3 -3
  29. data/lib/generators/graphql/templates/scalar.erb +2 -0
  30. data/lib/generators/graphql/templates/schema.erb +21 -33
  31. data/lib/generators/graphql/templates/union.erb +3 -1
  32. data/lib/generators/graphql/type_generator.rb +1 -1
  33. data/lib/graphql/analysis/analyze_query.rb +7 -0
  34. data/lib/graphql/analysis/ast/field_usage.rb +24 -1
  35. data/lib/graphql/analysis/ast/query_complexity.rb +126 -109
  36. data/lib/graphql/analysis/ast/visitor.rb +13 -5
  37. data/lib/graphql/analysis/ast.rb +11 -2
  38. data/lib/graphql/argument.rb +3 -3
  39. data/lib/graphql/backtrace/inspect_result.rb +0 -1
  40. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  41. data/lib/graphql/backtrace/table.rb +34 -3
  42. data/lib/graphql/backtrace/traced_error.rb +0 -1
  43. data/lib/graphql/backtrace/tracer.rb +40 -9
  44. data/lib/graphql/backtrace.rb +28 -19
  45. data/lib/graphql/backwards_compatibility.rb +2 -1
  46. data/lib/graphql/base_type.rb +1 -1
  47. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  48. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  49. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  50. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  51. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  52. data/lib/graphql/dataloader/null_dataloader.rb +22 -0
  53. data/lib/graphql/dataloader/request.rb +19 -0
  54. data/lib/graphql/dataloader/request_all.rb +19 -0
  55. data/lib/graphql/dataloader/source.rb +155 -0
  56. data/lib/graphql/dataloader.rb +308 -0
  57. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  58. data/lib/graphql/define/defined_object_proxy.rb +1 -1
  59. data/lib/graphql/define/instance_definable.rb +34 -4
  60. data/lib/graphql/define/type_definer.rb +5 -5
  61. data/lib/graphql/deprecated_dsl.rb +18 -5
  62. data/lib/graphql/deprecation.rb +9 -0
  63. data/lib/graphql/directive.rb +4 -4
  64. data/lib/graphql/enum_type.rb +7 -1
  65. data/lib/graphql/execution/errors.rb +110 -7
  66. data/lib/graphql/execution/execute.rb +8 -1
  67. data/lib/graphql/execution/instrumentation.rb +1 -1
  68. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  69. data/lib/graphql/execution/interpreter/arguments.rb +88 -0
  70. data/lib/graphql/execution/interpreter/arguments_cache.rb +103 -0
  71. data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
  72. data/lib/graphql/execution/interpreter/resolve.rb +37 -25
  73. data/lib/graphql/execution/interpreter/runtime.rb +685 -421
  74. data/lib/graphql/execution/interpreter.rb +42 -13
  75. data/lib/graphql/execution/lazy.rb +5 -1
  76. data/lib/graphql/execution/lookahead.rb +25 -110
  77. data/lib/graphql/execution/multiplex.rb +37 -25
  78. data/lib/graphql/field.rb +5 -1
  79. data/lib/graphql/function.rb +4 -0
  80. data/lib/graphql/input_object_type.rb +6 -0
  81. data/lib/graphql/integer_decoding_error.rb +17 -0
  82. data/lib/graphql/integer_encoding_error.rb +18 -2
  83. data/lib/graphql/interface_type.rb +7 -0
  84. data/lib/graphql/internal_representation/document.rb +2 -2
  85. data/lib/graphql/internal_representation/rewrite.rb +1 -1
  86. data/lib/graphql/internal_representation/scope.rb +2 -2
  87. data/lib/graphql/internal_representation/visit.rb +2 -2
  88. data/lib/graphql/introspection/directive_type.rb +8 -4
  89. data/lib/graphql/introspection/entry_points.rb +2 -2
  90. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  91. data/lib/graphql/introspection/field_type.rb +9 -5
  92. data/lib/graphql/introspection/input_value_type.rb +15 -3
  93. data/lib/graphql/introspection/introspection_query.rb +6 -92
  94. data/lib/graphql/introspection/schema_type.rb +4 -4
  95. data/lib/graphql/introspection/type_type.rb +16 -12
  96. data/lib/graphql/introspection.rb +96 -0
  97. data/lib/graphql/invalid_null_error.rb +18 -0
  98. data/lib/graphql/language/block_string.rb +20 -5
  99. data/lib/graphql/language/cache.rb +37 -0
  100. data/lib/graphql/language/document_from_schema_definition.rb +73 -25
  101. data/lib/graphql/language/lexer.rb +4 -3
  102. data/lib/graphql/language/lexer.rl +3 -3
  103. data/lib/graphql/language/nodes.rb +51 -89
  104. data/lib/graphql/language/parser.rb +552 -530
  105. data/lib/graphql/language/parser.y +114 -99
  106. data/lib/graphql/language/printer.rb +7 -2
  107. data/lib/graphql/language/sanitized_printer.rb +222 -0
  108. data/lib/graphql/language/token.rb +0 -4
  109. data/lib/graphql/language/visitor.rb +2 -2
  110. data/lib/graphql/language.rb +2 -0
  111. data/lib/graphql/name_validator.rb +2 -7
  112. data/lib/graphql/object_type.rb +44 -35
  113. data/lib/graphql/pagination/active_record_relation_connection.rb +14 -1
  114. data/lib/graphql/pagination/array_connection.rb +2 -2
  115. data/lib/graphql/pagination/connection.rb +75 -20
  116. data/lib/graphql/pagination/connections.rb +83 -31
  117. data/lib/graphql/pagination/relation_connection.rb +34 -14
  118. data/lib/graphql/parse_error.rb +0 -1
  119. data/lib/graphql/query/arguments.rb +4 -3
  120. data/lib/graphql/query/arguments_cache.rb +1 -2
  121. data/lib/graphql/query/context.rb +42 -7
  122. data/lib/graphql/query/executor.rb +0 -1
  123. data/lib/graphql/query/fingerprint.rb +26 -0
  124. data/lib/graphql/query/input_validation_result.rb +23 -6
  125. data/lib/graphql/query/literal_input.rb +1 -1
  126. data/lib/graphql/query/null_context.rb +24 -8
  127. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  128. data/lib/graphql/query/serial_execution.rb +1 -0
  129. data/lib/graphql/query/validation_pipeline.rb +5 -2
  130. data/lib/graphql/query/variable_validation_error.rb +1 -1
  131. data/lib/graphql/query/variables.rb +14 -4
  132. data/lib/graphql/query.rb +68 -13
  133. data/lib/graphql/railtie.rb +9 -1
  134. data/lib/graphql/rake_task.rb +12 -9
  135. data/lib/graphql/relay/array_connection.rb +10 -12
  136. data/lib/graphql/relay/base_connection.rb +26 -13
  137. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  138. data/lib/graphql/relay/connection_type.rb +1 -1
  139. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  140. data/lib/graphql/relay/mutation.rb +1 -0
  141. data/lib/graphql/relay/node.rb +3 -0
  142. data/lib/graphql/relay/range_add.rb +23 -9
  143. data/lib/graphql/relay/relation_connection.rb +8 -10
  144. data/lib/graphql/relay/type_extensions.rb +2 -0
  145. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  146. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  147. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  148. data/lib/graphql/rubocop.rb +4 -0
  149. data/lib/graphql/scalar_type.rb +16 -1
  150. data/lib/graphql/schema/addition.rb +247 -0
  151. data/lib/graphql/schema/argument.rb +210 -12
  152. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  153. data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
  154. data/lib/graphql/schema/build_from_definition.rb +213 -86
  155. data/lib/graphql/schema/default_type_error.rb +2 -0
  156. data/lib/graphql/schema/directive/deprecated.rb +1 -1
  157. data/lib/graphql/schema/directive/feature.rb +1 -1
  158. data/lib/graphql/schema/directive/flagged.rb +57 -0
  159. data/lib/graphql/schema/directive/include.rb +1 -1
  160. data/lib/graphql/schema/directive/skip.rb +1 -1
  161. data/lib/graphql/schema/directive/transform.rb +14 -2
  162. data/lib/graphql/schema/directive.rb +78 -2
  163. data/lib/graphql/schema/enum.rb +80 -9
  164. data/lib/graphql/schema/enum_value.rb +17 -6
  165. data/lib/graphql/schema/field/connection_extension.rb +46 -30
  166. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  167. data/lib/graphql/schema/field.rb +285 -133
  168. data/lib/graphql/schema/find_inherited_value.rb +4 -1
  169. data/lib/graphql/schema/finder.rb +5 -5
  170. data/lib/graphql/schema/input_object.rb +97 -89
  171. data/lib/graphql/schema/interface.rb +24 -19
  172. data/lib/graphql/schema/late_bound_type.rb +2 -2
  173. data/lib/graphql/schema/list.rb +7 -1
  174. data/lib/graphql/schema/loader.rb +137 -103
  175. data/lib/graphql/schema/member/accepts_definition.rb +8 -1
  176. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -19
  177. data/lib/graphql/schema/member/build_type.rb +14 -7
  178. data/lib/graphql/schema/member/has_arguments.rb +205 -12
  179. data/lib/graphql/schema/member/has_ast_node.rb +4 -1
  180. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  181. data/lib/graphql/schema/member/has_directives.rb +98 -0
  182. data/lib/graphql/schema/member/has_fields.rb +95 -30
  183. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  184. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  185. data/lib/graphql/schema/member/has_validators.rb +31 -0
  186. data/lib/graphql/schema/member/instrumentation.rb +0 -1
  187. data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
  188. data/lib/graphql/schema/member.rb +6 -0
  189. data/lib/graphql/schema/middleware_chain.rb +1 -1
  190. data/lib/graphql/schema/mutation.rb +4 -0
  191. data/lib/graphql/schema/non_null.rb +5 -0
  192. data/lib/graphql/schema/object.rb +47 -46
  193. data/lib/graphql/schema/possible_types.rb +9 -4
  194. data/lib/graphql/schema/printer.rb +16 -34
  195. data/lib/graphql/schema/relay_classic_mutation.rb +32 -4
  196. data/lib/graphql/schema/resolver/has_payload_type.rb +34 -4
  197. data/lib/graphql/schema/resolver.rb +123 -63
  198. data/lib/graphql/schema/scalar.rb +11 -1
  199. data/lib/graphql/schema/subscription.rb +57 -21
  200. data/lib/graphql/schema/timeout.rb +29 -15
  201. data/lib/graphql/schema/timeout_middleware.rb +3 -1
  202. data/lib/graphql/schema/type_expression.rb +1 -1
  203. data/lib/graphql/schema/type_membership.rb +18 -4
  204. data/lib/graphql/schema/union.rb +41 -1
  205. data/lib/graphql/schema/unique_within_type.rb +1 -2
  206. data/lib/graphql/schema/validation.rb +12 -2
  207. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  208. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  209. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  210. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  211. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  212. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  213. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  214. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  215. data/lib/graphql/schema/validator.rb +174 -0
  216. data/lib/graphql/schema/warden.rb +153 -28
  217. data/lib/graphql/schema.rb +364 -330
  218. data/lib/graphql/static_validation/all_rules.rb +1 -0
  219. data/lib/graphql/static_validation/base_visitor.rb +8 -5
  220. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  221. data/lib/graphql/static_validation/error.rb +3 -1
  222. data/lib/graphql/static_validation/literal_validator.rb +51 -26
  223. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +44 -87
  224. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +22 -6
  225. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +28 -22
  226. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  227. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
  228. data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -43
  229. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  230. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  231. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  232. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  233. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  234. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +6 -7
  235. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +9 -10
  236. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +8 -8
  237. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +4 -2
  238. data/lib/graphql/static_validation/validation_context.rb +9 -3
  239. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  240. data/lib/graphql/static_validation/validator.rb +42 -8
  241. data/lib/graphql/static_validation.rb +1 -0
  242. data/lib/graphql/string_encoding_error.rb +13 -3
  243. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +118 -19
  244. data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
  245. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  246. data/lib/graphql/subscriptions/event.rb +81 -30
  247. data/lib/graphql/subscriptions/instrumentation.rb +0 -1
  248. data/lib/graphql/subscriptions/serialize.rb +33 -6
  249. data/lib/graphql/subscriptions/subscription_root.rb +15 -4
  250. data/lib/graphql/subscriptions.rb +88 -45
  251. data/lib/graphql/tracing/active_support_notifications_tracing.rb +2 -1
  252. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  253. data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
  254. data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
  255. data/lib/graphql/tracing/platform_tracing.rb +43 -17
  256. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  257. data/lib/graphql/tracing/scout_tracing.rb +11 -0
  258. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  259. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  260. data/lib/graphql/tracing.rb +9 -33
  261. data/lib/graphql/types/big_int.rb +5 -1
  262. data/lib/graphql/types/int.rb +10 -3
  263. data/lib/graphql/types/iso_8601_date.rb +3 -3
  264. data/lib/graphql/types/iso_8601_date_time.rb +25 -10
  265. data/lib/graphql/types/relay/base_connection.rb +6 -90
  266. data/lib/graphql/types/relay/base_edge.rb +2 -34
  267. data/lib/graphql/types/relay/connection_behaviors.rb +156 -0
  268. data/lib/graphql/types/relay/default_relay.rb +27 -0
  269. data/lib/graphql/types/relay/edge_behaviors.rb +53 -0
  270. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  271. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  272. data/lib/graphql/types/relay/node.rb +2 -4
  273. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  274. data/lib/graphql/types/relay/node_field.rb +2 -20
  275. data/lib/graphql/types/relay/nodes_field.rb +2 -20
  276. data/lib/graphql/types/relay/page_info.rb +2 -14
  277. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  278. data/lib/graphql/types/relay.rb +11 -3
  279. data/lib/graphql/types/string.rb +8 -2
  280. data/lib/graphql/unauthorized_error.rb +2 -2
  281. data/lib/graphql/union_type.rb +2 -0
  282. data/lib/graphql/upgrader/member.rb +1 -0
  283. data/lib/graphql/upgrader/schema.rb +1 -0
  284. data/lib/graphql/version.rb +1 -1
  285. data/lib/graphql.rb +65 -31
  286. data/readme.md +3 -6
  287. metadata +77 -112
  288. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
  289. data/lib/graphql/literal_validation_error.rb +0 -6
  290. data/lib/graphql/types/relay/base_field.rb +0 -22
  291. data/lib/graphql/types/relay/base_interface.rb +0 -29
  292. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # The thing being validated
7
+ # @return [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class<GraphQL::Schema::InputObject>]
8
+ attr_reader :validated
9
+
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
11
+ # @param allow_blank [Boolean] if `true`, then objects that respond to `.blank?` and return true for `.blank?` will skip this validation
12
+ # @param allow_null [Boolean] if `true`, then incoming `null`s will skip this validation
13
+ def initialize(validated:, allow_blank: false, allow_null: false)
14
+ @validated = validated
15
+ @allow_blank = allow_blank
16
+ @allow_null = allow_null
17
+ end
18
+
19
+ # @param object [Object] The application object that this argument's field is being resolved for
20
+ # @param context [GraphQL::Query::Context]
21
+ # @param value [Object] The client-provided value for this argument (after parsing and coercing by the input type)
22
+ # @return [nil, Array<String>, String] Error message or messages to add
23
+ def validate(object, context, value)
24
+ raise GraphQL::RequiredImplementationMissingError, "Validator classes should implement #validate"
25
+ end
26
+
27
+ # This is like `String#%`, but it supports the case that only some of `string`'s
28
+ # values are present in `substitutions`
29
+ def partial_format(string, substitutions)
30
+ substitutions.each do |key, value|
31
+ sub_v = value.is_a?(String) ? value : value.to_s
32
+ string = string.gsub("%{#{key}}", sub_v)
33
+ end
34
+ string
35
+ end
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
+
43
+ # @param schema_member [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class<GraphQL::Schema::InputObject>]
44
+ # @param validates_hash [Hash{Symbol => Hash}, Hash{Class => Hash} nil] A configuration passed as `validates:`
45
+ # @return [Array<Validator>]
46
+ def self.from_config(schema_member, validates_hash)
47
+ if validates_hash.nil? || validates_hash.empty?
48
+ EMPTY_ARRAY
49
+ else
50
+ validates_hash = validates_hash.dup
51
+ allow_null = validates_hash.delete(:allow_null)
52
+ allow_blank = validates_hash.delete(:allow_blank)
53
+
54
+ # This could be {...}.compact on Ruby 2.4+
55
+ default_options = {}
56
+ if !allow_null.nil?
57
+ default_options[:allow_null] = allow_null
58
+ end
59
+ if !allow_blank.nil?
60
+ default_options[:allow_blank] = allow_blank
61
+ end
62
+
63
+ # allow_nil or allow_blank are the _only_ validations:
64
+ if validates_hash.empty?
65
+ validates_hash = default_options
66
+ end
67
+
68
+ validates_hash.map do |validator_name, options|
69
+ validator_class = case validator_name
70
+ when Class
71
+ validator_name
72
+ else
73
+ all_validators[validator_name] || raise(ArgumentError, "unknown validation: #{validator_name.inspect}")
74
+ end
75
+ if options.is_a?(Hash)
76
+ validator_class.new(validated: schema_member, **(default_options.merge(options)))
77
+ else
78
+ validator_class.new(options, validated: schema_member, **default_options)
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ # Add `validator_class` to be initialized when `validates:` is given `name`.
85
+ # (It's initialized with whatever options are given by the key `name`).
86
+ # @param name [Symbol]
87
+ # @param validator_class [Class]
88
+ # @return [void]
89
+ def self.install(name, validator_class)
90
+ all_validators[name] = validator_class
91
+ nil
92
+ end
93
+
94
+ # Remove whatever validator class is {.install}ed at `name`, if there is one
95
+ # @param name [Symbol]
96
+ # @return [void]
97
+ def self.uninstall(name)
98
+ all_validators.delete(name)
99
+ nil
100
+ end
101
+
102
+ class << self
103
+ attr_accessor :all_validators
104
+ end
105
+
106
+ self.all_validators = {}
107
+
108
+ include Schema::FindInheritedValue::EmptyObjects
109
+
110
+ class ValidationFailedError < GraphQL::ExecutionError
111
+ attr_reader :errors
112
+
113
+ def initialize(errors:)
114
+ @errors = errors
115
+ super(errors.join(", "))
116
+ end
117
+ end
118
+
119
+ # @param validators [Array<Validator>]
120
+ # @param object [Object]
121
+ # @param context [Query::Context]
122
+ # @param value [Object]
123
+ # @return [void]
124
+ # @raises [ValidationFailedError]
125
+ def self.validate!(validators, object, context, value, as: nil)
126
+ # Assuming the default case is no errors, reduce allocations in that case.
127
+ # This will be replaced with a mutable array if we actually get any errors.
128
+ all_errors = EMPTY_ARRAY
129
+
130
+ validators.each do |validator|
131
+ validated = as || validator.validated
132
+ errors = validator.validate(object, context, value)
133
+ if errors &&
134
+ (errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
135
+ (errors.is_a?(String))
136
+ if all_errors.frozen? # It's empty
137
+ all_errors = []
138
+ end
139
+ interpolation_vars = { validated: validated.graphql_name }
140
+ if errors.is_a?(String)
141
+ all_errors << (errors % interpolation_vars)
142
+ else
143
+ errors = errors.map { |e| e % interpolation_vars }
144
+ all_errors.concat(errors)
145
+ end
146
+ end
147
+ end
148
+
149
+ if all_errors.any?
150
+ raise ValidationFailedError.new(errors: all_errors)
151
+ end
152
+ nil
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+
159
+ require "graphql/schema/validator/length_validator"
160
+ GraphQL::Schema::Validator.install(:length, GraphQL::Schema::Validator::LengthValidator)
161
+ require "graphql/schema/validator/numericality_validator"
162
+ GraphQL::Schema::Validator.install(:numericality, GraphQL::Schema::Validator::NumericalityValidator)
163
+ require "graphql/schema/validator/format_validator"
164
+ GraphQL::Schema::Validator.install(:format, GraphQL::Schema::Validator::FormatValidator)
165
+ require "graphql/schema/validator/inclusion_validator"
166
+ GraphQL::Schema::Validator.install(:inclusion, GraphQL::Schema::Validator::InclusionValidator)
167
+ require "graphql/schema/validator/exclusion_validator"
168
+ GraphQL::Schema::Validator.install(:exclusion, GraphQL::Schema::Validator::ExclusionValidator)
169
+ require "graphql/schema/validator/required_validator"
170
+ GraphQL::Schema::Validator.install(:required, GraphQL::Schema::Validator::RequiredValidator)
171
+ require "graphql/schema/validator/allow_null_validator"
172
+ GraphQL::Schema::Validator.install(:allow_null, GraphQL::Schema::Validator::AllowNullValidator)
173
+ require "graphql/schema/validator/allow_blank_validator"
174
+ GraphQL::Schema::Validator.install(:allow_blank, GraphQL::Schema::Validator::AllowBlankValidator)
@@ -37,10 +37,53 @@ 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]
43
- # @param deep_check [Boolean]
44
87
  def initialize(filter, context:, schema:)
45
88
  @schema = schema.interpreter? ? schema : schema.graphql_definition
46
89
  # Cache these to avoid repeated hits to the inheritance chain when one isn't present
@@ -51,12 +94,12 @@ module GraphQL
51
94
  @visibility_cache = read_through { |m| filter.call(m, context) }
52
95
  end
53
96
 
54
- # @return [Array<GraphQL::BaseType>] Visible types in the schema
97
+ # @return [Hash<String, GraphQL::BaseType>] Visible types in the schema
55
98
  def types
56
99
  @types ||= begin
57
100
  vis_types = {}
58
- @schema.types.each do |n, t|
59
- if visible_type?(t)
101
+ @schema.types(@context).each do |n, t|
102
+ if visible_and_reachable_type?(t)
60
103
  vis_types[n] = t
61
104
  end
62
105
  end
@@ -67,8 +110,8 @@ module GraphQL
67
110
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
68
111
  def get_type(type_name)
69
112
  @visible_types ||= read_through do |name|
70
- type_defn = @schema.get_type(name)
71
- 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)
72
115
  type_defn
73
116
  else
74
117
  nil
@@ -85,17 +128,16 @@ module GraphQL
85
128
 
86
129
  # @return Boolean True if the type is visible and reachable in the schema
87
130
  def reachable_type?(type_name)
88
- type = get_type(type_name)
131
+ type = get_type(type_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
89
132
  type && reachable_type_set.include?(type)
90
133
  end
91
134
 
92
135
  # @return [GraphQL::Field, nil] The field named `field_name` on `parent_type`, if it exists
93
136
  def get_field(parent_type, field_name)
94
-
95
137
  @visible_parent_fields ||= read_through do |type|
96
138
  read_through do |f_name|
97
- field_defn = @schema.get_field(type, f_name)
98
- if field_defn && visible_field?(field_defn)
139
+ field_defn = @schema.get_field(type, f_name, @context)
140
+ if field_defn && visible_field?(field_defn, nil, type)
99
141
  field_defn
100
142
  else
101
143
  nil
@@ -106,10 +148,17 @@ module GraphQL
106
148
  @visible_parent_fields[parent_type][field_name]
107
149
  end
108
150
 
151
+ # @return [GraphQL::Argument, nil] The argument named `argument_name` on `parent_type`, if it exists and is visible
152
+ def get_argument(parent_type, argument_name)
153
+ argument = parent_type.get_argument(argument_name, @context)
154
+ return argument if argument && visible_argument?(argument, @context)
155
+ end
156
+
109
157
  # @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
110
158
  def possible_types(type_defn)
111
159
  @visible_possible_types ||= read_through { |type_defn|
112
- @schema.possible_types(type_defn, @context).select { |t| visible_type?(t) }
160
+ pt = @schema.possible_types(type_defn, @context)
161
+ pt.select { |t| visible_and_reachable_type?(t) }
113
162
  }
114
163
  @visible_possible_types[type_defn]
115
164
  end
@@ -117,26 +166,31 @@ module GraphQL
117
166
  # @param type_defn [GraphQL::ObjectType, GraphQL::InterfaceType]
118
167
  # @return [Array<GraphQL::Field>] Fields on `type_defn`
119
168
  def fields(type_defn)
120
- @visible_fields ||= read_through { |t| @schema.get_fields(t).each_value.select { |f| visible_field?(f) } }
169
+ @visible_fields ||= read_through { |t| @schema.get_fields(t, @context).values }
121
170
  @visible_fields[type_defn]
122
171
  end
123
172
 
124
173
  # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
125
174
  # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
126
175
  def arguments(argument_owner)
127
- @visible_arguments ||= read_through { |o| o.arguments.each_value.select { |a| visible_field?(a) } }
176
+ @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a) } }
128
177
  @visible_arguments[argument_owner]
129
178
  end
130
179
 
131
180
  # @return [Array<GraphQL::EnumType::EnumValue>] Visible members of `enum_defn`
132
181
  def enum_values(enum_defn)
133
- @visible_enum_values ||= read_through { |e| e.values.each_value.select { |enum_value_defn| visible?(enum_value_defn) } }
134
- @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]
135
189
  end
136
190
 
137
191
  # @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
138
192
  def interfaces(obj_type)
139
- @visible_interfaces ||= read_through { |t| t.interfaces.select { |i| visible?(i) } }
193
+ @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible_type?(i) } }
140
194
  @visible_interfaces[obj_type]
141
195
  end
142
196
 
@@ -153,20 +207,35 @@ module GraphQL
153
207
  end
154
208
  end
155
209
 
156
- private
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)
212
+ # This field is visible in its own right
213
+ visible?(field_defn) &&
214
+ # This field's return type is visible
215
+ visible_and_reachable_type?(field_defn.type.unwrap) &&
216
+ # This field is either defined on this object type,
217
+ # or the interface it's inherited from is also visible
218
+ ((field_defn.respond_to?(:owner) && field_defn.owner == owner) || field_on_visible_interface?(field_defn, owner))
219
+ end
157
220
 
158
- def union_memberships(obj_type)
159
- @unions ||= read_through { |obj_type| @schema.union_memberships(obj_type).select { |u| visible?(u) } }
160
- @unions[obj_type]
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]
161
228
  end
162
229
 
163
- def visible_field?(field_defn)
164
- visible?(field_defn) && visible_type?(field_defn.type.unwrap)
230
+ def visible_type_membership?(type_membership, _ctx = nil)
231
+ visible?(type_membership)
165
232
  end
166
233
 
167
- def visible_type?(type_defn)
168
- @type_visibility ||= read_through do |type_defn|
169
- next false unless visible?(type_defn)
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)
170
239
  next true if root_type?(type_defn) || type_defn.introspection?
171
240
 
172
241
  if type_defn.kind.union?
@@ -178,7 +247,48 @@ module GraphQL
178
247
  end
179
248
  end
180
249
 
181
- @type_visibility[type_defn]
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]
256
+ end
257
+
258
+ # We need this to tell whether a field was inherited by an interface
259
+ # even when that interface is hidden from `#interfaces`
260
+ def unfiltered_interfaces(type_defn)
261
+ @unfiltered_interfaces ||= read_through(&:interfaces)
262
+ @unfiltered_interfaces[type_defn]
263
+ end
264
+
265
+ # If this field was inherited from an interface, and the field on that interface is _hidden_,
266
+ # then treat this inherited field as hidden.
267
+ # (If it _wasn't_ inherited, then don't hide it for this reason.)
268
+ def field_on_visible_interface?(field_defn, type_defn)
269
+ if type_defn.kind.object?
270
+ any_interface_has_field = false
271
+ any_interface_has_visible_field = false
272
+ ints = unfiltered_interfaces(type_defn)
273
+ ints.each do |interface_type|
274
+ if (iface_field_defn = interface_type.get_field(field_defn.graphql_name, @context))
275
+ any_interface_has_field = true
276
+
277
+ if interfaces(type_defn).include?(interface_type) && visible_field?(iface_field_defn, nil, interface_type)
278
+ any_interface_has_visible_field = true
279
+ end
280
+ end
281
+ end
282
+
283
+ if any_interface_has_field
284
+ any_interface_has_visible_field
285
+ else
286
+ # it's the object's own field
287
+ true
288
+ end
289
+ else
290
+ true
291
+ end
182
292
  end
183
293
 
184
294
  def root_type?(type_defn)
@@ -208,7 +318,7 @@ module GraphQL
208
318
  end
209
319
 
210
320
  def visible_possible_types?(type_defn)
211
- possible_types(type_defn).any? { |t| visible_type?(t) }
321
+ possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
212
322
  end
213
323
 
214
324
  def visible?(member)
@@ -223,6 +333,7 @@ module GraphQL
223
333
  return @reachable_type_set if defined?(@reachable_type_set)
224
334
 
225
335
  @reachable_type_set = Set.new
336
+ rt_hash = {}
226
337
 
227
338
  unvisited_types = []
228
339
  ['query', 'mutation', 'subscription'].each do |op_name|
@@ -230,8 +341,18 @@ module GraphQL
230
341
  unvisited_types << root_type if root_type
231
342
  end
232
343
  unvisited_types.concat(@schema.introspection_system.types.values)
344
+
345
+ directives.each do |dir_class|
346
+ arguments(dir_class).each do |arg_defn|
347
+ arg_t = arg_defn.type.unwrap
348
+ if get_type(arg_t.graphql_name) # rubocop:disable Development/ContextIsPassedCop -- `self` is query-aware
349
+ unvisited_types << arg_t
350
+ end
351
+ end
352
+ end
353
+
233
354
  @schema.orphan_types.each do |orphan_type|
234
- 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
235
356
  unvisited_types << orphan_type
236
357
  end
237
358
  end
@@ -239,6 +360,10 @@ module GraphQL
239
360
  until unvisited_types.empty?
240
361
  type = unvisited_types.pop
241
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
242
367
  if type.kind.input_object?
243
368
  # recurse into visible arguments
244
369
  arguments(type).each do |argument|