graphql 1.9.17 → 1.11.7

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 (230) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +18 -2
  3. data/lib/generators/graphql/install_generator.rb +27 -0
  4. data/lib/generators/graphql/object_generator.rb +52 -8
  5. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  6. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  7. data/lib/generators/graphql/templates/base_field.erb +2 -0
  8. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  9. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  10. data/lib/generators/graphql/templates/base_mutation.erb +2 -0
  11. data/lib/generators/graphql/templates/base_object.erb +2 -0
  12. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  13. data/lib/generators/graphql/templates/base_union.erb +2 -0
  14. data/lib/generators/graphql/templates/enum.erb +2 -0
  15. data/lib/generators/graphql/templates/graphql_controller.erb +14 -10
  16. data/lib/generators/graphql/templates/interface.erb +2 -0
  17. data/lib/generators/graphql/templates/loader.erb +2 -0
  18. data/lib/generators/graphql/templates/mutation.erb +2 -0
  19. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  20. data/lib/generators/graphql/templates/object.erb +2 -0
  21. data/lib/generators/graphql/templates/query_type.erb +2 -0
  22. data/lib/generators/graphql/templates/scalar.erb +2 -0
  23. data/lib/generators/graphql/templates/schema.erb +10 -0
  24. data/lib/generators/graphql/templates/union.erb +3 -1
  25. data/lib/graphql/analysis/ast/field_usage.rb +1 -1
  26. data/lib/graphql/analysis/ast/query_complexity.rb +178 -67
  27. data/lib/graphql/analysis/ast/visitor.rb +3 -3
  28. data/lib/graphql/analysis/ast.rb +12 -11
  29. data/lib/graphql/argument.rb +10 -38
  30. data/lib/graphql/backtrace/table.rb +10 -2
  31. data/lib/graphql/backtrace/tracer.rb +2 -1
  32. data/lib/graphql/base_type.rb +4 -0
  33. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  34. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
  35. data/lib/graphql/define/assign_enum_value.rb +1 -1
  36. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  37. data/lib/graphql/define/assign_object_field.rb +3 -3
  38. data/lib/graphql/define/defined_object_proxy.rb +3 -0
  39. data/lib/graphql/define/instance_definable.rb +18 -108
  40. data/lib/graphql/directive/deprecated_directive.rb +1 -12
  41. data/lib/graphql/directive.rb +8 -1
  42. data/lib/graphql/enum_type.rb +5 -71
  43. data/lib/graphql/execution/directive_checks.rb +2 -2
  44. data/lib/graphql/execution/errors.rb +2 -3
  45. data/lib/graphql/execution/execute.rb +1 -1
  46. data/lib/graphql/execution/instrumentation.rb +1 -1
  47. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  48. data/lib/graphql/execution/interpreter/arguments.rb +51 -0
  49. data/lib/graphql/execution/interpreter/arguments_cache.rb +79 -0
  50. data/lib/graphql/execution/interpreter/handles_raw_value.rb +25 -0
  51. data/lib/graphql/execution/interpreter/runtime.rb +227 -254
  52. data/lib/graphql/execution/interpreter.rb +34 -11
  53. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  54. data/lib/graphql/execution/lookahead.rb +39 -114
  55. data/lib/graphql/execution/multiplex.rb +14 -5
  56. data/lib/graphql/field.rb +14 -118
  57. data/lib/graphql/filter.rb +1 -1
  58. data/lib/graphql/function.rb +1 -30
  59. data/lib/graphql/input_object_type.rb +6 -24
  60. data/lib/graphql/integer_decoding_error.rb +17 -0
  61. data/lib/graphql/interface_type.rb +7 -23
  62. data/lib/graphql/internal_representation/scope.rb +2 -2
  63. data/lib/graphql/internal_representation/visit.rb +2 -2
  64. data/lib/graphql/introspection/base_object.rb +2 -5
  65. data/lib/graphql/introspection/directive_type.rb +1 -1
  66. data/lib/graphql/introspection/entry_points.rb +7 -7
  67. data/lib/graphql/introspection/field_type.rb +7 -3
  68. data/lib/graphql/introspection/input_value_type.rb +33 -9
  69. data/lib/graphql/introspection/introspection_query.rb +6 -92
  70. data/lib/graphql/introspection/schema_type.rb +4 -9
  71. data/lib/graphql/introspection/type_type.rb +11 -7
  72. data/lib/graphql/introspection.rb +96 -0
  73. data/lib/graphql/invalid_null_error.rb +18 -0
  74. data/lib/graphql/language/block_string.rb +24 -5
  75. data/lib/graphql/language/definition_slice.rb +21 -10
  76. data/lib/graphql/language/document_from_schema_definition.rb +89 -64
  77. data/lib/graphql/language/lexer.rb +7 -3
  78. data/lib/graphql/language/lexer.rl +7 -3
  79. data/lib/graphql/language/nodes.rb +52 -91
  80. data/lib/graphql/language/parser.rb +719 -717
  81. data/lib/graphql/language/parser.y +104 -98
  82. data/lib/graphql/language/printer.rb +1 -1
  83. data/lib/graphql/language/sanitized_printer.rb +222 -0
  84. data/lib/graphql/language/visitor.rb +2 -2
  85. data/lib/graphql/language.rb +2 -1
  86. data/lib/graphql/name_validator.rb +6 -7
  87. data/lib/graphql/non_null_type.rb +0 -10
  88. data/lib/graphql/object_type.rb +45 -56
  89. data/lib/graphql/pagination/active_record_relation_connection.rb +41 -0
  90. data/lib/graphql/pagination/array_connection.rb +77 -0
  91. data/lib/graphql/pagination/connection.rb +208 -0
  92. data/lib/graphql/pagination/connections.rb +145 -0
  93. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  94. data/lib/graphql/pagination/relation_connection.rb +185 -0
  95. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  96. data/lib/graphql/pagination.rb +6 -0
  97. data/lib/graphql/query/arguments.rb +4 -2
  98. data/lib/graphql/query/context.rb +36 -9
  99. data/lib/graphql/query/fingerprint.rb +26 -0
  100. data/lib/graphql/query/input_validation_result.rb +23 -6
  101. data/lib/graphql/query/literal_input.rb +30 -10
  102. data/lib/graphql/query/null_context.rb +5 -1
  103. data/lib/graphql/query/validation_pipeline.rb +4 -1
  104. data/lib/graphql/query/variable_validation_error.rb +1 -1
  105. data/lib/graphql/query/variables.rb +16 -7
  106. data/lib/graphql/query.rb +64 -15
  107. data/lib/graphql/rake_task/validate.rb +3 -0
  108. data/lib/graphql/rake_task.rb +9 -9
  109. data/lib/graphql/relay/array_connection.rb +10 -12
  110. data/lib/graphql/relay/base_connection.rb +23 -13
  111. data/lib/graphql/relay/connection_type.rb +2 -1
  112. data/lib/graphql/relay/edge_type.rb +1 -0
  113. data/lib/graphql/relay/edges_instrumentation.rb +1 -1
  114. data/lib/graphql/relay/mutation.rb +1 -86
  115. data/lib/graphql/relay/node.rb +2 -2
  116. data/lib/graphql/relay/range_add.rb +14 -5
  117. data/lib/graphql/relay/relation_connection.rb +8 -10
  118. data/lib/graphql/scalar_type.rb +15 -59
  119. data/lib/graphql/schema/argument.rb +113 -11
  120. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  121. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
  122. data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
  123. data/lib/graphql/schema/build_from_definition.rb +212 -190
  124. data/lib/graphql/schema/built_in_types.rb +5 -5
  125. data/lib/graphql/schema/default_type_error.rb +2 -0
  126. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  127. data/lib/graphql/schema/directive/include.rb +1 -1
  128. data/lib/graphql/schema/directive/skip.rb +1 -1
  129. data/lib/graphql/schema/directive.rb +34 -3
  130. data/lib/graphql/schema/enum.rb +52 -4
  131. data/lib/graphql/schema/enum_value.rb +6 -1
  132. data/lib/graphql/schema/field/connection_extension.rb +44 -20
  133. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  134. data/lib/graphql/schema/field.rb +200 -129
  135. data/lib/graphql/schema/find_inherited_value.rb +13 -0
  136. data/lib/graphql/schema/finder.rb +13 -11
  137. data/lib/graphql/schema/input_object.rb +131 -22
  138. data/lib/graphql/schema/interface.rb +26 -8
  139. data/lib/graphql/schema/introspection_system.rb +108 -37
  140. data/lib/graphql/schema/late_bound_type.rb +3 -2
  141. data/lib/graphql/schema/list.rb +47 -0
  142. data/lib/graphql/schema/loader.rb +134 -96
  143. data/lib/graphql/schema/member/base_dsl_methods.rb +29 -12
  144. data/lib/graphql/schema/member/build_type.rb +19 -5
  145. data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
  146. data/lib/graphql/schema/member/has_arguments.rb +105 -5
  147. data/lib/graphql/schema/member/has_ast_node.rb +20 -0
  148. data/lib/graphql/schema/member/has_fields.rb +20 -10
  149. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  150. data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
  151. data/lib/graphql/schema/member/validates_input.rb +33 -0
  152. data/lib/graphql/schema/member.rb +6 -0
  153. data/lib/graphql/schema/mutation.rb +5 -1
  154. data/lib/graphql/schema/non_null.rb +30 -0
  155. data/lib/graphql/schema/object.rb +65 -12
  156. data/lib/graphql/schema/possible_types.rb +9 -4
  157. data/lib/graphql/schema/printer.rb +0 -15
  158. data/lib/graphql/schema/relay_classic_mutation.rb +5 -3
  159. data/lib/graphql/schema/resolver/has_payload_type.rb +5 -2
  160. data/lib/graphql/schema/resolver.rb +26 -18
  161. data/lib/graphql/schema/scalar.rb +27 -3
  162. data/lib/graphql/schema/subscription.rb +8 -18
  163. data/lib/graphql/schema/timeout.rb +29 -15
  164. data/lib/graphql/schema/traversal.rb +1 -1
  165. data/lib/graphql/schema/type_expression.rb +21 -13
  166. data/lib/graphql/schema/type_membership.rb +2 -2
  167. data/lib/graphql/schema/union.rb +37 -3
  168. data/lib/graphql/schema/unique_within_type.rb +1 -2
  169. data/lib/graphql/schema/validation.rb +10 -2
  170. data/lib/graphql/schema/warden.rb +115 -29
  171. data/lib/graphql/schema.rb +903 -195
  172. data/lib/graphql/static_validation/all_rules.rb +1 -0
  173. data/lib/graphql/static_validation/base_visitor.rb +10 -6
  174. data/lib/graphql/static_validation/literal_validator.rb +52 -27
  175. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +43 -83
  176. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +17 -5
  177. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +33 -25
  178. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  179. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  180. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  181. data/lib/graphql/static_validation/rules/fields_will_merge.rb +29 -21
  182. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  183. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  184. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  185. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -2
  186. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -5
  187. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
  188. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
  189. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  190. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
  191. data/lib/graphql/static_validation/type_stack.rb +2 -2
  192. data/lib/graphql/static_validation/validation_context.rb +1 -1
  193. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  194. data/lib/graphql/static_validation/validator.rb +30 -8
  195. data/lib/graphql/static_validation.rb +1 -0
  196. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +89 -19
  197. data/lib/graphql/subscriptions/broadcast_analyzer.rb +84 -0
  198. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  199. data/lib/graphql/subscriptions/event.rb +23 -5
  200. data/lib/graphql/subscriptions/instrumentation.rb +10 -5
  201. data/lib/graphql/subscriptions/serialize.rb +22 -4
  202. data/lib/graphql/subscriptions/subscription_root.rb +15 -5
  203. data/lib/graphql/subscriptions.rb +108 -35
  204. data/lib/graphql/tracing/active_support_notifications_tracing.rb +14 -10
  205. data/lib/graphql/tracing/appoptics_tracing.rb +171 -0
  206. data/lib/graphql/tracing/appsignal_tracing.rb +8 -0
  207. data/lib/graphql/tracing/data_dog_tracing.rb +8 -0
  208. data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
  209. data/lib/graphql/tracing/platform_tracing.rb +53 -9
  210. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  211. data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
  212. data/lib/graphql/tracing/scout_tracing.rb +19 -0
  213. data/lib/graphql/tracing/skylight_tracing.rb +8 -0
  214. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  215. data/lib/graphql/tracing.rb +14 -34
  216. data/lib/graphql/types/big_int.rb +1 -1
  217. data/lib/graphql/types/int.rb +9 -2
  218. data/lib/graphql/types/iso_8601_date.rb +3 -3
  219. data/lib/graphql/types/iso_8601_date_time.rb +25 -10
  220. data/lib/graphql/types/relay/base_connection.rb +11 -7
  221. data/lib/graphql/types/relay/base_edge.rb +2 -1
  222. data/lib/graphql/types/string.rb +7 -1
  223. data/lib/graphql/unauthorized_error.rb +1 -1
  224. data/lib/graphql/union_type.rb +13 -28
  225. data/lib/graphql/unresolved_type_error.rb +2 -2
  226. data/lib/graphql/version.rb +1 -1
  227. data/lib/graphql.rb +31 -6
  228. data/readme.md +1 -1
  229. metadata +34 -9
  230. data/lib/graphql/literal_validation_error.rb +0 -6
