graphql 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +31 -41
  3. data/lib/graphql/argument.rb +23 -21
  4. data/lib/graphql/base_type.rb +5 -8
  5. data/lib/graphql/define/assign_argument.rb +5 -2
  6. data/lib/graphql/define/type_definer.rb +2 -1
  7. data/lib/graphql/directive.rb +34 -36
  8. data/lib/graphql/directive/include_directive.rb +3 -7
  9. data/lib/graphql/directive/skip_directive.rb +3 -7
  10. data/lib/graphql/enum_type.rb +78 -76
  11. data/lib/graphql/execution_error.rb +1 -3
  12. data/lib/graphql/field.rb +99 -95
  13. data/lib/graphql/input_object_type.rb +49 -47
  14. data/lib/graphql/interface_type.rb +31 -34
  15. data/lib/graphql/introspection.rb +19 -18
  16. data/lib/graphql/introspection/directive_location_enum.rb +8 -0
  17. data/lib/graphql/introspection/directive_type.rb +1 -3
  18. data/lib/graphql/introspection/field_type.rb +1 -1
  19. data/lib/graphql/introspection/fields_field.rb +1 -1
  20. data/lib/graphql/introspection/introspection_query.rb +1 -3
  21. data/lib/graphql/introspection/possible_types_field.rb +7 -1
  22. data/lib/graphql/introspection/schema_field.rb +13 -9
  23. data/lib/graphql/introspection/type_by_name_field.rb +13 -17
  24. data/lib/graphql/introspection/typename_field.rb +12 -8
  25. data/lib/graphql/language.rb +5 -9
  26. data/lib/graphql/language/lexer.rb +668 -0
  27. data/lib/graphql/language/lexer.rl +149 -0
  28. data/lib/graphql/language/parser.rb +842 -116
  29. data/lib/graphql/language/parser.y +264 -0
  30. data/lib/graphql/language/token.rb +21 -0
  31. data/lib/graphql/list_type.rb +33 -31
  32. data/lib/graphql/non_null_type.rb +33 -31
  33. data/lib/graphql/object_type.rb +52 -55
  34. data/lib/graphql/query.rb +83 -80
  35. data/lib/graphql/query/context.rb +5 -1
  36. data/lib/graphql/query/directive_resolution.rb +16 -0
  37. data/lib/graphql/query/executor.rb +3 -3
  38. data/lib/graphql/query/input_validation_result.rb +17 -15
  39. data/lib/graphql/query/serial_execution.rb +5 -5
  40. data/lib/graphql/query/serial_execution/execution_context.rb +4 -3
  41. data/lib/graphql/query/serial_execution/selection_resolution.rb +19 -21
  42. data/lib/graphql/query/serial_execution/value_resolution.rb +1 -1
  43. data/lib/graphql/query/type_resolver.rb +22 -18
  44. data/lib/graphql/query/variable_validation_error.rb +14 -12
  45. data/lib/graphql/schema.rb +87 -77
  46. data/lib/graphql/schema/each_item_validator.rb +16 -12
  47. data/lib/graphql/schema/field_validator.rb +14 -10
  48. data/lib/graphql/schema/implementation_validator.rb +26 -22
  49. data/lib/graphql/schema/middleware_chain.rb +2 -1
  50. data/lib/graphql/schema/possible_types.rb +34 -0
  51. data/lib/graphql/schema/printer.rb +122 -120
  52. data/lib/graphql/schema/type_expression.rb +1 -0
  53. data/lib/graphql/schema/type_map.rb +3 -10
  54. data/lib/graphql/schema/type_reducer.rb +65 -81
  55. data/lib/graphql/schema/type_validator.rb +45 -41
  56. data/lib/graphql/static_validation.rb +7 -9
  57. data/lib/graphql/static_validation/all_rules.rb +29 -24
  58. data/lib/graphql/static_validation/arguments_validator.rb +39 -35
  59. data/lib/graphql/static_validation/literal_validator.rb +44 -40
  60. data/lib/graphql/static_validation/message.rb +30 -26
  61. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +15 -11
  62. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +14 -10
  63. data/lib/graphql/static_validation/rules/directives_are_defined.rb +16 -12
  64. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +59 -0
  65. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +25 -21
  66. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +28 -24
  67. data/lib/graphql/static_validation/rules/fields_will_merge.rb +84 -80
  68. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +49 -43
  69. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +22 -17
  70. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +19 -15
  71. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +25 -20
  72. data/lib/graphql/static_validation/rules/fragments_are_used.rb +36 -23
  73. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +29 -25
  74. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +21 -17
  75. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +79 -70
  76. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +24 -20
  77. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +122 -119
  78. data/lib/graphql/static_validation/type_stack.rb +138 -129
  79. data/lib/graphql/static_validation/validator.rb +29 -25
  80. data/lib/graphql/type_kinds.rb +42 -40
  81. data/lib/graphql/union_type.rb +22 -16
  82. data/lib/graphql/version.rb +1 -1
  83. data/readme.md +12 -27
  84. data/spec/graphql/base_type_spec.rb +3 -3
  85. data/spec/graphql/directive_spec.rb +10 -18
  86. data/spec/graphql/enum_type_spec.rb +7 -7
  87. data/spec/graphql/execution_error_spec.rb +1 -1
  88. data/spec/graphql/field_spec.rb +14 -13
  89. data/spec/graphql/id_type_spec.rb +6 -6
  90. data/spec/graphql/input_object_type_spec.rb +39 -39
  91. data/spec/graphql/interface_type_spec.rb +16 -32
  92. data/spec/graphql/introspection/directive_type_spec.rb +5 -9
  93. data/spec/graphql/introspection/input_value_type_spec.rb +10 -4
  94. data/spec/graphql/introspection/introspection_query_spec.rb +2 -2
  95. data/spec/graphql/introspection/schema_type_spec.rb +2 -2
  96. data/spec/graphql/introspection/type_type_spec.rb +34 -6
  97. data/spec/graphql/language/parser_spec.rb +299 -105
  98. data/spec/graphql/language/visitor_spec.rb +4 -4
  99. data/spec/graphql/list_type_spec.rb +11 -11
  100. data/spec/graphql/object_type_spec.rb +10 -10
  101. data/spec/graphql/query/arguments_spec.rb +7 -7
  102. data/spec/graphql/query/context_spec.rb +11 -3
  103. data/spec/graphql/query/executor_spec.rb +26 -19
  104. data/spec/graphql/query/serial_execution/execution_context_spec.rb +6 -6
  105. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -2
  106. data/spec/graphql/query/type_resolver_spec.rb +3 -3
  107. data/spec/graphql/query_spec.rb +6 -38
  108. data/spec/graphql/scalar_type_spec.rb +28 -19
  109. data/spec/graphql/schema/field_validator_spec.rb +1 -1
  110. data/spec/graphql/schema/middleware_chain_spec.rb +12 -1
  111. data/spec/graphql/schema/printer_spec.rb +12 -4
  112. data/spec/graphql/schema/rescue_middleware_spec.rb +1 -1
  113. data/spec/graphql/schema/type_expression_spec.rb +2 -2
  114. data/spec/graphql/schema/type_reducer_spec.rb +21 -36
  115. data/spec/graphql/schema/type_validator_spec.rb +9 -9
  116. data/spec/graphql/schema_spec.rb +1 -1
  117. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +4 -4
  118. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +4 -4
  119. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +5 -5
  120. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +39 -0
  121. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +5 -5
  122. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -4
  123. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
  124. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +1 -1
  125. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +2 -2
  126. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +2 -2
  127. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +2 -2
  128. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +3 -3
  129. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +3 -3
  130. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +5 -5
  131. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +3 -1
  132. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +4 -4
  133. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +3 -3
  134. data/spec/graphql/static_validation/type_stack_spec.rb +3 -2
  135. data/spec/graphql/static_validation/validator_spec.rb +26 -6
  136. data/spec/graphql/union_type_spec.rb +5 -4
  137. data/spec/spec_helper.rb +2 -5
  138. data/spec/support/dairy_app.rb +30 -9
  139. data/spec/support/dairy_data.rb +1 -1
  140. data/spec/support/star_wars_data.rb +26 -26
  141. data/spec/support/star_wars_schema.rb +1 -1
  142. metadata +40 -21
  143. data/lib/graphql/language/transform.rb +0 -113
  144. data/lib/graphql/query/directive_chain.rb +0 -44
  145. data/lib/graphql/repl.rb +0 -27
  146. data/spec/graphql/language/transform_spec.rb +0 -156
