graphql 2.2.17 → 2.5.16

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.
Files changed (240) 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_generator.rb +46 -0
  4. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  5. data/lib/generators/graphql/templates/base_resolver.erb +2 -0
  6. data/lib/generators/graphql/templates/schema.erb +3 -0
  7. data/lib/generators/graphql/type_generator.rb +1 -1
  8. data/lib/graphql/analysis/analyzer.rb +90 -0
  9. data/lib/graphql/analysis/field_usage.rb +82 -0
  10. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  11. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  12. data/lib/graphql/analysis/query_complexity.rb +263 -0
  13. data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
  14. data/lib/graphql/analysis/visitor.rb +280 -0
  15. data/lib/graphql/analysis.rb +95 -1
  16. data/lib/graphql/autoload.rb +38 -0
  17. data/lib/graphql/backtrace/table.rb +118 -55
  18. data/lib/graphql/backtrace.rb +1 -19
  19. data/lib/graphql/current.rb +57 -0
  20. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  21. data/lib/graphql/dashboard/installable.rb +22 -0
  22. data/lib/graphql/dashboard/limiters.rb +93 -0
  23. data/lib/graphql/dashboard/operation_store.rb +199 -0
  24. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  25. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  26. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  27. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  28. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  29. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  30. data/lib/graphql/dashboard/statics/icon.png +0 -0
  31. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  36. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  37. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  38. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  39. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  40. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  41. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  42. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  43. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  44. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  45. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  46. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  47. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  48. data/lib/graphql/dashboard.rb +158 -0
  49. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  50. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  51. data/lib/graphql/dataloader/async_dataloader.rb +46 -19
  52. data/lib/graphql/dataloader/null_dataloader.rb +51 -10
  53. data/lib/graphql/dataloader/source.rb +20 -9
  54. data/lib/graphql/dataloader.rb +153 -45
  55. data/lib/graphql/date_encoding_error.rb +1 -1
  56. data/lib/graphql/dig.rb +2 -1
  57. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  58. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  59. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  60. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +63 -5
  61. data/lib/graphql/execution/interpreter/runtime.rb +321 -222
  62. data/lib/graphql/execution/interpreter.rb +23 -30
  63. data/lib/graphql/execution/lookahead.rb +18 -11
  64. data/lib/graphql/execution/multiplex.rb +6 -5
  65. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  66. data/lib/graphql/introspection/directive_type.rb +1 -1
  67. data/lib/graphql/introspection/entry_points.rb +2 -2
  68. data/lib/graphql/introspection/field_type.rb +1 -1
  69. data/lib/graphql/introspection/schema_type.rb +6 -11
  70. data/lib/graphql/introspection/type_type.rb +5 -5
  71. data/lib/graphql/invalid_name_error.rb +1 -1
  72. data/lib/graphql/invalid_null_error.rb +20 -17
  73. data/lib/graphql/language/cache.rb +13 -0
  74. data/lib/graphql/language/comment.rb +18 -0
  75. data/lib/graphql/language/document_from_schema_definition.rb +64 -35
  76. data/lib/graphql/language/lexer.rb +72 -42
  77. data/lib/graphql/language/nodes.rb +93 -52
  78. data/lib/graphql/language/parser.rb +168 -61
  79. data/lib/graphql/language/printer.rb +31 -15
  80. data/lib/graphql/language/sanitized_printer.rb +1 -1
  81. data/lib/graphql/language.rb +61 -1
  82. data/lib/graphql/pagination/connection.rb +1 -1
  83. data/lib/graphql/query/context/scoped_context.rb +1 -1
  84. data/lib/graphql/query/context.rb +46 -47
  85. data/lib/graphql/query/null_context.rb +3 -5
  86. data/lib/graphql/query/partial.rb +179 -0
  87. data/lib/graphql/query/validation_pipeline.rb +2 -2
  88. data/lib/graphql/query/variable_validation_error.rb +1 -1
  89. data/lib/graphql/query.rb +123 -69
  90. data/lib/graphql/railtie.rb +7 -0
  91. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  92. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  93. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  94. data/lib/graphql/rubocop.rb +2 -0
  95. data/lib/graphql/schema/addition.rb +26 -13
  96. data/lib/graphql/schema/always_visible.rb +7 -2
  97. data/lib/graphql/schema/argument.rb +57 -8
  98. data/lib/graphql/schema/build_from_definition.rb +116 -49
  99. data/lib/graphql/schema/directive/flagged.rb +4 -2
  100. data/lib/graphql/schema/directive.rb +54 -2
  101. data/lib/graphql/schema/enum.rb +107 -24
  102. data/lib/graphql/schema/enum_value.rb +10 -2
  103. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  104. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  105. data/lib/graphql/schema/field.rb +134 -45
  106. data/lib/graphql/schema/field_extension.rb +1 -1
  107. data/lib/graphql/schema/has_single_input_argument.rb +6 -2
  108. data/lib/graphql/schema/input_object.rb +122 -64
  109. data/lib/graphql/schema/interface.rb +23 -5
  110. data/lib/graphql/schema/introspection_system.rb +6 -17
  111. data/lib/graphql/schema/late_bound_type.rb +4 -0
  112. data/lib/graphql/schema/list.rb +3 -3
  113. data/lib/graphql/schema/loader.rb +3 -2
  114. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
  115. data/lib/graphql/schema/member/has_arguments.rb +44 -58
  116. data/lib/graphql/schema/member/has_dataloader.rb +62 -0
  117. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  118. data/lib/graphql/schema/member/has_directives.rb +4 -4
  119. data/lib/graphql/schema/member/has_fields.rb +26 -6
  120. data/lib/graphql/schema/member/has_interfaces.rb +6 -6
  121. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  122. data/lib/graphql/schema/member/has_validators.rb +1 -1
  123. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  124. data/lib/graphql/schema/member/type_system_helpers.rb +17 -4
  125. data/lib/graphql/schema/member.rb +1 -0
  126. data/lib/graphql/schema/mutation.rb +7 -0
  127. data/lib/graphql/schema/object.rb +25 -8
  128. data/lib/graphql/schema/printer.rb +1 -0
  129. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  130. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  131. data/lib/graphql/schema/resolver.rb +29 -23
  132. data/lib/graphql/schema/scalar.rb +1 -6
  133. data/lib/graphql/schema/subscription.rb +52 -6
  134. data/lib/graphql/schema/timeout.rb +19 -2
  135. data/lib/graphql/schema/type_expression.rb +2 -2
  136. data/lib/graphql/schema/union.rb +1 -1
  137. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  138. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  139. data/lib/graphql/schema/validator.rb +3 -1
  140. data/lib/graphql/schema/visibility/migration.rb +188 -0
  141. data/lib/graphql/schema/visibility/profile.rb +445 -0
  142. data/lib/graphql/schema/visibility/visit.rb +190 -0
  143. data/lib/graphql/schema/visibility.rb +311 -0
  144. data/lib/graphql/schema/warden.rb +190 -20
  145. data/lib/graphql/schema.rb +695 -167
  146. data/lib/graphql/static_validation/all_rules.rb +2 -2
  147. data/lib/graphql/static_validation/base_visitor.rb +6 -5
  148. data/lib/graphql/static_validation/literal_validator.rb +4 -4
  149. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  150. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  151. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  152. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  153. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  154. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  155. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  156. data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
  157. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  158. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  159. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  160. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  161. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  162. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  163. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  164. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  165. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  166. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  167. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  168. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  169. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  170. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  171. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  172. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  173. data/lib/graphql/static_validation/validation_context.rb +18 -2
  174. data/lib/graphql/static_validation/validator.rb +6 -1
  175. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +5 -3
  176. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  177. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  178. data/lib/graphql/subscriptions/event.rb +13 -2
  179. data/lib/graphql/subscriptions/serialize.rb +1 -1
  180. data/lib/graphql/subscriptions.rb +7 -5
  181. data/lib/graphql/testing/helpers.rb +48 -16
  182. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  183. data/lib/graphql/testing.rb +1 -0
  184. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  185. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  186. data/lib/graphql/tracing/appoptics_trace.rb +5 -1
  187. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  188. data/lib/graphql/tracing/appsignal_trace.rb +32 -59
  189. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  190. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  191. data/lib/graphql/tracing/data_dog_trace.rb +46 -162
  192. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  193. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  194. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  195. data/lib/graphql/tracing/detailed_trace.rb +141 -0
  196. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  197. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  198. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  199. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  200. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  201. data/lib/graphql/tracing/notifications_trace.rb +183 -37
  202. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  203. data/lib/graphql/tracing/null_trace.rb +9 -0
  204. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  205. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  206. data/lib/graphql/tracing/perfetto_trace.rb +818 -0
  207. data/lib/graphql/tracing/platform_tracing.rb +1 -1
  208. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  209. data/lib/graphql/tracing/prometheus_trace.rb +73 -73
  210. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  211. data/lib/graphql/tracing/scout_trace.rb +32 -58
  212. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  213. data/lib/graphql/tracing/sentry_trace.rb +64 -98
  214. data/lib/graphql/tracing/statsd_trace.rb +33 -45
  215. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  216. data/lib/graphql/tracing/trace.rb +111 -1
  217. data/lib/graphql/tracing.rb +31 -30
  218. data/lib/graphql/type_kinds.rb +2 -1
  219. data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
  220. data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
  221. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  222. data/lib/graphql/types.rb +18 -11
  223. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  224. data/lib/graphql/version.rb +1 -1
  225. data/lib/graphql.rb +64 -54
  226. metadata +197 -22
  227. data/lib/graphql/analysis/ast/analyzer.rb +0 -91
  228. data/lib/graphql/analysis/ast/field_usage.rb +0 -82
  229. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  230. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  231. data/lib/graphql/analysis/ast/query_complexity.rb +0 -182
  232. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  233. data/lib/graphql/analysis/ast.rb +0 -94
  234. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  235. data/lib/graphql/backtrace/trace.rb +0 -93
  236. data/lib/graphql/backtrace/tracer.rb +0 -80
  237. data/lib/graphql/language/token.rb +0 -34
  238. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  239. data/lib/graphql/schema/null_mask.rb +0 -11
  240. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -26,7 +26,7 @@ module GraphQL
