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