graphql 0.11.0 → 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/base_type.rb +6 -2
  3. data/lib/graphql/definition_helpers.rb +0 -5
  4. data/lib/graphql/definition_helpers/defined_by_config.rb +106 -102
  5. data/lib/graphql/definition_helpers/non_null_with_bang.rb +13 -9
  6. data/lib/graphql/definition_helpers/string_named_hash.rb +19 -15
  7. data/lib/graphql/definition_helpers/type_definer.rb +25 -21
  8. data/lib/graphql/enum_type.rb +8 -2
  9. data/lib/graphql/float_type.rb +1 -1
  10. data/lib/graphql/input_object_type.rb +27 -6
  11. data/lib/graphql/interface_type.rb +10 -0
  12. data/lib/graphql/introspection/fields_field.rb +2 -2
  13. data/lib/graphql/list_type.rb +12 -2
  14. data/lib/graphql/non_null_type.rb +11 -1
  15. data/lib/graphql/object_type.rb +19 -0
  16. data/lib/graphql/query.rb +2 -14
  17. data/lib/graphql/query/input_validation_result.rb +23 -0
  18. data/lib/graphql/query/literal_input.rb +3 -1
  19. data/lib/graphql/query/serial_execution/field_resolution.rb +3 -1
  20. data/lib/graphql/query/serial_execution/selection_resolution.rb +21 -15
  21. data/lib/graphql/query/variable_validation_error.rb +18 -0
  22. data/lib/graphql/query/variables.rb +4 -8
  23. data/lib/graphql/scalar_type.rb +10 -4
  24. data/lib/graphql/schema.rb +4 -2
  25. data/lib/graphql/schema/printer.rb +12 -3
  26. data/lib/graphql/schema/type_reducer.rb +4 -3
  27. data/lib/graphql/static_validation.rb +1 -0
  28. data/lib/graphql/static_validation/all_rules.rb +1 -0
  29. data/lib/graphql/static_validation/rules/document_does_not_exceed_max_depth.rb +79 -0
  30. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  31. data/lib/graphql/static_validation/validation_context.rb +63 -0
  32. data/lib/graphql/static_validation/validator.rb +1 -52
  33. data/lib/graphql/version.rb +1 -1
  34. data/readme.md +21 -22
  35. data/spec/graphql/enum_type_spec.rb +8 -0
  36. data/spec/graphql/input_object_type_spec.rb +101 -3
  37. data/spec/graphql/introspection/schema_type_spec.rb +6 -6
  38. data/spec/graphql/introspection/type_type_spec.rb +6 -6
  39. data/spec/graphql/language/transform_spec.rb +9 -5
  40. data/spec/graphql/list_type_spec.rb +23 -0
  41. data/spec/graphql/object_type_spec.rb +11 -4
  42. data/spec/graphql/query/executor_spec.rb +34 -5
  43. data/spec/graphql/query_spec.rb +22 -3
  44. data/spec/graphql/scalar_type_spec.rb +28 -0
  45. data/spec/graphql/schema/type_reducer_spec.rb +2 -2
  46. data/spec/graphql/static_validation/complexity_validator_spec.rb +15 -0
  47. data/spec/graphql/static_validation/rules/document_does_not_exceed_max_depth_spec.rb +93 -0
  48. data/spec/support/dairy_app.rb +2 -2
  49. data/spec/support/minimum_input_object.rb +8 -5
  50. metadata +10 -4
  51. data/spec/graphql/static_validation/complexity_validator.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1cd9ee8f70346b2d0419202f3997fd562149ab8f
4
- data.tar.gz: a0faf5a06c605e3d44cd23b65e0e6b8cd8fc04a6
3
+ metadata.gz: dd3e3f13402965d47c58fd6a3bc5f4252a208417
4
+ data.tar.gz: 2b0d0396fdf6fbed1b234cefb21dfb50578b2b49
5
5
  SHA512:
6
- metadata.gz: ac440bd4b73f1f5c3c9de705928bfb90f4498ad0d614c856c3d9ad74fcead1b1881b663176dd368b48cbe7b651dddc1e32258799d4c334ea9347b8c550ce69f9
7
- data.tar.gz: 3e573c20b9ab0ba44fdbd1a2e73a373b4d5c6e822ee5c4ebfc21c08849ea4ac5c37ac5933ca9e5c8128f0415a908b2c30868d4b7830d4e67f8008ff70c4434d9
6
+ metadata.gz: 221f5b736dbdd2c43cb45b1000b5d843b2a4101d63733a6200b79cc87889c3c0619830bf26e8fba1fa0e980875cb4061246642b2c8cdf45e2f9a6c7bf916d52a
7
+ data.tar.gz: f67031ea6cb082c2b0906108fb8e772cb4f5464470c0bf0ca449d52b8eb1a21e6e2a8b6040633c99129fc82c7cda8850b30007ea7a137ffb12977b6127e66c37
@@ -77,8 +77,12 @@ module GraphQL
77
77
  alias :inspect :to_s
78
78
 
79
79
  def valid_input?(value)
80
- return true if value.nil?
81
- valid_non_null_input?(value)
80
+ validate_input(value).valid?
81
+ end
82
+
83
+ def validate_input(value)
84
+ return GraphQL::Query::InputValidationResult.new if value.nil?
85
+ validate_non_null_input(value)
82
86
  end
83
87
 
84
88
  def coerce_input(value)
@@ -1,8 +1,3 @@
1
- module GraphQL
2
- module DefinitionHelpers
3
- end
4
- end
5
-
6
1
  require 'graphql/definition_helpers/non_null_with_bang'
7
2
  require 'graphql/definition_helpers/defined_by_config'
8
3
  require 'graphql/definition_helpers/string_named_hash'
@@ -1,119 +1,123 @@
1
- # Provide a two-step definition process.
2
- #
3
- # 1. Use a config object to gather definitions
4
- # 2. Transfer definitions to an actual instance of an object
5
- #
6
- module GraphQL::DefinitionHelpers::DefinedByConfig
7
- def self.included(base)
8
- base.extend(ClassMethods)
9
- end
1
+ module GraphQL
2
+ module DefinitionHelpers
3
+ # Provide a two-step definition process.
4
+ #
5
+ # 1. Use a config object to gather definitions
6
+ # 2. Transfer definitions to an actual instance of an object
7
+ #
8
+ module DefinedByConfig
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ end
10
12
 
11
- # This object is `instance_eval`'d when defining _any_ object in the schema.
12
- # Then, the applicable properties of this object are transfered to the given instance.
13
- class DefinitionConfig
14
- def self.attr_definable(*names)
15
- attr_accessor(*names)
16
- names.each do |name|
17
- ivar_name = "@#{name}".to_sym
18
- define_method(name) do |new_value=nil|
19
- new_value && self.instance_variable_set(ivar_name, new_value)
20
- instance_variable_get(ivar_name)
13
+ # This object is `instance_eval`'d when defining _any_ object in the schema.
14
+ # Then, the applicable properties of this object are transfered to the given instance.
15
+ class DefinitionConfig
16
+ def self.attr_definable(*names)
17
+ attr_accessor(*names)
18
+ names.each do |name|
19
+ ivar_name = "@#{name}".to_sym
20
+ define_method(name) do |new_value=nil|
21
+ new_value && self.instance_variable_set(ivar_name, new_value)
22
+ instance_variable_get(ivar_name)
23
+ end
24
+ end
21
25
  end
22
- end
23
- end
24
26
 
25
- attr_definable :name, :description,
26
- :interfaces, # object
27
- :deprecation_reason, # field
28
- :type, # field / argument
29
- :property, # field
30
- :resolve, # field / directive
31
- :resolve_type, # interface / union
32
- :possible_types, # interface / union
33
- :default_value, # argument
34
- :on, # directive
35
- :coerce, #scalar
36
- :coerce_input, #scalar
37
- :coerce_result #scalar
27
+ attr_definable :name, :description,
28
+ :interfaces, # object
29
+ :deprecation_reason, # field
30
+ :type, # field / argument
31
+ :property, # field
32
+ :resolve, # field / directive
33
+ :resolve_type, # interface / union
34
+ :possible_types, # interface / union
35
+ :default_value, # argument
36
+ :on, # directive
37
+ :coerce, #scalar
38
+ :coerce_input, #scalar
39
+ :coerce_result #scalar
38
40
 
39
- attr_reader :fields, :input_fields, :arguments, :values
41
+ attr_reader :fields, :input_fields, :arguments, :values
40
42
 