@@ -12,16 +12,6 @@ module GraphQL
12
12
  #
13
13
  # Also, `#unsubscribe` terminates the subscription.
14
14
  class Subscription < GraphQL::Schema::Resolver
15
- class EarlyTerminationError < StandardError
16
- end
17
-
18
- # Raised when `unsubscribe` is called; caught by `subscriptions.rb`
19
- class UnsubscribedError < EarlyTerminationError
20
- end
21
-
22
- # Raised when `no_update` is returned; caught by `subscriptions.rb`
23
- class NoUpdateError < EarlyTerminationError
24
- end
25
15
  extend GraphQL::Schema::Resolver::HasPayloadType
26
16
  extend GraphQL::Schema::Member::HasFields
27
17
 
@@ -39,12 +29,12 @@ module GraphQL
39
29
  def resolve(**args)
40
30
  # Dispatch based on `@mode`, which will raise a `NoMethodError` if we ever
41
31
  # have an unexpected `@mode`
42
- public_send("resolve_#{@mode}", args)
32
+ public_send("resolve_#{@mode}", **args)
43
33
  end
44
34
 
45
35
  # Wrap the user-defined `#subscribe` hook
46
- def resolve_subscribe(args)
47
- ret_val = args.any? ? subscribe(args) : subscribe
36
+ def resolve_subscribe(**args)
37
+ ret_val = args.any? ? subscribe(**args) : subscribe
48
38
  if ret_val == :no_response