@@ -1,48 +1,52 @@
1
- # Test whether `ast_value` is a valid input for `type`
2
- class GraphQL::StaticValidation::LiteralValidator
3
- def validate(ast_value, type)
4
- if type.kind.non_null?
5
- (!ast_value.nil?) && validate(ast_value, type.of_type)
6
- elsif type.kind.list?
7
- item_type = type.of_type
8
- ensure_array(ast_value).all? { |val| validate(val, item_type) }
9
- elsif type.kind.scalar? && !ast_value.is_a?(GraphQL::Language::Nodes::AbstractNode) && !ast_value.is_a?(Array)
10
- type.valid_input?(ast_value)
11
- elsif type.kind.enum? && ast_value.is_a?(GraphQL::Language::Nodes::Enum)
12
- type.valid_input?(ast_value.name)
13
- elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
14
- required_input_fields_are_present(type, ast_value) &&
15
- present_input_field_values_are_valid(type, ast_value)
16
- elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
17
- true
18
- else
19
- false
20
- end
21
- end
1
+ module GraphQL
2
+ module StaticValidation
3
+ # Test whether `ast_value` is a valid input for `type`
4
+ class LiteralValidator
5
+ def validate(ast_value, type)
6
+ if type.kind.non_null?
7
+ (!ast_value.nil?) && validate(ast_value, type.of_type)
8
+ elsif type.kind.list?
9
+ item_type = type.of_type
10
+ ensure_array(ast_value).all? { |val| validate(val, item_type) }
11
+ elsif type.kind.scalar? && !ast_value.is_a?(GraphQL::Language::Nodes::AbstractNode) && !ast_value.is_a?(Array)
12
+ type.valid_input?(ast_value)
13
+ elsif type.kind.enum? && ast_value.is_a?(GraphQL::Language::Nodes::Enum)
14
+ type.valid_input?(ast_value.name)
15
+ elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
16
+ required_input_fields_are_present(type, ast_value) &&
17
+ present_input_field_values_are_valid(type, ast_value)
18
+ elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
19
+ true
20
+ else
21
+ false
22
+ end
23
+ end
22
24
 
