graphql 0.17.2 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +1 -0
  3. data/lib/graphql/analysis/query_depth.rb +1 -1
  4. data/lib/graphql/base_type.rb +25 -1
  5. data/lib/graphql/define.rb +2 -0
  6. data/lib/graphql/define/assign_connection.rb +11 -0
  7. data/lib/graphql/define/assign_global_id_field.rb +11 -0
  8. data/lib/graphql/define/assign_object_field.rb +21 -20
  9. data/lib/graphql/define/defined_object_proxy.rb +2 -2
  10. data/lib/graphql/define/instance_definable.rb +13 -3
  11. data/lib/graphql/field.rb +1 -1
  12. data/lib/graphql/language/generation.rb +57 -6
  13. data/lib/graphql/language/lexer.rb +434 -212
  14. data/lib/graphql/language/lexer.rl +18 -0
  15. data/lib/graphql/language/nodes.rb +75 -0
  16. data/lib/graphql/language/parser.rb +853 -341
  17. data/lib/graphql/language/parser.y +114 -17
  18. data/lib/graphql/query.rb +15 -1
  19. data/lib/graphql/relay.rb +13 -0
  20. data/lib/graphql/relay/array_connection.rb +80 -0
  21. data/lib/graphql/relay/base_connection.rb +138 -0
  22. data/lib/graphql/relay/connection_field.rb +54 -0
  23. data/lib/graphql/relay/connection_type.rb +25 -0
  24. data/lib/graphql/relay/edge.rb +22 -0
  25. data/lib/graphql/relay/edge_type.rb +14 -0
  26. data/lib/graphql/relay/global_id_resolve.rb +15 -0
  27. data/lib/graphql/relay/global_node_identification.rb +124 -0
  28. data/lib/graphql/relay/mutation.rb +146 -0
  29. data/lib/graphql/relay/page_info.rb +13 -0
  30. data/lib/graphql/relay/relation_connection.rb +98 -0
  31. data/lib/graphql/schema.rb +3 -0
  32. data/lib/graphql/schema/printer.rb +12 -2
  33. data/lib/graphql/static_validation/message.rb +9 -5
  34. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  35. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  36. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  37. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +7 -7
  38. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  39. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  40. data/lib/graphql/static_validation/rules/fields_will_merge.rb +6 -6
  41. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +17 -9
  42. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  43. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +1 -1
  44. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  45. data/lib/graphql/static_validation/rules/fragments_are_used.rb +17 -6
  46. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  47. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +2 -2
  48. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -5
  49. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  50. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +12 -11
  51. data/lib/graphql/static_validation/type_stack.rb +33 -2
  52. data/lib/graphql/static_validation/validation_context.rb +5 -0
  53. data/lib/graphql/version.rb +1 -1
  54. data/readme.md +16 -4
  55. data/spec/graphql/analysis/analyze_query_spec.rb +31 -2
  56. data/spec/graphql/analysis/query_complexity_spec.rb +24 -0
  57. data/spec/graphql/argument_spec.rb +1 -1
  58. data/spec/graphql/define/instance_definable_spec.rb +9 -0
  59. data/spec/graphql/field_spec.rb +1 -1
  60. data/spec/graphql/internal_representation/rewrite_spec.rb +3 -3
  61. data/spec/graphql/language/generation_spec.rb +25 -4
  62. data/spec/graphql/language/parser_spec.rb +116 -1
  63. data/spec/graphql/query_spec.rb +10 -0
  64. data/spec/graphql/relay/array_connection_spec.rb +164 -0
  65. data/spec/graphql/relay/connection_type_spec.rb +37 -0
  66. data/spec/graphql/relay/global_node_identification_spec.rb +149 -0
  67. data/spec/graphql/relay/mutation_spec.rb +55 -0
  68. data/spec/graphql/relay/page_info_spec.rb +106 -0
  69. data/spec/graphql/relay/relation_connection_spec.rb +348 -0
  70. data/spec/graphql/schema/printer_spec.rb +8 -0
  71. data/spec/graphql/schema/reduce_types_spec.rb +1 -1
  72. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +12 -6
  73. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +8 -4
  74. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
  75. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
  76. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +7 -2
  77. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -2
  78. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
  79. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +5 -3
  80. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
  81. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +5 -2
  82. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +10 -2
  83. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +6 -3
  84. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +8 -4
  85. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
  86. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +6 -3
  87. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +6 -3
  88. data/spec/spec_helper.rb +7 -0
  89. data/spec/support/dairy_app.rb +11 -10
  90. data/spec/support/star_wars_data.rb +65 -58
  91. data/spec/support/star_wars_schema.rb +192 -54
  92. metadata +84 -2