49
39
  context.skip
50
40
  else
@@ -62,10 +52,10 @@ module GraphQL
62
52
  end
63
53
 
64
54
  # Wrap the user-provided `#update` hook
65
- def resolve_update(args)
66
- ret_val = args.any? ? update(args) : update
55
+ def resolve_update(**args)
56
+ ret_val = args.any? ? update(**args) : update
67
57
  if ret_val == :no_update
68
- raise NoUpdateError
58
+ throw :graphql_no_subscription_update
69
59
  else
70
60
  ret_val
71
61
  end
@@ -90,14 +80,14 @@ module GraphQL
90
80
 
91
81
  # Call this to halt execution and remove this subscription from the system
92
82
  def unsubscribe
93
- raise UnsubscribedError
83
+ throw :graphql_subscription_unsubscribed
94
84
  end
95
85
 
86
+ READING_SCOPE = ::Object.new
96
87
  # Call this method to provide a new subscription_scope; OR
97
88
  # call it without an argument to get the subscription_scope
98
89
  # @param new_scope [Symbol]
99
90
  # @return [Symbol]
100
- READING_SCOPE = ::Object.new
101
91
  def self.subscription_scope(new_scope = READING_SCOPE)
102
92
  if new_scope != READING_SCOPE
103
93
  @subscription_scope = new_scope
