graphql 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +10 -0
  3. data/lib/graphql/base_type.rb +8 -5
  4. data/lib/graphql/compatibility.rb +3 -0
  5. data/lib/graphql/compatibility/execution_specification.rb +414 -0
  6. data/lib/graphql/compatibility/query_parser_specification.rb +117 -0
  7. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +81 -0
  8. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +78 -0
  9. data/lib/graphql/compatibility/schema_parser_specification.rb +239 -0
  10. data/lib/graphql/define/instance_definable.rb +53 -21
  11. data/lib/graphql/directive.rb +1 -1
  12. data/lib/graphql/enum_type.rb +31 -8
  13. data/lib/graphql/execution/directive_checks.rb +0 -6
  14. data/lib/graphql/input_object_type.rb +6 -4
  15. data/lib/graphql/introspection/arguments_field.rb +3 -1
  16. data/lib/graphql/introspection/enum_values_field.rb +10 -5
  17. data/lib/graphql/introspection/fields_field.rb +1 -1
  18. data/lib/graphql/introspection/input_fields_field.rb +2 -2
  19. data/lib/graphql/introspection/interfaces_field.rb +7 -1
  20. data/lib/graphql/introspection/possible_types_field.rb +1 -1
  21. data/lib/graphql/introspection/schema_type.rb +1 -1
  22. data/lib/graphql/introspection/type_by_name_field.rb +4 -2
  23. data/lib/graphql/introspection/type_type.rb +7 -6
  24. data/lib/graphql/language/lexer.rl +0 -4
  25. data/lib/graphql/language/parser.rb +1 -1
  26. data/lib/graphql/language/parser.y +1 -1
  27. data/lib/graphql/list_type.rb +3 -4
  28. data/lib/graphql/non_null_type.rb +4 -8
  29. data/lib/graphql/object_type.rb +5 -3
  30. data/lib/graphql/query.rb +48 -12
  31. data/lib/graphql/query/context.rb +7 -1
  32. data/lib/graphql/query/serial_execution/execution_context.rb +8 -3
  33. data/lib/graphql/query/serial_execution/field_resolution.rb +8 -5
  34. data/lib/graphql/query/serial_execution/operation_resolution.rb +2 -2
  35. data/lib/graphql/query/serial_execution/selection_resolution.rb +4 -21
  36. data/lib/graphql/query/serial_execution/value_resolution.rb +59 -99
  37. data/lib/graphql/query/variables.rb +7 -2
  38. data/lib/graphql/scalar_type.rb +1 -1
  39. data/lib/graphql/schema.rb +49 -18
  40. data/lib/graphql/schema/build_from_definition.rb +248 -0
  41. data/lib/graphql/schema/instrumented_field_map.rb +23 -0
  42. data/lib/graphql/schema/loader.rb +4 -11
  43. data/lib/graphql/schema/possible_types.rb +4 -2
  44. data/lib/graphql/schema/printer.rb +1 -1
  45. data/lib/graphql/schema/type_expression.rb +4 -4
  46. data/lib/graphql/schema/type_map.rb +1 -1
  47. data/lib/graphql/schema/validation.rb +4 -0
  48. data/lib/graphql/schema/warden.rb +114 -0
  49. data/lib/graphql/static_validation/literal_validator.rb +10 -7
  50. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -3
  51. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  52. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  53. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -14
  54. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  55. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  56. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -4
  57. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +2 -1
  58. data/lib/graphql/static_validation/validation_context.rb +7 -1
  59. data/lib/graphql/union_type.rb +6 -3
  60. data/lib/graphql/unresolved_type_error.rb +1 -2
  61. data/lib/graphql/version.rb +1 -1
  62. data/readme.md +1 -5
  63. data/spec/graphql/compatibility/execution_specification_spec.rb +3 -0
  64. data/spec/graphql/compatibility/query_parser_specification_spec.rb +5 -0
  65. data/spec/graphql/compatibility/schema_parser_specification_spec.rb +5 -0
  66. data/spec/graphql/define/instance_definable_spec.rb +20 -0
  67. data/spec/graphql/directive_spec.rb +11 -0
  68. data/spec/graphql/enum_type_spec.rb +20 -1
  69. data/spec/graphql/input_object_type_spec.rb +9 -9
  70. data/spec/graphql/introspection/directive_type_spec.rb +4 -4
  71. data/spec/graphql/introspection/input_value_type_spec.rb +6 -6
  72. data/spec/graphql/introspection/type_type_spec.rb +28 -26
  73. data/spec/graphql/language/parser_spec.rb +27 -17
  74. data/spec/graphql/list_type_spec.rb +2 -2
  75. data/spec/graphql/query/variables_spec.rb +1 -0
  76. data/spec/graphql/scalar_type_spec.rb +3 -3
  77. data/spec/graphql/schema/build_from_definition_spec.rb +693 -0
  78. data/spec/graphql/schema/type_expression_spec.rb +3 -3
  79. data/spec/graphql/schema/validation_spec.rb +7 -3
  80. data/spec/graphql/schema/warden_spec.rb +510 -0
  81. data/spec/graphql/schema_spec.rb +129 -0
  82. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +1 -1
  83. data/spec/graphql/static_validation/type_stack_spec.rb +3 -3
  84. data/spec/spec_helper.rb +27 -1
  85. data/spec/support/dairy_app.rb +8 -5
  86. metadata +21 -3
  87. data/lib/graphql/language/parser_tests.rb +0 -809
