graphql 0.10.3 → 0.10.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql.rb +5 -1
- data/lib/graphql/definition_helpers/defined_by_config.rb +1 -1
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/language/transform.rb +1 -0
- data/lib/graphql/query.rb +1 -1
- data/lib/graphql/query/arguments.rb +7 -0
- data/lib/graphql/query/context.rb +7 -2
- data/lib/graphql/static_validation/literal_validator.rb +24 -6
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +6 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/introspection/type_type_spec.rb +1 -1
- data/spec/graphql/language/parser_spec.rb +1 -0
- data/spec/graphql/language/transform_spec.rb +1 -0
- data/spec/graphql/query/arguments_spec.rb +4 -0
- data/spec/graphql/query/context_spec.rb +15 -0
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +11 -4
- data/spec/support/dairy_app.rb +5 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47e57c2465fd5d01d22c69ddfc1e964dc0b93239
|
4
|
+
data.tar.gz: c759b0a07b75f19ac51f31c77c89be361c5ab897
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e16ebf74d11acb3deece7cab048c89b99a1d564b2daba526bc0234609c56fded00beb8fa38066aa8c0330dc29c5aef532e4231cfd485ff615547ee6eb60ae538
|
7
|
+
data.tar.gz: 84c3ec086585193859acc914e29cf74f62473360994394125e12b83a0ee67d3e5382fc0c4c1f537a706f5a4d0c6c9f07ad8f6c3b6cb762f8de4cb9cbd02d7dd5
|
data/lib/graphql.rb
CHANGED
@@ -22,7 +22,11 @@ module GraphQL
|
|
22
22
|
def self.parse(string, as: nil)
|
23
23
|
parser = as ? GraphQL::PARSER.send(as) : GraphQL::PARSER
|
24
24
|
tree = parser.parse(string)
|
25
|
-
GraphQL::TRANSFORM.apply(tree)
|
25
|
+
document = GraphQL::TRANSFORM.apply(tree)
|
26
|
+
if !document.is_a?(GraphQL::Language::Nodes::Document)
|
27
|
+
raise("Parse failed! Sorry, somehow we failed to turn this string into a document. Please report this bug!")
|
28
|
+
end
|
29
|
+
document
|
26
30
|
rescue Parslet::ParseFailed => error
|
27
31
|
line, col = error.cause.source.line_and_column(error.cause.pos)
|
28
32
|
raise GraphQL::ParseError.new(error.message, line, col, string)
|
@@ -78,7 +78,7 @@ module GraphQL::DefinitionHelpers::DefinedByConfig
|
|
78
78
|
end
|
79
79
|
argument.name = name
|
80
80
|
type && argument.type = type
|
81
|
-
desc && argument.
|
81
|
+
desc && argument.description = desc
|
82
82
|
default_value && argument.default_value = default_value
|
83
83
|
input_fields[name.to_s] = argument
|
84
84
|
end
|
@@ -91,7 +91,7 @@ module GraphQL
|
|
91
91
|
value_enum
|
92
92
|
)}
|
93
93
|
rule(:value_sign?) { match('[\-\+]').maybe }
|
94
|
-
rule(:value_array) { (str("[") >> (value >> separator?).repeat(0) >> str("]")).as(:array) }
|
94
|
+
rule(:value_array) { (str("[") >> space? >> (value >> separator?).repeat(0) >> str("]")).as(:array) }
|
95
95
|
rule(:value_boolean) { (str("true") | str("false")).as(:boolean) }
|
96
96
|
rule(:value_float) { (value_sign? >> match('\d').repeat(1) >> str(".") >> match('\d').repeat(1) >> (match("[eE]") >> value_sign? >> match('\d').repeat(1)).maybe).as(:float) }
|
97
97
|
rule(:value_input_object) { str("{") >> space? >> value_input_object_pair.repeat(1).as(:input_object) >> space? >> str("}") }
|
@@ -93,6 +93,7 @@ module GraphQL
|
|
93
93
|
|
94
94
|
# Values
|
95
95
|
rule(array: sequence(:v)) { v }
|
96
|
+
rule(array: simple(:v)) { [] } # just `nil`
|
96
97
|
rule(boolean: simple(:v)) { v == "true" ? true : false }
|
97
98
|
rule(input_object: sequence(:v)) { create_node(:InputObject, pairs: v, line: v.first.line, col: v.first.col) }
|
98
99
|
rule(input_object_name: simple(:n), input_object_value: simple(:v)) { create_node(:Argument, name: n.to_s, value: v, position_source: n)}
|
data/lib/graphql/query.rb
CHANGED
@@ -37,7 +37,7 @@ class GraphQL::Query
|
|
37
37
|
def initialize(schema, query_string, context: nil, variables: {}, debug: false, validate: true, operation_name: nil)
|
38
38
|
@schema = schema
|
39
39
|
@debug = debug
|
40
|
-
@context = Context.new(values: context)
|
40
|
+
@context = Context.new(query: self, values: context)
|
41
41
|
@validate = validate
|
42
42
|
@operation_name = operation_name
|
43
43
|
@fragments = {}
|
@@ -7,6 +7,7 @@ module GraphQL
|
|
7
7
|
extend Forwardable
|
8
8
|
|
9
9
|
def initialize(values)
|
10
|
+
@hash = values
|
10
11
|
@values = values.inject({}) do |memo, (inner_key, inner_value)|
|
11
12
|
memo[inner_key.to_s] = wrap_value(inner_value)
|
12
13
|
memo
|
@@ -19,6 +20,12 @@ module GraphQL
|
|
19
20
|
@values[key.to_s]
|
20
21
|
end
|
21
22
|
|
23
|
+
# Get the original Ruby hash
|
24
|
+
# @return [Hash] the original values hash
|
25
|
+
def to_h
|
26
|
+
@hash
|
27
|
+
end
|
28
|
+
|
22
29
|
def_delegators :@values, :keys, :values, :each
|
23
30
|
|
24
31
|
private
|
@@ -12,9 +12,14 @@ module GraphQL
|
|
12
12
|
# @return [Array<GraphQL::ExecutionError>] errors returned during execution
|
13
13
|
attr_reader :errors
|
14
14
|
|
15
|
+
# @return [GraphQL::Query] The query whose context this is
|
16
|
+
attr_reader :query
|
17
|
+
|
15
18
|
# Make a new context which delegates key lookup to `values`
|
16
|
-
# @param [
|
17
|
-
|
19
|
+
# @param query [GraphQL::Query] the query who owns this context
|
20
|
+
# @param values [Hash] A hash of arbitrary values which will be accessible at query-time
|
21
|
+
def initialize(query:, values:)
|
22
|
+
@query = query
|
18
23
|
@values = values
|
19
24
|
@errors = []
|
20
25
|
end
|
@@ -11,16 +11,34 @@ class GraphQL::StaticValidation::LiteralValidator
|
|
11
11
|
elsif type.kind.enum? && ast_value.is_a?(GraphQL::Language::Nodes::Enum)
|
12
12
|
type.valid_input?(ast_value.name)
|
13
13
|
elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
|
14
|
-
|
15
|
-
|
16
|
-
field_type = fields[value.name].type
|
17
|
-
present_if_required = field_type.kind.non_null? ? !value.nil? : true
|
18
|
-
present_if_required && validate(value.value, field_type)
|
19
|
-
end
|
14
|
+
required_input_fields_are_present(type, ast_value) &&
|
15
|
+
present_input_field_values_are_valid(type, ast_value)
|
20
16
|
elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
21
17
|
true
|
22
18
|
else
|
23
19
|
false
|
24
20
|
end
|
25
21
|
end
|
22
|
+
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
|
27
|
+
def required_input_fields_are_present(type, ast_node)
|
28
|
+
required_field_names = type.input_fields
|
29
|
+
.values
|
30
|
+
.select { |f| f.type.kind.non_null? }
|
31
|
+
.map(&:name)
|
32
|
+
present_field_names = ast_node.pairs.map(&:name)
|
33
|
+
missing_required_field_names = required_field_names - present_field_names
|
34
|
+
missing_required_field_names.none?
|
35
|
+
end
|
36
|
+
|
37
|
+
def present_input_field_values_are_valid(type, ast_node)
|
38
|
+
fields = type.input_fields
|
39
|
+
ast_node.pairs.all? do |value|
|
40
|
+
field_type = fields[value.name].type
|
41
|
+
validate(value.value, field_type)
|
42
|
+
end
|
43
|
+
end
|
26
44
|
end
|
@@ -5,7 +5,12 @@ class GraphQL::StaticValidation::ArgumentLiteralsAreCompatible < GraphQL::Static
|
|
5
5
|
arg_defn = defn.arguments[node.name]
|
6
6
|
valid = validator.validate(node.value, arg_defn.type)
|
7
7
|
if !valid
|
8
|
-
|
8
|
+
field_name = if parent.respond_to?(:alias)
|
9
|
+
parent.alias || parent.name
|
10
|
+
else
|
11
|
+
parent.name
|
12
|
+
end
|
13
|
+
context.errors << message("Argument #{node.name} on #{parent.class.name.split("::").last} '#{field_name}' has an invalid value", parent)
|
9
14
|
end
|
10
15
|
end
|
11
16
|
end
|
data/lib/graphql/version.rb
CHANGED
@@ -103,7 +103,7 @@ describe GraphQL::Introspection::TypeType do
|
|
103
103
|
"description"=>"Properties for finding a dairy product",
|
104
104
|
"kind"=>"INPUT_OBJECT",
|
105
105
|
"inputFields"=>[
|
106
|
-
{"name"=>"source", "type"=>{ "name" => "
|
106
|
+
{"name"=>"source", "type"=>{ "name" => "Non-Null"}, "defaultValue"=>nil},
|
107
107
|
{"name"=>"fatContent", "type"=>{ "name" => "Float"}, "defaultValue"=>nil}
|
108
108
|
]
|
109
109
|
}
|
@@ -112,6 +112,7 @@ describe GraphQL::Language::Parser do
|
|
112
112
|
|
113
113
|
it 'gets arrays' do
|
114
114
|
assert(parser.value.parse_with_debug('[true, 1, "my string", -5.123e56]'), 'array of values')
|
115
|
+
assert(parser.value.parse_with_debug('[ -5.123e56, $myVar ]'), 'array of values with whitespace')
|
115
116
|
assert(parser.value.parse_with_debug('[]'), 'empty array')
|
116
117
|
assert(parser.value.parse_with_debug('[[true, 1], ["my string", -5.123e56]]'), 'array of arrays')
|
117
118
|
end
|
@@ -9,6 +9,10 @@ describe GraphQL::Query::Context do
|
|
9
9
|
field :contextAstNodeName, types.String do
|
10
10
|
resolve -> (target, args, ctx) { ctx.ast_node.class.name }
|
11
11
|
end
|
12
|
+
|
13
|
+
field :queryName, types.String do
|
14
|
+
resolve -> (target, args, ctx) { ctx.query.class.name }
|
15
|
+
end
|
12
16
|
}}
|
13
17
|
let(:schema) { GraphQL::Schema.new(query: query_type, mutation: nil)}
|
14
18
|
let(:result) { schema.execute(query_string, context: {"some_key" => "some value"})}
|
@@ -34,4 +38,15 @@ describe GraphQL::Query::Context do
|
|
34
38
|
assert_equal(expected, result)
|
35
39
|
end
|
36
40
|
end
|
41
|
+
|
42
|
+
describe "access to the query" do
|
43
|
+
let(:query_string) { %|
|
44
|
+
query getCtx { queryName }
|
45
|
+
|}
|
46
|
+
|
47
|
+
it 'provides access to the AST node' do
|
48
|
+
expected = {"data" => {"queryName" => "GraphQL::Query"}}
|
49
|
+
assert_equal(expected, result)
|
50
|
+
end
|
51
|
+
end
|
37
52
|
end
|
@@ -7,6 +7,7 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
|
|
7
7
|
cheese(id: 1) { source @skip(if: {id: 1})}
|
8
8
|
yakSource: searchDairy(product: [{source: YAK, fatContent: 1.1}]) { source }
|
9
9
|
badSource: searchDairy(product: [{source: 1.1}]) { source }
|
10
|
+
missingSource: searchDairy(product: [{fatContent: 1.1}]) { source }
|
10
11
|
}
|
11
12
|
|
12
13
|
fragment cheeseFields on Cheese {
|
@@ -17,8 +18,8 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
|
|
17
18
|
let(:validator) { GraphQL::StaticValidation::Validator.new(schema: DummySchema, rules: [GraphQL::StaticValidation::ArgumentLiteralsAreCompatible]) }
|
18
19
|
let(:errors) { validator.validate(document) }
|
19
20
|
|
20
|
-
it 'finds undefined arguments to fields and directives' do
|
21
|
-
assert_equal(
|
21
|
+
it 'finds undefined or missing-required arguments to fields and directives' do
|
22
|
+
assert_equal(5, errors.length)
|
22
23
|
|
23
24
|
query_root_error = {
|
24
25
|
"message"=>"Argument id on Field 'cheese' has an invalid value",
|
@@ -33,14 +34,20 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
|
|
33
34
|
assert_includes(errors, directive_error)
|
34
35
|
|
35
36
|
input_object_error = {
|
36
|
-
"message"=>"Argument product on Field '
|
37
|
+
"message"=>"Argument product on Field 'badSource' has an invalid value",
|
37
38
|
"locations"=>[{"line"=>6, "column"=>7}]
|
38
39
|
}
|
39
40
|
assert_includes(errors, input_object_error)
|
40
41
|
|
42
|
+
missing_required_field_error = {
|
43
|
+
"message"=>"Argument product on Field 'missingSource' has an invalid value",
|
44
|
+
"locations"=>[{"line"=>7, "column"=>7}]
|
45
|
+
}
|
46
|
+
assert_includes(errors, missing_required_field_error)
|
47
|
+
|
41
48
|
fragment_error = {
|
42
49
|
"message"=>"Argument source on Field 'similarCheese' has an invalid value",
|
43
|
-
"locations"=>[{"line"=>
|
50
|
+
"locations"=>[{"line"=>11, "column"=>7}]
|
44
51
|
}
|
45
52
|
assert_includes(errors, fragment_error)
|
46
53
|
end
|
data/spec/support/dairy_app.rb
CHANGED
@@ -102,8 +102,11 @@ end
|
|
102
102
|
DairyProductInputType = GraphQL::InputObjectType.define {
|
103
103
|
name "DairyProductInput"
|
104
104
|
description "Properties for finding a dairy product"
|
105
|
-
input_field :source, DairyAnimalEnum
|
106
|
-
|
105
|
+
input_field :source, !DairyAnimalEnum do
|
106
|
+
description "Where it came from"
|
107
|
+
end
|
108
|
+
|
109
|
+
input_field :fatContent, types.Float, "How much fat it has"
|
107
110
|
}
|
108
111
|
|
109
112
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-11-
|
11
|
+
date: 2015-11-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parslet
|