graphql 1.4.5 → 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/enum_generator.rb +33 -0
- data/lib/generators/graphql/function_generator.rb +15 -0
- data/lib/generators/graphql/install_generator.rb +118 -0
- data/lib/generators/graphql/interface_generator.rb +27 -0
- data/lib/generators/graphql/loader_generator.rb +17 -0
- data/lib/generators/graphql/mutation_generator.rb +19 -0
- data/lib/generators/graphql/object_generator.rb +34 -0
- data/lib/generators/graphql/templates/enum.erb +4 -0
- data/lib/generators/graphql/templates/function.erb +17 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +32 -0
- data/lib/generators/graphql/templates/interface.erb +4 -0
- data/lib/generators/graphql/templates/loader.erb +15 -0
- data/lib/generators/graphql/templates/mutation.erb +12 -0
- data/lib/generators/graphql/templates/object.erb +5 -0
- data/lib/generators/graphql/templates/query_type.erb +15 -0
- data/lib/generators/graphql/templates/schema.erb +34 -0
- data/lib/generators/graphql/templates/union.erb +4 -0
- data/lib/generators/graphql/type_generator.rb +78 -0
- data/lib/generators/graphql/union_generator.rb +33 -0
- data/lib/graphql.rb +10 -0
- data/lib/graphql/analysis/analyze_query.rb +1 -1
- data/lib/graphql/analysis/query_complexity.rb +6 -50
- data/lib/graphql/analysis/query_depth.rb +1 -1
- data/lib/graphql/argument.rb +21 -0
- data/lib/graphql/compatibility/execution_specification/counter_schema.rb +3 -3
- data/lib/graphql/define.rb +1 -0
- data/lib/graphql/define/assign_argument.rb +3 -19
- data/lib/graphql/define/assign_mutation_function.rb +34 -0
- data/lib/graphql/define/assign_object_field.rb +26 -14
- data/lib/graphql/define/defined_object_proxy.rb +21 -0
- data/lib/graphql/define/instance_definable.rb +61 -11
- data/lib/graphql/directive.rb +6 -1
- data/lib/graphql/execution/directive_checks.rb +1 -0
- data/lib/graphql/execution/execute.rb +14 -9
- data/lib/graphql/execution/field_result.rb +1 -0
- data/lib/graphql/execution/lazy.rb +8 -17
- data/lib/graphql/execution/lazy/lazy_method_map.rb +2 -0
- data/lib/graphql/execution/lazy/resolve.rb +1 -0
- data/lib/graphql/execution/selection_result.rb +1 -0
- data/lib/graphql/execution/typecast.rb +39 -26
- data/lib/graphql/field.rb +15 -3
- data/lib/graphql/field/resolve.rb +3 -3
- data/lib/graphql/function.rb +134 -0
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/input_object_type.rb +1 -1
- data/lib/graphql/internal_representation.rb +1 -1
- data/lib/graphql/internal_representation/node.rb +35 -107
- data/lib/graphql/internal_representation/rewrite.rb +189 -183
- data/lib/graphql/internal_representation/visit.rb +38 -0
- data/lib/graphql/introspection/input_value_type.rb +10 -1
- data/lib/graphql/introspection/schema_type.rb +1 -1
- data/lib/graphql/language/lexer.rb +6 -3
- data/lib/graphql/language/lexer.rl +6 -3
- data/lib/graphql/object_type.rb +53 -13
- data/lib/graphql/query.rb +30 -14
- data/lib/graphql/query/arguments.rb +2 -0
- data/lib/graphql/query/context.rb +2 -2
- data/lib/graphql/query/literal_input.rb +9 -0
- data/lib/graphql/query/serial_execution/field_resolution.rb +2 -2
- data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
- data/lib/graphql/relay.rb +1 -0
- data/lib/graphql/relay/array_connection.rb +1 -1
- data/lib/graphql/relay/base_connection.rb +34 -15
- data/lib/graphql/relay/connection_resolve.rb +7 -2
- data/lib/graphql/relay/mutation.rb +45 -4
- data/lib/graphql/relay/node.rb +18 -6
- data/lib/graphql/relay/range_add.rb +45 -0
- data/lib/graphql/relay/relation_connection.rb +17 -2
- data/lib/graphql/runtime_type_error.rb +1 -0
- data/lib/graphql/schema.rb +40 -5
- data/lib/graphql/schema/base_64_encoder.rb +1 -0
- data/lib/graphql/schema/build_from_definition.rb +56 -21
- data/lib/graphql/schema/default_parse_error.rb +10 -0
- data/lib/graphql/schema/loader.rb +8 -1
- data/lib/graphql/schema/null_mask.rb +1 -0
- data/lib/graphql/schema/validation.rb +35 -0
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/arguments_validator.rb +7 -4
- data/lib/graphql/static_validation/definition_dependencies.rb +183 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +28 -96
- data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +23 -0
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -5
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -31
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +11 -41
- data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +2 -2
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -7
- data/lib/graphql/static_validation/validation_context.rb +22 -1
- data/lib/graphql/static_validation/validator.rb +4 -1
- data/lib/graphql/string_type.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +12 -3
- data/spec/generators/graphql/enum_generator_spec.rb +29 -0
- data/spec/generators/graphql/function_generator_spec.rb +33 -0
- data/spec/generators/graphql/install_generator_spec.rb +185 -0
- data/spec/generators/graphql/interface_generator_spec.rb +32 -0
- data/spec/generators/graphql/loader_generator_spec.rb +31 -0
- data/spec/generators/graphql/mutation_generator_spec.rb +28 -0
- data/spec/generators/graphql/object_generator_spec.rb +42 -0
- data/spec/generators/graphql/union_generator_spec.rb +50 -0
- data/spec/graphql/analysis/query_complexity_spec.rb +2 -1
- data/spec/graphql/define/instance_definable_spec.rb +38 -0
- data/spec/graphql/directive/skip_directive_spec.rb +1 -0
- data/spec/graphql/directive_spec.rb +18 -0
- data/spec/graphql/execution/typecast_spec.rb +41 -46
- data/spec/graphql/field_spec.rb +1 -1
- data/spec/graphql/function_spec.rb +128 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +166 -129
- data/spec/graphql/introspection/type_type_spec.rb +1 -1
- data/spec/graphql/language/lexer_spec.rb +6 -0
- data/spec/graphql/object_type_spec.rb +73 -2
- data/spec/graphql/query/arguments_spec.rb +28 -0
- data/spec/graphql/query/variables_spec.rb +7 -1
- data/spec/graphql/query_spec.rb +30 -0
- data/spec/graphql/relay/base_connection_spec.rb +26 -8
- data/spec/graphql/relay/connection_resolve_spec.rb +45 -0
- data/spec/graphql/relay/connection_type_spec.rb +21 -0
- data/spec/graphql/relay/node_spec.rb +30 -2
- data/spec/graphql/relay/range_add_spec.rb +113 -0
- data/spec/graphql/schema/build_from_definition_spec.rb +114 -0
- data/spec/graphql/schema/loader_spec.rb +1 -0
- data/spec/graphql/schema/printer_spec.rb +2 -2
- data/spec/graphql/schema/validation_spec.rb +80 -11
- data/spec/graphql/schema/warden_spec.rb +10 -10
- data/spec/graphql/schema_spec.rb +18 -1
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +16 -0
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +50 -3
- data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +27 -0
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +57 -0
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +14 -0
- data/spec/graphql/string_type_spec.rb +7 -0
- data/spec/spec_helper.rb +3 -3
- data/spec/support/base_generator_test.rb +7 -0
- data/spec/support/dummy/schema.rb +32 -30
- data/spec/support/star_wars/schema.rb +81 -23
- metadata +98 -20
- data/lib/graphql/internal_representation/selection.rb +0 -85
@@ -138,6 +138,26 @@ describe GraphQL::Query::Arguments do
|
|
138
138
|
1
|
139
139
|
}
|
140
140
|
end
|
141
|
+
|
142
|
+
field :noArgTest, types.Int do
|
143
|
+
resolve ->(obj, args, ctx) {
|
144
|
+
arg_values_array << args
|
145
|
+
1
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
field :noDefaultsTest, types.Int do
|
150
|
+
argument :a, types.Int
|
151
|
+
argument :b, types.Int
|
152
|
+
resolve ->(obj, args, ctx) {
|
153
|
+
arg_values_array << args
|
154
|
+
1
|
155
|
+
}
|
156
|
+
resolve ->(obj, args, ctx) {
|
157
|
+
arg_values_array << args
|
158
|
+
1
|
159
|
+
}
|
160
|
+
end
|
141
161
|
end
|
142
162
|
|
143
163
|
GraphQL::Schema.define(query: query)
|
@@ -170,6 +190,14 @@ describe GraphQL::Query::Arguments do
|
|
170
190
|
assert_equal({"a" => 1, "b" => 2}, last_args.to_h)
|
171
191
|
end
|
172
192
|
|
193
|
+
it "uses Field#default_arguments when no args are provided" do
|
194
|
+
schema.execute("{ argTest noArgTest noDefaultsTest }")
|
195
|
+
|
196
|
+
assert schema.query.get_field("argTest").default_arguments.eql?(arg_values[0])
|
197
|
+
assert GraphQL::Query::Arguments::NO_ARGS.eql?(arg_values[1])
|
198
|
+
assert GraphQL::Query::Arguments::NO_ARGS.eql?(arg_values[2])
|
199
|
+
end
|
200
|
+
|
173
201
|
it "works from variables" do
|
174
202
|
variables = { "arg" => { "a" => 1, "d" => nil } }
|
175
203
|
schema.execute("query ArgTest($arg: TestInput){ argTest(d: $arg) }", variables: variables)
|
@@ -33,6 +33,12 @@ describe GraphQL::Query::Variables do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
describe "nullable variables" do
|
36
|
+
module ObjectWithThingsCount
|
37
|
+
def self.thingsCount(args, ctx) # rubocop:disable Style/MethodName
|
38
|
+
1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
36
42
|
let(:schema) { GraphQL::Schema.from_definition(%|
|
37
43
|
type Query {
|
38
44
|
thingsCount(ids: [ID!]): Int!
|
@@ -45,7 +51,7 @@ describe GraphQL::Query::Variables do
|
|
45
51
|
}
|
46
52
|
|}
|
47
53
|
let(:result) {
|
48
|
-
schema.execute(query_string, variables: provided_variables, root_value:
|
54
|
+
schema.execute(query_string, variables: provided_variables, root_value: ObjectWithThingsCount)
|
49
55
|
}
|
50
56
|
|
51
57
|
describe "when they are present, but null" do
|
data/spec/graphql/query_spec.rb
CHANGED
@@ -420,4 +420,34 @@ describe GraphQL::Query do
|
|
420
420
|
assert_equal({"cheeseId" => 2}, query.provided_variables)
|
421
421
|
end
|
422
422
|
end
|
423
|
+
|
424
|
+
describe "parse errors" do
|
425
|
+
let(:invalid_query_string) {
|
426
|
+
<<-GRAPHQL
|
427
|
+
{
|
428
|
+
getStuff
|
429
|
+
nonsense
|
430
|
+
This is broken 1
|
431
|
+
}
|
432
|
+
GRAPHQL
|
433
|
+
}
|
434
|
+
it "adds an entry to the errors key" do
|
435
|
+
res = schema.execute(" { ")
|
436
|
+
assert_equal 1, res["errors"].length
|
437
|
+
assert_equal "Unexpected end of document", res["errors"][0]["message"]
|
438
|
+
assert_equal [], res["errors"][0]["locations"]
|
439
|
+
|
440
|
+
res = schema.execute(invalid_query_string)
|
441
|
+
assert_equal 1, res["errors"].length
|
442
|
+
assert_equal %|Parse error on "1" (INT) at [4, 26]|, res["errors"][0]["message"]
|
443
|
+
assert_equal({"line" => 4, "column" => 26}, res["errors"][0]["locations"][0])
|
444
|
+
end
|
445
|
+
|
446
|
+
it "can be configured to raise" do
|
447
|
+
raise_schema = schema.redefine(parse_error: ->(err, ctx) { raise err })
|
448
|
+
assert_raises(GraphQL::ParseError) {
|
449
|
+
raise_schema.execute(invalid_query_string)
|
450
|
+
}
|
451
|
+
end
|
452
|
+
end
|
423
453
|
end
|
@@ -2,6 +2,15 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe GraphQL::Relay::BaseConnection do
|
5
|
+
module Encoder
|
6
|
+
module_function
|
7
|
+
def encode(str, nonce: false); str; end
|
8
|
+
def decode(str, nonce: false); str; end
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:schema) { OpenStruct.new(cursor_encoder: Encoder) }
|
12
|
+
let(:context) { OpenStruct.new(schema: schema) }
|
13
|
+
|
5
14
|
describe ".connection_for_nodes" do
|
6
15
|
it "resolves most specific connection type" do
|
7
16
|
class SpecialArray < Array; end
|
@@ -15,16 +24,25 @@ describe GraphQL::Relay::BaseConnection do
|
|
15
24
|
end
|
16
25
|
end
|
17
26
|
|
18
|
-
describe "
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
27
|
+
describe "arguments" do
|
28
|
+
it "limits pagination args to positive numbers" do
|
29
|
+
args = {
|
30
|
+
first: 1,
|
31
|
+
last: -1,
|
32
|
+
}
|
33
|
+
conn = GraphQL::Relay::BaseConnection.new([], args, context: context)
|
34
|
+
assert_equal 1, conn.first
|
35
|
+
assert_equal 0, conn.last
|
24
36
|
|
25
|
-
|
26
|
-
|
37
|
+
args = {
|
38
|
+
first: nil,
|
39
|
+
}
|
40
|
+
conn = GraphQL::Relay::BaseConnection.new([], args, context: context)
|
41
|
+
assert_equal nil, conn.first
|
42
|
+
end
|
43
|
+
end
|
27
44
|
|
45
|
+
describe "#context" do
|
28
46
|
it "Has public access to the field context" do
|
29
47
|
conn = GraphQL::Relay::BaseConnection.new([], {}, context: context)
|
30
48
|
assert_equal context, conn.context
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe GraphQL::Relay::ConnectionResolve do
|
5
|
+
describe "when an execution error is returned" do
|
6
|
+
let(:query_string) { <<-GRAPHQL
|
7
|
+
query getError($error: String!){
|
8
|
+
rebels {
|
9
|
+
ships(nameIncludes: $error) {
|
10
|
+
edges {
|
11
|
+
node {
|
12
|
+
name
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
GRAPHQL
|
19
|
+
}
|
20
|
+
|
21
|
+
it "adds an error" do
|
22
|
+
result = star_wars_query(query_string, { "error" => "error"})
|
23
|
+
assert_equal 1, result["errors"].length
|
24
|
+
assert_equal "error from within connection", result["errors"][0]["message"]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "adds an error for a lazy error" do
|
28
|
+
result = star_wars_query(query_string, { "error" => "lazyError"})
|
29
|
+
assert_equal 1, result["errors"].length
|
30
|
+
assert_equal "lazy error from within connection", result["errors"][0]["message"]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "adds an error for a lazy raised error" do
|
34
|
+
result = star_wars_query(query_string, { "error" => "lazyRaisedError"})
|
35
|
+
assert_equal 1, result["errors"].length
|
36
|
+
assert_equal "lazy raised error from within connection", result["errors"][0]["message"]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "adds an error for a raised error" do
|
40
|
+
result = star_wars_query(query_string, { "error" => "raisedError"})
|
41
|
+
assert_equal 1, result["errors"].length
|
42
|
+
assert_equal "error raised from within connection", result["errors"][0]["message"]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -56,5 +56,26 @@ describe GraphQL::Relay::ConnectionType do
|
|
56
56
|
assert_equal ["Yavin", "Echo Base", "Secret Hideout"] , bases["nodes"].map { |e| e["name"] }
|
57
57
|
end
|
58
58
|
end
|
59
|
+
|
60
|
+
|
61
|
+
describe "when an execution error is raised" do
|
62
|
+
let(:query_string) {%|
|
63
|
+
{
|
64
|
+
basesWithNullName {
|
65
|
+
edges {
|
66
|
+
node {
|
67
|
+
name
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|}
|
73
|
+
|
74
|
+
it "nullifies the parent and adds an error" do
|
75
|
+
result = star_wars_query(query_string)
|
76
|
+
assert_equal nil, result["data"]["basesWithNullName"]["edges"][0]["node"]
|
77
|
+
assert_equal "Boom!", result["errors"][0]["message"]
|
78
|
+
end
|
79
|
+
end
|
59
80
|
end
|
60
81
|
end
|
@@ -9,7 +9,21 @@ describe GraphQL::Relay::Node do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
describe ".field" do
|
12
|
-
describe "with custom
|
12
|
+
describe "with custom definition" do
|
13
|
+
it 'creates a field with the custom definition' do
|
14
|
+
faction = StarWars::DATA['Faction'][0]
|
15
|
+
|
16
|
+
node_field = GraphQL::Relay::Node.field do
|
17
|
+
name "nod3"
|
18
|
+
description "The Relay Node Field"
|
19
|
+
resolve ->(_, _ , _) { faction }
|
20
|
+
end
|
21
|
+
|
22
|
+
assert_equal "nod3", node_field.name
|
23
|
+
assert_equal "The Relay Node Field", node_field.description
|
24
|
+
assert_equal faction, node_field.resolve(nil, { 'id' => '1' }, nil)
|
25
|
+
end
|
26
|
+
|
13
27
|
it "executes the custom resolve instead of relay default" do
|
14
28
|
id = "resolver_is_hardcoded_so_this_does_not_matter"
|
15
29
|
|
@@ -131,7 +145,21 @@ describe GraphQL::Relay::Node do
|
|
131
145
|
end
|
132
146
|
|
133
147
|
describe ".plural_identifying_field" do
|
134
|
-
describe "with custom
|
148
|
+
describe "with custom definition" do
|
149
|
+
it 'creates a field with the custom definition' do
|
150
|
+
factions = StarWars::DATA['Faction']
|
151
|
+
|
152
|
+
node_field = GraphQL::Relay::Node.plural_field do
|
153
|
+
name "nodez"
|
154
|
+
description "The Relay Nodes Field"
|
155
|
+
resolve ->(_, _ , _) { factions }
|
156
|
+
end
|
157
|
+
|
158
|
+
assert_equal "nodez", node_field.name
|
159
|
+
assert_equal "The Relay Nodes Field", node_field.description
|
160
|
+
assert_equal factions, node_field.resolve_proc.call(nil, { 'ids' => ['1', '2'] }, nil)
|
161
|
+
end
|
162
|
+
|
135
163
|
it "executes the custom resolve instead of relay default" do
|
136
164
|
id = ["resolver_is_hardcoded_so_this_does_not_matter", "another_id"]
|
137
165
|
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe GraphQL::Relay::RangeAdd do
|
5
|
+
# Make sure that the encoder is found through `ctx.schema`:
|
6
|
+
module PassThroughEncoder
|
7
|
+
def self.encode(plaintext, nonce: false)
|
8
|
+
"__#{plaintext}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.decode(ciphertext, nonce: false)
|
12
|
+
ciphertext[2..-1]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:schema) {
|
17
|
+
menus = [
|
18
|
+
OpenStruct.new(
|
19
|
+
name: "Los Primos",
|
20
|
+
items: [
|
21
|
+
OpenStruct.new(name: "California Burrito", price: 699),
|
22
|
+
OpenStruct.new(name: "Fish Taco", price: 399),
|
23
|
+
]
|
24
|
+
)
|
25
|
+
]
|
26
|
+
|
27
|
+
item = GraphQL::ObjectType.define do
|
28
|
+
name "Item"
|
29
|
+
field :price, !types.Int
|
30
|
+
field :name, !types.String
|
31
|
+
end
|
32
|
+
menu = GraphQL::ObjectType.define do
|
33
|
+
name "Menu"
|
34
|
+
field :name, !types.String
|
35
|
+
field :items, !item.connection_type
|
36
|
+
end
|
37
|
+
query = GraphQL::ObjectType.define do
|
38
|
+
name "Query"
|
39
|
+
field :menus, types[menu], resolve: Proc.new { menus }
|
40
|
+
end
|
41
|
+
add_item = GraphQL::Relay::Mutation.define do
|
42
|
+
name "AddItem"
|
43
|
+
input_field :name, !types.String
|
44
|
+
input_field :price, !types.Int
|
45
|
+
input_field :menu_idx, !types.Int
|
46
|
+
|
47
|
+
return_field :item_edge, item.edge_type
|
48
|
+
return_field :items, item.connection_type
|
49
|
+
return_field :menu, menu
|
50
|
+
resolve ->(obj, input, ctx) {
|
51
|
+
this_menu = menus[input[:menu_idx]]
|
52
|
+
new_item = OpenStruct.new(name: input[:name], price: input[:price])
|
53
|
+
this_menu.items << new_item
|
54
|
+
range_add = GraphQL::Relay::RangeAdd.new(
|
55
|
+
parent: this_menu,
|
56
|
+
item: new_item,
|
57
|
+
collection: this_menu.items,
|
58
|
+
context: ctx,
|
59
|
+
)
|
60
|
+
|
61
|
+
{
|
62
|
+
menu: range_add.parent,
|
63
|
+
items: range_add.connection,
|
64
|
+
item_edge: range_add.edge,
|
65
|
+
}
|
66
|
+
}
|
67
|
+
end
|
68
|
+
mutation = GraphQL::ObjectType.define do
|
69
|
+
name "Mutation"
|
70
|
+
field :add_item, add_item.field
|
71
|
+
end
|
72
|
+
|
73
|
+
GraphQL::Schema.define(query: query, mutation: mutation, cursor_encoder: PassThroughEncoder)
|
74
|
+
}
|
75
|
+
|
76
|
+
|
77
|
+
describe "returning Relay objects" do
|
78
|
+
let(:query_str) { <<-GRAPHQL
|
79
|
+
mutation {
|
80
|
+
add_item(input: {name: "Chilaquiles", price: 699, menu_idx: 0}) {
|
81
|
+
menu {
|
82
|
+
name
|
83
|
+
}
|
84
|
+
item_edge {
|
85
|
+
node {
|
86
|
+
name
|
87
|
+
price
|
88
|
+
}
|
89
|
+
}
|
90
|
+
items {
|
91
|
+
edges {
|
92
|
+
node {
|
93
|
+
name
|
94
|
+
}
|
95
|
+
cursor
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
GRAPHQL
|
101
|
+
}
|
102
|
+
|
103
|
+
it "returns a connection and an edge" do
|
104
|
+
res = schema.execute(query_str)
|
105
|
+
|
106
|
+
mutation_res = res["data"]["add_item"]
|
107
|
+
assert_equal("Los Primos", mutation_res["menu"]["name"])
|
108
|
+
assert_equal({"name"=>"Chilaquiles", "price"=>699}, mutation_res["item_edge"]["node"])
|
109
|
+
assert_equal(["California Burrito", "Fish Taco", "Chilaquiles"], mutation_res["items"]["edges"].map { |e| e["node"]["name"] })
|
110
|
+
assert_equal(["__1", "__2", "__3"], mutation_res["items"]["edges"].map { |e| e["cursor"] })
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -700,4 +700,118 @@ SCHEMA
|
|
700
700
|
assert_equal 'Specified query type "Foo" not found in document.', err.message
|
701
701
|
end
|
702
702
|
end
|
703
|
+
|
704
|
+
describe "executable schemas from string" do
|
705
|
+
let(:schema_defn) {
|
706
|
+
<<-GRAPHQL
|
707
|
+
type Todo {text: String, from_context: String}
|
708
|
+
type Query { all_todos: [Todo]}
|
709
|
+
type Mutation { todo_add(text: String!): Todo}
|
710
|
+
GRAPHQL
|
711
|
+
}
|
712
|
+
|
713
|
+
Todo = Struct.new(:text, :from_context)
|
714
|
+
|
715
|
+
class RootResolver
|
716
|
+
attr_accessor :todos
|
717
|
+
|
718
|
+
def initialize
|
719
|
+
@todos = [Todo.new("Pay the bills.")]
|
720
|
+
end
|
721
|
+
|
722
|
+
def all_todos
|
723
|
+
@todos
|
724
|
+
end
|
725
|
+
|
726
|
+
def todo_add(args, ctx) # this is a method and accepting arguments
|
727
|
+
todo = Todo.new(args[:text], ctx[:context_value])
|
728
|
+
@todos << todo
|
729
|
+
todo
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
it "calls methods with args if args are defined" do
|
734
|
+
schema = GraphQL::Schema.from_definition(schema_defn)
|
735
|
+
root_values = RootResolver.new
|
736
|
+
schema.execute("mutation { todoAdd: todo_add(text: \"Buy Milk\") { text } }", root_value: root_values, context: {context_value: "bar"})
|
737
|
+
result = schema.execute("query { allTodos: all_todos { text, from_context } }", root_value: root_values)
|
738
|
+
assert_equal(result.to_json, '{"data":{"allTodos":[{"text":"Pay the bills.","from_context":null},{"text":"Buy Milk","from_context":"bar"}]}}')
|
739
|
+
end
|
740
|
+
|
741
|
+
describe "hash of resolvers" do
|
742
|
+
let(:todos) { [Todo.new("Pay the bills.")] }
|
743
|
+
let(:schema) { GraphQL::Schema.from_definition(schema_defn, default_resolve: resolve_hash) }
|
744
|
+
let(:resolve_hash) {
|
745
|
+
h = base_hash
|
746
|
+
h["Query"] ||= {}
|
747
|
+
h["Query"]["all_todos"] = ->(obj, args, ctx) { obj }
|
748
|
+
h["Mutation"] ||= {}
|
749
|
+
h["Mutation"]["todo_add"] = ->(obj, args, ctx) {
|
750
|
+
todo = Todo.new(args[:text], ctx[:context_value])
|
751
|
+
obj << todo
|
752
|
+
todo
|
753
|
+
}
|
754
|
+
h
|
755
|
+
}
|
756
|
+
describe "with defaults" do
|
757
|
+
let(:base_hash) {
|
758
|
+
# Fallback is to resolve by sending the field name
|
759
|
+
Hash.new { |h, k| h[k] = Hash.new { |h2, k2| ->(obj, args, ctx) { obj.public_send(k2) } } }
|
760
|
+
}
|
761
|
+
|
762
|
+
it "accepts a hash of resolve functions" do
|
763
|
+
schema.execute("mutation { todoAdd: todo_add(text: \"Buy Milk\") { text } }", context: {context_value: "bar"}, root_value: todos)
|
764
|
+
result = schema.execute("query { allTodos: all_todos { text, from_context } }", root_value: todos)
|
765
|
+
assert_equal(result.to_json, '{"data":{"allTodos":[{"text":"Pay the bills.","from_context":null},{"text":"Buy Milk","from_context":"bar"}]}}')
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
describe "wihtout defaults" do
|
770
|
+
let(:base_hash) { {} }
|
771
|
+
it "raises a KeyError" do
|
772
|
+
assert_raises(KeyError) do
|
773
|
+
schema.execute("mutation { todoAdd: todo_add(text: \"Buy Milk\") { text } }", context: {context_value: "bar"}, root_value: todos)
|
774
|
+
end
|
775
|
+
end
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
describe "custom resolve behavior" do
|
780
|
+
class AppResolver
|
781
|
+
def initialize
|
782
|
+
@todos = [Todo.new("Pay the bills.")]
|
783
|
+
@resolves = {
|
784
|
+
"Query" => {
|
785
|
+
"all_todos" => ->(obj, args, ctx) { @todos },
|
786
|
+
},
|
787
|
+
"Mutation" => {
|
788
|
+
"todo_add" => ->(obj, args, ctx) {
|
789
|
+
todo = Todo.new(args[:text], ctx[:context_value])
|
790
|
+
@todos << todo
|
791
|
+
todo
|
792
|
+
},
|
793
|
+
},
|
794
|
+
"Todo" => {
|
795
|
+
"text" => ->(obj, args, ctx) { obj.text },
|
796
|
+
"from_context" => ->(obj, args, ctx) { obj.from_context },
|
797
|
+
}
|
798
|
+
}
|
799
|
+
end
|
800
|
+
|
801
|
+
def call(type, field, obj, args, ctx)
|
802
|
+
@resolves
|
803
|
+
.fetch(type.name)
|
804
|
+
.fetch(field.name)
|
805
|
+
.call(obj, args, ctx)
|
806
|
+
end
|
807
|
+
end
|
808
|
+
|
809
|
+
it "accepts a default_resolve callable" do
|
810
|
+
schema = GraphQL::Schema.from_definition(schema_defn, default_resolve: AppResolver.new)
|
811
|
+
schema.execute("mutation { todoAdd: todo_add(text: \"Buy Milk\") { text } }", context: {context_value: "bar"})
|
812
|
+
result = schema.execute("query { allTodos: all_todos { text, from_context } }")
|
813
|
+
assert_equal(result.to_json, '{"data":{"allTodos":[{"text":"Pay the bills.","from_context":null},{"text":"Buy Milk","from_context":"bar"}]}}')
|
814
|
+
end
|
815
|
+
end
|
816
|
+
end
|
703
817
|
end
|