graphql 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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