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
@@ -34,6 +34,7 @@ module GraphQL
34
34
  GraphQL::StaticValidation::VariableUsagesAreAllowed,
35
35
  GraphQL::StaticValidation::MutationRootExists,
36
36
  GraphQL::StaticValidation::SubscriptionRootExists,
37
+ GraphQL::StaticValidation::InputObjectNamesAreUnique,
37
38
  ]
38
39
  end
39
40
  end
@@ -71,7 +71,7 @@ module GraphQL
71
71
  module ContextMethods
72
72
  def on_operation_definition(node, parent)
73
73
  object_type = @schema.root_type_for_operation(node.operation_type)
74
- @object_types.push(object_type)
74
+ push_type(object_type)
75
75
  @path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
76
76
  super
77
77
  @object_types.pop
@@ -98,9 +98,9 @@ module GraphQL
98
98
  @field_definitions.push(field_definition)
99
99
  if !field_definition.nil?
100
100
  next_object_type = field_definition.type.unwrap
101
- @object_types.push(next_object_type)
101
+ push_type(next_object_type)
102
102
  else
103
- @object_types.push(nil)
103
+ push_type(nil)
104
104
  end
105
105
  @path.push(node.alias || node.name)
106
106
  super
@@ -120,7 +120,7 @@ module GraphQL
120
120
  argument_defn = if (arg = @argument_definitions.last)
121
121
  arg_type = arg.type.unwrap
122
122
  if arg_type.kind.input_object?
123
- arg_type.input_fields[node.name]
123
+ arg_type.arguments[node.name]
124
124
  else
125
125
  nil
126
126
  end
@@ -187,15 +187,19 @@ module GraphQL
187
187
 
188
188
  def on_fragment_with_type(node)
189
189
  object_type = if node.type
190
- @schema.types.fetch(node.type.name, nil)
190
+ @schema.get_type(node.type.name)
191
191
  else
192
192
  @object_types.last
193
193
  end
194
- @object_types.push(object_type)
194
+ push_type(object_type)
195
195
  yield(node)
196
196
  @object_types.pop
197
197
  @path.pop
198
198
  end
199
+
200
+ def push_type(t)
201
+ @object_types.push(t)
202
+ end
199
203
  end
200
204
 
201
205
  private
@@ -6,59 +6,73 @@ module GraphQL
6
6
  def initialize(context:)
7
7
  @context = context
8
8
  @warden = context.warden
9
+ @invalid_response = GraphQL::Query::InputValidationResult.new(valid: false, problems: [])
10
+ @valid_response = GraphQL::Query::InputValidationResult.new(valid: true, problems: [])
9
11
  end
10
12
 
11
13
  def validate(ast_value, type)
14
+ catch(:invalid) do
15
+ recursively_validate(ast_value, type)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def recursively_validate(ast_value, type)
12
22
  if type.nil?
13
23
  # this means we're an undefined argument, see #present_input_field_values_are_valid
14
24
  maybe_raise_if_invalid(ast_value) do
15
- false
25
+ @invalid_response
16
26
  end
17
27
  elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
18
28
  maybe_raise_if_invalid(ast_value) do
19
- !type.kind.non_null?
29
+ type.kind.non_null? ? @invalid_response : @valid_response
20
30
  end
21
31
  elsif type.kind.non_null?
22
32
  maybe_raise_if_invalid(ast_value) do
23
- (!ast_value.nil?)
24
- end && validate(ast_value, type.of_type)
33
+ ast_value.nil? ?
34
+ @invalid_response :
35
+ recursively_validate(ast_value, type.of_type)
36
+ end
25
37
  elsif type.kind.list?
26
38
  item_type = type.of_type
27
- ensure_array(ast_value).all? { |val| validate(val, item_type) }
39
+ results = ensure_array(ast_value).map { |val| recursively_validate(val, item_type) }
40
+ merge_results(results)
28
41
  elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