@@ -26,6 +26,9 @@ module GraphQL
26
26
  :mutation_execution_strategy,
27
27
  :subscription_execution_strategy
28
28
 
29
+ # @return [GraphQL::Relay::GlobalNodeIdentification] the node identification instance for this schema, when using Relay
30
+ attr_accessor :node_identification
31
+
29
32
  # @return [Array<#call>] Middlewares suitable for MiddlewareChain, applied to fields during execution
30
33
  attr_reader :middleware
31
34
 
@@ -28,7 +28,17 @@ module GraphQL
28
28
 
29
29
  def print_filtered_schema(schema, type_filter)
30
30
  types = schema.types.values.select{ |type| type_filter.call(type) }.sort_by(&:name)
31
- types.map{ |type| print_type(type) }.join("\n\n")
31
+ type_definitions = types.map{ |type| print_type(type) }
32
+
33
+ [print_schema_definition(schema)].concat(type_definitions).join("\n\n")
34
+ end
35
+
36
+ def print_schema_definition(schema)
37
+ operations = [:query, :mutation, :subscription].map do |operation_type|
38
+ object_type = schema.public_send(operation_type)
39
+ " #{operation_type}: #{object_type.name}\n" if object_type
40
+ end.compact.join
41
+ "schema {\n#{operations}}"
32
42
  end
33
43
 
34
44
  BUILTIN_SCALARS = Set.new(["String", "Boolean", "Int", "Float", "ID"])
@@ -122,7 +132,7 @@ module GraphQL
122
132
 
123
133
  class UnionPrinter
124
134
  def self.print(type)
125
- "union #{type.name} = #{type.possible_types.map(&:to_s).join(" | ")}\n}"
135
+ "union #{type.name} = #{type.possible_types.map(&:to_s).join(" | ")}"
126
136
  end
127
137
  end
128
138
 
@@ -6,23 +6,27 @@ module GraphQL
6
6
  # Convenience for validators
7
7
  module MessageHelper
8
8
  # Error `message` is located at `node`
9
- def message(message, node)
10
- GraphQL::StaticValidation::Message.new(message, line: node.line, col: node.col)
9
+ def message(message, node, context: nil, path: nil)
10
+ path ||= context.path
11
+ GraphQL::StaticValidation::Message.new(message, line: node.line, col: node.col, path: path)
11
12
  end
12
13
  end
13
- attr_reader :message, :line, :col
14
14
 
15
- def initialize(message, line: nil, col: nil)
15
+ attr_reader :message, :line, :col, :path
16
+
17
+ def initialize(message, line: nil, col: nil, path: [])
16
18
  @message = message
17
19
  @line = line
18
20
  @col = col
21
+ @path = path
19
22
  end
20
23
 
21
24
  # A hash representation of this Message
22
25
  def to_h
23
26
  {
24
27
  "message" => message,
25
- "locations" => locations
28
+ "locations" => locations,
29
+ "path" => path,
26
30
  }
27
31
  end
28
32
 
@@ -10,7 +10,7 @@ module GraphQL
10
10
  if !valid
11
11
  kind_of_node = node_type(parent)
12
12
  error_arg_name = parent_name(parent, defn)
13
- context.errors << message("Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value", parent)
13
+ context.errors << message("Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value", parent, context: context)
14
14
  end
15
15
  end