26
26
  context.directive_definition || context.field_definition
27
27
  end
28
28
 
29
- parent_type = context.warden.get_argument(defn, parent_name(parent, defn))
29
+ parent_type = context.types.argument(defn, parent_name(parent, defn))
30
30
  parent_type ? parent_type.type.unwrap : nil
31
31
  end
32
32
 
@@ -34,7 +34,7 @@ module GraphQL
34
34
  parent_type = get_parent_type(context, parent)
35
35
  return unless parent_type && parent_type.kind.input_object?
36
36
 
37
- required_fields = context.warden.arguments(parent_type)
37
+ required_fields = context.types.arguments(parent_type)
38
38
  .select{ |arg| arg.type.kind.non_null? && !arg.default_value? }
39
39
  .map!(&:graphql_name)
40
40
 
@@ -43,7 +43,7 @@ module GraphQL
43
43
 
44
44
  missing_fields.each do |missing_field|
45
45
  path = [*context.path, missing_field]
46
- missing_field_type = context.warden.get_argument(parent_type, missing_field).type
46
+ missing_field_type = context.types.argument(parent_type, missing_field).type
47
47
  add_error(RequiredInputObjectAttributesArePresentError.new(
48
48
  "Argument '#{missing_field}' on InputObject '#{parent_type.to_type_signature}' is required. Expected type #{missing_field_type.to_type_signature}",
49
49
  argument_name: missing_field,
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ module SubscriptionRootExistsAndSingleSubscriptionSelection
5
+ def on_operation_definition(node, parent)
6
+ if node.operation_type == "subscription"
7
+ if context.types.subscription_root.nil?
8
+ add_error(GraphQL::StaticValidation::SubscriptionRootExistsError.new(
9
+ 'Schema is not configured for subscriptions',
10
+ nodes: node
11
+ ))
12
+ elsif node.selections.size != 1
13
+ add_error(GraphQL::StaticValidation::NotSingleSubscriptionError.new(
14
+ 'A subscription operation may only have one selection',
15
+ nodes: node,
16
+ ))
17
+ else
18
+ super
19
+ end
20
+ else
21
+ super
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -19,13 +19,17 @@ module GraphQL
19
19
  :on_field,
20
20
  ]
21
21
 
22
- DIRECTIVE_NODE_HOOKS.each do |method_name|
23
- define_method(method_name) do |node, parent|
24
- if node.directives.any?
22
+ VALIDATE_DIRECTIVE_LOCATION_ON_NODE = <<~RUBY
23
+ def %{method_name}(node, parent)
24
+ if !node.directives.empty?
25
25
  validate_directive_location(node)
26
26
  end
27
27
  super(node, parent)
28
28
  end
29
+ RUBY
30
+ DIRECTIVE_NODE_HOOKS.each do |method_name|
31
+ # Can't use `define_method {...}` here because the proc can't be isolated for use in non-main Ractors
32
+ module_eval(VALIDATE_DIRECTIVE_LOCATION_ON_NODE % { method_name: method_name }) # rubocop:disable Development/NoEvalCop
29
33
  end
30
34
 
31
35
  private
@@ -5,36 +5,27 @@ module GraphQL
5
5
  def on_variable_definition(node, parent)
6
6
  if !node.default_value.nil?
7
7
  value = node.default_value
8
- if node.type.is_a?(GraphQL::Language::Nodes::NonNullType)
9
- add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new(
10
- "Non-null variable $#{node.name} can't have a default value",
11
- nodes: node,
12
- name: node.name,
13
- error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_ON_NON_NULL]
14
- ))
8
+ type = context.schema.type_from_ast(node.type, context: context)
9
+ if type.nil?
10
+ # This is handled by another validator
15
11
  else