41
- def initialize
42
- @interfaces = []
43
- @possible_types = []
44
- @on = []
45
- @fields = {}
46
- @arguments = {}
47
- @values = []
48
- @input_fields = {}
49
- end
43
+ def initialize
44
+ @interfaces = []
45
+ @possible_types = []
46
+ @on = []
47
+ @fields = {}
48
+ @arguments = {}
49
+ @values = []
50
+ @input_fields = {}
51
+ end
50
52
 
51
- def types
52
- GraphQL::DefinitionHelpers::TypeDefiner.instance
53
- end
53
+ def types
54
+ GraphQL::DefinitionHelpers::TypeDefiner.instance
55
+ end
54
56
 
55
- def field(name, type = nil, desc = nil, field: nil, property: nil, &block)
56
- if block_given?
57
- field = GraphQL::Field.define(&block)
58
- else
59
- field ||= GraphQL::Field.new
60
- end
61
- type && field.type = type
62
- desc && field.description = desc
63
- property && field.property = property
64
- field.name ||= name.to_s
65
- fields[name.to_s] = field
66
- end
57
+ def field(name, type = nil, desc = nil, field: nil, property: nil, &block)
58
+ if block_given?
59
+ field = GraphQL::Field.define(&block)
60
+ else
61
+ field ||= GraphQL::Field.new
62
+ end
63
+ type && field.type = type
64
+ desc && field.description = desc
65
+ property && field.property = property
66
+ field.name ||= name.to_s
67
+ fields[name.to_s] = field
68
+ end
67
69
 
68
- # For EnumType
69
- def value(name, desc = nil, deprecation_reason: nil, value: name)
70
- values << GraphQL::EnumType::EnumValue.new(name: name, description: desc, deprecation_reason: deprecation_reason, value: value)
71
- end
70
+ # For EnumType
71
+ def value(name, desc = nil, deprecation_reason: nil, value: name)
72
+ values << GraphQL::EnumType::EnumValue.new(name: name, description: desc, deprecation_reason: deprecation_reason, value: value)
73
+ end
72
74
 
73
- # For InputObjectType
74
- def input_field(name, type = nil, desc = nil, default_value: nil, &block)
75
- argument = if block_given?
76
- GraphQL::Argument.define(&block)
77
- else
78
- GraphQL::Argument.new
79
- end
80
- argument.name = name
81
- type && argument.type = type
82
- desc && argument.description = desc
83
- default_value && argument.default_value = default_value
84
- input_fields[name.to_s] = argument
85
- end
75
+ # For InputObjectType
76
+ def input_field(name, type = nil, desc = nil, default_value: nil, &block)
77
+ argument = if block_given?
78
+ GraphQL::Argument.define(&block)
79
+ else
80
+ GraphQL::Argument.new
81
+ end
82
+ argument.name = name
83
+ type && argument.type = type
84
+ desc && argument.description = desc
85
+ default_value && argument.default_value = default_value
86
+ input_fields[name.to_s] = argument
87
+ end
86
88
 
87
- def argument(name, type, description = nil, default_value: nil)
88
- argument = GraphQL::Argument.new
89
- argument.name = name.to_s
90
- argument.type = type
91
- argument.description = description
92
- argument.default_value = default_value
93
- @arguments[name.to_s] = argument
94
- end
89
+ def argument(name, type, description = nil, default_value: nil)
90
+ argument = GraphQL::Argument.new
91
+ argument.name = name.to_s
92
+ argument.type = type
93
+ argument.description = description
94
+ argument.default_value = default_value
95
+ @arguments[name.to_s] = argument
96
+ end
95
97
 
96
- def to_instance(object, attributes)
97
- attributes.each do |attr_name|
98
- configured_value = self.public_send(attr_name)
99
- object.public_send("#{attr_name}=", configured_value)
98
+ def to_instance(object, attributes)
99
+ attributes.each do |attr_name|
100
+ configured_value = self.public_send(attr_name)
101
+ object.public_send("#{attr_name}=", configured_value)
102
+ end
103
+ object
104
+ end
100
105
  end
101
- object
102
- end
103
- end
104
106
 