29
- true
42
+ @valid_response
30
43
  elsif type.kind.scalar? && constant_scalar?(ast_value)
31
44
  maybe_raise_if_invalid(ast_value) do
32
- type.valid_input?(ast_value, @context)
45
+ type.validate_input(ast_value, @context)
33
46
  end
34
47
  elsif type.kind.enum?
35
48
  maybe_raise_if_invalid(ast_value) do
36
49
  if ast_value.is_a?(GraphQL::Language::Nodes::Enum)
37
- type.valid_input?(ast_value.name, @context)
50
+ type.validate_input(ast_value.name, @context)
38
51
  else
39
52
  # if our ast_value isn't an Enum it's going to be invalid so return false
40
- false
53
+ @invalid_response
41
54
  end
42
55
  end
43
56
  elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
44
57
  maybe_raise_if_invalid(ast_value) do
45
- required_input_fields_are_present(type, ast_value) && present_input_field_values_are_valid(type, ast_value)
58
+ merge_results([
59
+ required_input_fields_are_present(type, ast_value),
60
+ present_input_field_values_are_valid(type, ast_value)
61
+ ])
46
62
  end
47
63
  else
48
64
  maybe_raise_if_invalid(ast_value) do
49
- false
65
+ @invalid_response
50
66
  end
51
67
  end
52
68
  end
53
69
 
54
- private
55
-
70
+ # When `error_bubbling` is false, we want to bail on the first failure that we find.
71
+ # Use `throw` to escape the current call stack, returning the invalid response.
56
72
  def maybe_raise_if_invalid(ast_value)
57
73
  ret = yield
58
- if !@context.schema.error_bubbling && !ret
59
- e = LiteralValidationError.new
60
- e.ast_value = ast_value
61
- raise e
74
+ if !@context.schema.error_bubbling && !ret.valid?
75
+ throw(:invalid, ret)
62
76
  else
63
77
  ret
64
78
  end
@@ -81,34 +95,45 @@ module GraphQL
81
95
  def required_input_fields_are_present(type, ast_node)
82
96
  # TODO - would be nice to use these to create an error message so the caller knows
83
97
  # that required fields are missing
84
- required_field_names = @warden.arguments(type)
85
- .select { |f| f.type.kind.non_null? }
98
+ required_field_names = type.arguments.each_value
99
+ .select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
86
100
  .map(&:name)
101
+
87
102
  present_field_names = ast_node.arguments.map(&:name)
88
103
  missing_required_field_names = required_field_names - present_field_names
89
104
  if @context.schema.error_bubbling
90
- missing_required_field_names.empty?
105
+ missing_required_field_names.empty? ? @valid_response : @invalid_response
91
106
  else
92
- missing_required_field_names.all? do |name|
93
- validate(GraphQL::Language::Nodes::NullValue.new(name: name), @warden.arguments(type).find { |f| f.name == name }.type )
107
+ results = missing_required_field_names.map do |name|
108
+ arg_type = @warden.get_argument(type, name).type
109
+ recursively_validate(GraphQL::Language::Nodes::NullValue.new(name: name), arg_type)
94
110
  end
111
+ merge_results(results)
95
112
  end
96
113
  end
97
114
 
98
115
  def present_input_field_values_are_valid(type, ast_node)
99
- field_map = @warden.arguments(type).reduce({}) { |m, f| m[f.name] = f; m}
100
- ast_node.arguments.all? do |value|
101
- field = field_map[value.name]
116
+ results = ast_node.arguments.map do |value|
117
+ field = @warden.get_argument(type, value.name)
102
118
  # we want to call validate on an argument even if it's an invalid one
103
119
  # so that our raise exception is on it instead of the entire InputObject
104
- type = field && field.type
105
- validate(value.value, type)
120
+ field_type = field && field.type
121
+ recursively_validate(value.value, field_type)
106
122
  end