@@ -14,7 +14,7 @@ module GraphQL
14
14
  def initialize(schema)
15
15
  @object_types = schema.types.values.select { |type| type.kind.object? }
16
16
 
17
- @storage = Hash.new do |hash, key|
17
+ @interface_implementers = Hash.new do |hash, key|
18
18
  hash[key] = @object_types.select { |type| type.interfaces.include?(key) }.sort_by(&:name)
19
19
  end
20
20
  end
@@ -24,7 +24,9 @@ module GraphQL
24
24
  when GraphQL::UnionType
25
25
  type_defn.possible_types
26
26
  when GraphQL::InterfaceType
27
- @storage[type_defn]
27
+ @interface_implementers[type_defn]
28
+ when GraphQL::ObjectType
29
+ [type_defn]
28
30
  else
29
31
  raise "#{type_defn} doesn't have possible types"
30
32
  end
@@ -162,7 +162,7 @@ module GraphQL
162
162
  include ArgsPrinter
163
163
  include DescriptionPrinter
164
164
  def print_fields(type)
165
- type.all_fields.map.with_index{ |field, i|
165
+ type.all_fields.map.with_index { |field, i|
166
166
  "#{print_description(field, ' ', i == 0)}"\
167
167
  " #{field.name}#{print_args(field, ' ')}: #{field.type}#{print_deprecated(field)}"
168
168
  }.join("\n")
@@ -1,17 +1,17 @@
1
1
  module GraphQL
2
2
  class Schema
3
3
  module TypeExpression
4
- def self.build_type(schema, ast_node)
4
+ def self.build_type(types, ast_node)
5
5
  case ast_node
6
6
  when GraphQL::Language::Nodes::TypeName
7
7
  type_name = ast_node.name
8
- schema.types[type_name]
8
+ types[type_name]
9
9
  when GraphQL::Language::Nodes::NonNullType
10
10
  ast_inner_type = ast_node.of_type
11
- build_type(schema, ast_inner_type).to_non_null_type
11
+ build_type(types, ast_inner_type).to_non_null_type
12
12
  when GraphQL::Language::Nodes::ListType
13
13
  ast_inner_type = ast_node.of_type
14
- build_type(schema, ast_inner_type).to_list_type
14
+ build_type(types, ast_inner_type).to_list_type
15
15
  end
16
16
  end
17
17
  end
@@ -8,7 +8,7 @@ module GraphQL
8
8
  # If you want a type, but want to handle the undefined case, use {#fetch}.
9
9
  class TypeMap
10
10
  extend Forwardable
11
- def_delegators :@storage, :key?, :keys, :values, :to_h, :fetch
11
+ def_delegators :@storage, :key?, :keys, :values, :to_h, :fetch, :each, :each_value
12
12
 
13
13
  def initialize
14
14
  @storage = {}
@@ -99,6 +99,10 @@ module GraphQL
99
99
  ARGUMENTS_ARE_VALID = Rules.assert_named_items_are_valid("argument", ->(type) { type.arguments.values })
100
100
 
101
101
  DEFAULT_VALUE_IS_VALID_FOR_TYPE = ->(type) {
102
+ if !type.default_value.nil? && type.type.is_a?(NonNullType)
103
+ return %Q(Variable #{type.name} of type "#{type.type}" is required and will not use the default value. Perhaps you meant to use type "#{type.type.of_type}".)
104
+ end
105
+
102
106
  if !type.default_value.nil?
103
107
  coerced_value = begin
104
108
  type.type.coerce_result(type.default_value)
@@ -0,0 +1,114 @@
1
+ module GraphQL
2
+ class Schema
3
+ # Restrict access to a {GraphQL::Schema} with a user-defined mask.
4
+ #
5
+ # The mask is object that responds to `#visible?(schema_member)`.
6
+ #
7
+ # When validating and executing a query, all access to schema members
8
+ # should go through a warden. If you access the schema directly,
9
+ # you may show a client something that it shouldn't be allowed to see.
10
+ #
11
+ # Masks can be provided in {Schema#execute} (or {Query#initialize}) with the `mask:` keyword.
12
+ #
13
+ # @example Hidding private fields
14
+ # private_members = -> (member) { member.metadata[:private] }
15
+ # result = Schema.execute(query_string, except: private_members)
16
+ #
17
+ # @example Custom mask implementation
18
+ # # It must respond to `#call(member)`.
19
+ # class MissingRequiredFlags
20
+ # def initialize(user)
21
+ # @user = user
22
+ # end
23
+ #
24
+ # # Return `false` if any required flags are missing
25
+ # def call(member)
26
+ # member.metadata[:required_flags].any? do |flag|
27
+ # !@user.has_flag?(flag)
28
+ # end
29
+ # end
30
+ # end
31
+ #
32
+ # # Then, use the custom filter in query:
33
+ # missing_required_flags = MissingRequiredFlags.new(current_user)
34
+ #
35
+ # # This query can only access members which match the user's flags
36
+ # result = Schema.execute(query_string, except: missing_required_flags)
37
+ #
38
+ class Warden
39
+ # @param schema [GraphQL::Schema]
40
+ # @param mask [<#call(member)>] Objects are hidden when `.call(member)` returns true
41
+ def initialize(schema, mask)
42
+ @mask = mask
43
+ @schema = schema
44
+ end
45
+
46
+ # @return [Array<GraphQL::BaseType>] Visible types in the schema
47
+ def types
48
+ @schema.types.each_value.select { |t| visible?(t) }
49
+ end
50
+
51
+ # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
52
+ def get_type(type_name)
53
+ type_defn = @schema.types.fetch(type_name, nil)
54
+ if type_defn && visible?(type_defn)
55
+ type_defn
56
+ else
57
+ nil
58
+ end
59
+ end
60
+
61
+ # @return [GraphQL::Field, nil] The field named `field_name` on `parent_type`, if it exists
62
+ def get_field(parent_type, field_name)
63
+ field_defn = @schema.get_field(parent_type, field_name)
64
+ if field_defn && visible_field?(field_defn)
65
+ field_defn
66
+ else
67
+ nil
68
+ end
69
+ end
70
+
71
+ # @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
72
+ def possible_types(type_defn)
73
+ @schema.possible_types(type_defn).select { |t| visible?(t) }
74
+ end
75
+
76
+ # @param type_defn [GraphQL::ObjectType, GraphQL::InterfaceType]
77
+ # @return [Array<GraphQL::Field>] Fields on `type_defn`
78
+ def fields(type_defn)
79
+ type_defn.all_fields.select { |f| visible_field?(f) }
80
+ end
81
+
82
+ # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
83
+ # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
84
+ def arguments(argument_owner)
85
+ argument_owner.arguments.each_value.select { |a| visible_field?(a) }
86
+ end
87
+
88
+ # @return [Array<GraphQL::EnumType::EnumValue>] Visible members of `enum_defn`
89
+ def enum_values(enum_defn)
90
+ enum_defn.values.each_value.select { |enum_value_defn| visible?(enum_value_defn) }
91
+ end
92
+
93
+ # @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
94
+ def interfaces(obj_type)
95
+ obj_type.interfaces.select { |t| visible?(t) }
96
+ end
97
+
98
+ # @return [Array<GraphQL::Field>] Visible input fields on `input_obj_type`
99
+ def input_fields(input_obj_type)
100
+ input_obj_type.arguments.each_value.select { |f| visible_field?(f) }
101
+ end
102
+
103
+ private
104
+
105
+ def visible_field?(field_defn)
106
+ visible?(field_defn) && visible?(field_defn.type.unwrap)
107
+ end
108
+
109
+ def visible?(member)
110
+ !@mask.call(member)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -2,6 +2,10 @@ module GraphQL
2
2
  module StaticValidation
3
3
  # Test whether `ast_value` is a valid input for `type`
4
4
  class LiteralValidator
5
+ def initialize(warden:)
6
+ @warden = warden
7
+ end
8
+
5
9
  def validate(ast_value, type)
6
10
  if type.kind.non_null?
7
11
  (!ast_value.nil?) && validate(ast_value, type.of_type)
@@ -9,9 +13,9 @@ module GraphQL
9
13
  item_type = type.of_type
10
14
  ensure_array(ast_value).all? { |val| validate(val, item_type) }
11
15
  elsif type.kind.scalar? && !ast_value.is_a?(GraphQL::Language::Nodes::AbstractNode) && !ast_value.is_a?(Array)
12
- type.valid_input?(ast_value)
16
+ type.valid_input?(ast_value, @warden)
13
17
  elsif type.kind.enum? && ast_value.is_a?(GraphQL::Language::Nodes::Enum)
14
- type.valid_input?(ast_value.name)
18
+ type.valid_input?(ast_value.name, @warden)
15
19
  elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
16
20
  required_input_fields_are_present(type, ast_value) &&
17
21
  present_input_field_values_are_valid(type, ast_value)
@@ -27,8 +31,7 @@ module GraphQL
27
31
 
28
32
 
29
33
  def required_input_fields_are_present(type, ast_node)
30
- required_field_names = type.input_fields
31
- .values
34
+ required_field_names = @warden.input_fields(type)
32
35
  .select { |f| f.type.kind.non_null? }
33
36
  .map(&:name)
34
37
  present_field_names = ast_node.arguments.map(&:name)
@@ -37,10 +40,10 @@ module GraphQL
37
40
  end
38
41
 
39
42
  def present_input_field_values_are_valid(type, ast_node)
40
- fields = type.input_fields
43
+ field_map = @warden.input_fields(type).reduce({}) { |m, f| m[f.name] = f; m}
41
44
  ast_node.arguments.all? do |value|
42
- field = fields[value.name]
43
- field ? validate(value.value, field.type) : true
45
+ field = field_map[value.name]
46
+ field && validate(value.value, field.type)
44
47
  end
45
48
  end
46
49
 
@@ -3,11 +3,9 @@ module GraphQL
3
3
  class ArgumentLiteralsAreCompatible < GraphQL::StaticValidation::ArgumentsValidator
4
4
  def validate_node(parent, node, defn, context)
5
5
  return if node.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
6
- validator = GraphQL::StaticValidation::LiteralValidator.new
7
6
  arg_defn = defn.arguments[node.name]
8
7
  return unless arg_defn
9
- valid = validator.validate(node.value, arg_defn.type)
10
- if !valid
8
+ if !context.valid_literal?(node.value, arg_defn.type)
11
9
  kind_of_node = node_type(parent)
12
10
  error_arg_name = parent_name(parent, defn)
13
11
  context.errors << message("Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value. Expected type '#{arg_defn.type}'.", parent, context: context)
@@ -2,7 +2,7 @@ module GraphQL
2
2
  module StaticValidation
3
3
  class ArgumentsAreDefined < GraphQL::StaticValidation::ArgumentsValidator
4
4
  def validate_node(parent, node, defn, context)
5
- argument_defn = defn.arguments[node.name]
5
+ argument_defn = context.warden.arguments(defn).find { |arg| arg.name == node.name }
6
6
  if argument_defn.nil?
7
7
  kind_of_node = node_type(parent)
8
8
  error_arg_name = parent_name(parent, defn)
@@ -21,7 +21,7 @@ module GraphQL
21
21
  return GraphQL::Language::Visitor::SKIP
22
22
  end
23
23
 
24
- field = context.schema.get_field(parent_type, ast_field.name)
24
+ field = context.warden.get_field(parent_type, ast_field.name)
25
25
  if field.nil?
26
26
  context.errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", ast_field, context: context)
27
27
  return GraphQL::Language::Visitor::SKIP
@@ -23,7 +23,8 @@ module GraphQL
23
23
  context.visitor[GraphQL::Language::Nodes::Document].leave << ->(doc_node, parent) {
24
24
  spreads_to_validate.each do |frag_spread|
25
25
  fragment_child_name = context.fragments[frag_spread.node.name].type.name
26
- fragment_child = context.schema.types.fetch(fragment_child_name, nil) # Might be non-existent type name
26
+ fragment_child = context.warden.get_type(fragment_child_name)
27
+ # Might be non-existent type name
27
28
  if fragment_child
28
29
  validate_fragment_in_scope(frag_spread.parent_type, fragment_child, frag_spread.node, context, frag_spread.path)
29
30
  end
@@ -38,25 +39,13 @@ module GraphQL
38
39
  # It's not a valid fragment type, this error was handled someplace else
39
40
  return
40
41
  end
41
- intersecting_types = get_possible_types(parent_type, context.schema) & get_possible_types(child_type, context.schema)
42
+ intersecting_types = context.warden.possible_types(parent_type.unwrap) & context.warden.possible_types(child_type.unwrap)
42
43
  if intersecting_types.none?
43
44
  name = node.respond_to?(:name) ? " #{node.name}" : ""
44
45
  context.errors << message("Fragment#{name} on #{child_type.name} can't be spread inside #{parent_type.name}", node, path: path)
45
46
  end
46
47
  end
47
48
 
48
- def get_possible_types(type, schema)
49
- if type.kind.wraps?
50
- get_possible_types(type.of_type, schema)
51
- elsif type.kind.object?
52
- [type]
53
- elsif type.kind.resolves?
54
- schema.possible_types(type)
55
- else
56
- []
57
- end
58
- end
59
-
60
49
  class FragmentSpread
61
50
  attr_reader :node, :parent_type, :path
62
51
  def initialize(node:, parent_type:, path:)
@@ -19,7 +19,7 @@ module GraphQL
19
19
  def validate_type_exists(node, context)
20
20
  return unless node.type
21
21
  type_name = node.type.name
22
- type = context.schema.types.fetch(type_name, nil)
22
+ type = context.warden.get_type(type_name)
23
23
  if type.nil?
24
24
  context.errors << message("No such type #{type_name}, so it can't be a fragment condition", node, context: context)
25
25
  GraphQL::Language::Visitor::SKIP
@@ -24,7 +24,7 @@ module GraphQL
24
24
  # Inline fragment on the same type
25
25
  else
26
26
  type_name = node_type.to_query_string
27
- type_def = context.schema.types.fetch(type_name, nil)
27
+ type_def = context.warden.get_type(type_name)
28
28
  if type_def.nil? || !type_def.kind.composite?
29
29
  context.errors << message("Invalid fragment on type #{type_name} (must be Union, Interface or Object)", node, context: context)
30
30
  GraphQL::Language::Visitor::SKIP
@@ -4,21 +4,20 @@ module GraphQL
4
4
  include GraphQL::StaticValidation::Message::MessageHelper
5
5
 
6
6
  def validate(context)
7
- literal_validator = GraphQL::StaticValidation::LiteralValidator.new
8
7
  context.visitor[GraphQL::Language::Nodes::VariableDefinition] << ->(node, parent) {
9
8
  if !node.default_value.nil?
10
- validate_default_value(node, literal_validator, context)
9
+ validate_default_value(node, context)
11
10
  end
12
11
  }
13
12
  end
14
13
 
15
- def validate_default_value(node, literal_validator, context)
14
+ def validate_default_value(node, context)
16
15
  value = node.default_value
17
16
  if node.type.is_a?(GraphQL::Language::Nodes::NonNullType)
18
17
  context.errors << message("Non-null variable $#{node.name} can't have a default value", node, context: context)
19
18
  else
20
19
  type = context.schema.type_from_ast(node.type)
21
- if !literal_validator.validate(value, type)
20
+ if !context.valid_literal?(value, type)
22
21
  context.errors << message("Default value for $#{node.name} doesn't match type #{type}", node, context: context)
23
22
  end
24
23
  end
@@ -13,7 +13,8 @@ module GraphQL
13
13
 
14
14
  def validate_is_input_type(node, context)
15
15
  type_name = get_type_name(node.type)
16
- type = context.schema.types.fetch(type_name, nil)
16
+ type = context.warden.get_type(type_name)
17
+
17
18
  if type.nil?
18
19
  context.errors << message("#{type_name} isn't a defined input type (on $#{node.name})", node, context: context)
19
20
  elsif !type.kind.input?
@@ -11,13 +11,14 @@ module GraphQL
11
11
  # It also provides limited access to the {TypeStack} instance,
12
12
  # which tracks state as you climb in and out of different fields.
13
13
  class ValidationContext
14
- attr_reader :query, :schema, :document, :errors, :visitor, :fragments, :operations
14
+ attr_reader :query, :schema, :document, :errors, :visitor, :fragments, :operations, :warden
15
15
  def initialize(query)
16
16
  @query = query
17
17
  @schema = query.schema
18
18
  @document = query.document
19
19
  @fragments = {}
20
20
  @operations = {}
21
+ @warden = query.warden
21
22
 
22
23
  document.definitions.each do |definition|
23
24
  case definition
@@ -69,6 +70,11 @@ module GraphQL
69
70
  @type_stack.argument_definitions[-2]
70
71
  end
71
72
 
73
+ def valid_literal?(ast_value, type)
74
+ @literal_validator ||= LiteralValidator.new(warden: @warden)
75
+ @literal_validator.validate(ast_value, type)
76
+ end
77
+
72
78
  # Don't try to validate dynamic fields
73
79
  # since they aren't defined by the type system
74
80
  def skip_field?(field_name)
@@ -41,9 +41,12 @@ module GraphQL
41
41
  def possible_types
42
42
  @clean_possible_types ||= begin
43
43
  ensure_defined
44
- @dirty_possible_types.map { |type| GraphQL::BaseType.resolve_related_type(type) }
45
- rescue
46
- @dirty_possible_types
44
+
45
+ if @dirty_possible_types.respond_to?(:map)
46
+ @dirty_possible_types.map { |type| GraphQL::BaseType.resolve_related_type(type) }
47
+ else
48
+ @dirty_possible_types
49
+ end
47
50
  end
48
51
  end
49
52
  end
@@ -2,8 +2,7 @@ module GraphQL
2
2
  # Error raised when the value provided for a field can't be resolved to one of the possible types
3
3
  # for the field.
4
4
  class UnresolvedTypeError < GraphQL::Error
5
- def initialize(field_name, schema, field_type, parent_type, received_type)
6
- possible_types = field_type.kind.resolves? ? schema.possible_types(field_type) : [field_type]
5
+ def initialize(field_name, field_type, parent_type, received_type, possible_types)
7
6
  message = %|The value from "#{field_name}" on "#{parent_type}" could not be resolved to "#{field_type}". (Received: #{received_type.inspect}, Expected: [#{possible_types.map(&:inspect).join(", ")}])|
8
7
  super(message)
9
8
  end
@@ -1,3 +1,3 @@
1
1
  module GraphQL
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
data/readme.md CHANGED
@@ -50,13 +50,10 @@ See "Getting Started" on the [website](https://rmosolgo.github.io/graphql-ruby/)
50
50
  - Validators are order-dependent, is this a smell?
51
51
  - Tests for interference between validators are poor
52
52
  - Maybe this is a candidate for a rewrite?
53
- - Add Rails-y argument validations, eg `less_than: 100`, `max_length: 255`, `one_of: [...]`
54
- - Must be customizable
55
53
  - Relay:
56
54
  - Reduce duplication in ArrayConnection / RelationConnection
57
55
  - Improve API for creating edges (better RANGE_ADD support)
58
56
  - If the new edge isn't a member of the connection's objects, raise a nice error
59
- - Missing Enum value should raise a descriptive error, not "key not found"
60
57
  - `args` should whitelist keys -- if you request a key that isn't defined for the field, it should 💥
61
58
  - Fix middleware ([discussion](https://github.com/rmosolgo/graphql-ruby/issues/186))
62
59
  - Handle out-of-bounds lookup, eg `graphql-batch`
@@ -69,6 +66,5 @@ See "Getting Started" on the [website](https://rmosolgo.github.io/graphql-ruby/)
69
66
  - Support working with AST as data
70
67
  - Adding fields to selections (`__typename` can go anywhere, others are type-specific)
71
68
  - Renaming fragments from local names to unique names
72
- - Support AST subclasses? This would be hard, I think classes are used as hash keys in many places.
73
- - Support object deep-copy (schema, type, field, argument)? To support multiple schemas based on the same types. ([discussion](https://github.com/rmosolgo/graphql-ruby/issues/269))
74
69
  - Document encrypted & versioned cursors
70
+ - Make it faster
@@ -0,0 +1,3 @@
1
+ require "spec_helper"
2
+
3
+ SerialExecutionSuite = GraphQL::Compatibility::ExecutionSpecification.build_suite(GraphQL::Query::SerialExecution)