graphql 0.10.3 → 0.10.4
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.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
|