23
25
 
24
- private
26
+ private
25
27
 
26
28
 
27
- def required_input_fields_are_present(type, ast_node)
28
- required_field_names = type.input_fields
29
- .values
30
- .select { |f| f.type.kind.non_null? }
31
- .map(&:name)
32
- present_field_names = ast_node.arguments.map(&:name)
33
- missing_required_field_names = required_field_names - present_field_names
34
- missing_required_field_names.none?
35
- end
29
+ def required_input_fields_are_present(type, ast_node)
30
+ required_field_names = type.input_fields
31
+ .values
32
+ .select { |f| f.type.kind.non_null? }
33
+ .map(&:name)
34
+ present_field_names = ast_node.arguments.map(&:name)
35
+ missing_required_field_names = required_field_names - present_field_names
36
+ missing_required_field_names.none?
37
+ end
36
38
 
37
- def present_input_field_values_are_valid(type, ast_node)
38
- fields = type.input_fields
39
- ast_node.arguments.all? do |value|
40
- field = fields[value.name]
41
- field ? validate(value.value, field.type) : true
42
- end
43
- end
39
+ def present_input_field_values_are_valid(type, ast_node)
40
+ fields = type.input_fields
41
+ ast_node.arguments.all? do |value|
42
+ field = fields[value.name]
43
+ field ? validate(value.value, field.type) : true
44
+ end
45
+ end
44
46
 
45
- def ensure_array(value)
46
- value.is_a?(Array) ? value : [value]
47
+ def ensure_array(value)
48
+ value.is_a?(Array) ? value : [value]
49
+ end
50
+ end
47
51
  end
