graphql 0.18.6 → 0.18.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/introspection/type_by_name_field.rb +2 -2
- data/lib/graphql/language/generation.rb +1 -1
- data/lib/graphql/language/parser.rb +320 -306
- data/lib/graphql/language/parser.y +4 -1
- data/lib/graphql/relay/mutation.rb +1 -1
- data/lib/graphql/schema/loader.rb +10 -3
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/fragments_are_named.rb +19 -0
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +4 -2
- data/spec/graphql/introspection/type_type_spec.rb +3 -1
- data/spec/graphql/language/generation_spec.rb +1 -1
- data/spec/graphql/language/parser_spec.rb +28 -0
- data/spec/graphql/relay/mutation_spec.rb +4 -0
- data/spec/graphql/schema/loader_spec.rb +27 -1
- data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +24 -0
- data/spec/graphql/static_validation/validator_spec.rb +12 -0
- metadata +5 -2
@@ -228,7 +228,7 @@ rule
|
|
228
228
|
}
|
229
229
|
|
230
230
|
fragment_definition:
|
231
|
-
FRAGMENT
|
231
|
+
FRAGMENT fragment_name_opt ON name_without_on directives_list_opt selection_set {
|
232
232
|
return make_node(:FragmentDefinition, {
|
233
233
|
name: val[1],
|
234
234
|
type: val[3],
|
@@ -239,6 +239,9 @@ rule
|
|
239
239
|
)
|
240
240
|
}
|
241
241
|
|
242
|
+
fragment_name_opt:
|
243
|
+
/* none */ { return nil }
|
244
|
+
| name_without_on
|
242
245
|
|
243
246
|
type_system_definition:
|
244
247
|
schema_definition
|
@@ -80,7 +80,7 @@ module GraphQL
|
|
80
80
|
results_hash = @resolve_proc.call(args[:input], ctx)
|
81
81
|
Result.new(arguments: args, result: results_hash)
|
82
82
|
}
|
83
|
-
GraphQL::Field.define do
|
83
|
+
GraphQL::Field.define(description: self.description) do
|
84
84
|
type(field_return_type)
|
85
85
|
argument :input, !field_input_type
|
86
86
|
resolve(field_resolve_proc)
|
@@ -22,9 +22,13 @@ module GraphQL
|
|
22
22
|
types[type_object.name] = type_object
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
kargs = { :orphan_types => types.values }
|
26
|
+
[:query, :mutation, :subscription].each do |root|
|
27
|
+
type = schema["#{root}Type"]
|
28
|
+
kargs[root] = types.fetch(type.fetch("name")) if type
|
29
|
+
end
|
26
30
|
|
27
|
-
Schema.
|
31
|
+
Schema.define(**kargs)
|
28
32
|
end
|
29
33
|
|
30
34
|
class << self
|
@@ -61,7 +65,7 @@ module GraphQL
|
|
61
65
|
InterfaceType.define(
|
62
66
|
name: type["name"],
|
63
67
|
description: type["description"],
|
64
|
-
fields: Hash[type["fields"].map { |field|
|
68
|
+
fields: Hash[(type["fields"] || []).map { |field|
|
65
69
|
[field["name"], define_type(field.merge("kind" => "FIELD"), type_resolver)]
|
66
70
|
}]
|
67
71
|
)
|
@@ -77,6 +81,9 @@ module GraphQL
|
|
77
81
|
ObjectType.define(
|
78
82
|
name: type["name"],
|
79
83
|
description: type["description"],
|
84
|
+
interfaces: (type["interfaces"] || []).map { |interface|
|
85
|
+
type_resolver.call(interface)
|
86
|
+
},
|
80
87
|
fields: Hash[type["fields"].map { |field|
|
81
88
|
[field["name"], define_type(field.merge("kind" => "FIELD"), type_resolver)]
|
82
89
|
}]
|
@@ -9,6 +9,7 @@ module GraphQL
|
|
9
9
|
GraphQL::StaticValidation::DirectivesAreDefined,
|
10
10
|
GraphQL::StaticValidation::DirectivesAreInValidLocations,
|
11
11
|
GraphQL::StaticValidation::FragmentsAreFinite,
|
12
|
+
GraphQL::StaticValidation::FragmentsAreNamed,
|
12
13
|
GraphQL::StaticValidation::FragmentsAreUsed,
|
13
14
|
GraphQL::StaticValidation::FragmentTypesExist,
|
14
15
|
GraphQL::StaticValidation::FragmentsAreOnCompositeTypes,
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module StaticValidation
|
3
|
+
class FragmentsAreNamed
|
4
|
+
include GraphQL::StaticValidation::Message::MessageHelper
|
5
|
+
|
6
|
+
def validate(context)
|
7
|
+
context.visitor[GraphQL::Language::Nodes::FragmentDefinition] << -> (node, parent) { validate_name_exists(node, context) }
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def validate_name_exists(node, context)
|
13
|
+
if node.name.nil?
|
14
|
+
context.errors << message("Fragment definition has no name", node, context: context)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -38,7 +38,7 @@ module GraphQL
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def find_difference(fragments, allowed_fragment_names)
|
41
|
-
fragments.select {|f| !allowed_fragment_names.include?(f.name) }
|
41
|
+
fragments.select {|f| f.name && !allowed_fragment_names.include?(f.name) }
|
42
42
|
end
|
43
43
|
|
44
44
|
class FragmentInstance
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -63,8 +63,8 @@ end
|
|
63
63
|
|
64
64
|
# Then create your schema
|
65
65
|
Schema = GraphQL::Schema.define do
|
66
|
-
query QueryType
|
67
|
-
max_depth 8
|
66
|
+
query QueryType
|
67
|
+
max_depth 8
|
68
68
|
end
|
69
69
|
```
|
70
70
|
|
@@ -135,3 +135,5 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
|
|
135
135
|
- Reduce duplication in ArrayConnection / RelationConnection
|
136
136
|
- Improve API for creating edges (better RANGE_ADD support)
|
137
137
|
- If the new edge isn't a member of the connection's objects, raise a nice error
|
138
|
+
- Missing Enum value should raise a descriptive error, not "key not found"
|
139
|
+
- `args` should whitelist keys -- if you request a key that isn't defined for the field, it should 💥
|
@@ -8,6 +8,7 @@ describe GraphQL::Introspection::TypeType do
|
|
8
8
|
dairyAnimal: __type(name: "DairyAnimal") { name, kind, enumValues(includeDeprecated: false) { name, isDeprecated } }
|
9
9
|
dairyProduct: __type(name: "DairyProduct") { name, kind, possibleTypes { name } }
|
10
10
|
animalProduct: __type(name: "AnimalProduct") { name, kind, possibleTypes { name }, fields { name } }
|
11
|
+
missingType: __type(name: "NotAType") { name }
|
11
12
|
}
|
12
13
|
|}
|
13
14
|
let(:result) { DummySchema.execute(query_string, context: {}, variables: {"cheeseId" => 2}) }
|
@@ -64,7 +65,8 @@ describe GraphQL::Introspection::TypeType do
|
|
64
65
|
"fields"=>[
|
65
66
|
{"name"=>"source"},
|
66
67
|
]
|
67
|
-
}
|
68
|
+
},
|
69
|
+
"missingType" => nil,
|
68
70
|
}}
|
69
71
|
assert_equal(expected, result)
|
70
72
|
end
|
@@ -7,7 +7,7 @@ describe GraphQL::Language::Generation do
|
|
7
7
|
myField: someField(someArg: $someVar, ok: 1.4) @skip(if: $anotherVar) @thing(or: "Whatever")
|
8
8
|
anotherField(someArg: [1, 2, 3]) {
|
9
9
|
nestedField
|
10
|
-
...
|
10
|
+
...moreNestedFields @skip(if: $skipNested)
|
11
11
|
}
|
12
12
|
... on OtherType @include(unless: false) {
|
13
13
|
field(arg: [{ key: "value", anotherKey: 0.9, anotherAnotherKey: WHATEVER }])
|
@@ -4,4 +4,32 @@ require 'graphql/language/parser_tests'
|
|
4
4
|
describe GraphQL::Language::Parser do
|
5
5
|
include GraphQL::Language::ParserTests
|
6
6
|
subject { GraphQL::Language::Parser }
|
7
|
+
|
8
|
+
describe "anonymous fragment extension" do
|
9
|
+
let(:document) { GraphQL.parse(query_string) }
|
10
|
+
let(:query_string) {%|
|
11
|
+
fragment on NestedType @or(something: "ok") {
|
12
|
+
anotherNestedField
|
13
|
+
}
|
14
|
+
|}
|
15
|
+
|
16
|
+
describe ".parse" do
|
17
|
+
it "parses queries" do
|
18
|
+
assert document
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "visited nodes" do
|
22
|
+
let(:fragment) { document.definitions.first }
|
23
|
+
|
24
|
+
it "creates an anonymous fragment definition" do
|
25
|
+
assert fragment.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
|
26
|
+
assert_equal nil, fragment.name
|
27
|
+
assert_equal 1, fragment.selections.length
|
28
|
+
assert_equal "NestedType", fragment.type
|
29
|
+
assert_equal 1, fragment.directives.length
|
30
|
+
assert_equal [2, 7], fragment.position
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
7
35
|
end
|
@@ -47,4 +47,8 @@ describe GraphQL::Relay::Mutation do
|
|
47
47
|
new_ship_name = result["data"]["introduceShip"]["shipEdge"]["node"]["name"]
|
48
48
|
assert_equal("Bagel", new_ship_name)
|
49
49
|
end
|
50
|
+
|
51
|
+
it "applies the description to the derived field" do
|
52
|
+
assert_equal "Add a ship to this faction", IntroduceShipMutation.field.description
|
53
|
+
end
|
50
54
|
end
|
@@ -45,6 +45,22 @@ describe GraphQL::Schema::Loader do
|
|
45
45
|
field :body, !types.String
|
46
46
|
end
|
47
47
|
|
48
|
+
media_type = GraphQL::InterfaceType.define do
|
49
|
+
name "Media"
|
50
|
+
description "!!!"
|
51
|
+
field :type, !types.String
|
52
|
+
end
|
53
|
+
|
54
|
+
video_type = GraphQL::ObjectType.define do
|
55
|
+
name "Video"
|
56
|
+
interfaces [media_type]
|
57
|
+
end
|
58
|
+
|
59
|
+
audio_type = GraphQL::ObjectType.define do
|
60
|
+
name "Audio"
|
61
|
+
interfaces [media_type]
|
62
|
+
end
|
63
|
+
|
48
64
|
post_type = GraphQL::ObjectType.define do
|
49
65
|
name "Post"
|
50
66
|
description "A blog post"
|
@@ -53,6 +69,7 @@ describe GraphQL::Schema::Loader do
|
|
53
69
|
field :title, !types.String
|
54
70
|
field :body, !types.String
|
55
71
|
field :comments, types[!comment_type]
|
72
|
+
field :attachment, media_type
|
56
73
|
end
|
57
74
|
|
58
75
|
content_type = GraphQL::UnionType.define do
|
@@ -76,7 +93,16 @@ describe GraphQL::Schema::Loader do
|
|
76
93
|
end
|
77
94
|
end
|
78
95
|
|
79
|
-
GraphQL::
|
96
|
+
ping_mutation = GraphQL::Relay::Mutation.define do
|
97
|
+
name "Ping"
|
98
|
+
end
|
99
|
+
|
100
|
+
mutation_root = GraphQL::ObjectType.define do
|
101
|
+
name "Mutation"
|
102
|
+
field :ping, field: ping_mutation.field
|
103
|
+
end
|
104
|
+
|
105
|
+
GraphQL::Schema.define(query: query_root, mutation: mutation_root, orphan_types: [audio_type, video_type])
|
80
106
|
}
|
81
107
|
|
82
108
|
let(:schema_json) {
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GraphQL::StaticValidation::FragmentTypesExist do
|
4
|
+
let(:query_string) {"
|
5
|
+
fragment on Cheese {
|
6
|
+
id
|
7
|
+
flavor
|
8
|
+
}
|
9
|
+
"}
|
10
|
+
|
11
|
+
let(:validator) { GraphQL::StaticValidation::Validator.new(schema: DummySchema, rules: [GraphQL::StaticValidation::FragmentsAreNamed]) }
|
12
|
+
let(:query) { GraphQL::Query.new(DummySchema, query_string) }
|
13
|
+
let(:errors) { validator.validate(query)[:errors] }
|
14
|
+
|
15
|
+
it "finds non-existent types on fragments" do
|
16
|
+
assert_equal(1, errors.length)
|
17
|
+
fragment_def_error = {
|
18
|
+
"message"=>"Fragment definition has no name",
|
19
|
+
"locations"=>[{"line"=>2, "column"=>5}],
|
20
|
+
"path"=>["fragment "],
|
21
|
+
}
|
22
|
+
assert_includes(errors, fragment_def_error, "on fragment definitions")
|
23
|
+
end
|
24
|
+
end
|
@@ -65,5 +65,17 @@ describe GraphQL::StaticValidation::Validator do
|
|
65
65
|
assert_equal(1, errors.length)
|
66
66
|
end
|
67
67
|
end
|
68
|
+
|
69
|
+
describe "fragments with no names" do
|
70
|
+
let(:query_string) {%|
|
71
|
+
fragment on Cheese {
|
72
|
+
id
|
73
|
+
flavor
|
74
|
+
}
|
75
|
+
|}
|
76
|
+
it "marks an error" do
|
77
|
+
assert_equal(1, errors.length)
|
78
|
+
end
|
79
|
+
end
|
68
80
|
end
|
69
81
|
end
|
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.18.
|
4
|
+
version: 0.18.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: codeclimate-test-reporter
|
@@ -372,6 +372,7 @@ files:
|
|
372
372
|
- lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb
|
373
373
|
- lib/graphql/static_validation/rules/fragment_types_exist.rb
|
374
374
|
- lib/graphql/static_validation/rules/fragments_are_finite.rb
|
375
|
+
- lib/graphql/static_validation/rules/fragments_are_named.rb
|
375
376
|
- lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb
|
376
377
|
- lib/graphql/static_validation/rules/fragments_are_used.rb
|
377
378
|
- lib/graphql/static_validation/rules/required_arguments_are_present.rb
|
@@ -452,6 +453,7 @@ files:
|
|
452
453
|
- spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb
|
453
454
|
- spec/graphql/static_validation/rules/fragment_types_exist_spec.rb
|
454
455
|
- spec/graphql/static_validation/rules/fragments_are_finite_spec.rb
|
456
|
+
- spec/graphql/static_validation/rules/fragments_are_named_spec.rb
|
455
457
|
- spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb
|
456
458
|
- spec/graphql/static_validation/rules/fragments_are_used_spec.rb
|
457
459
|
- spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb
|
@@ -559,6 +561,7 @@ test_files:
|
|
559
561
|
- spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb
|
560
562
|
- spec/graphql/static_validation/rules/fragment_types_exist_spec.rb
|
561
563
|
- spec/graphql/static_validation/rules/fragments_are_finite_spec.rb
|
564
|
+
- spec/graphql/static_validation/rules/fragments_are_named_spec.rb
|
562
565
|
- spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb
|
563
566
|
- spec/graphql/static_validation/rules/fragments_are_used_spec.rb
|
564
567
|
- spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb
|