16
- type = context.schema.type_from_ast(node.type, context: context)
17
- if type.nil?
18
- # This is handled by another validator
19
- else
20
- validation_result = context.validate_literal(value, type)
12
+ validation_result = context.validate_literal(value, type)
21
13
 
22
- if !validation_result.valid?
23
- problems = validation_result.problems
24
- first_problem = problems && problems.first
25
- if first_problem
26
- error_message = first_problem["explanation"]
27
- end
28
-
29
- error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
30
- add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new(
31
- error_message,
32
- nodes: node,
33
- name: node.name,
34
- type: type.to_type_signature,
35
- error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_TYPE],
36
- ))
14
+ if !validation_result.valid?
15
+ problems = validation_result.problems
16
+ first_problem = problems && problems.first
17
+ if first_problem
18
+ error_message = first_problem["explanation"]
37
19
  end
20
+
21
+ error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
22
+ add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new(
23
+ error_message,
24
+ nodes: node,
25
+ name: node.name,
26
+ type: type.to_type_signature,
27
+ error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_TYPE],
28
+ ))
38
29
  end
39
30
  end
40
31
  end
@@ -4,7 +4,7 @@ module GraphQL
4
4
  module VariableNamesAreUnique
5
5
  def on_operation_definition(node, parent)
6
6
  var_defns = node.variables