48
52
  end
@@ -1,32 +1,36 @@
1
- # Generates GraphQL-compliant validation message.
2
- # Only supports one "location", too bad :(
3
- class GraphQL::StaticValidation::Message
4
- # Convenience for validators
5
- module MessageHelper
6
- # Error `message` is located at `node`
7
- def message(message, node)
8
- GraphQL::StaticValidation::Message.new(message, line: node.line, col: node.col)
9
- end
10
- end
11
- attr_reader :message, :line, :col
1
+ module GraphQL
2
+ module StaticValidation
3
+ # Generates GraphQL-compliant validation message.
4
+ # Only supports one "location", too bad :(
5
+ class Message
6
+ # Convenience for validators
7
+ module MessageHelper
8
+ # Error `message` is located at `node`
9
+ def message(message, node)
10
+ GraphQL::StaticValidation::Message.new(message, line: node.line, col: node.col)
11
+ end
12
+ end
13
+ attr_reader :message, :line, :col
12
14
 
13
- def initialize(message, line: nil, col: nil)
14
- @message = message
15
- @line = line
16
- @col = col
17
- end
15
+ def initialize(message, line: nil, col: nil)
16
+ @message = message
17
+ @line = line
18
+ @col = col
19
+ end
18
20
 
19
- # A hash representation of this Message
20
- def to_h
21
- {
22
- "message" => message,
23
- "locations" => locations
24
- }
25
- end
21
+ # A hash representation of this Message
22
+ def to_h
23
+ {
24
+ "message" => message,
25
+ "locations" => locations
26
+ }
27
+ end
26
28
 
27
- private
29
+ private
28
30
 
29
- def locations
30
- @line.nil? && @col.nil ? [] : [{"line" => @line, "column" => @col}]
31
+ def locations
32
+ @line.nil? && @col.nil? ? [] : [{"line" => @line, "column" => @col}]
33
+ end
34
+ end
31
35
  end
32
36
  end
@@ -1,14 +1,18 @@
1
- class GraphQL::StaticValidation::ArgumentLiteralsAreCompatible < GraphQL::StaticValidation::ArgumentsValidator
2
- def validate_node(parent, node, defn, context)
3
- return if node.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
4
- validator = GraphQL::StaticValidation::LiteralValidator.new
5
- arg_defn = defn.arguments[node.name]
6
- return unless arg_defn
7
- valid = validator.validate(node.value, arg_defn.type)
8
- if !valid
9
- kind_of_node = node_type(parent)
10
- error_arg_name = parent_name(parent, defn)
11
- context.errors << message("Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value", parent)
1
+ module GraphQL
2
+ module StaticValidation
3
+ class ArgumentLiteralsAreCompatible < GraphQL::StaticValidation::ArgumentsValidator
4
+ def validate_node(parent, node, defn, context)
5
+ return if node.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
6
+ validator = GraphQL::StaticValidation::LiteralValidator.new
7
+ arg_defn = defn.arguments[node.name]
8
+ return unless arg_defn
9
+ valid = validator.validate(node.value, arg_defn.type)
10
+ if !valid
11
+ kind_of_node = node_type(parent)
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)
14
+ end
15
+ end
12
16
  end
13
17
  end
14
18
  end
@@ -1,13 +1,17 @@
1
- class GraphQL::StaticValidation::ArgumentsAreDefined < GraphQL::StaticValidation::ArgumentsValidator
2
- def validate_node(parent, node, defn, context)
3
- argument_defn = defn.arguments[node.name]
4
- if argument_defn.nil?
5
- kind_of_node = node_type(parent)
6
- error_arg_name = parent_name(parent, defn)
7
- context.errors << message("#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'", parent)
8
- GraphQL::Language::Visitor::SKIP
9
- else
10
- nil
1
+ module GraphQL
2
+ module StaticValidation
3
+ class ArgumentsAreDefined < GraphQL::StaticValidation::ArgumentsValidator
4
+ def validate_node(parent, node, defn, context)
5
+ argument_defn = defn.arguments[node.name]
6
+ if argument_defn.nil?
7
+ kind_of_node = node_type(parent)
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)
10
+ GraphQL::Language::Visitor::SKIP
11
+ else
12
+ nil
13
+ end
14
+ end
11
15
  end