123
+ merge_results(results)
107
124
  end
108
125
 
109
126
  def ensure_array(value)
110
127
  value.is_a?(Array) ? value : [value]
111
128
  end
129
+
130
+ def merge_results(results_list)
131
+ merged_result = Query::InputValidationResult.new
132
+ results_list.each do |inner_result|
133
+ merged_result.merge_result!([], inner_result)
134
+ end
135
+ merged_result
136
+ end
112
137
  end
113
138
  end
114
139
  end
@@ -2,103 +2,63 @@
2
2
  module GraphQL
3
3
  module StaticValidation
4
4
  module ArgumentLiteralsAreCompatible
5
- # TODO dedup with ArgumentsAreDefined
6
5
  def on_argument(node, parent)
7
- parent_defn = case parent
8
- when GraphQL::Language::Nodes::InputObject
9
- arg_defn = context.argument_definition
10
- if arg_defn.nil?
11
- nil
12
- else
13
- arg_ret_type = arg_defn.type.unwrap
14
- if !arg_ret_type.is_a?(GraphQL::InputObjectType)
15
- nil
16
- else
17
- arg_ret_type
18
- end
19
- end
20
- when GraphQL::Language::Nodes::Directive
21
- context.schema.directives[parent.name]
22
- when GraphQL::Language::Nodes::Field
23
- context.field_definition
24
- else
25
- raise "Unexpected argument parent: #{parent.class} (##{parent})"
6
+ # Check the child arguments first;
7
+ # don't add a new error if one of them reports an error
8
+ super
9
+
10
+ # Don't validate variables here
11
+ if node.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
12
+ return
26
13
  end
27
14
 
28
- if parent_defn && !node.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
29
- arg_defn = parent_defn.arguments[node.name]
30
- if arg_defn
31
- begin
32
- valid = context.valid_literal?(node.value, arg_defn.type)
33
- rescue GraphQL::CoercionError => err
34
- context.schema.error_bubbling
35
- if !context.schema.error_bubbling && !arg_defn.type.unwrap.kind.scalar?
36
- # if error bubbling is disabled and the arg that caused this error isn't a scalar then
37
- # short-circuit here so we avoid bubbling this up to whatever input_object / array contains us
38
- return super
39
- end
40
- error = GraphQL::StaticValidation::ArgumentLiteralsAreCompatibleError.new(err.message, nodes: parent, type: "CoercionError", extensions: err.extensions)
41
- rescue GraphQL::LiteralValidationError => err
42
- # check to see if the ast node that caused the error to be raised is
43
- # the same as the node we were checking here.
44
- arg_type = arg_defn.type
45
- if arg_type.kind.non_null?
46
- arg_type = arg_type.of_type
47
- end
15
+ if @context.schema.error_bubbling || context.errors.none? { |err| err.path.take(@path.size) == @path }
16
+ parent_defn = parent_definition(parent)
48
17
 
49
- matched = if arg_type.kind.list?
50
- # for a list we claim an error if the node is contained in our list
51
- Array(node.value).include?(err.ast_value)
52
- elsif arg_type.kind.input_object? && node.value.is_a?(GraphQL::Language::Nodes::InputObject)
53
- # for an input object we check the arguments
54
- node.value.arguments.include?(err.ast_value)
18
+ if parent_defn && (arg_defn = parent_defn.arguments[node.name])
19
+ validation_result = context.validate_literal(node.value, arg_defn.type)
20
+ if !validation_result.valid?
21
+ kind_of_node = node_type(parent)
22
+ error_arg_name = parent_name(parent, parent_defn)
23
+ string_value = if node.value == Float::INFINITY
24
+ ""
55
25
  else
56
- # otherwise we just check equality
57
- node.value == (err.ast_value)
26
+ " (#{GraphQL::Language::Printer.new.print(node.value)})"
58
27
  end
