graphql 0.11.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/base_type.rb +6 -2
- data/lib/graphql/definition_helpers.rb +0 -5
- data/lib/graphql/definition_helpers/defined_by_config.rb +106 -102
- data/lib/graphql/definition_helpers/non_null_with_bang.rb +13 -9
- data/lib/graphql/definition_helpers/string_named_hash.rb +19 -15
- data/lib/graphql/definition_helpers/type_definer.rb +25 -21
- data/lib/graphql/enum_type.rb +8 -2
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/input_object_type.rb +27 -6
- data/lib/graphql/interface_type.rb +10 -0
- data/lib/graphql/introspection/fields_field.rb +2 -2
- data/lib/graphql/list_type.rb +12 -2
- data/lib/graphql/non_null_type.rb +11 -1
- data/lib/graphql/object_type.rb +19 -0
- data/lib/graphql/query.rb +2 -14
- data/lib/graphql/query/input_validation_result.rb +23 -0
- data/lib/graphql/query/literal_input.rb +3 -1
- data/lib/graphql/query/serial_execution/field_resolution.rb +3 -1
- data/lib/graphql/query/serial_execution/selection_resolution.rb +21 -15
- data/lib/graphql/query/variable_validation_error.rb +18 -0
- data/lib/graphql/query/variables.rb +4 -8
- data/lib/graphql/scalar_type.rb +10 -4
- data/lib/graphql/schema.rb +4 -2
- data/lib/graphql/schema/printer.rb +12 -3
- data/lib/graphql/schema/type_reducer.rb +4 -3
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/document_does_not_exceed_max_depth.rb +79 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +63 -0
- data/lib/graphql/static_validation/validator.rb +1 -52
- data/lib/graphql/version.rb +1 -1
- data/readme.md +21 -22
- data/spec/graphql/enum_type_spec.rb +8 -0
- data/spec/graphql/input_object_type_spec.rb +101 -3
- data/spec/graphql/introspection/schema_type_spec.rb +6 -6
- data/spec/graphql/introspection/type_type_spec.rb +6 -6
- data/spec/graphql/language/transform_spec.rb +9 -5
- data/spec/graphql/list_type_spec.rb +23 -0
- data/spec/graphql/object_type_spec.rb +11 -4
- data/spec/graphql/query/executor_spec.rb +34 -5
- data/spec/graphql/query_spec.rb +22 -3
- data/spec/graphql/scalar_type_spec.rb +28 -0
- data/spec/graphql/schema/type_reducer_spec.rb +2 -2
- data/spec/graphql/static_validation/complexity_validator_spec.rb +15 -0
- data/spec/graphql/static_validation/rules/document_does_not_exceed_max_depth_spec.rb +93 -0
- data/spec/support/dairy_app.rb +2 -2
- data/spec/support/minimum_input_object.rb +8 -5
- metadata +10 -4
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd3e3f13402965d47c58fd6a3bc5f4252a208417
|
4
|
+
data.tar.gz: 2b0d0396fdf6fbed1b234cefb21dfb50578b2b49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 221f5b736dbdd2c43cb45b1000b5d843b2a4101d63733a6200b79cc87889c3c0619830bf26e8fba1fa0e980875cb4061246642b2c8cdf45e2f9a6c7bf916d52a
|
7
|
+
data.tar.gz: f67031ea6cb082c2b0906108fb8e772cb4f5464470c0bf0ca449d52b8eb1a21e6e2a8b6040633c99129fc82c7cda8850b30007ea7a137ffb12977b6127e66c37
|
data/lib/graphql/base_type.rb
CHANGED
@@ -77,8 +77,12 @@ module GraphQL
|
|
77
77
|
alias :inspect :to_s
|
78
78
|
|
79
79
|
def valid_input?(value)
|
80
|
-
|
81
|
-
|
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,119 +1,123 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
41
|
+
attr_reader :fields, :input_fields, :arguments, :values
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
43
|
+
def initialize
|
44
|
+
@interfaces = []
|
45
|
+
@possible_types = []
|
46
|
+
@on = []
|
47
|
+
@fields = {}
|
48
|
+
@arguments = {}
|
49
|
+
@values = []
|
50
|
+
@input_fields = {}
|
51
|
+
end
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
-
|
53
|
+
def types
|
54
|
+
GraphQL::DefinitionHelpers::TypeDefiner.instance
|
55
|
+
end
|
54
56
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
|
5
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
data/lib/graphql/enum_type.rb
CHANGED
@@ -41,8 +41,14 @@ class GraphQL::EnumType < GraphQL::BaseType
|
|
41
41
|
GraphQL::TypeKinds::ENUM
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
|
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
|
data/lib/graphql/float_type.rb
CHANGED
@@ -20,23 +20,44 @@ class GraphQL::InputObjectType < GraphQL::BaseType
|
|
20
20
|
GraphQL::TypeKinds::INPUT_OBJECT
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
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
|