graphql 1.7.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/directive.rb +1 -1
- data/lib/graphql/field.rb +2 -0
- data/lib/graphql/input_object_type.rb +2 -2
- data/lib/graphql/query/arguments.rb +47 -0
- data/lib/graphql/query/arguments_cache.rb +2 -3
- data/lib/graphql/query/literal_input.rb +4 -12
- data/lib/graphql/relay/array_connection.rb +24 -0
- data/lib/graphql/relay/connection_type.rb +4 -0
- data/lib/graphql/relay/relation_connection.rb +16 -2
- data/lib/graphql/schema/traversal.rb +32 -9
- data/lib/graphql/subscriptions/event.rb +2 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/input_object_type_spec.rb +1 -1
- data/spec/graphql/list_type_spec.rb +5 -1
- data/spec/graphql/query/arguments_spec.rb +19 -0
- data/spec/graphql/relay/array_connection_spec.rb +34 -4
- data/spec/graphql/relay/relation_connection_spec.rb +29 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/dummy/schema.rb +5 -5
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d524553e65b5b66f89705c436377523f7d4ac976
|
4
|
+
data.tar.gz: 8cb44e1162bc0b1aa1be8fb8c78665f5bd8f20e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8eb7ed959efa258714dab38486844d32c75a17a4040c89db3478c2b151e4222982ecfcc41a48b82b0a08df1b54aa871a1567bfcdd043ede9abba1b69b31fe3c8
|
7
|
+
data.tar.gz: 9f753512aec476ebeab407d159c80b75d94aedfb74d8d266910e73c7a88b1934f53bf1f643498eb8bb32798b500c3cfb086a6d9e7f928d993b04a650b5fde0d9
|
data/lib/graphql/directive.rb
CHANGED
@@ -10,7 +10,7 @@ module GraphQL
|
|
10
10
|
include GraphQL::Define::InstanceDefinable
|
11
11
|
accepts_definitions :locations, :name, :description, :arguments, :default_directive, argument: GraphQL::Define::AssignArgument
|
12
12
|
|
13
|
-
attr_accessor :locations, :arguments, :name, :description
|
13
|
+
attr_accessor :locations, :arguments, :name, :description, :arguments_class
|
14
14
|
# @api private
|
15
15
|
attr_writer :default_directive
|
16
16
|
ensure_defined(:locations, :arguments, :name, :description, :default_directive?)
|
data/lib/graphql/field.rb
CHANGED
@@ -179,6 +179,8 @@ module GraphQL
|
|
179
179
|
# @return [Object, GraphQL::Function] The function used to derive this field
|
180
180
|
attr_accessor :function
|
181
181
|
|
182
|
+
attr_accessor :arguments_class
|
183
|
+
|
182
184
|
attr_writer :connection
|
183
185
|
|
184
186
|
# @return [nil, String] Prefix for subscription names from this field
|
@@ -30,7 +30,7 @@ module GraphQL
|
|
30
30
|
argument: GraphQL::Define::AssignArgument
|
31
31
|
)
|
32
32
|
|
33
|
-
attr_accessor :mutation, :arguments
|
33
|
+
attr_accessor :mutation, :arguments, :arguments_class
|
34
34
|
ensure_defined(:mutation, :arguments, :input_fields)
|
35
35
|
alias :input_fields :arguments
|
36
36
|
|
@@ -96,7 +96,7 @@ module GraphQL
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
-
|
99
|
+
arguments_class.instantiate_arguments(input_values)
|
100
100
|
end
|
101
101
|
|
102
102
|
# @api private
|
@@ -1,12 +1,59 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
class Query
|
4
|
+
class StaticArguments
|
5
|
+
attr_reader :argument_definitions
|
6
|
+
|
7
|
+
def initialize(argument_definitions:)
|
8
|
+
@argument_definitions = argument_definitions
|
9
|
+
end
|
10
|
+
|
11
|
+
def instantiate_arguments(values)
|
12
|
+
arg_class.new(values, argument_definitions: argument_definitions)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def arg_class
|
18
|
+
@arg_class ||= begin
|
19
|
+
klass = Class.new(GraphQL::Query::Arguments).instance_exec(self) do |static_arguments|
|
20
|
+
static_arguments.argument_definitions.each do |_arg_name, arg_definition|
|
21
|
+
expose_as = arg_definition.expose_as.to_s
|
22
|
+
|
23
|
+
# Don't define a helper method if it would override something.
|
24
|
+
if instance_methods.include?(expose_as)
|
25
|
+
warn(
|
26
|
+
"Unable to define a helper for argument with name '#{expose_as}' "\
|
27
|
+
"as this is a reserved name. If you're using an argument such as "\
|
28
|
+
"`argument #{expose_as}`, consider renaming this argument.`"
|
29
|
+
)
|
30
|
+
next
|
31
|
+
end
|
32
|
+
|
33
|
+
define_method(expose_as) do
|
34
|
+
self[expose_as]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
klass
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
4
46
|
# Read-only access to values, normalizing all keys to strings
|
5
47
|
#
|
6
48
|
# {Arguments} recursively wraps the input in {Arguments} instances.
|
7
49
|
class Arguments
|
8
50
|
extend GraphQL::Delegate
|
9
51
|
|
52
|
+
def self.construct_arguments_class(argument_owner)
|
53
|
+
arguments_class = GraphQL::Query::StaticArguments.new(argument_definitions: argument_owner.arguments)
|
54
|
+
argument_owner.arguments_class = arguments_class
|
55
|
+
end
|
56
|
+
|
10
57
|
def initialize(values, argument_definitions:)
|
11
58
|
@argument_values = values.inject({}) do |memo, (inner_key, inner_value)|
|
12
59
|
arg_defn = argument_definitions[inner_key.to_s]
|
@@ -8,13 +8,12 @@ module GraphQL
|
|
8
8
|
Hash.new do |h1, irep_or_ast_node|
|
9
9
|
h1[irep_or_ast_node] = Hash.new do |h2, definition|
|
10
10
|
ast_node = irep_or_ast_node.is_a?(GraphQL::InternalRepresentation::Node) ? irep_or_ast_node.ast_node : irep_or_ast_node
|
11
|
-
ast_arguments = ast_node.arguments
|
12
11
|
h2[definition] = if definition.arguments.none?
|
13
12
|
GraphQL::Query::Arguments::NO_ARGS
|
14
13
|
else
|
15
14
|
GraphQL::Query::LiteralInput.from_arguments(
|
16
|
-
|
17
|
-
definition
|
15
|
+
ast_node.arguments,
|
16
|
+
definition,
|
18
17
|
query.variables,
|
19
18
|
)
|
20
19
|
end
|
@@ -41,21 +41,12 @@ module GraphQL
|
|
41
41
|
when GraphQL::InputObjectType
|
42
42
|
# TODO smell: handling AST vs handling plain Ruby
|
43
43
|
next_args = ast_node.is_a?(Hash) ? ast_node : ast_node.arguments
|
44
|
-
from_arguments(next_args, type
|
44
|
+
from_arguments(next_args, type, variables)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
def self.
|
50
|
-
if argument_defns.values.none?(&:default_value?)
|
51
|
-
GraphQL::Query::Arguments::NO_ARGS
|
52
|
-
else
|
53
|
-
from_arguments([], argument_defns, nil)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.from_arguments(ast_arguments, argument_defns, variables)
|
58
|
-
# Variables is nil when making .defaults_for
|
49
|
+
def self.from_arguments(ast_arguments, argument_owner, variables)
|
59
50
|
context = variables ? variables.context : nil
|
60
51
|
values_hash = {}
|
61
52
|
indexed_arguments = case ast_arguments
|
@@ -67,6 +58,7 @@ module GraphQL
|
|
67
58
|
raise ArgumentError, "Unexpected ast_arguments: #{ast_arguments}"
|
68
59
|
end
|
69
60
|
|
61
|
+
argument_defns = argument_owner.arguments
|
70
62
|
argument_defns.each do |arg_name, arg_defn|
|
71
63
|
ast_arg = indexed_arguments[arg_name]
|
72
64
|
# First, check the argument in the AST.
|
@@ -113,7 +105,7 @@ module GraphQL
|
|
113
105
|
end
|
114
106
|
end
|
115
107
|
|
116
|
-
|
108
|
+
argument_owner.arguments_class.instantiate_arguments(values_hash)
|
117
109
|
end
|
118
110
|
end
|
119
111
|
end
|
@@ -7,6 +7,30 @@ module GraphQL
|
|
7
7
|
encode(idx.to_s)
|
8
8
|
end
|
9
9
|
|
10
|
+
def has_next_page
|
11
|
+
if first
|
12
|
+
# There are more items after these items
|
13
|
+
sliced_nodes.count > first
|
14
|
+
elsif GraphQL::Relay::ConnectionType.bidirectional_pagination && before
|
15
|
+
# The original array is longer than the `before` index
|
16
|
+
index_from_cursor(before) < nodes.length
|
17
|
+
else
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_previous_page
|
23
|
+
if last
|
24
|
+
# There are items preceding the ones in this result
|
25
|
+
sliced_nodes.count > last
|
26
|
+
elsif GraphQL::Relay::ConnectionType.bidirectional_pagination && after
|
27
|
+
# We've paginated into the Array a bit, there are some behind us
|
28
|
+
index_from_cursor(after) > 0
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
10
34
|
private
|
11
35
|
|
12
36
|
def first
|
@@ -3,10 +3,14 @@ module GraphQL
|
|
3
3
|
module Relay
|
4
4
|
module ConnectionType
|
5
5
|
class << self
|
6
|
+
# @return [Boolean] If true, connection types get a `nodes` shortcut field
|
6
7
|
attr_accessor :default_nodes_field
|
8
|
+
# @return [Boolean] If true, connections check for reverse-direction `has*Page` values
|
9
|
+
attr_accessor :bidirectional_pagination
|
7
10
|
end
|
8
11
|
|
9
12
|
self.default_nodes_field = false
|
13
|
+
self.bidirectional_pagination = false
|
10
14
|
|
11
15
|
# Create a connection which exposes edges of this type
|
12
16
|
def self.create_type(wrapped_type, edge_type: wrapped_type.edge_type, edge_class: GraphQL::Relay::Edge, nodes_field: ConnectionType.default_nodes_field, &block)
|
@@ -24,11 +24,25 @@ module GraphQL
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def has_next_page
|
27
|
-
|
27
|
+
if first
|
28
|
+
paged_nodes_length >= first && sliced_nodes_count > first
|
29
|
+
elsif GraphQL::Relay::ConnectionType.bidirectional_pagination && last
|
30
|
+
sliced_nodes_count > last
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
28
34
|
end
|
29
35
|
|
30
36
|
def has_previous_page
|
31
|
-
|
37
|
+
if last
|
38
|
+
paged_nodes_length >= last && sliced_nodes_count > last
|
39
|
+
elsif GraphQL::Relay::ConnectionType.bidirectional_pagination && after
|
40
|
+
# We've already paginated through the collection a bit,
|
41
|
+
# there are nodes behind us
|
42
|
+
offset_from_cursor(after) > 0
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
32
46
|
end
|
33
47
|
|
34
48
|
def first
|
@@ -42,6 +42,11 @@ module GraphQL
|
|
42
42
|
visit_roots = [member.query, member.mutation, member.subscription]
|
43
43
|
if @introspection
|
44
44
|
visit_roots << GraphQL::Introspection::SchemaType
|
45
|
+
if member.query
|
46
|
+
# Visit this so that arguments class is preconstructed
|
47
|
+
# Skip validation since it begins with __
|
48
|
+
visit_field_on_type(member.query, GraphQL::Introspection::TypeByNameField, dynamic_field: true)
|
49
|
+
end
|
45
50
|
end
|
46
51
|
visit_roots.concat(member.orphan_types)
|
47
52
|
visit_roots.compact!
|
@@ -51,6 +56,9 @@ module GraphQL
|
|
51
56
|
@type_reference_map[argument.type.unwrap.to_s] << argument
|
52
57
|
visit(argument.type, "Directive argument #{member.name}.#{name}")
|
53
58
|
end
|
59
|
+
# Construct arguments class here, which is later used to generate GraphQL::Query::Arguments
|
60
|
+
# to be passed to a resolver proc
|
61
|
+
GraphQL::Query::Arguments.construct_arguments_class(member)
|
54
62
|
when GraphQL::BaseType
|
55
63
|
type_defn = member.unwrap
|
56
64
|
prev_type = @type_map[type_defn.name]
|
@@ -74,6 +82,10 @@ module GraphQL
|
|
74
82
|
@type_reference_map[arg.type.unwrap.to_s] << arg
|
75
83
|
visit(arg.type, "Input field #{type_defn.name}.#{name}")
|
76
84
|
end
|
85
|
+
|
86
|
+
# Construct arguments class here, which is later used to generate GraphQL::Query::Arguments
|
87
|
+
# to be passed to a resolver proc
|
88
|
+
GraphQL::Query::Arguments.construct_arguments_class(type_defn)
|
77
89
|
end
|
78
90
|
elsif !prev_type.equal?(type_defn)
|
79
91
|
# If the previous entry in the map isn't the same object we just found, raise.
|
@@ -87,17 +99,28 @@ module GraphQL
|
|
87
99
|
|
88
100
|
def visit_fields(type_defn)
|
89
101
|
type_defn.all_fields.each do |field_defn|
|
90
|
-
|
91
|
-
|
92
|
-
|
102
|
+
visit_field_on_type(type_defn, field_defn)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def visit_field_on_type(type_defn, field_defn, dynamic_field: false)
|
107
|
+
instrumented_field_defn = @field_instrumenters.reduce(field_defn) do |defn, inst|
|
108
|
+
inst.instrument(type_defn, defn)
|
109
|
+
end
|
110
|
+
if !dynamic_field
|
93
111
|
@instrumented_field_map[type_defn.name][instrumented_field_defn.name] = instrumented_field_defn
|
94
|
-
@type_reference_map[instrumented_field_defn.type.unwrap.name] << instrumented_field_defn
|
95
|
-
visit(instrumented_field_defn.type, "Field #{type_defn.name}.#{instrumented_field_defn.name}'s return type")
|
96
|
-
instrumented_field_defn.arguments.each do |name, arg|
|
97
|
-
@type_reference_map[arg.type.unwrap.to_s] << arg
|
98
|
-
visit(arg.type, "Argument #{name} on #{type_defn.name}.#{instrumented_field_defn.name}")
|
99
|
-
end
|
100
112
|
end
|
113
|
+
@type_reference_map[instrumented_field_defn.type.unwrap.name] << instrumented_field_defn
|
114
|
+
visit(instrumented_field_defn.type, "Field #{type_defn.name}.#{instrumented_field_defn.name}'s return type")
|
115
|
+
|
116
|
+
instrumented_field_defn.arguments.each do |name, arg|
|
117
|
+
@type_reference_map[arg.type.unwrap.to_s] << arg
|
118
|
+
visit(arg.type, "Argument #{name} on #{type_defn.name}.#{instrumented_field_defn.name}")
|
119
|
+
end
|
120
|
+
|
121
|
+
# Construct arguments class here, which is later used to generate GraphQL::Query::Arguments
|
122
|
+
# to be passed to a resolver proc
|
123
|
+
GraphQL::Query::Arguments.construct_arguments_class(instrumented_field_defn)
|
101
124
|
end
|
102
125
|
|
103
126
|
def validate_type(member, context_description)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
# test_via: ../subscriptions.rb
|
2
3
|
module GraphQL
|
3
4
|
class Subscriptions
|
4
5
|
# This thing can be:
|
@@ -37,7 +38,7 @@ module GraphQL
|
|
37
38
|
when Hash
|
38
39
|
GraphQL::Query::LiteralInput.from_arguments(
|
39
40
|
arguments,
|
40
|
-
field
|
41
|
+
field,
|
41
42
|
nil,
|
42
43
|
)
|
43
44
|
else
|
data/lib/graphql/version.rb
CHANGED
@@ -182,7 +182,7 @@ describe GraphQL::InputObjectType do
|
|
182
182
|
|
183
183
|
it "has problem with correct path" do
|
184
184
|
paths = result.problems.map { |p| p["path"] }
|
185
|
-
assert_equal(
|
185
|
+
assert_equal([["isDelicious"]], paths)
|
186
186
|
end
|
187
187
|
|
188
188
|
it "has correct problem explanation" do
|
@@ -37,10 +37,14 @@ describe GraphQL::ListType do
|
|
37
37
|
|
38
38
|
describe "list of input objects" do
|
39
39
|
let(:input_object) do
|
40
|
-
GraphQL::InputObjectType.define do
|
40
|
+
input_object = GraphQL::InputObjectType.define do
|
41
41
|
name "SomeInputObjectType"
|
42
42
|
argument :float, !types.Float
|
43
43
|
end
|
44
|
+
|
45
|
+
GraphQL::Query::Arguments.construct_arguments_class(input_object)
|
46
|
+
|
47
|
+
input_object
|
44
48
|
end
|
45
49
|
|
46
50
|
let(:input_object_list) { input_object.to_list_type }
|
@@ -291,4 +291,23 @@ describe GraphQL::Query::Arguments do
|
|
291
291
|
assert_equal 30, args['inputObject']['a']
|
292
292
|
end
|
293
293
|
end
|
294
|
+
|
295
|
+
describe "construct_arguments_class" do
|
296
|
+
let(:input_object) do
|
297
|
+
GraphQL::InputObjectType.define do
|
298
|
+
argument :foo, types.Int
|
299
|
+
argument :bar, types.Int
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
it "generates argument classes that responds to keys as functions" do
|
304
|
+
assert_equal nil, input_object.arguments_class
|
305
|
+
|
306
|
+
GraphQL::Query::Arguments.construct_arguments_class(input_object)
|
307
|
+
args = input_object.arguments_class.instantiate_arguments({foo: 3, bar: -90})
|
308
|
+
|
309
|
+
assert_equal 3, args.foo
|
310
|
+
assert_equal -90, args.bar
|
311
|
+
end
|
312
|
+
end
|
294
313
|
end
|
@@ -11,6 +11,10 @@ describe GraphQL::Relay::ArrayConnection do
|
|
11
11
|
result["data"]["rebels"]["ships"]["edges"].last["cursor"]
|
12
12
|
end
|
13
13
|
|
14
|
+
def get_page_info(result, key = "bases")
|
15
|
+
result["data"]["rebels"][key]["pageInfo"]
|
16
|
+
end
|
17
|
+
|
14
18
|
describe "results" do
|
15
19
|
let(:query_string) {%|
|
16
20
|
query getShips($first: Int, $after: String, $last: Int, $before: String, $nameIncludes: String){
|
@@ -61,6 +65,36 @@ describe GraphQL::Relay::ArrayConnection do
|
|
61
65
|
assert_equal("NQ==", result["data"]["rebels"]["ships"]["pageInfo"]["endCursor"])
|
62
66
|
end
|
63
67
|
|
68
|
+
it "provides bidirectional_pagination" do
|
69
|
+
result = star_wars_query(query_string, "first" => 1)
|
70
|
+
last_cursor = get_last_cursor(result)
|
71
|
+
|
72
|
+
# When going forwards, bi-directional pagination
|
73
|
+
# returns `true` even for `hasPreviousPage`
|
74
|
+
result = star_wars_query(query_string, "first" => 1, "after" => last_cursor)
|
75
|
+
assert_equal(true, get_page_info(result, "ships")["hasNextPage"])
|
76
|
+
assert_equal(false, get_page_info(result, "ships")["hasPreviousPage"])
|
77
|
+
|
78
|
+
result = with_bidirectional_pagination {
|
79
|
+
star_wars_query(query_string, "first" => 3, "after" => last_cursor)
|
80
|
+
}
|
81
|
+
assert_equal(true, get_page_info(result, "ships")["hasNextPage"])
|
82
|
+
assert_equal(true, get_page_info(result, "ships")["hasPreviousPage"])
|
83
|
+
|
84
|
+
# When going backwards, bi-directional pagination
|
85
|
+
# returns true for `hasNextPage`
|
86
|
+
last_cursor = get_last_cursor(result)
|
87
|
+
result = star_wars_query(query_string, "last" => 1, "before" => last_cursor)
|
88
|
+
assert_equal(false, get_page_info(result, "ships")["hasNextPage"])
|
89
|
+
assert_equal(true, get_page_info(result, "ships")["hasPreviousPage"])
|
90
|
+
|
91
|
+
result = with_bidirectional_pagination {
|
92
|
+
star_wars_query(query_string, "last" => 2, "before" => last_cursor)
|
93
|
+
}
|
94
|
+
assert_equal(true, get_page_info(result, "ships")["hasNextPage"])
|
95
|
+
assert_equal(true, get_page_info(result, "ships")["hasPreviousPage"])
|
96
|
+
end
|
97
|
+
|
64
98
|
it 'slices the result' do
|
65
99
|
result = star_wars_query(query_string, "first" => 1)
|
66
100
|
assert_equal(["X-Wing"], get_names(result))
|
@@ -142,10 +176,6 @@ describe GraphQL::Relay::ArrayConnection do
|
|
142
176
|
result["data"]["rebels"]["bases"]["edges"].map { |e| e["node"]["name"] }
|
143
177
|
end
|
144
178
|
|
145
|
-
def get_page_info(result)
|
146
|
-
result["data"]["rebels"]["bases"]["pageInfo"]
|
147
|
-
end
|
148
|
-
|
149
179
|
let(:query_string) {%|
|
150
180
|
query getShips($first: Int, $after: String, $last: Int, $before: String){
|
151
181
|
rebels {
|
@@ -74,6 +74,35 @@ describe GraphQL::Relay::RelationConnection do
|
|
74
74
|
)
|
75
75
|
end
|
76
76
|
|
77
|
+
it "provides bidirectional_pagination" do
|
78
|
+
result = star_wars_query(query_string, "first" => 1)
|
79
|
+
last_cursor = get_last_cursor(result)
|
80
|
+
|
81
|
+
result = star_wars_query(query_string, "first" => 1, "after" => last_cursor)
|
82
|
+
assert_equal true, get_page_info(result)["hasNextPage"]
|
83
|
+
assert_equal false, get_page_info(result)["hasPreviousPage"]
|
84
|
+
|
85
|
+
result = with_bidirectional_pagination {
|
86
|
+
star_wars_query(query_string, "first" => 1, "after" => last_cursor)
|
87
|
+
}
|
88
|
+
assert_equal true, get_page_info(result)["hasNextPage"]
|
89
|
+
assert_equal true, get_page_info(result)["hasPreviousPage"]
|
90
|
+
|
91
|
+
result = star_wars_query(query_string, "first" => 100)
|
92
|
+
last_cursor = get_last_cursor(result)
|
93
|
+
|
94
|
+
result = star_wars_query(query_string, "last" => 1, "before" => last_cursor)
|
95
|
+
assert_equal false, get_page_info(result)["hasNextPage"]
|
96
|
+
assert_equal true, get_page_info(result)["hasPreviousPage"]
|
97
|
+
|
98
|
+
result = with_bidirectional_pagination {
|
99
|
+
star_wars_query(query_string, "last" => 1, "before" => last_cursor)
|
100
|
+
}
|
101
|
+
assert_equal true, get_page_info(result)["hasNextPage"]
|
102
|
+
assert_equal true, get_page_info(result)["hasPreviousPage"]
|
103
|
+
|
104
|
+
end
|
105
|
+
|
77
106
|
it 'slices the result' do
|
78
107
|
result = star_wars_query(query_string, "first" => 2)
|
79
108
|
assert_equal(["Death Star", "Shield Generator"], get_names(result))
|
data/spec/spec_helper.rb
CHANGED
@@ -68,6 +68,14 @@ def star_wars_query(string, variables={}, context: {})
|
|
68
68
|
GraphQL::Query.new(StarWars::Schema, string, variables: variables, context: context).result
|
69
69
|
end
|
70
70
|
|
71
|
+
def with_bidirectional_pagination
|
72
|
+
prev_value = GraphQL::Relay::ConnectionType.bidirectional_pagination
|
73
|
+
GraphQL::Relay::ConnectionType.bidirectional_pagination = true
|
74
|
+
yield
|
75
|
+
ensure
|
76
|
+
GraphQL::Relay::ConnectionType.bidirectional_pagination = prev_value
|
77
|
+
end
|
78
|
+
|
71
79
|
module TestTracing
|
72
80
|
class << self
|
73
81
|
def clear
|
@@ -109,7 +109,7 @@ module Dummy
|
|
109
109
|
field :flavors, types[types.String], "Chocolate, Strawberry, etc" do
|
110
110
|
argument :limit, types.Int
|
111
111
|
resolve ->(milk, args, ctx) {
|
112
|
-
args[:limit] ? milk.flavors.first(args
|
112
|
+
args[:limit] ? milk.flavors.first(args.limit) : milk.flavors
|
113
113
|
}
|
114
114
|
end
|
115
115
|
field :executionError do
|
@@ -234,7 +234,7 @@ module Dummy
|
|
234
234
|
name "DeepNonNull"
|
235
235
|
field :nonNullInt, !types.Int do
|
236
236
|
argument :returning, types.Int
|
237
|
-
resolve ->(obj, args, ctx) { args
|
237
|
+
resolve ->(obj, args, ctx) { args.returning }
|
238
238
|
end
|
239
239
|
|
240
240
|
field :deepNonNull, DeepNonNullType.to_non_null_type do
|
@@ -328,7 +328,7 @@ module Dummy
|
|
328
328
|
argument :executionErrorAtIndex, types.Int
|
329
329
|
resolve ->(obj, args, ctx) {
|
330
330
|
result = CHEESES.values + MILKS.values
|
331
|
-
result[args
|
331
|
+
result[args.executionErrorAtIndex] = GraphQL::ExecutionError.new("missing dairy") if args.executionErrorAtIndex
|
332
332
|
result
|
333
333
|
}
|
334
334
|
end
|
@@ -384,7 +384,7 @@ module Dummy
|
|
384
384
|
description("Push a value onto a global array :D")
|
385
385
|
argument :value, !types.Int, as: :val
|
386
386
|
resolve ->(o, args, ctx) {
|
387
|
-
GLOBAL_VALUES << args
|
387
|
+
GLOBAL_VALUES << args.val
|
388
388
|
GLOBAL_VALUES
|
389
389
|
}
|
390
390
|
end
|
@@ -394,7 +394,7 @@ module Dummy
|
|
394
394
|
argument :input, !ReplaceValuesInputType
|
395
395
|
resolve ->(o, args, ctx) {
|
396
396
|
GLOBAL_VALUES.clear
|
397
|
-
GLOBAL_VALUES.push(*args
|
397
|
+
GLOBAL_VALUES.push(*args.input[:values])
|
398
398
|
GLOBAL_VALUES
|
399
399
|
}
|
400
400
|
end
|