graphql 0.11.0 → 0.11.1

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 (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