105
- module ClassMethods
106
- # Pass the block to this class's `DefinitionConfig`,
107
- # The return the result of {DefinitionConfig#to_instance}
108
- def define(&block)
109
- config = DefinitionConfig.new
110
- block && config.instance_eval(&block)
111
- config.to_instance(self.new, @defined_attrs)
112
- end
107
+ module ClassMethods
108
+ # Pass the block to this class's `DefinitionConfig`,
109
+ # The return the result of {DefinitionConfig#to_instance}
110
+ def define(&block)
111
+ config = DefinitionConfig.new
112
+ block && config.instance_eval(&block)
113
+ config.to_instance(self.new, @defined_attrs)
114
+ end
113
115
 
114
- def defined_by_config(*defined_attrs)
115
- @defined_attrs ||= []
116
- @defined_attrs += defined_attrs
116
+ def defined_by_config(*defined_attrs)
117
+ @defined_attrs ||= []
118
+ @defined_attrs += defined_attrs
119
+ end
120
+ end
117
121
  end
118
122
  end
119
123
  end
@@ -1,11 +1,15 @@
1
- # Wrap the object in NonNullType in response to `!`
2
- # @example required Int type
3
- # !GraphQL::INT_TYPE
4
- #
5
- module GraphQL::DefinitionHelpers::NonNullWithBang
6
- # Make the type non-null
7
- # @return [GraphQL::NonNullType] a non-null type which wraps the original type
8
- def !
9
- to_non_null_type
1
+ module GraphQL
2
+ module DefinitionHelpers
3
+ # Wrap the object in NonNullType in response to `!`
4
+ # @example required Int type
5
+ # !GraphQL::INT_TYPE
6
+ #
7
+ module NonNullWithBang
8
+ # Make the type non-null
9
+ # @return [GraphQL::NonNullType] a non-null type which wraps the original type
10
+ def !
11
+ to_non_null_type
12
+ end
13
+ end
10
14
  end
11
15
  end
@@ -1,18 +1,22 @@
1
- # Accepts a hash with symbol keys.
2
- # - convert keys to strings
3
- # - if the value responds to `name=`, then assign the hash key as `name`
4
- #
5
- # Used by {ObjectType#fields}, {Field#arguments} and others.
6
- class GraphQL::DefinitionHelpers::StringNamedHash
7
- # Normalized hash for the input
8
- # @return [Hash] Hash with string keys
9
- attr_reader :to_h
1
+ module GraphQL
2
+ module DefinitionHelpers
3
+ # Accepts a hash with symbol keys.
4
+ # - convert keys to strings
5
+ # - if the value responds to `name=`, then assign the hash key as `name`
6
+ #
7
+ # Used by {ObjectType#fields}, {Field#arguments} and others.
8
+ class StringNamedHash
9
+ # Normalized hash for the input
10
+ # @return [Hash] Hash with string keys
11
+ attr_reader :to_h
10
12
 
11
- # @param input_hash [Hash] Hash to be normalized
12
- def initialize(input_hash)
13
- @to_h = input_hash
14
- .reduce({}) { |memo, (key, value)| memo[key.to_s] = value; memo }
15
- # Set the name of the value based on its key
16
- @to_h.each {|k, v| v.respond_to?("name=") && v.name = k }
13
+ # @param input_hash [Hash] Hash to be normalized
14
+ def initialize(input_hash)
15
+ @to_h = input_hash
16
+ .reduce({}) { |memo, (key, value)| memo[key.to_s] = value; memo }
17
+ # Set the name of the value based on its key
18
+ @to_h.each {|k, v| v.respond_to?("name=") && v.name = k }
19
+ end
20
+ end
17
21
  end
18
22
  end
@@ -1,25 +1,29 @@
1
- # Some conveniences for definining return & argument types.
2
- #
3
- # Passed into initialization blocks, eg {ObjectType#initialize}, {Field#initialize}
4
- class GraphQL::DefinitionHelpers::TypeDefiner
5
- include Singleton
1
+ module GraphQL
2
+ module DefinitionHelpers
3
+ # Some conveniences for definining return & argument types.
4
+ #
5
+ # Passed into initialization blocks, eg {ObjectType#initialize}, {Field#initialize}
6
+ class TypeDefiner
7
+ include Singleton
6
8
 