16
16
  end
@@ -6,7 +6,7 @@ module GraphQL
6
6
  if argument_defn.nil?
7
7
  kind_of_node = node_type(parent)
8
8
  error_arg_name = parent_name(parent, defn)
9
- context.errors << message("#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'", parent)
9
+ context.errors << message("#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'", parent, context: context)
10
10
  GraphQL::Language::Visitor::SKIP
11
11
  else
12
12
  nil
@@ -6,15 +6,15 @@ module GraphQL
6
6
  def validate(context)
7
7
  directive_names = context.schema.directives.keys
8
8
  context.visitor[GraphQL::Language::Nodes::Directive] << -> (node, parent) {
9
- validate_directive(node, directive_names, context.errors)
9
+ validate_directive(node, directive_names, context)
10
10
  }
11
11
  end
12
12
 
13
13
  private
14
14
 
15
- def validate_directive(ast_directive, directive_names, errors)
15
+ def validate_directive(ast_directive, directive_names, context)
16
16
  if !directive_names.include?(ast_directive.name)
17
- errors << message("Directive @#{ast_directive.name} is not defined", ast_directive)
17
+ context.errors << message("Directive @#{ast_directive.name} is not defined", ast_directive, context: context)
18
18
  end
19
19
  end
20
20
  end
@@ -8,7 +8,7 @@ module GraphQL
8
8
  directives = context.schema.directives
9
9
 
10
10
  context.visitor[Nodes::Directive] << -> (node, parent) {
11
- validate_location(node, parent, directives, context.errors)
11
+ validate_location(node, parent, directives, context)
12
12
  }
13
13
  end
14
14
 
@@ -33,25 +33,25 @@ module GraphQL
33
33
 
34
34
  SIMPLE_LOCATION_NODES = SIMPLE_LOCATIONS.keys
35
35
 
36
- def validate_location(ast_directive, ast_parent, directives, errors)
36
+ def validate_location(ast_directive, ast_parent, directives, context)
37
37
  directive_defn = directives[ast_directive.name]
38
38
  case ast_parent
39
39
  when Nodes::OperationDefinition
40
40
  required_location = GraphQL::Directive.const_get(ast_parent.operation_type.upcase)
41
- assert_includes_location(directive_defn, ast_directive, required_location, errors)
41
+ assert_includes_location(directive_defn, ast_directive, required_location, context)
42
42
  when *SIMPLE_LOCATION_NODES
43
43
  required_location = SIMPLE_LOCATIONS[ast_parent.class]
44
- assert_includes_location(directive_defn, ast_directive, required_location, errors)
44
+ assert_includes_location(directive_defn, ast_directive, required_location, context)
45
45
  else
46
- errors << message("Directives can't be applied to #{ast_parent.class.name}s", ast_directive)
46
+ context.errors << message("Directives can't be applied to #{ast_parent.class.name}s", ast_directive, context: context)
47
47
  end
48
48
  end
49
49
 
50
- def assert_includes_location(directive_defn, directive_ast, required_location, errors)
50
+ def assert_includes_location(directive_defn, directive_ast, required_location, context)
51
51
  if !directive_defn.locations.include?(required_location)
52
52
  location_name = LOCATION_MESSAGE_NAMES[required_location]
53
53
  allowed_location_names = directive_defn.locations.map { |loc| LOCATION_MESSAGE_NAMES[loc] }
54
- errors << message("'@#{directive_defn.name}' can't be applied to #{location_name} (allowed: #{allowed_location_names.join(", ")})", directive_ast)
54
+ context.errors << message("'@#{directive_defn.name}' can't be applied to #{location_name} (allowed: #{allowed_location_names.join(", ")})", directive_ast, context: context)
55
55
  end
56
56
  end
57
57
  end
@@ -9,21 +9,21 @@ module GraphQL
9
9
  return if context.skip_field?(node.name)
10
10
  parent_type = context.object_types[-2]
11
11
  parent_type = parent_type.unwrap