7
- if var_defns.any?
7
+ if !var_defns.empty?
8
8
  vars_by_name = Hash.new { |h, k| h[k] = [] }
9
9
  var_defns.each { |v| vars_by_name[v.name] << v }
10
10
  vars_by_name.each do |name, defns|
@@ -21,7 +21,7 @@ module GraphQL
21
21
  end
22
22
  node_values = node_values.select { |value| value.is_a? GraphQL::Language::Nodes::VariableIdentifier }
23
23
 
24
- if node_values.any?
24
+ if !node_values.empty?
25
25
  argument_owner = case parent
26
26
  when GraphQL::Language::Nodes::Field
27
27
  context.field_definition
@@ -65,7 +65,7 @@ module GraphQL
65
65
  end
66
66
  end
67
67
 
68
- arg_defn = context.warden.get_argument(argument_owner, arg_node.name)
68
+ arg_defn = @types.argument(argument_owner, arg_node.name)
69
69
  arg_defn_type = arg_defn.type
70
70
 
71
71
  # If the argument is non-null, but it was given a default value,
@@ -4,11 +4,20 @@ module GraphQL
4
4
  module VariablesAreInputTypes
5
5
  def on_variable_definition(node, parent)
6
6
  type_name = get_type_name(node.type)
7
- type = context.warden.get_type(type_name)
7
+ type = context.query.types.type(type_name)
8
8
 
9
9
  if type.nil?
