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
@@ -77,7 +77,7 @@ describe GraphQL::Relay::ConnectionInstrumentation do
|
|
77
77
|
# Before the object is wrapped in a connection, the instrumentation sees `Array`
|
78
78
|
assert_equal ["StarWars::FactionRecord", "Array", "GraphQL::Relay::ArrayConnection"], ctx[:before_built_ins]
|
79
79
|
# After the object is wrapped in a connection, it sees the connection object
|
80
|
-
assert_equal ["StarWars::Faction", "
|
80
|
+
assert_equal ["StarWars::Faction", "StarWars::ShipConnectionWithParentType", "GraphQL::Types::Relay::PageInfo"], ctx[:after_built_ins]
|
81
81
|
end
|
82
82
|
end
|
83
83
|
end
|
@@ -18,6 +18,15 @@ describe GraphQL::Relay::MongoRelationConnection do
|
|
18
18
|
ships.map { |e| e["node"]["name"] }
|
19
19
|
end
|
20
20
|
|
21
|
+
def get_residents(ship)
|
22
|
+
ship["residents"]["edges"].map { |e| e["node"]["name"] }
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_ships_residents(result)
|
26
|
+
ships = result["data"]["federation"]["bases"]["edges"]
|
27
|
+
Hash[ships.map { |e| [e["node"]["name"], get_residents(e["node"])] }]
|
28
|
+
end
|
29
|
+
|
21
30
|
def get_page_info(result)
|
22
31
|
result["data"]["federation"]["bases"]["pageInfo"]
|
23
32
|
end
|
@@ -482,4 +491,49 @@ describe GraphQL::Relay::MongoRelationConnection do
|
|
482
491
|
connection = GraphQL::Relay::BaseConnection.connection_for_nodes(relation)
|
483
492
|
assert_equal GraphQL::Relay::MongoRelationConnection, connection
|
484
493
|
end
|
494
|
+
|
495
|
+
describe "relations" do
|
496
|
+
let(:query_string) {%|
|
497
|
+
query getShips {
|
498
|
+
federation {
|
499
|
+
bases {
|
500
|
+
... basesConnection
|
501
|
+
}
|
502
|
+
}
|
503
|
+
}
|
504
|
+
|
505
|
+
fragment basesConnection on BasesConnectionWithTotalCount {
|
506
|
+
edges {
|
507
|
+
cursor
|
508
|
+
node {
|
509
|
+
name
|
510
|
+
residents {
|
511
|
+
edges {
|
512
|
+
node {
|
513
|
+
name
|
514
|
+
}
|
515
|
+
}
|
516
|
+
}
|
517
|
+
}
|
518
|
+
}
|
519
|
+
}
|
520
|
+
|}
|
521
|
+
|
522
|
+
it "Mongoid::Association::Referenced::HasMany::Targets::Enumerable" do
|
523
|
+
result = star_trek_query(query_string)
|
524
|
+
assert_equal get_ships_residents(result), {
|
525
|
+
"Deep Space Station K-7" => [
|
526
|
+
"Shir th'Talias",
|
527
|
+
"Lurry",
|
528
|
+
"Mackenzie Calhoun"
|
529
|
+
],
|
530
|
+
"Regula I" => [
|
531
|
+
"V. Madison",
|
532
|
+
"D. March",
|
533
|
+
"C. Marcus"
|
534
|
+
],
|
535
|
+
"Deep Space Nine" => []
|
536
|
+
}
|
537
|
+
end
|
538
|
+
end
|
485
539
|
end
|
@@ -11,6 +11,15 @@ describe GraphQL::Schema::Field do
|
|
11
11
|
assert_equal :ok, arg_defn.metadata[:custom]
|
12
12
|
end
|
13
13
|
|
14
|
+
it "can add argument directly with add_argument" do
|
15
|
+
argument = Jazz::Query.fields["instruments"].arguments["family"]
|
16
|
+
|
17
|
+
field.add_argument(argument)
|
18
|
+
|
19
|
+
assert_equal "family", field.arguments["family"].name
|
20
|
+
assert_equal Jazz::Family, field.arguments["family"].type
|
21
|
+
end
|
22
|
+
|
14
23
|
it "attaches itself to its graphql_definition as type_class" do
|
15
24
|
assert_equal field, field.graphql_definition.metadata[:type_class]
|
16
25
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe GraphQL::Schema::List do
|
6
|
+
let(:of_type) { Jazz::Musician }
|
7
|
+
let(:list_type) { GraphQL::Schema::List.new(of_type) }
|
8
|
+
|
9
|
+
it "returns list? to be true" do
|
10
|
+
assert list_type.list?
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns non_null? to be false" do
|
14
|
+
refute list_type.non_null?
|
15
|
+
end
|
16
|
+
|
17
|
+
it "returns kind to be GraphQL::TypeKinds::LIST" do
|
18
|
+
assert_equal GraphQL::TypeKinds::LIST, list_type.kind
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns correct type signature" do
|
22
|
+
assert_equal "[Musician]", list_type.to_type_signature
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "comparison operator" do
|
26
|
+
it "will return false if list types 'of_type' are different" do
|
27
|
+
new_of_type = Jazz::InspectableKey
|
28
|
+
new_list_type = GraphQL::Schema::List.new(new_of_type)
|
29
|
+
|
30
|
+
refute_equal list_type, new_list_type
|
31
|
+
end
|
32
|
+
|
33
|
+
it "will return true if list types 'of_type' are the same" do
|
34
|
+
new_of_type = Jazz::Musician
|
35
|
+
new_list_type = GraphQL::Schema::List.new(new_of_type)
|
36
|
+
|
37
|
+
assert_equal list_type, new_list_type
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "to_graphql" do
|
42
|
+
it "will return a list type" do
|
43
|
+
assert_kind_of GraphQL::ListType, list_type.to_graphql
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe GraphQL::Schema::Member::Scoped do
|
5
|
+
class ScopeSchema < GraphQL::Schema
|
6
|
+
class BaseObject < GraphQL::Schema::Object
|
7
|
+
end
|
8
|
+
|
9
|
+
class Item < BaseObject
|
10
|
+
def self.scope_items(items, context)
|
11
|
+
if context[:french]
|
12
|
+
items.select { |i| i.name == "Trombone" }
|
13
|
+
elsif context[:english]
|
14
|
+
items.select { |i| i.name == "Paperclip" }
|
15
|
+
else
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
field :name, String, null: false
|
21
|
+
end
|
22
|
+
|
23
|
+
class FrenchItem < Item
|
24
|
+
def self.scope_items(items, context)
|
25
|
+
super(items, {french: true})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Equipment < BaseObject
|
30
|
+
field :designation, String, null: false, method: :name
|
31
|
+
end
|
32
|
+
|
33
|
+
class BaseUnion < GraphQL::Schema::Union
|
34
|
+
end
|
35
|
+
|
36
|
+
class Thing < BaseUnion
|
37
|
+
def self.scope_items(items, context)
|
38
|
+
l = context.fetch(:first_letter)
|
39
|
+
items.select { |i| i.name.start_with?(l) }
|
40
|
+
end
|
41
|
+
|
42
|
+
possible_types Item, Equipment
|
43
|
+
|
44
|
+
def self.resolve_type(item, ctx)
|
45
|
+
if item.name == "Turbine"
|
46
|
+
Equipment
|
47
|
+
else
|
48
|
+
Item
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Query < BaseObject
|
54
|
+
field :items, [Item], null: false
|
55
|
+
field :unscoped_items, [Item], null: false,
|
56
|
+
scope: false,
|
57
|
+
method: :items
|
58
|
+
field :french_items, [FrenchItem], null: false,
|
59
|
+
method: :items
|
60
|
+
field :items_connection, Item.connection_type, null: false,
|
61
|
+
method: :items
|
62
|
+
|
63
|
+
def items
|
64
|
+
[
|
65
|
+
OpenStruct.new(name: "Trombone"),
|
66
|
+
OpenStruct.new(name: "Paperclip"),
|
67
|
+
]
|
68
|
+
end
|
69
|
+
|
70
|
+
field :things, [Thing], null: false
|
71
|
+
def things
|
72
|
+
items + [OpenStruct.new(name: "Turbine")]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
query(Query)
|
77
|
+
end
|
78
|
+
|
79
|
+
describe ".scope_items(items, ctx)" do
|
80
|
+
def get_item_names_with_context(ctx, field_name: "items")
|
81
|
+
query_str = "
|
82
|
+
{
|
83
|
+
#{field_name} {
|
84
|
+
name
|
85
|
+
}
|
86
|
+
}
|
87
|
+
"
|
88
|
+
res = ScopeSchema.execute(query_str, context: ctx)
|
89
|
+
res["data"][field_name].map { |i| i["name"] }
|
90
|
+
end
|
91
|
+
|
92
|
+
it "applies to lists when scope: true" do
|
93
|
+
assert_equal [], get_item_names_with_context({})
|
94
|
+
assert_equal ["Trombone"], get_item_names_with_context({french: true})
|
95
|
+
assert_equal ["Paperclip"], get_item_names_with_context({english: true})
|
96
|
+
end
|
97
|
+
|
98
|
+
it "is bypassed when scope: false" do
|
99
|
+
assert_equal ["Trombone", "Paperclip"], get_item_names_with_context({}, field_name: "unscopedItems")
|
100
|
+
end
|
101
|
+
|
102
|
+
it "is inherited" do
|
103
|
+
assert_equal ["Trombone"], get_item_names_with_context({}, field_name: "frenchItems")
|
104
|
+
end
|
105
|
+
|
106
|
+
it "is called for connection fields" do
|
107
|
+
query_str = "
|
108
|
+
{
|
109
|
+
itemsConnection {
|
110
|
+
edges {
|
111
|
+
node {
|
112
|
+
name
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
"
|
118
|
+
res = ScopeSchema.execute(query_str, context: {english: true})
|
119
|
+
names = res["data"]["itemsConnection"]["edges"].map { |e| e["node"]["name"] }
|
120
|
+
assert_equal ["Paperclip"], names
|
121
|
+
end
|
122
|
+
|
123
|
+
it "is called for abstract types" do
|
124
|
+
query_str = "
|
125
|
+
{
|
126
|
+
things {
|
127
|
+
... on Item {
|
128
|
+
name
|
129
|
+
}
|
130
|
+
... on Equipment {
|
131
|
+
designation
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
"
|
136
|
+
res = ScopeSchema.execute(query_str, context: {first_letter: "T"})
|
137
|
+
things = res["data"]["things"]
|
138
|
+
assert_equal [{ "name" => "Trombone" }, {"designation" => "Turbine"}], things
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "Schema::Field.scoped?" do
|
143
|
+
it "prefers the override value" do
|
144
|
+
assert_equal false, ScopeSchema::Query.fields["unscopedItems"].scoped?
|
145
|
+
end
|
146
|
+
|
147
|
+
it "defaults to true for lists" do
|
148
|
+
assert_equal true, ScopeSchema::Query.fields["items"].type.list?
|
149
|
+
assert_equal true, ScopeSchema::Query.fields["items"].scoped?
|
150
|
+
end
|
151
|
+
|
152
|
+
it "defaults to true for connections" do
|
153
|
+
assert_equal true, ScopeSchema::Query.fields["itemsConnection"].connection?
|
154
|
+
assert_equal true, ScopeSchema::Query.fields["itemsConnection"].scoped?
|
155
|
+
end
|
156
|
+
|
157
|
+
it "defaults to false for others" do
|
158
|
+
assert_equal false, ScopeSchema::Item.fields["name"].scoped?
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe GraphQL::Schema::NonNull do
|
6
|
+
let(:of_type) { Jazz::Musician }
|
7
|
+
let(:non_null_type) { GraphQL::Schema::NonNull.new(of_type) }
|
8
|
+
|
9
|
+
it "returns list? to be false" do
|
10
|
+
refute non_null_type.list?
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns non_null? to be true" do
|
14
|
+
assert non_null_type.non_null?
|
15
|
+
end
|
16
|
+
|
17
|
+
it "returns kind to be GraphQL::TypeKinds::NON_NULL" do
|
18
|
+
assert_equal GraphQL::TypeKinds::NON_NULL, non_null_type.kind
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns correct type signature" do
|
22
|
+
assert_equal "Musician!", non_null_type.to_type_signature
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "comparison operator" do
|
26
|
+
it "will return false if list types 'of_type' are different" do
|
27
|
+
new_of_type = Jazz::InspectableKey
|
28
|
+
new_non_null_type = GraphQL::Schema::NonNull.new(new_of_type)
|
29
|
+
|
30
|
+
refute_equal non_null_type, new_non_null_type
|
31
|
+
end
|
32
|
+
|
33
|
+
it "will return true if list types 'of_type' are the same" do
|
34
|
+
new_of_type = Jazz::Musician
|
35
|
+
new_non_null_type = GraphQL::Schema::NonNull.new(new_of_type)
|
36
|
+
|
37
|
+
assert_equal non_null_type, new_non_null_type
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "to_graphql" do
|
42
|
+
it "will return a non null type" do
|
43
|
+
assert_kind_of GraphQL::NonNullType, non_null_type.to_graphql
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -4,6 +4,7 @@ require "spec_helper"
|
|
4
4
|
describe GraphQL::Schema::Object do
|
5
5
|
describe "class attributes" do
|
6
6
|
let(:object_class) { Jazz::Ensemble }
|
7
|
+
|
7
8
|
it "tells type data" do
|
8
9
|
assert_equal "Ensemble", object_class.graphql_name
|
9
10
|
assert_equal "A group of musicians playing together", object_class.description
|
@@ -52,6 +53,20 @@ describe GraphQL::Schema::Object do
|
|
52
53
|
anonymous_class.graphql_name
|
53
54
|
end
|
54
55
|
end
|
56
|
+
|
57
|
+
class OverrideNameObject < GraphQL::Schema::Object
|
58
|
+
class << self
|
59
|
+
def default_graphql_name
|
60
|
+
"Override"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it "can override the default graphql_name" do
|
66
|
+
override_name_object = OverrideNameObject
|
67
|
+
|
68
|
+
assert_equal "Override", override_name_object.graphql_name
|
69
|
+
end
|
55
70
|
end
|
56
71
|
|
57
72
|
describe "implementing interfaces" do
|
@@ -76,7 +76,7 @@ describe GraphQL::Schema::Resolver do
|
|
76
76
|
|
77
77
|
class PrepResolver1 < BaseResolver
|
78
78
|
argument :int, Integer, required: true
|
79
|
-
|
79
|
+
undef_method :load_int
|
80
80
|
def load_int(i)
|
81
81
|
i * 10
|
82
82
|
end
|
@@ -127,37 +127,122 @@ describe GraphQL::Schema::Resolver do
|
|
127
127
|
class PrepResolver5 < PrepResolver1
|
128
128
|
type Integer, null: true
|
129
129
|
|
130
|
-
def
|
130
|
+
def ready?(int:)
|
131
131
|
check_for_magic_number(int)
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
135
135
|
class PrepResolver6 < PrepResolver5
|
136
|
-
def
|
136
|
+
def ready?(**args)
|
137
137
|
LazyBlock.new {
|
138
138
|
super
|
139
139
|
}
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
143
|
-
class PrepResolver7 <
|
143
|
+
class PrepResolver7 < GraphQL::Schema::Mutation
|
144
|
+
argument :int, Integer, required: true
|
145
|
+
field :errors, [String], null: true
|
146
|
+
field :int, Integer, null: true
|
147
|
+
|
148
|
+
def ready?(int:)
|
149
|
+
if int == 13
|
150
|
+
return false, { errors: ["Bad number!"] }
|
151
|
+
else
|
152
|
+
true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def resolve(int:)
|
157
|
+
{ int: int }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
module HasValue
|
162
|
+
include GraphQL::Schema::Interface
|
163
|
+
field :value, Integer, null: false
|
164
|
+
def self.resolve_type(obj, ctx)
|
165
|
+
if obj.is_a?(Integer)
|
166
|
+
IntegerWrapper
|
167
|
+
else
|
168
|
+
raise "Unexpected: #{obj.inspect}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class IntegerWrapper < GraphQL::Schema::Object
|
174
|
+
implements HasValue
|
175
|
+
field :value, Integer, null: false, method: :object
|
176
|
+
end
|
177
|
+
|
178
|
+
class PrepResolver9 < BaseResolver
|
179
|
+
argument :int_id, ID, required: true, loads: HasValue
|
180
|
+
# Make sure the lazy object is resolved properly:
|
181
|
+
type HasValue, null: false
|
182
|
+
def object_from_id(type, id, ctx)
|
183
|
+
# Make sure a lazy object is handled appropriately
|
184
|
+
LazyBlock.new {
|
185
|
+
# Make sure that the right type ends up here
|
186
|
+
id.to_i + type.graphql_name.length
|
187
|
+
}
|
188
|
+
end
|
189
|
+
|
190
|
+
def resolve(int:)
|
191
|
+
int * 3
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class PrepResolver10 < BaseResolver
|
196
|
+
argument :int1, Integer, required: true
|
197
|
+
argument :int2, Integer, required: true
|
144
198
|
type Integer, null: true
|
199
|
+
def authorized?(int1:, int2:)
|
200
|
+
if int1 + int2 > context[:max_int]
|
201
|
+
raise GraphQL::ExecutionError, "Inputs too big"
|
202
|
+
elsif context[:min_int] && (int1 + int2 < context[:min_int])
|
203
|
+
false
|
204
|
+
else
|
205
|
+
true
|
206
|
+
end
|
207
|
+
end
|
145
208
|
|
146
|
-
def
|
147
|
-
|
209
|
+
def resolve(int1:, int2:)
|
210
|
+
int1 + int2
|
148
211
|
end
|
212
|
+
end
|
149
213
|
|
150
|
-
|
151
|
-
|
214
|
+
class PrepResolver11 < PrepResolver10
|
215
|
+
def authorized?(int1:, int2:)
|
216
|
+
LazyBlock.new { super(int1: int1 * 2, int2: int2) }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class PrepResolver12 < GraphQL::Schema::Mutation
|
221
|
+
argument :int1, Integer, required: true
|
222
|
+
argument :int2, Integer, required: true
|
223
|
+
field :error_messages, [String], null: true
|
224
|
+
field :value, Integer, null: true
|
225
|
+
def authorized?(int1:, int2:)
|
226
|
+
if int1 + int2 > context[:max_int]
|
227
|
+
return false, { error_messages: ["Inputs must be less than #{context[:max_int]} (but you provided #{int1 + int2})"] }
|
228
|
+
else
|
229
|
+
true
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def resolve(int1:, int2:)
|
234
|
+
{ value: int1 + int2 }
|
152
235
|
end
|
153
236
|
end
|
154
237
|
|
155
|
-
class
|
156
|
-
def
|
157
|
-
|
238
|
+
class PrepResolver13 < PrepResolver12
|
239
|
+
def authorized?(int1:, int2:)
|
240
|
+
# Increment the numbers so we can be sure they're passing through here
|
241
|
+
LazyBlock.new { super(int1: int1 + 1, int2: int2 + 1) }
|
158
242
|
end
|
159
243
|
end
|
160
244
|
|
245
|
+
|
161
246
|
class Query < GraphQL::Schema::Object
|
162
247
|
class CustomField < GraphQL::Schema::Field
|
163
248
|
def resolve_field(*args)
|
@@ -188,12 +273,17 @@ describe GraphQL::Schema::Resolver do
|
|
188
273
|
field :prep_resolver_5, resolver: PrepResolver5
|
189
274
|
field :prep_resolver_6, resolver: PrepResolver6
|
190
275
|
field :prep_resolver_7, resolver: PrepResolver7
|
191
|
-
field :
|
276
|
+
field :prep_resolver_9, resolver: PrepResolver9
|
277
|
+
field :prep_resolver_10, resolver: PrepResolver10
|
278
|
+
field :prep_resolver_11, resolver: PrepResolver11
|
279
|
+
field :prep_resolver_12, resolver: PrepResolver12
|
280
|
+
field :prep_resolver_13, resolver: PrepResolver13
|
192
281
|
end
|
193
282
|
|
194
283
|
class Schema < GraphQL::Schema
|
195
284
|
query(Query)
|
196
285
|
lazy_resolve LazyBlock, :value
|
286
|
+
orphan_types IntegerWrapper
|
197
287
|
end
|
198
288
|
end
|
199
289
|
|
@@ -280,17 +370,25 @@ describe GraphQL::Schema::Resolver do
|
|
280
370
|
refute res.key?("errors"), "#{description}: silent auth failure (no top-level error)"
|
281
371
|
end
|
282
372
|
|
283
|
-
describe "
|
373
|
+
describe "ready?" do
|
284
374
|
it "can raise errors" do
|
285
375
|
res = exec_query("{ int: prepResolver5(int: 5) }")
|
286
376
|
assert_equal 50, res["data"]["int"]
|
287
|
-
add_error_assertions("prepResolver5", "
|
377
|
+
add_error_assertions("prepResolver5", "ready?")
|
288
378
|
end
|
289
379
|
|
290
380
|
it "can raise errors in lazy sync" do
|
291
381
|
res = exec_query("{ int: prepResolver6(int: 5) }")
|
292
382
|
assert_equal 50, res["data"]["int"]
|
293
|
-
add_error_assertions("prepResolver6", "lazy
|
383
|
+
add_error_assertions("prepResolver6", "lazy ready?")
|
384
|
+
end
|
385
|
+
|
386
|
+
it "can return false and data" do
|
387
|
+
res = exec_query("{ int: prepResolver7(int: 13) { errors int } }")
|
388
|
+
assert_equal ["Bad number!"], res["data"]["int"]["errors"]
|
389
|
+
|
390
|
+
res = exec_query("{ int: prepResolver7(int: 213) { errors int } }")
|
391
|
+
assert_equal 213, res["data"]["int"]["int"]
|
294
392
|
end
|
295
393
|
end
|
296
394
|
|
@@ -319,17 +417,59 @@ describe GraphQL::Schema::Resolver do
|
|
319
417
|
end
|
320
418
|
|
321
419
|
describe "validating arguments" do
|
322
|
-
|
323
|
-
"
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
420
|
+
describe ".authorized?" do
|
421
|
+
it "can raise an error to halt" do
|
422
|
+
res = exec_query("{ prepResolver10(int1: 5, int2: 6) }", context: { max_int: 9 })
|
423
|
+
assert_equal ["Inputs too big"], res["errors"].map { |e| e["message"] }
|
424
|
+
|
425
|
+
res = exec_query("{ prepResolver10(int1: 5, int2: 6) }", context: { max_int: 90 })
|
426
|
+
assert_equal 11, res["data"]["prepResolver10"]
|
427
|
+
end
|
428
|
+
|
429
|
+
it "can return a lazy object" do
|
430
|
+
# This is too big because it's modified in the overridden authorized? hook:
|
431
|
+
res = exec_query("{ prepResolver11(int1: 3, int2: 5) }", context: { max_int: 9 })
|
432
|
+
assert_equal ["Inputs too big"], res["errors"].map { |e| e["message"] }
|
433
|
+
|
434
|
+
res = exec_query("{ prepResolver11(int1: 3, int2: 5) }", context: { max_int: 90 })
|
435
|
+
assert_equal 8, res["data"]["prepResolver11"]
|
436
|
+
end
|
437
|
+
|
438
|
+
it "can return data early" do
|
439
|
+
res = exec_query("{ prepResolver12(int1: 9, int2: 5) { errorMessages } }", context: { max_int: 9 })
|
440
|
+
assert_equal ["Inputs must be less than 9 (but you provided 14)"], res["data"]["prepResolver12"]["errorMessages"]
|
441
|
+
# This works
|
442
|
+
res = exec_query("{ prepResolver12(int1: 2, int2: 5) { value } }", context: { max_int: 9 })
|
443
|
+
assert_equal 7, res["data"]["prepResolver12"]["value"]
|
332
444
|
end
|
445
|
+
|
446
|
+
it "can return data early in a promise" do
|
447
|
+
# This is too big because it's modified in the overridden authorized? hook:
|
448
|
+
res = exec_query("{ prepResolver13(int1: 4, int2: 4) { errorMessages } }", context: { max_int: 9 })
|
449
|
+
assert_equal ["Inputs must be less than 9 (but you provided 10)"], res["data"]["prepResolver13"]["errorMessages"]
|
450
|
+
# This works
|
451
|
+
res = exec_query("{ prepResolver13(int1: 2, int2: 5) { value } }", context: { max_int: 9 })
|
452
|
+
assert_equal 7, res["data"]["prepResolver13"]["value"]
|
453
|
+
end
|
454
|
+
|
455
|
+
it "can return false to halt" do
|
456
|
+
str = <<-GRAPHQL
|
457
|
+
{
|
458
|
+
prepResolver10(int1: 5, int2: 10)
|
459
|
+
prepResolver11(int1: 3, int2: 5)
|
460
|
+
}
|
461
|
+
GRAPHQL
|
462
|
+
res = exec_query(str, context: { max_int: 100, min_int: 20 })
|
463
|
+
assert_equal({ "prepResolver10" => nil, "prepResolver11" => nil }, res["data"])
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
describe "Loading inputs" do
|
469
|
+
it "calls object_from_id" do
|
470
|
+
res = exec_query('{ prepResolver9(intId: "5") { value } }')
|
471
|
+
# (5 + 8) * 3
|
472
|
+
assert_equal 39, res["data"]["prepResolver9"]["value"]
|
333
473
|
end
|
334
474
|
end
|
335
475
|
end
|