graphql 1.8.6 → 1.8.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 +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
|