59
- if !matched
60
- # This node isn't the node that caused the error,
61
- # So halt this visit but continue visiting the rest of the tree
62
- return super
63
- end
64
- end
65
-
66
- if !valid
67
- error ||= begin
68
- kind_of_node = node_type(parent)
69
- error_arg_name = parent_name(parent, parent_defn)
70
28
 
71
- GraphQL::StaticValidation::ArgumentLiteralsAreCompatibleError.new(
72
- "Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value. Expected type '#{arg_defn.type}'.",
73
- nodes: parent,
74
- type: kind_of_node,
75
- argument: node.name
76
- )
29
+ problems = validation_result.problems
30
+ first_problem = problems && problems.first
31
+ if first_problem
32
+ message = first_problem["message"]
33
+ # This is some legacy stuff from when `CoercionError` was raised thru the stack
34
+ if message
35
+ coerce_extensions = first_problem["extensions"] || {
36
+ "code" => "argumentLiteralsIncompatible"
37
+ }
38
+ end
77
39
  end
78
- add_error(error)
79
- end
80
- end
81
- end
82
40
 
83
- super
84
- end
41
+ error_options = {
42
+ nodes: parent,
43
+ type: kind_of_node,
44
+ argument: node.name
45
+ }
46
+ if coerce_extensions
47
+ error_options[:coerce_extensions] = coerce_extensions
48
+ end
85
49
 
50
+ message ||= "Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value#{string_value}. Expected type '#{arg_defn.type.to_type_signature}'."
86
51
 
87
- private
52
+ error = GraphQL::StaticValidation::ArgumentLiteralsAreCompatibleError.new(
53
+ message,
54
+ **error_options
55
+ )
88
56
 
89
- def parent_name(parent, type_defn)
90
- if parent.is_a?(GraphQL::Language::Nodes::Field)
91
- parent.alias || parent.name
92
- elsif parent.is_a?(GraphQL::Language::Nodes::InputObject)
93
- type_defn.name
94
- else
95
- parent.name
57
+ add_error(error)
58
+ end
59
+ end
96
60
  end
97
61
  end
98
-
99
- def node_type(parent)
100
- parent.class.name.split("::").last
101
- end
102
62
  end
103
63
  end
104
64
  end
@@ -5,19 +5,31 @@ module GraphQL
5
5
  attr_reader :type_name
6
6
  attr_reader :argument_name
7
7
 
8
- def initialize(message, path: nil, nodes: [], type:, argument: nil, extensions: nil)
8
+ def initialize(message, path: nil, nodes: [], type:, argument: nil, extensions: nil, coerce_extensions: nil)
9
9
  super(message, path: path, nodes: nodes)
10
10
  @type_name = type
11
11
  @argument_name = argument
12
12
  @extensions = extensions
13
+ @coerce_extensions = coerce_extensions
13
14
  end
14
15
 
15
16
  # A hash representation of this Message
16
17
  def to_h
17
- extensions = {
18
- "code" => code,
19
- "typeName" => type_name
20
- }.tap { |h| h["argumentName"] = argument_name unless argument_name.nil? }
18
+ if @coerce_extensions
19
+ extensions = @coerce_extensions
20
+ # This is for legacy compat -- but this key is supposed to be a GraphQL type name :confounded:
21
+ extensions["typeName"] = "CoercionError"
22
+ else
23
+ extensions = {
24
+ "code" => code,
25
+ "typeName" => type_name
26
+ }
27
+
28
+ if argument_name
29
+ extensions["argumentName"] = argument_name
30
+ end
31
+ end
32
+
21
33
  extensions.merge!(@extensions) unless @extensions.nil?