12
16
  end
13
17
  end
@@ -1,18 +1,22 @@
1
- class GraphQL::StaticValidation::DirectivesAreDefined
2
- include GraphQL::StaticValidation::Message::MessageHelper
1
+ module GraphQL
2
+ module StaticValidation
3
+ class DirectivesAreDefined
4
+ include GraphQL::StaticValidation::Message::MessageHelper
3
5
 
4
- def validate(context)
5
- directive_names = context.schema.directives.keys
6
- context.visitor[GraphQL::Language::Nodes::Directive] << -> (node, parent) {
7
- validate_directive(node, directive_names, context.errors)
8
- }
9
- end
6
+ def validate(context)
7
+ directive_names = context.schema.directives.keys
8
+ context.visitor[GraphQL::Language::Nodes::Directive] << -> (node, parent) {
9
+ validate_directive(node, directive_names, context.errors)
10
+ }
11
+ end
10
12
 
11
- private
13
+ private
12
14
 
13
- def validate_directive(ast_directive, directive_names, errors)
14
- if !directive_names.include?(ast_directive.name)
15
- errors << message("Directive @#{ast_directive.name} is not defined", ast_directive)
15
+ def validate_directive(ast_directive, directive_names, errors)
16
+ if !directive_names.include?(ast_directive.name)
17
+ errors << message("Directive @#{ast_directive.name} is not defined", ast_directive)
18
+ end
19
+ end
16
20
  end
17
21
  end
18
22
  end
@@ -0,0 +1,59 @@
1
+ module GraphQL
2
+ module StaticValidation
3
+ class DirectivesAreInValidLocations
4
+ include GraphQL::StaticValidation::Message::MessageHelper
5
+ include GraphQL::Language
6
+
7
+ def validate(context)
8
+ directives = context.schema.directives
9
+
10
+ context.visitor[Nodes::Directive] << -> (node, parent) {
11
+ validate_location(node, parent, directives, context.errors)
12
+ }
13
+ end
14
+
15
+ private
16
+
17
+ LOCATION_MESSAGE_NAMES = {
18
+ GraphQL::Directive::QUERY => "queries",
19
+ GraphQL::Directive::MUTATION => "mutations",
20
+ GraphQL::Directive::SUBSCRIPTION => "subscriptions",
21
+ GraphQL::Directive::FIELD => "fields",
22
+ GraphQL::Directive::FRAGMENT_DEFINITION => "fragment definitions",
23
+ GraphQL::Directive::FRAGMENT_SPREAD => "fragment spreads",
24
+ GraphQL::Directive::INLINE_FRAGMENT => "inline fragments",
25
+ }
26
+
27
+ SIMPLE_LOCATIONS = {
28
+ Nodes::Field => GraphQL::Directive::FIELD,
29
+ Nodes::InlineFragment => GraphQL::Directive::INLINE_FRAGMENT,
30
+ Nodes::FragmentSpread => GraphQL::Directive::FRAGMENT_SPREAD,
31
+ Nodes::FragmentDefinition => GraphQL::Directive::FRAGMENT_DEFINITION,
32
+ }
33
+
34
+ SIMPLE_LOCATION_NODES = SIMPLE_LOCATIONS.keys
35
+
36
+ def validate_location(ast_directive, ast_parent, directives, errors)
37
+ directive_defn = directives[ast_directive.name]
38
+ case ast_parent
39
+ when Nodes::OperationDefinition
40
+ required_location = GraphQL::Directive.const_get(ast_parent.operation_type.upcase)
41
+ assert_includes_location(directive_defn, ast_directive, required_location, errors)
42
+ when *SIMPLE_LOCATION_NODES
43
+ required_location = SIMPLE_LOCATIONS[ast_parent.class]
44
+ assert_includes_location(directive_defn, ast_directive, required_location, errors)
45
+ else
46
+ errors << message("Directives can't be applied to #{ast_parent.class.name}s", ast_directive)
47
+ end
48
+ end
49
+
50
+ def assert_includes_location(directive_defn, directive_ast, required_location, errors)
51
+ if !directive_defn.locations.include?(required_location)
52
+ location_name = LOCATION_MESSAGE_NAMES[required_location]
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)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,28 +1,32 @@
1
- class GraphQL::StaticValidation::FieldsAreDefinedOnType
2
- include GraphQL::StaticValidation::Message::MessageHelper
1
+ module GraphQL
2
+ module StaticValidation
3
+ class FieldsAreDefinedOnType
4
+ include GraphQL::StaticValidation::Message::MessageHelper
3
5
 