@@ -7,7 +7,7 @@ module GraphQL
7
7
  # to the `errors` key. Any already-resolved fields will be in the `data` key, so
8
8
  # you'll get a partial response.
9
9
  #
10
- # You can subclass `GraphQL::Schema::Timeout` and override the `handle_timeout` method
10
+ # You can subclass `GraphQL::Schema::Timeout` and override `max_seconds` and/or `handle_timeout`
11
11
  # to provide custom logic when a timeout error occurs.
12
12
  #
13
13
  # Note that this will stop a query _in between_ field resolutions, but
@@ -33,8 +33,6 @@ module GraphQL
33
33
  # end
34
34
  #
35
35
  class Timeout
36
- attr_reader :max_seconds
37
-
38
36
  def self.use(schema, **options)
39
37
  tracer = new(**options)
40
38
  schema.tracer(tracer)
@@ -48,32 +46,39 @@ module GraphQL
48
46
  def trace(key, data)
49
47
  case key
50
48
  when 'execute_multiplex'
51
- timeout_state = {
52
- timeout_at: Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) + max_seconds * 1000,
53
- timed_out: false
54
- }
55
-
56
49
  data.fetch(:multiplex).queries.each do |query|
50
+ timeout_duration_s = max_seconds(query)
51
+ timeout_state = if timeout_duration_s == false
52
+ # if the method returns `false`, don't apply a timeout
53
+ false
54
+ else
55
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
56
+ timeout_at = now + (max_seconds(query) * 1000)
57
+ {
58
+ timeout_at: timeout_at,
59
+ timed_out: false
60
+ }
61
+ end
57
62
  query.context.namespace(self.class)[:state] = timeout_state
58
63
  end
59
64
 
60
65
  yield
61
66
  when 'execute_field', 'execute_field_lazy'
62
- query = data[:context] ? data.fetch(:context).query : data.fetch(:query)
63
- timeout_state = query.context.namespace(self.class).fetch(:state)
64
- if Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
67
+ query_context = data[:context] || data[:query].context
68
+ timeout_state = query_context.namespace(self.class).fetch(:state)
69
+ # If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query.
70
+ if timeout_state != false && Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
65
71
  error = if data[:context]
66
- context = data.fetch(:context)
67
- GraphQL::Schema::Timeout::TimeoutError.new(context.parent_type, context.field)
72
+ GraphQL::Schema::Timeout::TimeoutError.new(query_context.parent_type, query_context.field)
68
73
  else
69
74
  field = data.fetch(:field)
70
75
  GraphQL::Schema::Timeout::TimeoutError.new(field.owner, field)
71
76
  end
72
77
 
73
78
  # Only invoke the timeout callback for the first timeout
74
- unless timeout_state[:timed_out]
79
+ if !timeout_state[:timed_out]
75
80
  timeout_state[:timed_out] = true
76
- handle_timeout(error, query)
81
+ handle_timeout(error, query_context.query)
77
82
  end
