graphql 0.18.6 → 0.18.7
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/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
|