12
- validate_field(context.errors, node, parent_type, parent)
12
+ validate_field(context, node, parent_type, parent)
13
13
  }
14
14
  end
15
15
 
16
16
  private
17
17
 
18
- def validate_field(errors, ast_field, parent_type, parent)
18
+ def validate_field(context, ast_field, parent_type, parent)
19
19
  if parent_type.kind.union?
20
- errors << message("Selections can't be made directly on unions (see selections on #{parent_type.name})", parent)
20
+ context.errors << message("Selections can't be made directly on unions (see selections on #{parent_type.name})", parent, context: context)
21
21
  return GraphQL::Language::Visitor::SKIP
22
22
  end
23
23
 
24
24
  field = parent_type.get_field(ast_field.name)
25
25
  if field.nil?
26
- errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", parent)
26
+ context.errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", parent, context: context)
27
27
  return GraphQL::Language::Visitor::SKIP
28
28
  end
29
29
  end
@@ -9,25 +9,25 @@ module GraphQL
9
9
  context.visitor[GraphQL::Language::Nodes::Field] << -> (node, parent) {
10
10
  return if context.skip_field?(node.name)
11
11
  field_defn = context.field_definition
12
- validate_field_selections(node, field_defn, context.errors)
12
+ validate_field_selections(node, field_defn, context)
13
13
  }
14
14
  end
15
15
 
16
16
  private
17
17
 
18
- def validate_field_selections(ast_field, field_defn, errors)
18
+ def validate_field_selections(ast_field, field_defn, context)
19
19
  resolved_type = field_defn.type.unwrap
20
20
 
21
21
  if resolved_type.kind.scalar? && ast_field.selections.any?
22
- error = message("Selections can't be made on scalars (field '#{ast_field.name}' returns #{resolved_type.name} but has selections [#{ast_field.selections.map(&:name).join(", ")}])", ast_field)
22
+ error = message("Selections can't be made on scalars (field '#{ast_field.name}' returns #{resolved_type.name} but has selections [#{ast_field.selections.map(&:name).join(", ")}])", ast_field, context: context)
23
23
  elsif resolved_type.kind.object? && ast_field.selections.none?
24
- error = message("Objects must have selections (field '#{ast_field.name}' returns #{resolved_type.name} but has no selections)", ast_field)
24
+ error = message("Objects must have selections (field '#{ast_field.name}' returns #{resolved_type.name} but has no selections)", ast_field, context: context)
25
25
  else
26
26
  error = nil
27
27
  end
28
28
 
29
29
  if !error.nil?
30
- errors << error
30
+ context.errors << error
31
31
  GraphQL::Language::Visitor::SKIP
32
32
  end
33
33
  end
@@ -22,7 +22,7 @@ module GraphQL
22
22
 
23
23
  def find_conflicts(field_map, context)
24
24
  field_map.each do |name, ast_fields|
25
- comparison = FieldDefinitionComparison.new(name, ast_fields)
25
+ comparison = FieldDefinitionComparison.new(name, ast_fields, context)
26
26
  context.errors.push(*comparison.errors)
27
27
 
28
28
 
@@ -63,27 +63,27 @@ module GraphQL
63
63
  include GraphQL::StaticValidation::Message::MessageHelper
64
64
  NAMED_VALUES = [GraphQL::Language::Nodes::Enum, GraphQL::Language::Nodes::VariableIdentifier]
65
65
  attr_reader :errors
66
- def initialize(name, defs)
66
+ def initialize(name, defs, context)
67
67
  errors = []
68
68
 
69
69
  names = defs.map(&:name).uniq
70
70
  if names.length != 1
71
- errors << message("Field '#{name}' has a field conflict: #{names.join(" or ")}?", defs.first)
71
+ errors << message("Field '#{name}' has a field conflict: #{names.join(" or ")}?", defs.first, context: context)
72
72
  end
73
73
 
74
74
  args = defs.map { |defn| reduce_list(defn.arguments)}.uniq
75
75
  if args.length != 1