78
83
 
79
84
  error
@@ -85,6 +90,15 @@ module GraphQL
85
90
  end
86
91
  end
87
92
 
93
+ # Called at the start of each query.
94
+ # The default implementation returns the `max_seconds:` value from installing this plugin.
95
+ #
96
+ # @param query [GraphQL::Query] The query that's about to run
97
+ # @return [Integer, false] The number of seconds after which to interrupt query execution and call {#handle_error}, or `false` to bypass the timeout.
98
+ def max_seconds(query)
99
+ @max_seconds
100
+ end
101
+
88
102
  # Invoked when a query times out.
89
103
  # @param error [GraphQL::Schema::Timeout::TimeoutError]
90
104
  # @param query [GraphQL::Error]
@@ -113,7 +113,7 @@ Some late-bound types couldn't be resolved:
113
113
  # Find the starting points, then visit them
114
114
  visit_roots = [member.query, member.mutation, member.subscription]
115
115
  if @introspection
116
- introspection_types = schema.introspection_system.object_types
116
+ introspection_types = schema.introspection_system.types.values
117
117
  visit_roots.concat(introspection_types)
118
118
  if member.query
119
119
  member.introspection_system.entry_points.each do |introspection_field|
@@ -5,29 +5,37 @@ module GraphQL
5
5
  module TypeExpression
6
6
  # Fetch a type from a type map by its AST specification.
7
7
  # Return `nil` if not found.
8
- # @param types [GraphQL::Schema::TypeMap]
8
+ # @param type_owner [#get_type] A thing for looking up types by name
9
9
  # @param ast_node [GraphQL::Language::Nodes::AbstractNode]
10
- # @return [GraphQL::BaseType, nil]
11
- def self.build_type(types, ast_node)
10
+ # @return [Class, GraphQL::Schema::NonNull, GraphQL::Schema:List]
11
+ def self.build_type(type_owner, ast_node)
12
12
  case ast_node
13
13
  when GraphQL::Language::Nodes::TypeName
14
- types.fetch(ast_node.name, nil)
14
+ type_owner.get_type(ast_node.name)
15
15
  when GraphQL::Language::Nodes::NonNullType
16
16
  ast_inner_type = ast_node.of_type
17
- inner_type = build_type(types, ast_inner_type)
18
- wrap_type(inner_type, GraphQL::NonNullType)
17
+ inner_type = build_type(type_owner, ast_inner_type)
18
+ wrap_type(inner_type, :to_non_null_type)
19
19
  when GraphQL::Language::Nodes::ListType
20
20
  ast_inner_type = ast_node.of_type
21
- inner_type = build_type(types, ast_inner_type)
22
- wrap_type(inner_type, GraphQL::ListType)
21
+ inner_type = build_type(type_owner, ast_inner_type)
22
+ wrap_type(inner_type, :to_list_type)
23
+ else
24
+ raise "Invariant: unexpected type from ast: #{ast_node.inspect}"
23
25
  end
24
26
  end
25
27
 
26
- def self.wrap_type(type, wrapper)
27
- if type.nil?
28
- nil
29
- else
30
- wrapper.new(of_type: type)
28
+ class << self
29
+ private
30
+
31
+ def wrap_type(type, wrapper_method)
32
+ if type.nil?
33
+ nil
34
+ elsif wrapper_method == :to_list_type || wrapper_method == :to_non_null_type
35
+ type.public_send(wrapper_method)
36
+ else
37
+ raise ArgumentError, "Unexpected wrapper method: #{wrapper_method.inspect}"
38
+ end
31
39
  end
32
40
  end
33
41
  end
@@ -8,7 +8,7 @@ module GraphQL
8
8
  # TODO: Not yet implemented for interfaces.
9
9
  class TypeMembership
10
10
  # @return [Class<GraphQL::Schema::Object>]
11
- attr_reader :object_type
11
+ attr_accessor :object_type
12
12
 
13
13
  # @return [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
14
14
  attr_reader :abstract_type
@@ -19,7 +19,7 @@ module GraphQL
19
19
  # @param abstract_type [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
20
20
  # @param object_type [Class<GraphQL::Schema::Object>]
21
21
  # @param options [Hash] Any options passed to `.possible_types` or `.implements`
22
- def initialize(abstract_type, object_type, options)
22
+ def initialize(abstract_type, object_type, **options)
23
23
  @abstract_type = abstract_type
24
24
  @object_type = object_type
25
25
  @options = options
@@ -3,12 +3,19 @@ module GraphQL
3
3
  class Schema
4
4
  class Union < GraphQL::Schema::Member
5
5
  extend GraphQL::Schema::Member::AcceptsDefinition
6
+ extend GraphQL::Schema::Member::HasUnresolvedTypeError
6
7
 
7
8
  class << self