7
- def Int; GraphQL::INT_TYPE; end
8
- def String; GraphQL::STRING_TYPE; end
9
- def Float; GraphQL::FLOAT_TYPE; end
10
- def Boolean; GraphQL::BOOLEAN_TYPE; end
11
- def ID; GraphQL::ID_TYPE; end
9
+ def Int; GraphQL::INT_TYPE; end
10
+ def String; GraphQL::STRING_TYPE; end
11
+ def Float; GraphQL::FLOAT_TYPE; end
12
+ def Boolean; GraphQL::BOOLEAN_TYPE; end
13
+ def ID; GraphQL::ID_TYPE; end
12
14
 
13
- # Make a {ListType} which wraps the input type
14
- #
15
- # @example making a list type
16
- # list_of_strings = types[types.String]
17
- # list_of_strings.inspect
18
- # # => "[String]"
19
- #
20
- # @param type [Type] A type to be wrapped in a ListType
21
- # @return [GraphQL::ListType] A ListType wrapping `type`
22
- def [](type)
23
- type.to_list_type
15
+ # Make a {ListType} which wraps the input type
16
+ #
17
+ # @example making a list type
18
+ # list_of_strings = types[types.String]
19
+ # list_of_strings.inspect
20
+ # # => "[String]"
21
+ #
22
+ # @param type [Type] A type to be wrapped in a ListType
23
+ # @return [GraphQL::ListType] A ListType wrapping `type`
24
+ def [](type)
25
+ type.to_list_type
26
+ end
27
+ end
24
28
  end
25
29
  end
@@ -41,8 +41,14 @@ class GraphQL::EnumType < GraphQL::BaseType
41
41
  GraphQL::TypeKinds::ENUM
42
42
  end
43
43
 
44
- def valid_non_null_input?(value_name)
45
- @values_by_name.key?(value_name)
44
+ def validate_non_null_input(value_name)
45
+ result = GraphQL::Query::InputValidationResult.new
46
+
47
+ if !@values_by_name.key?(value_name)
48
+ result.add_problem("Expected #{JSON.dump(value_name)} to be one of: #{@values_by_name.keys.join(', ')}")
49
+ end
50
+
51
+ result
46
52
  end
47
53
 
48
54
  # Get the underlying value for this enum value
@@ -1,6 +1,6 @@
1
1
  GraphQL::FLOAT_TYPE = GraphQL::ScalarType.define do
2
2
  name "Float"
3
3
  coerce -> (value) do
4
- value.respond_to?(:to_f) ? value.to_f : nil
4
+ value.is_a?(Numeric) ? value.to_f : nil
5
5
  end
6
6
  end
@@ -20,23 +20,44 @@ class GraphQL::InputObjectType < GraphQL::BaseType
20
20
  GraphQL::TypeKinds::INPUT_OBJECT
21
21
  end
22
22
 
23
- # assert that all present fields are defined
24
- # _and_ all defined fields have valid values
25
- def valid_non_null_input?(input)
26
- input.all? { |name, value| input_fields[name] } &&
27
- input_fields.all? { |name, field| field.type.valid_input?(input[name]) }
23
+ def validate_non_null_input(input)
24
+ result = GraphQL::Query::InputValidationResult.new
25
+
26
+ # Items in the input that are unexpected
27
+ input.each do |name, value|
28
+ if input_fields[name].nil?
29
+ result.add_problem("Field is not defined on #{self.name}", [name])
30
+ end
31
+ end
32
+
33
+ # Items in the input that are expected, but have invalid values
34
+ invalid_fields = input_fields.map do |name, field|
35
+ field_result = field.type.validate_input(input[name])
36
+ if !field_result.valid?
37
+ result.merge_result!(name, field_result)
38
+ end
39
+ end
40
+
41
+ result
28
42
  end
29
43
 
30
44
  def coerce_non_null_input(value)
31
45
  input_values = {}
46
+
32
47
  input_fields.each do |input_key, input_field_defn|
33
48
  field_value = value[input_key]
34
49
  field_value = input_field_defn.type.coerce_input(field_value)
50
+
51
+ # Try getting the default value
35
52
  if field_value.nil?
36
53
  field_value = input_field_defn.default_value
37
54
  end
38
- input_values[input_key] = field_value unless field_value.nil?
55
+
56
+ if !field_value.nil?
57
+ input_values[input_key] = field_value
58
+ end
39
59
  end
60
+
40
61
  GraphQL::Query::Arguments.new(input_values)
41
62
  end
42
63
  end