10
+ @all_possible_input_type_names ||= begin
11
+ names = []
12
+ context.types.all_types.each { |(t)|
13
+ if t.kind.input?
14
+ names << t.graphql_name
15
+ end
16
+ }
17
+ names
18
+ end
10
19
  add_error(GraphQL::StaticValidation::VariablesAreInputTypesError.new(
11
- "#{type_name} isn't a defined input type (on $#{node.name})",
20
+ "#{type_name} isn't a defined input type (on $#{node.name})#{context.did_you_mean_suggestion(type_name, @all_possible_input_type_names)}",
12
21
  nodes: node,
13
22
  name: node.name,
14
23
  type: type_name
@@ -13,14 +13,14 @@ module GraphQL
13
13
 
14
14
  attr_reader :query, :errors, :visitor,
15
15
  :on_dependency_resolve_handlers,
16
- :max_errors, :warden, :schema
16
+ :max_errors, :types, :schema
17
17
 
18
18
 
19
19
  def_delegators :@query, :document, :fragments, :operations
20
20
 
21
21
  def initialize(query, visitor_class, max_errors)
22
22
  @query = query
23
- @warden = query.warden
23
+ @types = query.types # TODO update migrated callers to use this accessor
24
24
  @schema = query.schema
25
25
  @literal_validator = LiteralValidator.new(context: query.context)
26
26
  @errors = []
@@ -29,6 +29,7 @@ module GraphQL
29
29
  @visitor = visitor_class.new(document, self)
30
30
  end
31
31
 
32
+ # TODO stop using def_delegators because of Array allocations
32
33
  def_delegators :@visitor,
33
34
  :path, :type_definition, :field_definition, :argument_definition,
34
35
  :parent_type_definition, :directive_definition, :object_types, :dependencies
@@ -48,6 +49,21 @@ module GraphQL
48
49
  def schema_directives
49
50
  @schema_directives ||= schema.directives
50
51
  end
52
+
53
+ def did_you_mean_suggestion(name, options)
54
+ if did_you_mean = schema.did_you_mean
55
+ suggestions = did_you_mean::SpellChecker.new(dictionary: options).correct(name)
56
+ case suggestions.size
57
+ when 0
58
+ ""
59
+ when 1
60
+ " (Did you mean `#{suggestions.first}`?)"
61
+ else
62
+ last_sugg = suggestions.pop
63
+ " (Did you mean #{suggestions.map {|s| "`#{s}`"}.join(", ")} or `#{last_sugg}`?)"
64
+ end
65
+ end
66
+ end
51
67
  end
52
68
  end
53
69
  end
@@ -27,6 +27,8 @@ module GraphQL
27
27
  # @param max_errors [Integer] Maximum number of errors before aborting validation. Any positive number will limit the number of errors. Defaults to nil for no limit.
28
28
  # @return [Array<Hash>]
29
29
  def validate(query, validate: true, timeout: nil, max_errors: nil)
30
+ errors = nil
31
+ query.current_trace.begin_validate(query, validate)
30
32
  query.current_trace.validate(validate: validate, query: query) do
31
33
  begin_t = Time.now
32
34
  errors = if validate == false
@@ -58,10 +60,13 @@ module GraphQL
58
60
  }
59
61
  end
60
62
  rescue GraphQL::ExecutionError => e
63
+ errors = [e]
61
64
  {
62
65
  remaining_timeout: nil,
63
- errors: [e],
66
+ errors: errors,
64
67
  }
68
+ ensure
69
+ query.current_trace.end_validate(query, validate, errors)
65
70
  end
66
71
 
67
72
  # Invoked when static validation times out.
@@ -81,6 +81,7 @@ module GraphQL
81
81
  # end
82
82
  # end
83
83
  #
84
+ # @see GraphQL::Testing::MockActionCable for test helpers
84
85
  class ActionCableSubscriptions < GraphQL::Subscriptions
85
86
  SUBSCRIPTION_PREFIX = "graphql-subscription:"
86
87
  EVENT_PREFIX = "graphql-event:"
@@ -107,7 +108,7 @@ module GraphQL
107
108
  when 2
108
109
  true
109
110
  else
110
- raise ArgumentError, "#{@serializer} must repond to `.load` accepting one or two arguments"
111
+ raise ArgumentError, "#{@serializer} must respond to `.load` accepting one or two arguments"
111
112
  end
112
113
  @transmit_ns = namespace
113
114
  super
@@ -166,11 +167,12 @@ module GraphQL
166
167
  #
167
168
  def setup_stream(channel, initial_event)
168
169
  topic = initial_event.topic
169
- channel.stream_from(stream_event_name(initial_event), coder: @action_cable_coder) do |message|
170
+ event_stream = stream_event_name(initial_event)
171
+ channel.stream_from(event_stream, coder: @action_cable_coder) do |message|
170
172
  events_by_fingerprint = @events[topic]
171
173
  object = nil
172
174
  events_by_fingerprint.each do |_fingerprint, events|
173
- if events.any? && events.first == initial_event
175
+ if !events.empty? && events.first == initial_event
174
176
  # The fingerprint has told us that this response should be shared by all subscribers,
175
177
  # so just run it once, then deliver the result to every subscriber
176
178
  first_event = events.first
@@ -9,7 +9,7 @@ module GraphQL
9
9
  # Assign the result to `context.namespace(:subscriptions)[:subscription_broadcastable]`
10
10
  # @api private
11
11
  # @see Subscriptions#broadcastable? for a public API
12
- class BroadcastAnalyzer < GraphQL::Analysis::AST::Analyzer
12
+ class BroadcastAnalyzer < GraphQL::Analysis::Analyzer
13
13
  def initialize(subject)
14
14
  super
15
15
  @default_broadcastable = subject.schema.subscriptions.default_broadcastable
@@ -28,9 +28,8 @@ module GraphQL
28
28
  end
29
29
 
30
30
  current_field = visitor.field_definition
31
- apply_broadcastable(current_field)
32
-
33
31
  current_type = visitor.parent_type_definition
32
+ apply_broadcastable(current_type, current_field)
34
33
  if current_type.kind.interface?
35
34
  pt = @query.possible_types(current_type)
36
35
  pt.each do |object_type|
@@ -38,7 +37,7 @@ module GraphQL
38
37
  # Inherited fields would be exactly the same object;
39
38
  # only check fields that are overrides of the inherited one
40
39
  if ot_field && ot_field != current_field
41
- apply_broadcastable(ot_field)
40
+ apply_broadcastable(object_type, ot_field)
42
41
  end
43
42
  end
44
43
  end
@@ -55,10 +54,16 @@ module GraphQL
55
54
  private
56
55
 
57
56
  # Modify `@subscription_broadcastable` based on `field_defn`'s configuration (and/or the default value)
58
- def apply_broadcastable(field_defn)
57
+ def apply_broadcastable(owner_type, field_defn)
59
58
  current_field_broadcastable = field_defn.introspection? || field_defn.broadcastable?
59
+
60
+ if current_field_broadcastable.nil? && owner_type.respond_to?(:default_broadcastable?)
61
+ current_field_broadcastable = owner_type.default_broadcastable?
62
+ end
63
+
60
64
  case current_field_broadcastable
61
65
  when nil
66
+ query.logger.debug { "`broadcastable: nil` for field: #{field_defn.path}" }
62
67
  # If the value wasn't set, mix in the default value:
63
68
  # - If the default is false and the current value is true, make it false
64
69
  # - If the default is true and the current value is true, it stays true
@@ -66,6 +71,7 @@ module GraphQL
66
71
  # - If the default is true and the current value is false, keep it false
67
72
  @subscription_broadcastable = @subscription_broadcastable && @default_broadcastable
68
73
  when false
74
+ query.logger.debug { "`broadcastable: false` for field: #{field_defn.path}" }
69
75
  # One non-broadcastable field is enough to make the whole subscription non-broadcastable
70
76
  @subscription_broadcastable = false
71
77
  when true
@@ -20,12 +20,22 @@ module GraphQL
20
20
  def after_resolve(value:, context:, object:, arguments:, **rest)
21
21
  if value.is_a?(GraphQL::ExecutionError)
22
22
  value
23
+ elsif @field.resolver&.method_defined?(:subscription_written?) &&
24
+ (subscription_namespace = context.namespace(:subscriptions)) &&
25
+ (subscriptions_by_path = subscription_namespace[:subscriptions])
26
+ (subscription_instance = subscriptions_by_path[context.current_path])
27
+ # If it was already written, don't append this event to be written later
28
+ if !subscription_instance.subscription_written?
29
+ events = context.namespace(:subscriptions)[:events]
30
+ events << subscription_instance.event
31
+ end
32
+ value
23
33
  elsif (events = context.namespace(:subscriptions)[:events])
24
34
  # This is the first execution, so gather an Event
25
35
  # for the backend to register:
26
36
  event = Subscriptions::Event.new(
27
37
  name: field.name,
28
- arguments: arguments_without_field_extras(arguments: arguments),
38
+ arguments: arguments,
29
39
  context: context,
30
40
  field: field,
31
41
  )
@@ -33,7 +43,7 @@ module GraphQL
33
43
  value
34
44
  elsif context.query.subscription_topic == Subscriptions::Event.serialize(
35
45
  field.name,
36
- arguments_without_field_extras(arguments: arguments),
46
+ arguments,
37
47
  field,
38
48
  scope: (field.subscription_scope ? context[field.subscription_scope] : nil),
39
49
  )
@@ -45,14 +55,6 @@ module GraphQL
45
55
  context.skip
46
56
  end
47
57
  end
48
-
49
- private
50
-
51
- def arguments_without_field_extras(arguments:)
52
- arguments.dup.tap do |event_args|
53
- field.extras.each { |k| event_args.delete(k) }
54
- end
55
- end
56
58
  end
57
59
  end
58
60
  end
@@ -20,7 +20,7 @@ module GraphQL
20
20
 
21
21
  def initialize(name:, arguments:, field: nil, context: nil, scope: nil)
22
22
  @name = name
23
- @arguments = arguments
23
+ @arguments = self.class.arguments_without_field_extras(arguments: arguments, field: field)
24
24
  @context = context
25
25
  field ||= context.field
26
26
  scope_key = field.subscription_scope
@@ -39,6 +39,7 @@ module GraphQL
39
39
  # @return [String] an identifier for this unit of subscription
40
40
  def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext.instance)