22
34
  super.merge({
23
35
  "extensions" => extensions
@@ -3,28 +3,9 @@ module GraphQL
3
3
  module StaticValidation
4
4
  module ArgumentsAreDefined
5
5
  def on_argument(node, parent)
6
- parent_defn = case parent
7
- when GraphQL::Language::Nodes::InputObject
8
- arg_defn = context.argument_definition
9
- if arg_defn.nil?
10
- nil
11
- else
12
- arg_ret_type = arg_defn.type.unwrap
13
- if !arg_ret_type.is_a?(GraphQL::InputObjectType)
14
- nil
15
- else
16
- arg_ret_type
17
- end
18
- end
19
- when GraphQL::Language::Nodes::Directive
20
- context.schema.directives[parent.name]
21
- when GraphQL::Language::Nodes::Field
22
- context.field_definition
23
- else
24
- raise "Unexpected argument parent: #{parent.class} (##{parent})"
25
- end
6
+ parent_defn = parent_definition(parent)
26
7
 
27
- if parent_defn && context.warden.arguments(parent_defn).any? { |arg| arg.name == node.name }
8
+ if parent_defn && context.warden.get_argument(parent_defn, node.name)
28
9
  super
29
10
  elsif parent_defn
30
11
  kind_of_node = node_type(parent)
@@ -44,19 +25,46 @@ module GraphQL
44
25
 
45
26
  private
46
27
 
28
+ # TODO smell: these methods are added to all visitors, since they're included in a module.
47
29
  def parent_name(parent, type_defn)
48
- if parent.is_a?(GraphQL::Language::Nodes::Field)
30
+ case parent
31
+ when GraphQL::Language::Nodes::Field
49
32
  parent.alias || parent.name
50
- elsif parent.is_a?(GraphQL::Language::Nodes::InputObject)
51
- type_defn.name
52
- else
33
+ when GraphQL::Language::Nodes::InputObject
34
+ type_defn.graphql_name
35
+ when GraphQL::Language::Nodes::Argument, GraphQL::Language::Nodes::Directive
53
36
  parent.name
37
+ else
38
+ raise "Invariant: Unexpected parent #{parent.inspect} (#{parent.class})"
54
39
  end
55
40
  end
56
41
 
57
42
  def node_type(parent)
58
43
  parent.class.name.split("::").last
59
44
  end
45
+
46
+ def parent_definition(parent)
47
+ case parent
48
+ when GraphQL::Language::Nodes::InputObject
49
+ arg_defn = context.argument_definition
50
+ if arg_defn.nil?
51
+ nil
52
+ else
53
+ arg_ret_type = arg_defn.type.unwrap
54
+ if arg_ret_type.kind.input_object?
55
+ arg_ret_type
56
+ else
57
+ nil
58
+ end
59
+ end
60
+ when GraphQL::Language::Nodes::Directive
61
+ context.schema.directives[parent.name]
62
+ when GraphQL::Language::Nodes::Field
63
+ context.field_definition
64
+ else
65
+ raise "Unexpected argument parent: #{parent.class} (##{parent})"
66
+ end
67
+ end
60
68
  end
61
69
  end
62
70
  end
@@ -56,7 +56,7 @@ module GraphQL
56
56
  "'@#{directive_defn.graphql_name}' can't be applied to #{location_name} (allowed: #{allowed_location_names.join(", ")})",
57
57
  nodes: directive_ast,
58
58
  target: location_name,
59
- name: directive_defn.name
59
+ name: directive_defn.graphql_name
60
60
  ))
61
61
  end
62
62
  end
@@ -9,16 +9,16 @@ module GraphQL
9
9
  if field.nil?
10
10
  if parent_type.kind.union?
11
11
  add_error(GraphQL::StaticValidation::FieldsHaveAppropriateSelectionsError.new(
12
- "Selections can't be made directly on unions (see selections on #{parent_type.name})",
12
+ "Selections can't be made directly on unions (see selections on #{parent_type.graphql_name})",
13
13
  nodes: parent,
14
- node_name: parent_type.name
14
+ node_name: parent_type.graphql_name
15
15
  ))
16
16
  else