9
+ def inherited(child_class)
10
+ add_unresolved_type_error(child_class)
11
+ super
12
+ end
13
+
8
14
  def possible_types(*types, context: GraphQL::Query::NullContext, **options)
9
15
  if types.any?
10
16
  types.each do |t|
11
- type_memberships << type_membership_class.new(self, t, options)
17
+ assert_valid_union_member(t)
18
+ type_memberships << type_membership_class.new(self, t, **options)
12
19
  end
13
20
  else
14
21
  visible_types = []
@@ -25,6 +32,7 @@ module GraphQL
25
32
  type_defn = GraphQL::UnionType.new
26
33
  type_defn.name = graphql_name
27
34
  type_defn.description = description
35
+ type_defn.ast_node = ast_node
28
36
  type_defn.type_memberships = type_memberships
29
37
  if respond_to?(:resolve_type)
30
38
  type_defn.resolve_type = method(:resolve_type)
@@ -45,11 +53,37 @@ module GraphQL
45
53
  GraphQL::TypeKinds::UNION
46
54
  end
47
55
 
48
- private
49
-
50
56
  def type_memberships
51
57
  @type_memberships ||= []
52
58
  end
59
+
60
+ # Update a type membership whose `.object_type` is a string or late-bound type
61
+ # so that the type membership's `.object_type` is the given `object_type`.
62
+ # (This is used for updating the union after the schema as lazily loaded the union member.)
63
+ # @api private
64
+ def assign_type_membership_object_type(object_type)
65
+ assert_valid_union_member(object_type)
66
+ type_memberships.each { |tm|
67
+ possible_type = tm.object_type
68
+ if possible_type.is_a?(String) && (possible_type == object_type.name)
69
+ # This is a match of Ruby class names, not graphql names,
70
+ # since strings are used to refer to constants.
71
+ tm.object_type = object_type
72
+ elsif possible_type.is_a?(LateBoundType) && possible_type.graphql_name == object_type.graphql_name
73
+ tm.object_type = object_type
74
+ end
75
+ }
76
+ nil
77
+ end
78
+
79
+ private
80
+
81
+ def assert_valid_union_member(type_defn)
82
+ if type_defn.is_a?(Module) && !type_defn.is_a?(Class)
83
+ # it's an interface type, defined as a module
84
+ raise ArgumentError, "Union possible_types can only be object types (not interface types), remove #{type_defn.graphql_name} (#{type_defn.inspect})"
85
+ end
86
+ end
53
87
  end
54
88
  end
55
89
  end
@@ -27,8 +27,7 @@ module GraphQL
27
27
  # @param node_id [String] A unique ID generated by {.encode}
28
28
  # @return [Array<(String, String)>] The type name & value passed to {.encode}
29
29
  def decode(node_id, separator: self.default_id_separator)
30
- # urlsafe_decode64 is for forward compatibility
31
- Base64Bp.urlsafe_decode64(node_id).split(separator, 2)
30
+ GraphQL::Schema::Base64Encoder.decode(node_id).split(separator, 2)
32
31
  end
33
32
  end
34
33
  end
@@ -133,9 +133,15 @@ module GraphQL
133
133
  end
134
134
  }
135
135
 
136
+ DEPRECATED_ARGUMENTS_ARE_OPTIONAL = ->(argument) {
137
+ if argument.deprecation_reason && argument.type.non_null?
138
+ "must be optional because it's deprecated"
139
+ end
140
+ }
141
+
136
142
  TYPE_IS_VALID_INPUT_TYPE = ->(type) {
137
143
  outer_type = type.type
138
- inner_type = outer_type.is_a?(GraphQL::BaseType) ? outer_type.unwrap : nil
144
+ inner_type = outer_type.respond_to?(:unwrap) ? outer_type.unwrap : nil
139
145
 
140
146
  case inner_type
141
147
  when GraphQL::ScalarType, GraphQL::InputObjectType, GraphQL::EnumType
@@ -154,7 +160,7 @@ module GraphQL
154
160
  }
155
161
 
156
162
  SCHEMA_CAN_FETCH_IDS = ->(schema) {
157
- has_node_field = schema.query && schema.query.all_fields.any?(&:relay_node_field)
163
+ has_node_field = schema.query && schema.query.fields.each_value.any?(&:relay_node_field)
158
164
  if has_node_field && schema.object_from_id_proc.nil?
159
165
  "schema contains `node(id:...)` field, so you must define a `object_from_id -> (id, ctx) { ... }` function"
160
166
  else
@@ -265,8 +271,10 @@ module GraphQL
265
271
  Rules::NAME_IS_STRING,
266
272
  Rules::RESERVED_NAME,
267
273
  Rules::DESCRIPTION_IS_STRING_OR_NIL,
274
+ Rules.assert_property(:deprecation_reason, String, NilClass),
268
275
  Rules::TYPE_IS_VALID_INPUT_TYPE,
269
276
  Rules::DEFAULT_VALUE_IS_VALID_FOR_TYPE,
277
+ Rules::DEPRECATED_ARGUMENTS_ARE_OPTIONAL,
270
278
  ],