4
- def validate(context)
5
- visitor = context.visitor
6
- visitor[GraphQL::Language::Nodes::Field] << -> (node, parent) {
7
- return if context.skip_field?(node.name)
8
- parent_type = context.object_types[-2]
9
- parent_type = parent_type.unwrap
10
- validate_field(context.errors, node, parent_type, parent)
11
- }
12
- end
6
+ def validate(context)
7
+ visitor = context.visitor
8
+ visitor[GraphQL::Language::Nodes::Field] << -> (node, parent) {
9
+ return if context.skip_field?(node.name)
10
+ parent_type = context.object_types[-2]
11
+ parent_type = parent_type.unwrap
12
+ validate_field(context.errors, node, parent_type, parent)
13
+ }
14
+ end
13
15
 
14
- private
16
+ private
15
17
 
16
- def validate_field(errors, ast_field, parent_type, parent)
17
- if parent_type.kind.union?
18
- errors << message("Selections can't be made directly on unions (see selections on #{parent_type.name})", parent)
19
- return GraphQL::Language::Visitor::SKIP
20
- end
18
+ def validate_field(errors, ast_field, parent_type, parent)
19
+ if parent_type.kind.union?
20
+ errors << message("Selections can't be made directly on unions (see selections on #{parent_type.name})", parent)
21
+ return GraphQL::Language::Visitor::SKIP
22
+ end
21
23
 
22
- field = parent_type.get_field(ast_field.name)
23
- if field.nil?
24
- errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", parent)
25
- return GraphQL::Language::Visitor::SKIP
24
+ field = parent_type.get_field(ast_field.name)
25
+ if field.nil?
26
+ errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", parent)
27
+ return GraphQL::Language::Visitor::SKIP
28
+ end
29
+ end
26
30
  end
27
31
  end
28
32
  end
@@ -1,32 +1,36 @@
1
- # Scalars _can't_ have selections
2
- # Objects _must_ have selections
3
- class GraphQL::StaticValidation::FieldsHaveAppropriateSelections
4
- include GraphQL::StaticValidation::Message::MessageHelper
1
+ module GraphQL
2
+ module StaticValidation
3
+ # Scalars _can't_ have selections
4
+ # Objects _must_ have selections
5
+ class FieldsHaveAppropriateSelections
6
+ include GraphQL::StaticValidation::Message::MessageHelper
5
7
 
6
- def validate(context)
7
- context.visitor[GraphQL::Language::Nodes::Field] << -> (node, parent) {
8
- return if context.skip_field?(node.name)
9
- field_defn = context.field_definition
10
- validate_field_selections(node, field_defn, context.errors)
11
- }
12
- end
8
+ def validate(context)
9
+ context.visitor[GraphQL::Language::Nodes::Field] << -> (node, parent) {
10
+ return if context.skip_field?(node.name)
11
+ field_defn = context.field_definition
12
+ validate_field_selections(node, field_defn, context.errors)
13
+ }
14
+ end
13
15
 
14
- private
16
+ private
15
17
 
16
- def validate_field_selections(ast_field, field_defn, errors)
17
- resolved_type = field_defn.type.unwrap
18
+ def validate_field_selections(ast_field, field_defn, errors)
19
+ resolved_type = field_defn.type.unwrap
18
20
 
19
- if resolved_type.kind.scalar? && ast_field.selections.any?
20
- 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)
21
- elsif resolved_type.kind.object? && ast_field.selections.none?
22
- error = message("Objects must have selections (field '#{ast_field.name}' returns #{resolved_type.name} but has no selections)", ast_field)
23
- else
24
- error = nil
25
- end
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)
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)
25
+ else
26
+ error = nil
27
+ end
26
28
 