41
41
  subscription = field.resolver || GraphQL::Schema::Subscription
42
+ arguments = arguments_without_field_extras(field: field, arguments: arguments)
42
43
  normalized_args = stringify_args(field, arguments.to_h, context)
43
44
  subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
44
45
  end
@@ -60,6 +61,16 @@ module GraphQL
60
61
  end
61
62
 
62
63
  class << self
64
+ def arguments_without_field_extras(arguments:, field:)
65
+ if !field.extras.empty?
66
+ arguments = arguments.dup
67
+ field.extras.each do |extra_key|
68
+ arguments.delete(extra_key)
69
+ end
70
+ end
71
+ arguments
72
+ end
73
+
63
74
  private
64
75
 
65
76
  # This method does not support cyclic references in the Hash,
@@ -137,7 +148,7 @@ module GraphQL
137
148
  end
138
149
 
139
150
  def get_arg_definition(arg_owner, arg_name, context)
140
- arg_owner.get_argument(arg_name, context) || arg_owner.arguments(context).each_value.find { |v| v.keyword.to_s == arg_name }
151
+ context.types.argument(arg_owner, arg_name) || context.types.arguments(arg_owner).find { |v| v.keyword.to_s == arg_name }
141
152
  end
142
153
  end
143
154
  end
@@ -146,7 +146,7 @@ module GraphQL
146
146
  elsif obj.is_a?(Date) || obj.is_a?(Time)