17
17
  add_error(GraphQL::StaticValidation::FieldsAreDefinedOnTypeError.new(
18
- "Field '#{node.name}' doesn't exist on type '#{parent_type.name}'",
18
+ "Field '#{node.name}' doesn't exist on type '#{parent_type.graphql_name}'",
19
19
  nodes: node,
20
20
  field: node.name,
21
- type: parent_type.name
21
+ type: parent_type.graphql_name
22
22
  ))
23
23
  end
24
24
  else
@@ -27,12 +27,12 @@ module GraphQL
27
27
  nil
28
28
  elsif resolved_type.kind.scalar? && ast_node.selections.any?
29
29
  if ast_node.selections.first.is_a?(GraphQL::Language::Nodes::InlineFragment)
30
- "Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has inline fragments [#{ast_node.selections.map(&:type).map(&:name).join(", ")}])"
30
+ "Selections can't be made on scalars (%{node_name} returns #{resolved_type.graphql_name} but has inline fragments [#{ast_node.selections.map(&:type).map(&:name).join(", ")}])"
31
31
  else
32
- "Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has selections [#{ast_node.selections.map(&:name).join(", ")}])"
32
+ "Selections can't be made on scalars (%{node_name} returns #{resolved_type.graphql_name} but has selections [#{ast_node.selections.map(&:name).join(", ")}])"
33
33
  end
34
34
  elsif resolved_type.kind.fields? && ast_node.selections.empty?
35
- "Field must have selections (%{node_name} returns #{resolved_type.name} but has no selections. Did you mean '#{ast_node.name} { ... }'?)"
35
+ "Field must have selections (%{node_name} returns #{resolved_type.graphql_name} but has no selections. Did you mean '#{ast_node.name} { ... }'?)"
36
36
  else
37
37
  nil
38
38
  end
@@ -55,13 +55,13 @@ module GraphQL
55
55
  "name": node_name.to_s
56
56
  }
57
57
  unless resolved_type.nil?
58
- extensions["type"] = resolved_type.to_s
58
+ extensions["type"] = resolved_type.to_type_signature
59
59
  end
60
60
  add_error(GraphQL::StaticValidation::FieldsHaveAppropriateSelectionsError.new(
61
61
  msg % { node_name: node_name },
62
62
  nodes: ast_node,
63
63
  node_name: node_name.to_s,
64
- type: resolved_type.nil? ? nil : resolved_type.to_s
64
+ type: resolved_type.nil? ? nil : resolved_type.graphql_name,
65
65
  ))
66
66
  false
67
67
  else
@@ -93,8 +93,8 @@ module GraphQL
93
93
 
94
94
  return if fragment1.nil? || fragment2.nil?
95
95
 
96
- fragment_type1 = context.schema.types[fragment1.type.name]
97
- fragment_type2 = context.schema.types[fragment2.type.name]
96
+ fragment_type1 = context.warden.get_type(fragment1.type.name)
97
+ fragment_type2 = context.warden.get_type(fragment2.type.name)
98
98
 
99
99
  return if fragment_type1.nil? || fragment_type2.nil?
100
100
 
@@ -146,7 +146,7 @@ module GraphQL
146
146
  fragment = context.fragments[fragment_name]
147
147
  return if fragment.nil?
148
148
 
149
- fragment_type = context.schema.types[fragment.type.name]
149
+ fragment_type = context.warden.get_type(fragment.type.name)
150
150
  return if fragment_type.nil?
151
151
 
152
152
  fragment_fields, fragment_spreads = fields_and_fragments_from_selection(fragment, owner_type: fragment_type, parents: [*fragment_spread.parents, fragment_type])
@@ -202,15 +202,16 @@ module GraphQL
202
202
  )
203
203
  end
204
204
 