76
- errors << message("Field '#{name}' has an argument conflict: #{args.map {|a| JSON.dump(a) }.join(" or ")}?", defs.first)
76
+ errors << message("Field '#{name}' has an argument conflict: #{args.map {|a| JSON.dump(a) }.join(" or ")}?", defs.first, context: context)
77
77
  end
78
78
 
79
79
  directive_names = defs.map { |defn| defn.directives.map(&:name) }.uniq
80
80
  if directive_names.length != 1
81
- errors << message("Field '#{name}' has a directive conflict: #{directive_names.map {|names| "[#{names.join(", ")}]"}.join(" or ")}?", defs.first)
81
+ errors << message("Field '#{name}' has a directive conflict: #{directive_names.map {|names| "[#{names.join(", ")}]"}.join(" or ")}?", defs.first, context: context)
82
82
  end
83
83
 
84
84
  directive_args = defs.map {|defn| defn.directives.map {|d| reduce_list(d.arguments) } }.uniq
85
85
  if directive_args.length != 1
86
- errors << message("Field '#{name}' has a directive argument conflict: #{directive_args.map {|args| JSON.dump(args)}.join(" or ")}?", defs.first)
86
+ errors << message("Field '#{name}' has a directive argument conflict: #{directive_args.map {|args| JSON.dump(args)}.join(" or ")}?", defs.first, context: context)
87
87
  end
88
88
 
89
89
  @errors = errors
@@ -9,7 +9,7 @@ module GraphQL
9
9
  fragment_parent = context.object_types[-2]
10
10
  fragment_child = context.object_types.last
11
11
  if fragment_child
12
- validate_fragment_in_scope(fragment_parent, fragment_child, node, context)
12
+ validate_fragment_in_scope(fragment_parent, fragment_child, node, context, context.path)
13
13
  end
14
14
  }
15
15
 
@@ -17,26 +17,25 @@ module GraphQL
17
17
 
18
18
  context.visitor[GraphQL::Language::Nodes::FragmentSpread] << -> (node, parent) {
19
19
  fragment_parent = context.object_types.last
20
- spreads_to_validate << [node, fragment_parent]
20
+ spreads_to_validate << FragmentSpread.new(node: node, parent_type: fragment_parent, path: context.path)
21
21
  }
22
22
 
23
- context.visitor[GraphQL::Language::Nodes::Document].leave << -> (node, parent) {
24
- spreads_to_validate.each do |spread_values|
25
- node, fragment_parent = spread_values
26
- fragment_child_name = context.fragments[node.name].type
23
+ context.visitor[GraphQL::Language::Nodes::Document].leave << -> (doc_node, parent) {
24
+ spreads_to_validate.each do |frag_spread|
25
+ fragment_child_name = context.fragments[frag_spread.node.name].type
27
26
  fragment_child = context.schema.types[fragment_child_name]
28
- validate_fragment_in_scope(fragment_parent, fragment_child, node, context)
27
+ validate_fragment_in_scope(frag_spread.parent_type, fragment_child, frag_spread.node, context, frag_spread.path)
29
28
  end
30
29
  }
31
30
  end
32
31
 
33
32
  private
34
33
 
35
- def validate_fragment_in_scope(parent_type, child_type, node, context)
34
+ def validate_fragment_in_scope(parent_type, child_type, node, context, path)
36
35
  intersecting_types = get_possible_types(parent_type, context.schema) & get_possible_types(child_type, context.schema)
37
36
  if intersecting_types.none?
38
37
  name = node.respond_to?(:name) ? " #{node.name}" : ""
39
- context.errors << message("Fragment#{name} on #{child_type.name} can't be spread inside #{parent_type.name}", node)
38
+ context.errors << message("Fragment#{name} on #{child_type.name} can't be spread inside #{parent_type.name}", node, path: path)
40
39
  end
41
40
  end
42
41
 
@@ -51,6 +50,15 @@ module GraphQL
51
50
  []
52
51
  end
53
52
  end
53
+
54
+ class FragmentSpread
55
+ attr_reader :node, :parent_type, :path
56
+ def initialize(node:, parent_type:, path:)
57
+ @node = node
58
+ @parent_type = parent_type
59
+ @path = path
60
+ end
61
+ end
54
62
  end
55
63
  end
56
64
  end
@@ -20,7 +20,7 @@ module GraphQL
20
20
  return unless node.type
21
21
  type = context.schema.types.fetch(node.type, nil)
22
22
  if type.nil?
23
- context.errors << message("No such type #{node.type}, so it can't be a fragment condition", node)
23
+ context.errors << message("No such type #{node.type}, so it can't be a fragment condition", node, context: context)
24
24
  GraphQL::Language::Visitor::SKIP
25
25
  end
26
26
  end
@@ -6,7 +6,7 @@ module GraphQL
6
6
  def validate(context)
7
7
  context.visitor[GraphQL::Language::Nodes::FragmentDefinition] << -> (node, parent) {
8
8
  if has_nested_spread(node, [], context)
9
- context.errors << message("Fragment #{node.name} contains an infinite loop", node)
9
+ context.errors << message("Fragment #{node.name} contains an infinite loop", node, context: context)
10
10
  end
11
11
  }
12
12
  end
@@ -23,7 +23,7 @@ module GraphQL
23
23
  return unless type_name
24
24
  type_def = context.schema.types[type_name]
25
25
  if type_def.nil? || !type_def.kind.composite?
26
- context.errors << message("Invalid fragment on type #{type_name} (must be Union, Interface or Object)", node)
26
+ context.errors << message("Invalid fragment on type #{type_name} (must be Union, Interface or Object)", node, context: context)
27
27
  GraphQL::Language::Visitor::SKIP
28
28
  end
29
29
  end
@@ -9,35 +9,46 @@ module GraphQL
9
9
  defined_fragments = []
10
10
 
11
11
  v[GraphQL::Language::Nodes::Document] << -> (node, parent) {
12
- defined_fragments = node.definitions.select { |defn| defn.is_a?(GraphQL::Language::Nodes::FragmentDefinition) }
12
+ defined_fragments = node.definitions
13
+ .select { |defn| defn.is_a?(GraphQL::Language::Nodes::FragmentDefinition) }
14
+ .map { |node| FragmentInstance.new(node: node, path: context.path) }
13
15
  }
14
16
 
15
17
  v[GraphQL::Language::Nodes::FragmentSpread] << -> (node, parent) {
16
- used_fragments << node
18
+ used_fragments << FragmentInstance.new(node: node, path: context.path)
17
19
  if defined_fragments.none? { |defn| defn.name == node.name }
18
20
  GraphQL::Language::Visitor::SKIP
19
21
  end
20
22
  }
21
- v[GraphQL::Language::Nodes::Document].leave << -> (node, parent) { add_errors(context.errors, used_fragments, defined_fragments) }
23
+ v[GraphQL::Language::Nodes::Document].leave << -> (node, parent) { add_errors(context, used_fragments, defined_fragments) }
22
24
  end
23
25
 
24
26
  private
25
27
 
26
- def add_errors(errors, used_fragments, defined_fragments)
28
+ def add_errors(context, used_fragments, defined_fragments)
27
29
  undefined_fragments = find_difference(used_fragments, defined_fragments.map(&:name))
28
30
  undefined_fragments.each do |fragment|
29
- errors << message("Fragment #{fragment.name} was used, but not defined", fragment)
31
+ context.errors << message("Fragment #{fragment.name} was used, but not defined", fragment.node, path: fragment.path)
30
32
  end
31
33
 
32
34
  unused_fragments = find_difference(defined_fragments, used_fragments.map(&:name))
33
35
  unused_fragments.each do |fragment|
34
- errors << message("Fragment #{fragment.name} was defined, but not used", fragment)
36
+ context.errors << message("Fragment #{fragment.name} was defined, but not used", fragment.node, path: fragment.path)
35
37
  end
36
38
  end
37
39
 
38
40
  def find_difference(fragments, allowed_fragment_names)
39
41
  fragments.select {|f| !allowed_fragment_names.include?(f.name) }
40
42
  end
43
+
44
+ class FragmentInstance
45
+ attr_reader :name, :node, :path
46
+ def initialize(node:, path:)
47
+ @node = node
48
+ @name = node.name
49
+ @path = path
50
+ end
51
+ end
41
52
  end
42
53
  end
43
54
  end
@@ -30,7 +30,7 @@ module GraphQL
30
30
 
31
31
  missing_names = required_argument_names - present_argument_names
32
32
  if missing_names.any?
33
- context.errors << message("#{ast_node.class.name.split("::").last} '#{ast_node.name}' is missing required arguments: #{missing_names.join(", ")}", ast_node)
33
+ context.errors << message("#{ast_node.class.name.split("::").last} '#{ast_node.name}' is missing required arguments: #{missing_names.join(", ")}", ast_node, context: context)
34
34
  end
35
35
  end
36
36
  end
@@ -15,11 +15,11 @@ module GraphQL
15
15
  def validate_default_value(node, literal_validator, context)
16
16
  value = node.default_value
17
17
  if node.type.is_a?(GraphQL::Language::Nodes::NonNullType)
18
- context.errors << message("Non-null variable $#{node.name} can't have a default value", node)
18
+ context.errors << message("Non-null variable $#{node.name} can't have a default value", node, context: context)
19
19
  else
20
20
  type = context.schema.type_from_ast(node.type)
21
21
  if !literal_validator.validate(value, type)
22
- context.errors << message("Default value for $#{node.name} doesn't match type #{type}", node)
22
+ context.errors << message("Default value for $#{node.name} doesn't match type #{type}", node, context: context)
23
23
  end
24
24
  end
25
25
  end
@@ -44,11 +44,11 @@ module GraphQL
44
44
  arg_inner_type = arg_defn_type.unwrap
45
45
 
46
46
  if var_inner_type != arg_inner_type
47
- context.errors << create_error("Type mismatch", var_type, ast_var, arg_defn, arg_node)
47
+ context.errors << create_error("Type mismatch", var_type, ast_var, arg_defn, arg_node, context)
48
48
  elsif list_dimension(var_type) != list_dimension(arg_defn_type)
49
- context.errors << create_error("List dimension mismatch", var_type, ast_var, arg_defn, arg_node)
49
+ context.errors << create_error("List dimension mismatch", var_type, ast_var, arg_defn, arg_node, context)
50
50
  elsif !non_null_levels_match(arg_defn_type, var_type)
51
- context.errors << create_error("Nullability mismatch", var_type, ast_var, arg_defn, arg_node)
51
+ context.errors << create_error("Nullability mismatch", var_type, ast_var, arg_defn, arg_node, context)
52
52
  end
53
53
  end
54
54
 
@@ -62,8 +62,8 @@ module GraphQL
62
62
  end
63
63
  end
64
64
 
65
- def create_error(error_message, var_type, ast_var, arg_defn, arg_node)
66
- message("#{error_message} on variable $#{ast_var.name} and argument #{arg_node.name} (#{var_type.to_s} / #{arg_defn.type.to_s})", arg_node)
65
+ def create_error(error_message, var_type, ast_var, arg_defn, arg_node, context)
66
+ message("#{error_message} on variable $#{ast_var.name} and argument #{arg_node.name} (#{var_type.to_s} / #{arg_defn.type.to_s})", arg_node, context: context)
67
67
  end
68
68
 
69
69
  def list_dimension(type)
@@ -15,7 +15,7 @@ module GraphQL
15
15
  type_name = get_type_name(node.type)
16
16
  type = context.schema.types[type_name]
17
17
  if !type.kind.input?
18
- context.errors << message("#{type.name} isn't a valid input type (on $#{node.name})", node)
18
+ context.errors << message("#{type.name} isn't a valid input type (on $#{node.name})", node, context: context)
19
19
  end
20
20
  end
21
21