27
- if !error.nil?
28
- errors << error
29
- GraphQL::Language::Visitor::SKIP
29
+ if !error.nil?
30
+ errors << error
31
+ GraphQL::Language::Visitor::SKIP
32
+ end
33
+ end
30
34
  end
31
35
  end
32
36
  end
@@ -1,100 +1,104 @@
1
- class GraphQL::StaticValidation::FieldsWillMerge
2
- def validate(context)
3
- fragments = {}
4
- has_selections = []
5
- visitor = context.visitor
6
- visitor[GraphQL::Language::Nodes::OperationDefinition] << -> (node, parent) {
7
- if node.selections.any?
8
- has_selections << node
1
+ module GraphQL
2
+ module StaticValidation
3
+ class FieldsWillMerge
4
+ def validate(context)
5
+ fragments = {}
6
+ has_selections = []
7
+ visitor = context.visitor
8
+ visitor[GraphQL::Language::Nodes::OperationDefinition] << -> (node, parent) {
9
+ if node.selections.any?
10
+ has_selections << node
11
+ end
12
+ }
13
+ visitor[GraphQL::Language::Nodes::Document].leave << -> (node, parent) {
14
+ has_selections.each { |node|
15
+ field_map = gather_fields_by_name(node.selections, {}, [], context)
16
+ find_conflicts(field_map, context)
17
+ }
18
+ }
9
19
  end
10
- }
11
- visitor[GraphQL::Language::Nodes::Document].leave << -> (node, parent) {
12
- has_selections.each { |node|
13
- field_map = gather_fields_by_name(node.selections, {}, [], context)
14
- find_conflicts(field_map, context)
15
- }
16
- }
17
- end
18
20
 
19
- private
21
+ private
20
22
 
21
- def find_conflicts(field_map, context)
22
- field_map.each do |name, ast_fields|
23
- comparison = FieldDefinitionComparison.new(name, ast_fields)
24
- context.errors.push(*comparison.errors)
23
+ def find_conflicts(field_map, context)
24
+ field_map.each do |name, ast_fields|
25
+ comparison = FieldDefinitionComparison.new(name, ast_fields)
26
+ context.errors.push(*comparison.errors)
25
27
 
26
28
 
27
- subfield_map = {}
28
- visited_fragments = []
29
- ast_fields.each do |defn|
30
- gather_fields_by_name(defn.selections, subfield_map, visited_fragments, context)
29
+ subfield_map = {}
30
+ visited_fragments = []
31
+ ast_fields.each do |defn|
32
+ gather_fields_by_name(defn.selections, subfield_map, visited_fragments, context)
33
+ end
34
+ find_conflicts(subfield_map, context)
35
+ end
31
36
  end
32
- find_conflicts(subfield_map, context)
33
- end
34
- end
35
37
 
36
- def gather_fields_by_name(fields, field_map, visited_fragments, context)
37
- fields.each do |field|
38
- if field.is_a?(GraphQL::Language::Nodes::InlineFragment)
39
- next_fields = field.selections
40
- elsif field.is_a?(GraphQL::Language::Nodes::FragmentSpread)
41
- if visited_fragments.include?(field.name)
42
- next
43
- else
44
- visited_fragments << field.name
38
+ def gather_fields_by_name(fields, field_map, visited_fragments, context)
39
+ fields.each do |field|
40
+ if field.is_a?(GraphQL::Language::Nodes::InlineFragment)
41
+ next_fields = field.selections
42
+ elsif field.is_a?(GraphQL::Language::Nodes::FragmentSpread)
43
+ if visited_fragments.include?(field.name)
44
+ next
45
+ else
46
+ visited_fragments << field.name
47
+ end
48
+ fragment_defn = context.fragments[field.name]
49
+ next_fields = fragment_defn ? fragment_defn.selections : []
50
+ else
51
+ name_in_selection = field.alias || field.name
52
+ field_map[name_in_selection] ||= []
53
+ field_map[name_in_selection].push(field)
54
+ next_fields = []
55
+ end
56
+ gather_fields_by_name(next_fields, field_map, visited_fragments, context)
45
57
  end