147
147
  # DateTime extends Date; for TimeWithZone, call `.utc` first.
148
148
  { TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
149
- elsif obj.is_a?(OpenStruct)
149
+ elsif defined?(OpenStruct) && obj.is_a?(OpenStruct)
150
150
  { OPEN_STRUCT_KEY => dump_value(obj.to_h) }
151
151
  elsif defined?(ActiveRecord::Relation) && obj.is_a?(ActiveRecord::Relation)
152
152
  dump_value(obj.to_a)
@@ -64,12 +64,12 @@ module GraphQL
64
64
  event_name = event_name.to_s
65
65
 
66
66
  # Try with the verbatim input first:
67
- field = @schema.get_field(@schema.subscription, event_name, context)
67
+ field = dummy_query.types.field(@schema.subscription, event_name) # rubocop:disable Development/ContextIsPassedCop
68
68
 
69
69
  if field.nil?
70
70
  # And if it wasn't found, normalize it:
71
71
  normalized_event_name = normalize_name(event_name)
72
- field = @schema.get_field(@schema.subscription, normalized_event_name, context)
72
+ field = dummy_query.types.field(@schema.subscription, normalized_event_name) # rubocop:disable Development/ContextIsPassedCop
73
73
  if field.nil?
74
74
  raise InvalidTriggerError, "No subscription matching trigger: #{event_name} (looked for #{@schema.subscription.graphql_name}.#{normalized_event_name})"
75
75
  end
@@ -235,7 +235,7 @@ module GraphQL
235
235
  if !query.valid?
236
236
  raise "Invalid query: #{query.validation_errors.map(&:to_h).inspect}"
237
237
  end
238
- GraphQL::Analysis::AST.analyze_query(query, @schema.query_analyzers)
238
+ GraphQL::Analysis.analyze_query(query, @schema.query_analyzers)
239
239
  query.context.namespace(:subscriptions)[:subscription_broadcastable]
240
240
  end
241
241
 
@@ -250,6 +250,8 @@ module GraphQL
250
250
  def normalize_arguments(event_name, arg_owner, args, context)
251
251
  case arg_owner
252
252
  when GraphQL::Schema::Field, Class
253
+ return args if args.nil?
254
+
253
255
  if arg_owner.is_a?(Class) && !arg_owner.kind.input_object?
254
256
  # it's a type, but not an input object
255
257
  return args
@@ -289,7 +291,7 @@ module GraphQL
289
291
  end
290
292
  end
291
293
 
292
- if missing_arg_names.any?
294
+ if !missing_arg_names.empty?
293
295
  arg_owner_name = if arg_owner.is_a?(GraphQL::Schema::Field)
294
296
  arg_owner.path
295
297
  elsif arg_owner.is_a?(Class)
@@ -302,7 +304,7 @@ module GraphQL
302
304
 
303
305
  normalized_args
304
306
  when GraphQL::Schema::List
305
- args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) }
307
+ args&.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) }
306
308
  when GraphQL::Schema::NonNull
307
309
  normalize_arguments(event_name, arg_owner.of_type, args, context)
308
310
  else
@@ -39,24 +39,50 @@ module GraphQL
39
39
  end
40
40
  end
41
41
 
42
- def run_graphql_field(schema, field_path, object, arguments: {}, context: {})
42
+ def run_graphql_field(schema, field_path, object, arguments: {}, context: {}, ast_node: nil, lookahead: nil, visibility_profile: nil)
43
43
  type_name, *field_names = field_path.split(".")
44
- dummy_query = GraphQL::Query.new(schema, context: context)
44
+ dummy_query = GraphQL::Query.new(schema, "{ __typename }", context: context, visibility_profile: visibility_profile)
45
45
  query_context = dummy_query.context
46
- object_type = dummy_query.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
46
+ dataloader = query_context.dataloader
47
+ object_type = dummy_query.types.type(type_name) # rubocop:disable Development/ContextIsPassedCop
47
48
  if object_type
48
49
  graphql_result = object
49
50
  field_names.each do |field_name|
50
51
  inner_object = graphql_result
51
- graphql_result = object_type.wrap(inner_object, query_context)
52
+ dataloader.run_isolated {
53
+ graphql_result = object_type.wrap(inner_object, query_context)
54
+ }
52
55
  if graphql_result.nil?
53
56
  return nil
54
57
  end
