graphql 1.8.6 → 1.8.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/templates/base_enum.erb +3 -1
- data/lib/generators/graphql/templates/base_input_object.erb +3 -1
- data/lib/generators/graphql/templates/base_interface.erb +4 -2
- data/lib/generators/graphql/templates/base_object.erb +3 -1
- data/lib/generators/graphql/templates/base_union.erb +3 -1
- data/lib/generators/graphql/templates/enum.erb +5 -3
- data/lib/generators/graphql/templates/interface.erb +6 -4
- data/lib/generators/graphql/templates/loader.erb +14 -12
- data/lib/generators/graphql/templates/mutation.erb +9 -6
- data/lib/generators/graphql/templates/mutation_type.erb +8 -6
- data/lib/generators/graphql/templates/object.erb +6 -4
- data/lib/generators/graphql/templates/query_type.erb +13 -11
- data/lib/generators/graphql/templates/union.erb +5 -3
- data/lib/graphql/argument.rb +19 -2
- data/lib/graphql/base_type.rb +1 -1
- data/lib/graphql/define/instance_definable.rb +2 -0
- data/lib/graphql/enum_type.rb +1 -0
- data/lib/graphql/execution/instrumentation.rb +28 -20
- data/lib/graphql/field.rb +3 -3
- data/lib/graphql/input_object_type.rb +2 -1
- data/lib/graphql/query.rb +1 -9
- data/lib/graphql/query/variables.rb +1 -18
- data/lib/graphql/relay.rb +0 -1
- data/lib/graphql/relay/mongo_relation_connection.rb +10 -0
- data/lib/graphql/relay/mutation.rb +2 -1
- data/lib/graphql/schema.rb +5 -4
- data/lib/graphql/schema/base_64_bp.rb +25 -0
- data/lib/graphql/schema/base_64_encoder.rb +5 -1
- data/lib/graphql/schema/field.rb +52 -22
- data/lib/graphql/schema/interface.rb +2 -0
- data/lib/graphql/schema/list.rb +3 -15
- data/lib/graphql/schema/member.rb +8 -4
- data/lib/graphql/schema/member/base_dsl_methods.rb +12 -6
- data/lib/graphql/schema/member/has_arguments.rb +7 -0
- data/lib/graphql/schema/member/relay_shortcuts.rb +47 -0
- data/lib/graphql/schema/member/scoped.rb +21 -0
- data/lib/graphql/schema/non_null.rb +8 -17
- data/lib/graphql/schema/resolver.rb +99 -76
- data/lib/graphql/schema/unique_within_type.rb +4 -1
- data/lib/graphql/schema/wrapper.rb +29 -0
- data/lib/graphql/static_validation/validation_context.rb +1 -3
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/type_kinds.rb +32 -8
- data/lib/graphql/types/relay/base_connection.rb +17 -3
- data/lib/graphql/types/relay/base_edge.rb +12 -0
- data/lib/graphql/version.rb +1 -1
- data/spec/generators/graphql/enum_generator_spec.rb +8 -6
- data/spec/generators/graphql/install_generator_spec.rb +24 -20
- data/spec/generators/graphql/interface_generator_spec.rb +6 -4
- data/spec/generators/graphql/loader_generator_spec.rb +30 -26
- data/spec/generators/graphql/mutation_generator_spec.rb +18 -13
- data/spec/generators/graphql/object_generator_spec.rb +12 -6
- data/spec/generators/graphql/union_generator_spec.rb +10 -4
- data/spec/graphql/authorization_spec.rb +55 -14
- data/spec/graphql/input_object_type_spec.rb +11 -0
- data/spec/graphql/language/generation_spec.rb +8 -6
- data/spec/graphql/language/nodes_spec.rb +8 -6
- data/spec/graphql/relay/connection_instrumentation_spec.rb +1 -1
- data/spec/graphql/relay/mongo_relation_connection_spec.rb +54 -0
- data/spec/graphql/schema/field_spec.rb +9 -0
- data/spec/graphql/schema/list_spec.rb +46 -0
- data/spec/graphql/schema/member/scoped_spec.rb +161 -0
- data/spec/graphql/schema/non_null_spec.rb +46 -0
- data/spec/graphql/schema/object_spec.rb +15 -0
- data/spec/graphql/schema/resolver_spec.rb +165 -25
- data/spec/graphql/subscriptions/serialize_spec.rb +0 -22
- data/spec/graphql/subscriptions_spec.rb +19 -31
- data/spec/support/global_id.rb +23 -0
- data/spec/support/lazy_helpers.rb +1 -1
- data/spec/support/star_trek/data.rb +19 -2
- data/spec/support/star_trek/schema.rb +37 -22
- data/spec/support/star_wars/schema.rb +24 -17
- metadata +220 -208
@@ -7,12 +7,14 @@ class GraphQLGeneratorsEnumGeneratorTest < BaseGeneratorTest
|
|
7
7
|
|
8
8
|
test "it generate enums with values" do
|
9
9
|
expected_content = <<-RUBY
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
module Types
|
11
|
+
class FamilyType < Types::BaseEnum
|
12
|
+
value "NIGHTSHADE"
|
13
|
+
value "BRASSICA", value: Family::COLE
|
14
|
+
value "UMBELLIFER", value: :umbellifer
|
15
|
+
value "LEGUME", value: "bean & friends"
|
16
|
+
value "CURCURBITS", value: 5
|
17
|
+
end
|
16
18
|
end
|
17
19
|
RUBY
|
18
20
|
|
@@ -48,15 +48,17 @@ RUBY
|
|
48
48
|
|
49
49
|
|
50
50
|
expected_query_type = <<-RUBY
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
51
|
+
module Types
|
52
|
+
class QueryType < Types::BaseObject
|
53
|
+
# Add root-level fields here.
|
54
|
+
# They will be entry points for queries on your schema.
|
55
|
+
|
56
|
+
# TODO: remove me
|
57
|
+
field :test_field, String, null: false,
|
58
|
+
description: \"An example field added by the generator\"
|
59
|
+
def test_field
|
60
|
+
\"Hello World!\"
|
61
|
+
end
|
60
62
|
end
|
61
63
|
end
|
62
64
|
RUBY
|
@@ -80,18 +82,20 @@ RUBY
|
|
80
82
|
end
|
81
83
|
|
82
84
|
expected_query_type = <<-RUBY
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
85
|
+
module Types
|
86
|
+
class QueryType < Types::BaseObject
|
87
|
+
# Add root-level fields here.
|
88
|
+
# They will be entry points for queries on your schema.
|
89
|
+
|
90
|
+
# TODO: remove me
|
91
|
+
field :test_field, String, null: false,
|
92
|
+
description: \"An example field added by the generator\"
|
93
|
+
def test_field
|
94
|
+
\"Hello World!\"
|
95
|
+
end
|
93
96
|
|
94
|
-
|
97
|
+
field :node, field: GraphQL::Relay::Node.field
|
98
|
+
end
|
95
99
|
end
|
96
100
|
RUBY
|
97
101
|
|
@@ -16,10 +16,12 @@ class GraphQLGeneratorsInterfaceGeneratorTest < BaseGeneratorTest
|
|
16
16
|
]
|
17
17
|
|
18
18
|
expected_content = <<-RUBY
|
19
|
-
module Types
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
module Types
|
20
|
+
module BirdType
|
21
|
+
include Types::BaseInterface
|
22
|
+
field :wingspan, Integer, null: false
|
23
|
+
field :foliage, [Types::ColorType], null: true
|
24
|
+
end
|
23
25
|
end
|
24
26
|
RUBY
|
25
27
|
|
@@ -9,19 +9,21 @@ class GraphQLGeneratorsLoaderGeneratorTest < BaseGeneratorTest
|
|
9
9
|
run_generator(["RecordLoader"])
|
10
10
|
|
11
11
|
expected_content = <<-RUBY
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
12
|
+
module Loaders
|
13
|
+
class RecordLoader < GraphQL::Batch::Loader
|
14
|
+
# Define `initialize` to store grouping arguments, eg
|
15
|
+
#
|
16
|
+
# Loaders::RecordLoader.for(group).load(value)
|
17
|
+
#
|
18
|
+
# def initialize()
|
19
|
+
# end
|
20
|
+
|
21
|
+
# `keys` contains each key from `.load(key)`.
|
22
|
+
# Find the corresponding values, then
|
23
|
+
# call `fulfill(key, value)` or `fulfill(key, nil)`
|
24
|
+
# for each key.
|
25
|
+
def perform(keys)
|
26
|
+
end
|
25
27
|
end
|
26
28
|
end
|
27
29
|
RUBY
|
@@ -33,19 +35,21 @@ RUBY
|
|
33
35
|
run_generator(["active_record::record_loader"])
|
34
36
|
|
35
37
|
expected_content = <<-RUBY
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
38
|
+
module Loaders
|
39
|
+
class ActiveRecord::RecordLoader < GraphQL::Batch::Loader
|
40
|
+
# Define `initialize` to store grouping arguments, eg
|
41
|
+
#
|
42
|
+
# Loaders::ActiveRecord::RecordLoader.for(group).load(value)
|
43
|
+
#
|
44
|
+
# def initialize()
|
45
|
+
# end
|
46
|
+
|
47
|
+
# `keys` contains each key from `.load(key)`.
|
48
|
+
# Find the corresponding values, then
|
49
|
+
# call `fulfill(key, value)` or `fulfill(key, nil)`
|
50
|
+
# for each key.
|
51
|
+
def perform(keys)
|
52
|
+
end
|
49
53
|
end
|
50
54
|
end
|
51
55
|
RUBY
|
@@ -20,27 +20,32 @@ class GraphQLGeneratorsMutationGeneratorTest < BaseGeneratorTest
|
|
20
20
|
end
|
21
21
|
|
22
22
|
UPDATE_NAME_MUTATION = <<-RUBY
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
module Mutations
|
24
|
+
class UpdateName < GraphQL::Schema::RelayClassicMutation
|
25
|
+
# TODO: define return fields
|
26
|
+
# field :post, Types::PostType, null: false
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
# TODO: define arguments
|
29
|
+
# argument :name, String, required: true
|
29
30
|
|
30
|
-
def resolve(**inputs)
|
31
31
|
# TODO: define resolve method
|
32
|
+
# def resolve(name:)
|
33
|
+
# { post: ... }
|
34
|
+
# end
|
32
35
|
end
|
33
36
|
end
|
34
37
|
RUBY
|
35
38
|
|
36
39
|
EXPECTED_MUTATION_TYPE = <<-RUBY
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
module Types
|
41
|
+
class MutationType < Types::BaseObject
|
42
|
+
field :updateName, mutation: Mutations::UpdateName
|
43
|
+
# TODO: remove me
|
44
|
+
field :test_field, String, null: false,
|
45
|
+
description: "An example field added by the generator"
|
46
|
+
def test_field
|
47
|
+
"Hello World"
|
48
|
+
end
|
44
49
|
end
|
45
50
|
end
|
46
51
|
RUBY
|
@@ -16,9 +16,11 @@ class GraphQLGeneratorsObjectGeneratorTest < BaseGeneratorTest
|
|
16
16
|
]
|
17
17
|
|
18
18
|
expected_content = <<-RUBY
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
module Types
|
20
|
+
class BirdType < Types::BaseObject
|
21
|
+
field :wingspan, Integer, null: false
|
22
|
+
field :foliage, [Types::ColorType], null: true
|
23
|
+
end
|
22
24
|
end
|
23
25
|
RUBY
|
24
26
|
|
@@ -32,7 +34,9 @@ RUBY
|
|
32
34
|
test "it generates classifed file" do
|
33
35
|
run_generator(["page"])
|
34
36
|
assert_file "app/graphql/types/page_type.rb", <<-RUBY
|
35
|
-
|
37
|
+
module Types
|
38
|
+
class PageType < Types::BaseObject
|
39
|
+
end
|
36
40
|
end
|
37
41
|
RUBY
|
38
42
|
end
|
@@ -40,8 +44,10 @@ RUBY
|
|
40
44
|
test "it makes Relay nodes" do
|
41
45
|
run_generator(["Page", "--node"])
|
42
46
|
assert_file "app/graphql/types/page_type.rb", <<-RUBY
|
43
|
-
|
44
|
-
|
47
|
+
module Types
|
48
|
+
class PageType < Types::BaseObject
|
49
|
+
implements GraphQL::Relay::Node.interface
|
50
|
+
end
|
45
51
|
end
|
46
52
|
RUBY
|
47
53
|
end
|
@@ -14,8 +14,10 @@ class GraphQLGeneratorsUnionGeneratorTest < BaseGeneratorTest
|
|
14
14
|
]
|
15
15
|
|
16
16
|
expected_content = <<-RUBY
|
17
|
-
|
18
|
-
|
17
|
+
module Types
|
18
|
+
class WingedCreatureType < Types::BaseUnion
|
19
|
+
possible_types [Types::InsectType, Types::BirdType]
|
20
|
+
end
|
19
21
|
end
|
20
22
|
RUBY
|
21
23
|
|
@@ -35,7 +37,9 @@ RUBY
|
|
35
37
|
]
|
36
38
|
|
37
39
|
expected_content = <<-RUBY
|
38
|
-
|
40
|
+
module Types
|
41
|
+
class WingedCreatureType < Types::BaseUnion
|
42
|
+
end
|
39
43
|
end
|
40
44
|
RUBY
|
41
45
|
|
@@ -50,7 +54,9 @@ RUBY
|
|
50
54
|
command = ["WingedCreature", "--directory", "app/mydirectory"]
|
51
55
|
|
52
56
|
expected_content = <<-RUBY
|
53
|
-
|
57
|
+
module Types
|
58
|
+
class WingedCreatureType < Types::BaseUnion
|
59
|
+
end
|
54
60
|
end
|
55
61
|
RUBY
|
56
62
|
|
@@ -272,18 +272,27 @@ describe GraphQL::Authorization do
|
|
272
272
|
field :hidden_interface, HiddenInterface, null: false, method: :itself
|
273
273
|
field :hidden_default_interface, HiddenDefaultInterface, null: false, method: :itself
|
274
274
|
field :hidden_connection, RelayObject.connection_type, null: :false, method: :empty_array
|
275
|
-
field :hidden_edge, RelayObject.edge_type, null: :false, method: :
|
275
|
+
field :hidden_edge, RelayObject.edge_type, null: :false, method: :edge_object
|
276
276
|
|
277
277
|
field :inaccessible, Integer, null: false, method: :object_id
|
278
278
|
field :inaccessible_object, InaccessibleObject, null: false, method: :itself
|
279
279
|
field :inaccessible_interface, InaccessibleInterface, null: false, method: :itself
|
280
280
|
field :inaccessible_default_interface, InaccessibleDefaultInterface, null: false, method: :itself
|
281
281
|
field :inaccessible_connection, RelayObject.connection_type, null: :false, method: :empty_array
|
282
|
-
field :inaccessible_edge, RelayObject.edge_type, null: :false, method: :
|
282
|
+
field :inaccessible_edge, RelayObject.edge_type, null: :false, method: :edge_object
|
283
283
|
|
284
284
|
field :unauthorized_object, UnauthorizedObject, null: true, method: :itself
|
285
|
-
field :unauthorized_connection, RelayObject.connection_type, null:
|
286
|
-
field :unauthorized_edge, RelayObject.edge_type, null:
|
285
|
+
field :unauthorized_connection, RelayObject.connection_type, null: false, method: :array_with_item
|
286
|
+
field :unauthorized_edge, RelayObject.edge_type, null: false, method: :edge_object
|
287
|
+
|
288
|
+
def edge_object
|
289
|
+
OpenStruct.new(node: 100)
|
290
|
+
end
|
291
|
+
|
292
|
+
def array_with_item
|
293
|
+
[1]
|
294
|
+
end
|
295
|
+
|
287
296
|
field :unauthorized_lazy_box, UnauthorizedBox, null: true do
|
288
297
|
argument :value, String, required: true
|
289
298
|
end
|
@@ -623,24 +632,56 @@ describe GraphQL::Authorization do
|
|
623
632
|
end
|
624
633
|
|
625
634
|
it "works with edges and connections" do
|
626
|
-
skip <<-MSG
|
627
|
-
This doesn't work because edge and connection type definitions
|
628
|
-
aren't class-based, and authorization is checked during class-based field execution.
|
629
|
-
MSG
|
630
635
|
query = <<-GRAPHQL
|
631
636
|
{
|
632
|
-
unauthorizedConnection {
|
633
|
-
|
637
|
+
unauthorizedConnection {
|
638
|
+
__typename
|
639
|
+
edges {
|
640
|
+
__typename
|
641
|
+
node {
|
642
|
+
__typename
|
643
|
+
}
|
644
|
+
}
|
645
|
+
nodes {
|
646
|
+
__typename
|
647
|
+
}
|
648
|
+
}
|
649
|
+
unauthorizedEdge {
|
650
|
+
__typename
|
651
|
+
node {
|
652
|
+
__typename
|
653
|
+
}
|
654
|
+
}
|
634
655
|
}
|
635
656
|
GRAPHQL
|
636
657
|
|
637
658
|
unauthorized_res = auth_execute(query, context: { unauthorized_relay: true })
|
638
|
-
|
639
|
-
|
659
|
+
conn = unauthorized_res["data"].fetch("unauthorizedConnection")
|
660
|
+
assert_equal "RelayObjectConnection", conn.fetch("__typename")
|
661
|
+
assert_equal nil, conn.fetch("nodes")
|
662
|
+
assert_equal [{"node" => nil, "__typename" => "RelayObjectEdge"}], conn.fetch("edges")
|
663
|
+
|
664
|
+
edge = unauthorized_res["data"].fetch("unauthorizedEdge")
|
665
|
+
assert_nil edge.fetch("node")
|
666
|
+
assert_equal "RelayObjectEdge", edge["__typename"]
|
667
|
+
|
668
|
+
unauthorized_object_paths = [
|
669
|
+
["unauthorizedConnection", "edges", 0, "node"],
|
670
|
+
["unauthorizedConnection", "nodes"],
|
671
|
+
["unauthorizedEdge", "node"]
|
672
|
+
]
|
673
|
+
|
674
|
+
assert_equal unauthorized_object_paths, unauthorized_res["errors"].map { |e| e["path"] }
|
640
675
|
|
641
676
|
authorized_res = auth_execute(query)
|
642
|
-
|
643
|
-
|
677
|
+
conn = authorized_res["data"].fetch("unauthorizedConnection")
|
678
|
+
assert_equal "RelayObjectConnection", conn.fetch("__typename")
|
679
|
+
assert_equal [{"__typename"=>"RelayObject"}], conn.fetch("nodes")
|
680
|
+
assert_equal [{"node" => {"__typename" => "RelayObject"}, "__typename" => "RelayObjectEdge"}], conn.fetch("edges")
|
681
|
+
|
682
|
+
edge = authorized_res["data"].fetch("unauthorizedEdge")
|
683
|
+
assert_equal "RelayObject", edge.fetch("node").fetch("__typename")
|
684
|
+
assert_equal "RelayObjectEdge", edge["__typename"]
|
644
685
|
end
|
645
686
|
|
646
687
|
it "authorizes _after_ resolving lazy objects" do
|
@@ -254,6 +254,10 @@ describe GraphQL::InputObjectType do
|
|
254
254
|
c: String = "Default"
|
255
255
|
d: Boolean = false
|
256
256
|
}
|
257
|
+
|
258
|
+
input SecondLevelInputObject {
|
259
|
+
example: ExampleInputObject = {b: 42, d: true}
|
260
|
+
}
|
257
261
|
|) }
|
258
262
|
let(:input_type) { schema.types['ExampleInputObject'] }
|
259
263
|
|
@@ -295,6 +299,13 @@ describe GraphQL::InputObjectType do
|
|
295
299
|
|
296
300
|
assert_equal false, result['d']
|
297
301
|
end
|
302
|
+
|
303
|
+
it "merges defaults of nested input objects" do
|
304
|
+
result = schema.types['SecondLevelInputObject'].coerce_isolated_input({})
|
305
|
+
assert_equal 42, result['example']['b']
|
306
|
+
assert_equal "Default", result['example']['c']
|
307
|
+
assert_equal true, result['example']['d']
|
308
|
+
end
|
298
309
|
end
|
299
310
|
|
300
311
|
describe "when sent into a query" do
|
@@ -7,11 +7,13 @@ describe GraphQL::Language::Generation do
|
|
7
7
|
GraphQL.parse('type Query { a: String! }')
|
8
8
|
}
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
let(:custom_printer_class) {
|
11
|
+
Class.new(GraphQL::Language::Printer) {
|
12
|
+
def print_field_definition(print_field_definition)
|
13
|
+
"<Field Hidden>"
|
14
|
+
end
|
15
|
+
}
|
16
|
+
}
|
15
17
|
|
16
18
|
it "accepts a custom printer" do
|
17
19
|
expected = <<-SCHEMA
|
@@ -30,7 +32,7 @@ type Query {
|
|
30
32
|
}
|
31
33
|
SCHEMA
|
32
34
|
|
33
|
-
assert_equal expected.chomp, GraphQL::Language::Generation.generate(document, printer:
|
35
|
+
assert_equal expected.chomp, GraphQL::Language::Generation.generate(document, printer: custom_printer_class.new)
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
@@ -27,11 +27,13 @@ describe GraphQL::Language::Nodes::AbstractNode do
|
|
27
27
|
GraphQL.parse('type Query { a: String! }')
|
28
28
|
}
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
let(:custom_printer_class) {
|
31
|
+
Class.new(GraphQL::Language::Printer) {
|
32
|
+
def print_field_definition(print_field_definition)
|
33
|
+
"<Field Hidden>"
|
34
|
+
end
|
35
|
+
}
|
36
|
+
}
|
35
37
|
|
36
38
|
it "accepts a custom printer" do
|
37
39
|
expected = <<-SCHEMA
|
@@ -39,7 +41,7 @@ type Query {
|
|
39
41
|
<Field Hidden>
|
40
42
|
}
|
41
43
|
SCHEMA
|
42
|
-
assert_equal expected.chomp, document.to_query_string(printer:
|
44
|
+
assert_equal expected.chomp, document.to_query_string(printer: custom_printer_class.new)
|
43
45
|
end
|
44
46
|
end
|
45
47
|
end
|