graphql 1.7.0 → 1.7.1
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/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
|