55
- visible_field = dummy_query.get_field(object_type, field_name)
58
+ visible_field = dummy_query.types.field(object_type, field_name) # rubocop:disable Development/ContextIsPassedCop
56
59
  if visible_field
57
- dummy_query.context.dataloader.run_isolated {
60
+ dataloader.run_isolated {
61
+ query_context[:current_field] = visible_field
58
62
  field_args = visible_field.coerce_arguments(graphql_result, arguments, query_context)
59
63
  field_args = schema.sync_lazy(field_args)
64
+ if !visible_field.extras.empty?
65
+ extra_args = {}
66
+ visible_field.extras.each do |extra|
67
+ extra_args[extra] = case extra
68
+ when :ast_node
69
+ ast_node ||= GraphQL::Language::Nodes::Field.new(name: visible_field.graphql_name)
70
+ when :lookahead
71
+ lookahead ||= begin
72
+ ast_node ||= GraphQL::Language::Nodes::Field.new(name: visible_field.graphql_name)
73
+ Execution::Lookahead.new(
74
+ query: dummy_query,
75
+ ast_nodes: [ast_node],
76
+ field: visible_field,
77
+ )
78
+ end
79
+ else
80
+ raise ArgumentError, "This extra isn't supported in `run_graphql_field` yet: `#{extra.inspect}`. Open an issue on GitHub to request it: https://github.com/rmosolgo/graphql-ruby/issues/new"
81
+ end
82
+ end
83
+
84
+ field_args = field_args.merge_extras(extra_args)
85
+ end
60
86
  graphql_result = visible_field.resolve(graphql_result, field_args.keyword_arguments, query_context)
61
87
  graphql_result = schema.sync_lazy(graphql_result)
62
88
  }
@@ -68,39 +94,45 @@ module GraphQL
68
94
  end
69
95
  end
70
96
  graphql_result
71
- elsif schema.has_defined_type?(type_name)
72
- raise TypeNotVisibleError.new(type_name: type_name)
73
97
  else
74
- raise TypeNotDefinedError.new(type_name: type_name)
98
+ unfiltered_type = schema.use_visibility_profile? ? schema.visibility.get_type(type_name) : schema.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
99
+ if unfiltered_type
100
+ raise TypeNotVisibleError.new(type_name: type_name)
101
+ else
102
+ raise TypeNotDefinedError.new(type_name: type_name)
103
+ end
75
104
  end
76
105
  end
77
106
 
78
- def with_resolution_context(schema, type:, object:, context:{})
107
+ def with_resolution_context(schema, type:, object:, context:{}, visibility_profile: nil)
79
108
  resolution_context = ResolutionAssertionContext.new(
80
109
  self,
81
110
  schema: schema,
82
111
  type_name: type,
83
112
  object: object,
84
- context: context
113
+ context: context,
114
+ visibility_profile: visibility_profile,
85
115
  )
86
116
  yield(resolution_context)
87
117
  end
88
118
 
89
119
  class ResolutionAssertionContext
90
- def initialize(test, type_name:, object:, schema:, context:)
120
+ def initialize(test, type_name:, object:, schema:, context:, visibility_profile:)
91
121
  @test = test
92
122
  @type_name = type_name
93
123
  @object = object
94
124
  @schema = schema
95
125
  @context = context
126
+ @visibility_profile = visibility_profile
96
127
  end
97
128
 
129
+ attr_reader :visibility_profile
98
130
 
99
131
  def run_graphql_field(field_name, arguments: {})
100
132
  if @schema
101
- @test.run_graphql_field(@schema, "#{@type_name}.#{field_name}", @object, arguments: arguments, context: @context)
133
+ @test.run_graphql_field(@schema, "#{@type_name}.#{field_name}", @object, arguments: arguments, context: @context, visibility_profile: @visibility_profile)
102
134
  else
103
- @test.run_graphql_field("#{@type_name}.#{field_name}", @object, arguments: arguments, context: @context)
135
+ @test.run_graphql_field("#{@type_name}.#{field_name}", @object, arguments: arguments, context: @context, visibility_profile: @visibility_profile)
104
136
  end
105
137
  end
106
138
  end
@@ -108,8 +140,8 @@ module GraphQL
108
140
  module SchemaHelpers
109
141
  include Helpers
110
142
 
111
- def run_graphql_field(field_path, object, arguments: {}, context: {})
112
- super(@@schema_class_for_helpers, field_path, object, arguments: arguments, context: context)
143
+ def run_graphql_field(field_path, object, arguments: {}, context: {}, visibility_profile: nil)
144
+ super(@@schema_class_for_helpers, field_path, object, arguments: arguments, context: context, visibility_profile: visibility_profile)
113
145
  end
114
146
 
115
147
  def with_resolution_context(*args, **kwargs, &block)