46
- fragment = context.fragments[field.name]
47
- next_fields = fragment.selections
48
- else
49
- name_in_selection = field.alias || field.name
50
- field_map[name_in_selection] ||= []
51
- field_map[name_in_selection].push(field)
52
- next_fields = []
58
+ field_map
53
59
  end
54
- gather_fields_by_name(next_fields, field_map, visited_fragments, context)
55
- end
56
- field_map
57
- end
58
60
 
59
- # Compare two field definitions, add errors to the list if there are any
60
- class FieldDefinitionComparison
61
- include GraphQL::StaticValidation::Message::MessageHelper
62
- NAMED_VALUES = [GraphQL::Language::Nodes::Enum, GraphQL::Language::Nodes::VariableIdentifier]
63
- attr_reader :errors
64
- def initialize(name, defs)
65
- errors = []
61
+ # Compare two field definitions, add errors to the list if there are any
62
+ class FieldDefinitionComparison
63
+ include GraphQL::StaticValidation::Message::MessageHelper
64
+ NAMED_VALUES = [GraphQL::Language::Nodes::Enum, GraphQL::Language::Nodes::VariableIdentifier]
65
+ attr_reader :errors
66
+ def initialize(name, defs)
67
+ errors = []
66
68
 
67
- names = defs.map(&:name).uniq
68
- if names.length != 1
69
- errors << message("Field '#{name}' has a field conflict: #{names.join(" or ")}?", defs.first)
70
- end
69
+ names = defs.map(&:name).uniq
70
+ if names.length != 1
71
+ errors << message("Field '#{name}' has a field conflict: #{names.join(" or ")}?", defs.first)
72
+ end
71
73
 
72
- args = defs.map { |defn| reduce_list(defn.arguments)}.uniq
73
- if args.length != 1
74
- errors << message("Field '#{name}' has an argument conflict: #{args.map {|a| JSON.dump(a) }.join(" or ")}?", defs.first)
75
- end
74
+ args = defs.map { |defn| reduce_list(defn.arguments)}.uniq
75
+ if args.length != 1
76
+ errors << message("Field '#{name}' has an argument conflict: #{args.map {|a| JSON.dump(a) }.join(" or ")}?", defs.first)
77
+ end
76
78
 
77
- directive_names = defs.map { |defn| defn.directives.map(&:name) }.uniq
78
- if directive_names.length != 1
79
- errors << message("Field '#{name}' has a directive conflict: #{directive_names.map {|names| "[#{names.join(", ")}]"}.join(" or ")}?", defs.first)
80
- end
79
+ directive_names = defs.map { |defn| defn.directives.map(&:name) }.uniq
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)
82
+ end
81
83
 
82
- directive_args = defs.map {|defn| defn.directives.map {|d| reduce_list(d.arguments) } }.uniq
83
- if directive_args.length != 1
84
- errors << message("Field '#{name}' has a directive argument conflict: #{directive_args.map {|args| JSON.dump(args)}.join(" or ")}?", defs.first)
85
- end
84
+ directive_args = defs.map {|defn| defn.directives.map {|d| reduce_list(d.arguments) } }.uniq
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)
87
+ end
86
88
 
87
- @errors = errors
88
- end
89
+ @errors = errors
90
+ end
89
91
 
90
- private
92
+ private
91
93
 
92
- # Turn AST tree into a hash
93
- # can't look up args, the names just have to match
94
- def reduce_list(args)
95
- args.reduce({}) do |memo, a|
96
- memo[a.name] = NAMED_VALUES.include?(a.value.class) ? a.value.name : a.value
97
- memo
94
+ # Turn AST tree into a hash
95
+ # can't look up args, the names just have to match
96
+ def reduce_list(args)
97
+ args.reduce({}) do |memo, a|
98
+ memo[a.name] = NAMED_VALUES.include?(a.value.class) ? a.value.name : a.value
99
+ memo
100
+ end
101
+ end
98
102
  end
99
103
  end
100
104
  end