271
279
  GraphQL::BaseType => [
272
280
  Rules::NAME_IS_STRING,
@@ -40,22 +40,33 @@ module GraphQL
40
40
  # @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
41
41
  # @param context [GraphQL::Query::Context]
42
42
  # @param schema [GraphQL::Schema]
43
- # @param deep_check [Boolean]
44
43
  def initialize(filter, context:, schema:)
45
- @schema = schema
44
+ @schema = schema.interpreter? ? schema : schema.graphql_definition
45
+ # Cache these to avoid repeated hits to the inheritance chain when one isn't present
46
+ @query = @schema.query
47
+ @mutation = @schema.mutation
48
+ @subscription = @schema.subscription
46
49
  @context = context
47
- @visibility_cache = read_through { |m| filter.call(m, @context) }
50
+ @visibility_cache = read_through { |m| filter.call(m, context) }
48
51
  end
49
52
 
50
- # @return [Array<GraphQL::BaseType>] Visible types in the schema
53
+ # @return [Hash<String, GraphQL::BaseType>] Visible types in the schema
51
54
  def types
52
- @types ||= @schema.types.each_value.select { |t| visible_type?(t) }
55
+ @types ||= begin
56
+ vis_types = {}
57
+ @schema.types.each do |n, t|
58
+ if visible_type?(t)
59
+ vis_types[n] = t
60
+ end
61
+ end
62
+ vis_types
63
+ end
53
64
  end
54
65
 
55
66
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
56
67
  def get_type(type_name)
57
68
  @visible_types ||= read_through do |name|
58
- type_defn = @schema.types.fetch(name, nil)
69
+ type_defn = @schema.get_type(name)
59
70
  if type_defn && visible_type?(type_defn)
60
71
  type_defn
61
72
  else
@@ -79,11 +90,10 @@ module GraphQL
79
90
 
80
91
  # @return [GraphQL::Field, nil] The field named `field_name` on `parent_type`, if it exists
81
92
  def get_field(parent_type, field_name)
82
-
83
93
  @visible_parent_fields ||= read_through do |type|
84
94
  read_through do |f_name|
85
95
  field_defn = @schema.get_field(type, f_name)
86
- if field_defn && visible_field?(field_defn)
96
+ if field_defn && visible_field?(type, field_defn)
87
97
  field_defn
88
98
  else
89
99
  nil
@@ -94,23 +104,32 @@ module GraphQL
94
104
  @visible_parent_fields[parent_type][field_name]
95
105
  end
96
106
 
107
+ # @return [GraphQL::Argument, nil] The argument named `argument_name` on `parent_type`, if it exists and is visible
108
+ def get_argument(parent_type, argument_name)
109
+ argument = parent_type.get_argument(argument_name)
110
+ return argument if argument && visible_argument?(argument)
111
+ end
112
+
97
113
  # @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
98
114
  def possible_types(type_defn)
99
- @visible_possible_types ||= read_through { |type_defn| @schema.possible_types(type_defn, @context).select { |t| visible_type?(t) } }
115
+ @visible_possible_types ||= read_through { |type_defn|
116
+ pt = @schema.possible_types(type_defn, @context)
117
+ pt.select { |t| visible_type?(t) }
118
+ }
100
119
  @visible_possible_types[type_defn]
101
120
  end
102
121
 
103
122
  # @param type_defn [GraphQL::ObjectType, GraphQL::InterfaceType]
104
123
  # @return [Array<GraphQL::Field>] Fields on `type_defn`
105
124
  def fields(type_defn)
106
- @visible_fields ||= read_through { |t| @schema.get_fields(t).each_value.select { |f| visible_field?(f) } }
125
+ @visible_fields ||= read_through { |t| @schema.get_fields(t).each_value.select { |f| visible_field?(t, f) } }
107
126
  @visible_fields[type_defn]
108
127
  end
109
128
 
110
129
  # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
111
130
  # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
112
131
  def arguments(argument_owner)
113
- @visible_arguments ||= read_through { |o| o.arguments.each_value.select { |a| visible_field?(a) } }
132
+ @visible_arguments ||= read_through { |o| o.arguments.each_value.select { |a| visible_argument?(a) } }
114
133
  @visible_arguments[argument_owner]
115
134
  end
116
135
 
@@ -122,7 +141,7 @@ module GraphQL
122
141
 
123
142
  # @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
124
143
  def interfaces(obj_type)
125
- @visible_interfaces ||= read_through { |t| t.interfaces.select { |i| visible?(i) } }
144
+ @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible?(i) } }
126
145
  @visible_interfaces[obj_type]
127
146
  end