205
- args = possible_arguments(node1, node2)
206
- if args.size > 1
207
- msg = "Field '#{response_key}' has an argument conflict: #{args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")}?"
205
+ if !same_arguments?(node1, node2)
206
+ args = [serialize_field_args(node1), serialize_field_args(node2)]
207
+ conflicts = args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")
208
+ msg = "Field '#{response_key}' has an argument conflict: #{conflicts}?"
208
209
  context.errors << GraphQL::StaticValidation::FieldsWillMergeError.new(
209
210
  msg,
210
211
  nodes: [node1, node2],
211
212
  path: [],
212
213
  field_name: response_key,
213
- conflicts: args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")
214
+ conflicts: conflicts
214
215
  )
215
216
  end
216
217
  end
@@ -316,7 +317,7 @@ module GraphQL
316
317
  definition = context.schema.get_field(owner_type, node.name)
317
318
  fields << Field.new(node, definition, owner_type, parents)
318
319
  when GraphQL::Language::Nodes::InlineFragment
319
- fragment_type = node.type ? context.schema.types[node.type.name] : owner_type
320
+ fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
320
321
  find_fields_and_fragments(node.selections, parents: [*parents, fragment_type], owner_type: owner_type, fields: fields, fragment_spreads: fragment_spreads) if fragment_type
321
322
  when GraphQL::Language::Nodes::FragmentSpread
322
323
  fragment_spreads << FragmentSpread.new(node.name, parents)
@@ -326,20 +327,19 @@ module GraphQL
326
327
  [fields, fragment_spreads]
327
328
  end
328
329
 
329
- def possible_arguments(field1, field2)
330
+ def same_arguments?(field1, field2)
330
331
  # Check for incompatible / non-identical arguments on this node:
331
- [field1, field2].map do |n|
332
- if n.arguments.any?
333
- serialized_args = {}
334
- n.arguments.each do |a|
335
- arg_value = a.value
336
- serialized_args[a.name] = serialize_arg(arg_value)
337
- end
338
- serialized_args
339
- else
340
- NO_ARGS
341
- end
342
- end.uniq
332
+ arguments1 = field1.arguments
333
+ arguments2 = field2.arguments
334
+
335
+ return false if arguments1.length != arguments2.length
336
+
337
+ arguments1.all? do |argument1|
338
+ argument2 = arguments2.find { |argument| argument.name == argument1.name }
339
+ return false if argument2.nil?
340
+
341
+ serialize_arg(argument1.value) == serialize_arg(argument2.value)
342
+ end
343
343
  end
344
344
 
345
345
  def serialize_arg(arg_value)
@@ -353,6 +353,14 @@ module GraphQL
353
353
  end
354
354
  end
355
355
 
356
+ def serialize_field_args(field)
357
+ serialized_args = {}
358
+ field.arguments.each do |argument|
359
+ serialized_args[argument.name] = serialize_arg(argument.value)
360
+ end
361
+ serialized_args
362
+ end
363
+
356
364
  def compared_fragments_key(frag1, frag2, exclusive)
357
365
  # Cache key to not compare two fragments more than once.
358
366
  # The key includes both fragment names sorted (this way we
@@ -50,12 +50,12 @@ module GraphQL
50
50
  if child_types.none? { |c| parent_types.include?(c) }
51
51
  name = node.respond_to?(:name) ? " #{node.name}" : ""
52
52
  add_error(GraphQL::StaticValidation::FragmentSpreadsArePossibleError.new(
53
- "Fragment#{name} on #{child_type.name} can't be spread inside #{parent_type.name}",
53
+ "Fragment#{name} on #{child_type.graphql_name} can't be spread inside #{parent_type.graphql_name}",
54
54
  nodes: node,
55
55
  path: path,
56
56
  fragment_name: name.empty? ? "unknown" : name,
57
- type: child_type.name,
58
- parent: parent_type.name
57
+ type: child_type.graphql_name,
58
+ parent: parent_type.graphql_name
59
59
  ))
60
60
  end
61
61
  end