graphql 1.4.5 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/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
|