128
147
 
@@ -146,33 +165,88 @@ module GraphQL
146
165
  @unions[obj_type]
147
166
  end
148
167
 
149
- def visible_field?(field_defn)
150
- visible?(field_defn) && visible_type?(field_defn.type.unwrap)
168
+ def visible_argument?(arg_defn)
169
+ visible?(arg_defn) && visible_type?(arg_defn.type.unwrap)
151
170
  end
152
171
 
153
- def visible_type?(type_defn)
154
- return false unless visible?(type_defn)
155
- return true if root_type?(type_defn)
156
- return true if type_defn.introspection?
157
-
158
- if type_defn.kind.union?
159
- visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
160
- elsif type_defn.kind.interface?
161
- visible_possible_types?(type_defn)
172
+ def visible_field?(owner_type, field_defn)
173
+ # This field is visible in its own right
174
+ visible?(field_defn) &&
175
+ # This field's return type is visible
176
+ visible_type?(field_defn.type.unwrap) &&
177
+ # This field is either defined on this object type,
178
+ # 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))
180
+ end
181
+
182
+ # We need this to tell whether a field was inherited by an interface
183
+ # even when that interface is hidden from `#interfaces`
184
+ def unfiltered_interfaces(type_defn)
185
+ @unfiltered_interfaces ||= read_through(&:interfaces)
186
+ @unfiltered_interfaces[type_defn]
187
+ end
188
+
189
+ # If this field was inherited from an interface, and the field on that interface is _hidden_,
190
+ # then treat this inherited field as hidden.
191
+ # (If it _wasn't_ inherited, then don't hide it for this reason.)
192
+ def field_on_visible_interface?(field_defn, type_defn)
193
+ if type_defn.kind.object?
194
+ any_interface_has_field = false
195
+ any_interface_has_visible_field = false
196
+ ints = unfiltered_interfaces(type_defn)
197
+ ints.each do |interface_type|
198
+ if (iface_field_defn = interface_type.get_field(field_defn.graphql_name))
199
+ any_interface_has_field = true
200
+
201
+ if interfaces(type_defn).include?(interface_type) && visible_field?(interface_type, iface_field_defn)
202
+ any_interface_has_visible_field = true
203
+ end
204
+ end
205
+ end
206
+
207
+ if any_interface_has_field
208
+ any_interface_has_visible_field
209
+ else
210
+ # it's the object's own field
211
+ true
212
+ end
162
213
  else
163
- referenced?(type_defn) || visible_abstract_type?(type_defn)
214
+ true
215
+ end
216
+ end
217
+
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
164
230
  end
231
+
232
+ @type_visibility[type_defn]
165
233
  end
166
234
 
167
235
  def root_type?(type_defn)
168
- @schema.root_types.include?(type_defn)
236
+ @query == type_defn ||
237
+ @mutation == type_defn ||
238
+ @subscription == type_defn
169
239
  end
170
240
 
171
241
  def referenced?(type_defn)
172
- members = @schema.references_to(type_defn.unwrap.name)
242
+ @references_to ||= @schema.references_to
243
+ graphql_name = type_defn.unwrap.graphql_name
244
+ members = @references_to[graphql_name] || NO_REFERENCES
173
245
  members.any? { |m| visible?(m) }
174
246
  end
175
247
 
248
+ NO_REFERENCES = [].freeze
249
+
176
250
  def orphan_type?(type_defn)
177
251
  @schema.orphan_types.include?(type_defn)
178
252
  end
@@ -185,7 +259,7 @@ module GraphQL
185
259
  end
186
260
 
187
261
  def visible_possible_types?(type_defn)
188
- @schema.possible_types(type_defn, @context).any? { |t| visible_type?(t) }
262
+ possible_types(type_defn).any? { |t| visible_type?(t) }
189
263
  end
190
264
 
191
265
  def visible?(member)
@@ -206,9 +280,21 @@ module GraphQL
206
280
  root_type = root_type_for_operation(op_name)
207
281
  unvisited_types << root_type if root_type
208
282
  end
209
- unvisited_types.concat(@schema.introspection_system.object_types)
283
+ unvisited_types.concat(@schema.introspection_system.types.values)
284
+
285
+ directives.each do |dir_class|
286
+ dir_class.arguments.values.each do |arg_defn|
287
+ arg_t = arg_defn.type.unwrap
288
+ if get_type(arg_t.graphql_name)
289
+ unvisited_types << arg_t
290
+ end
291
+ end
292
+ end
293
+
210
294
  @schema.orphan_types.each do |orphan_type|
211
- unvisited_types << orphan_type.graphql_definition if get_type(orphan_type.graphql_name)
295
+ if get_type(orphan_type.graphql_name)
296
+ unvisited_types << orphan_type
297
+ end
212
298
  end
213
299
 
214
300
  until unvisited_types.empty?