relay-rb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Rakefile +26 -0
- data/lib/relay/connection/array.rb +54 -0
- data/lib/relay/connection/connection.rb +78 -0
- data/lib/relay/node/global_id_field.rb +38 -0
- data/lib/relay/node/plural_identifying_root_field.rb +35 -0
- data/lib/relay/node.rb +50 -0
- data/lib/relay/version.rb +3 -0
- data/lib/relay.rb +6 -0
- data/spec/connection/connection_spec.rb +85 -0
- data/spec/node/data.rb +69 -0
- data/spec/node/data_global.rb +78 -0
- data/spec/node/node_global_spec.rb +50 -0
- data/spec/node/node_plural_spec.rb +51 -0
- data/spec/node/node_spec.rb +106 -0
- data/spec/spec_helper.rb +96 -0
- data/spec/star_wars/connection_spec.rb +251 -0
- data/spec/star_wars/data.rb +54 -0
- data/spec/star_wars/object_identification_spec.rb +139 -0
- data/spec/star_wars/schema.rb +115 -0
- metadata +116 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7c976d6b1f308d77fad6f43623d821150b45df13
|
4
|
+
data.tar.gz: d396a7d982e23b6ca71bafbbdbc438b444c0ea6b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 914555d8c843092f3a6fbe1cec290ae032784a4296b32eaf1245d65429693a9305e2e5e5a55ac84f54ba2a45b38cd7fc1813105b6410c4ca452e3c0fc3c29651
|
7
|
+
data.tar.gz: 70c0950fcf1b438467aef315b0f572febb7c17609cc4e9d6e00a0f23ea338e5a1b58d10e3560520e4dc47c5224e9fd96899ac1a67df10ebfcd7f7cc76fbec079
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Relay Ruby'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
14
|
+
end
|
15
|
+
|
16
|
+
Bundler::GemHelper.install_tasks
|
17
|
+
|
18
|
+
require 'rspec'
|
19
|
+
require 'rspec/core/rake_task'
|
20
|
+
|
21
|
+
RSpec::Core::RakeTask.new(:test) do |test|
|
22
|
+
test.verbose = false
|
23
|
+
test.rspec_opts = %w[--color --format documentation]
|
24
|
+
end
|
25
|
+
|
26
|
+
task default: :test
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Relay
|
4
|
+
module Connection
|
5
|
+
|
6
|
+
ARRAY_CONNECTION_PREFIX = 'arrayconnection'
|
7
|
+
|
8
|
+
Edge = Struct.new('Edge', :cursor, :node)
|
9
|
+
PageInfo = Struct.new('PageInfo', :startCursor, :endCursor, :hasPreviousPage, :hasNextPage)
|
10
|
+
ArrayConnection = Struct.new('ArrayConnection', :edges, :pageInfo)
|
11
|
+
EmptyConnection = ArrayConnection.new([], PageInfo.new(nil, nil, false, false))
|
12
|
+
|
13
|
+
def self.connection_from_array(data, args)
|
14
|
+
edges = data.each_with_index.map { |item, i| Edge.new(offset_to_cursor(i), item) }
|
15
|
+
|
16
|
+
start = [get_offset(args[:after], -1), -1].max + 1
|
17
|
+
finish = [get_offset(args[:before], edges.size + 1), edges.size + 1].min
|
18
|
+
|
19
|
+
edges = edges.slice(start, finish)
|
20
|
+
|
21
|
+
return EmptyConnection if edges.size == 0
|
22
|
+
|
23
|
+
first_preslice_cursor = edges.first[:cursor]
|
24
|
+
last_preslice_cursor = edges.last[:cursor]
|
25
|
+
|
26
|
+
edges = edges.slice(0, args[:first]) unless args[:first].nil?
|
27
|
+
egdes = edges.slice!(- args[:last], edges.size) unless args[:last].nil?
|
28
|
+
|
29
|
+
return EmptyConnection if edges.size == 0
|
30
|
+
|
31
|
+
ArrayConnection.new(edges, PageInfo.new(
|
32
|
+
edges.first[:cursor],
|
33
|
+
edges.last[:cursor],
|
34
|
+
edges.first[:cursor] != first_preslice_cursor,
|
35
|
+
edges.last[:cursor] != last_preslice_cursor
|
36
|
+
))
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.offset_to_cursor(index)
|
40
|
+
Base64.strict_encode64([ARRAY_CONNECTION_PREFIX, index].join(':'))
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.cursor_to_offset(cursor)
|
44
|
+
Base64.strict_decode64(cursor).split(':').last.to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.get_offset(cursor, default_offset)
|
48
|
+
return default_offset unless cursor
|
49
|
+
offset = cursor_to_offset(cursor)
|
50
|
+
offset.nil? ? default_offset : offset
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative 'array'
|
2
|
+
|
3
|
+
module Relay
|
4
|
+
module Connection
|
5
|
+
|
6
|
+
ConnectionArguments = [
|
7
|
+
GraphQL::GraphQLArgument.new(:before, GraphQL::GraphQLString),
|
8
|
+
GraphQL::GraphQLArgument.new(:after, GraphQL::GraphQLString),
|
9
|
+
GraphQL::GraphQLArgument.new(:first, GraphQL::GraphQLInt),
|
10
|
+
GraphQL::GraphQLArgument.new(:last, GraphQL::GraphQLInt)
|
11
|
+
]
|
12
|
+
|
13
|
+
class ConnectionConfiguration < GraphQL::Configuration::Base
|
14
|
+
slot :name, String
|
15
|
+
slot :node_type, GraphQL::GraphQLObjectType
|
16
|
+
slot :edge_fields, [GraphQL::GraphQLField], singular: :edge_field
|
17
|
+
slot :connection_fields, [GraphQL::GraphQLField], singular: :connection_field
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.connection_definitions(configuration)
|
21
|
+
name, node_type = configuration.name, configuration.node_type
|
22
|
+
|
23
|
+
edge_type = GraphQL::GraphQLObjectType.new do
|
24
|
+
name name + 'Edge'
|
25
|
+
description 'An edge in a connection'
|
26
|
+
|
27
|
+
field :node, node_type do
|
28
|
+
description 'The item at the end of the edge'
|
29
|
+
end
|
30
|
+
|
31
|
+
field :cursor, ! GraphQL::GraphQLString do
|
32
|
+
description 'A cursor for use in pagination'
|
33
|
+
end
|
34
|
+
|
35
|
+
fields configuration.edge_fields
|
36
|
+
end
|
37
|
+
|
38
|
+
connection_type = GraphQL::GraphQLObjectType.new do
|
39
|
+
name name + 'Connection'
|
40
|
+
description 'A connection to a list of items.'
|
41
|
+
|
42
|
+
field :pageInfo, !PageInfoType do
|
43
|
+
description 'Information to aid in pagination.'
|
44
|
+
end
|
45
|
+
|
46
|
+
field :edges, +edge_type do
|
47
|
+
description 'Information to aid in pagination.'
|
48
|
+
end
|
49
|
+
|
50
|
+
fields configuration.connection_fields
|
51
|
+
end
|
52
|
+
|
53
|
+
return edge_type, connection_type
|
54
|
+
end
|
55
|
+
|
56
|
+
PageInfoType = GraphQL::GraphQLObjectType.new do
|
57
|
+
name 'PageInfo'
|
58
|
+
description 'Information about pagination in a connection.'
|
59
|
+
|
60
|
+
field :hasNextPage, !GraphQL::GraphQLBoolean do
|
61
|
+
description 'When paginating forwards, are there more items?'
|
62
|
+
end
|
63
|
+
|
64
|
+
field :hasPreviousPage, !GraphQL::GraphQLBoolean do
|
65
|
+
description 'When paginating backwards, are there more items?'
|
66
|
+
end
|
67
|
+
|
68
|
+
field :startCursor, GraphQL::GraphQLString do
|
69
|
+
description 'When paginating backwards, the cursor to continue.'
|
70
|
+
end
|
71
|
+
|
72
|
+
field :endCursor, GraphQL::GraphQLString do
|
73
|
+
description 'When paginating forwards, the cursor to continue.'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'graphql'
|
2
|
+
|
3
|
+
module Relay
|
4
|
+
|
5
|
+
|
6
|
+
GLOBAL_ID_FIELD_DEFAULT_RESOLVE = -> (object) { object.id }
|
7
|
+
|
8
|
+
class GlobalIDFieldConfiguration < GraphQL::Configuration::Base
|
9
|
+
slot :name, String, coerce: -> (v) { v.to_s }
|
10
|
+
slot :type_name, String, coerce: -> (v) { v.to_s }
|
11
|
+
slot :resolve_id, Proc, null: true
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
class GraphQL::GraphQLObjectTypeConfiguration
|
16
|
+
|
17
|
+
def global_id_field(*args, &block)
|
18
|
+
configuration = GlobalIDFieldConfiguration.new(*args, &block)
|
19
|
+
|
20
|
+
resolve_id = configuration.resolve_id || GLOBAL_ID_FIELD_DEFAULT_RESOLVE
|
21
|
+
|
22
|
+
global_id_field = GraphQL::GraphQLFieldConfiguration.new do
|
23
|
+
name configuration.name
|
24
|
+
|
25
|
+
type !GraphQL::GraphQLID
|
26
|
+
|
27
|
+
resolve lambda { |object|
|
28
|
+
Relay::Node.to_global_id(configuration.type_name, resolve_id.call(object))
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
field(global_id_field)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Relay
|
2
|
+
module Node
|
3
|
+
|
4
|
+
class PluralIdentifyingRootFieldConfiguration < GraphQL::Configuration::Base
|
5
|
+
slot :name, String, coerce: -> (v) { v.to_s }
|
6
|
+
slot :input_type, GraphQL::GraphQLInputType
|
7
|
+
slot :output_type, GraphQL::GraphQLOutputType
|
8
|
+
slot :argument_name, String, coerce: -> (v) { v.to_s }
|
9
|
+
slot :resolve_single_input, Proc
|
10
|
+
end
|
11
|
+
|
12
|
+
class GraphQL::GraphQLObjectTypeConfiguration
|
13
|
+
|
14
|
+
def plural_identifying_root_field(*args, &block)
|
15
|
+
configuration = PluralIdentifyingRootFieldConfiguration.new(*args, &block)
|
16
|
+
|
17
|
+
plural_identifying_root_field = GraphQL::GraphQLFieldConfiguration.new do
|
18
|
+
name configuration.name
|
19
|
+
|
20
|
+
type +configuration.output_type
|
21
|
+
|
22
|
+
arg configuration.argument_name, !+!configuration.input_type
|
23
|
+
|
24
|
+
resolve lambda { |object, params|
|
25
|
+
params[configuration.argument_name.to_sym].map { |param| configuration.resolve_single_input.call(param) }
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
field(plural_identifying_root_field)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/lib/relay/node.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'graphql'
|
2
|
+
require 'base64'
|
3
|
+
require_relative 'node/global_id_field'
|
4
|
+
require_relative 'node/plural_identifying_root_field'
|
5
|
+
|
6
|
+
module Relay
|
7
|
+
|
8
|
+
module Node
|
9
|
+
|
10
|
+
def self.definitions(fetcher, resolver)
|
11
|
+
|
12
|
+
interface = GraphQL::GraphQLInterfaceType.new do
|
13
|
+
name 'Node'
|
14
|
+
description 'An object with ID'
|
15
|
+
|
16
|
+
field :id, ! GraphQL::GraphQLID do
|
17
|
+
description 'The id of the object'
|
18
|
+
end
|
19
|
+
|
20
|
+
resolve_type resolver
|
21
|
+
end
|
22
|
+
|
23
|
+
field = GraphQL::GraphQLField.new do
|
24
|
+
name 'node'
|
25
|
+
description 'Fetches an object given its ID'
|
26
|
+
type interface
|
27
|
+
|
28
|
+
arg :id, ! GraphQL::GraphQLID do
|
29
|
+
description 'The ID of an object'
|
30
|
+
end
|
31
|
+
|
32
|
+
resolve lambda { |root, params, info, *args|
|
33
|
+
fetcher.call(params[:id], info)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
{ interface: interface, field: field }
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.to_global_id(type, id)
|
41
|
+
Base64.strict_encode64([type, id].join(':'))
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.from_global_id(global_id)
|
45
|
+
return Base64.strict_decode64(global_id).split(':')
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/lib/relay.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'relay'
|
2
|
+
|
3
|
+
RSpec.describe 'Relay Connection' do
|
4
|
+
|
5
|
+
User = Struct.new('UserInRelayConnection', :name)
|
6
|
+
|
7
|
+
Users = [
|
8
|
+
User.new('Dan'),
|
9
|
+
User.new('Nick'),
|
10
|
+
User.new('Lee'),
|
11
|
+
User.new('Joe'),
|
12
|
+
User.new('Tim')
|
13
|
+
]
|
14
|
+
|
15
|
+
friend_edge, friend_connection = Relay::Connection.connection_definitions(
|
16
|
+
Relay::Connection::ConnectionConfiguration.new do
|
17
|
+
name 'Friend'
|
18
|
+
node_type -> { ConnectionUserType }
|
19
|
+
|
20
|
+
edge_field :friendship_time, GraphQL::GraphQLString do
|
21
|
+
resolve -> { 'Yesterday' }
|
22
|
+
end
|
23
|
+
|
24
|
+
connection_field :total_count, GraphQL::GraphQLInt do
|
25
|
+
resolve -> { Users.size }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
)
|
29
|
+
|
30
|
+
ConnectionUserType = GraphQL::GraphQLObjectType.new do
|
31
|
+
name 'User'
|
32
|
+
|
33
|
+
field :name, GraphQL::GraphQLString
|
34
|
+
|
35
|
+
field :friends do
|
36
|
+
type friend_connection
|
37
|
+
args Relay::Connection::ConnectionArguments
|
38
|
+
resolve lambda { |user, params|
|
39
|
+
Relay::Connection.connection_from_array(Users, params)
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
ConnectionQueryType = GraphQL::GraphQLObjectType.new do
|
45
|
+
name 'Query'
|
46
|
+
|
47
|
+
field :user, ConnectionUserType do
|
48
|
+
resolve -> { Users[0] }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
ConnectionSchema = GraphQL::GraphQLSchema.new do
|
53
|
+
query ConnectionQueryType
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def q1
|
58
|
+
%Q(
|
59
|
+
query FriendsQuery {
|
60
|
+
user {
|
61
|
+
name
|
62
|
+
friends(first: 2, after: "YXJyYXljb25uZWN0aW9uOjM=") {
|
63
|
+
total_count
|
64
|
+
edges {
|
65
|
+
friendship_time
|
66
|
+
cursor
|
67
|
+
node {
|
68
|
+
name
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
it "Should include connections and edge fields" do
|
79
|
+
document = GraphQL::Language.parse(q1)
|
80
|
+
executor = GraphQL::Executor.new(document, ConnectionSchema)
|
81
|
+
puts executor.execute({})
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
end
|
data/spec/node/data.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Relay
|
2
|
+
module Node
|
3
|
+
module Data
|
4
|
+
|
5
|
+
UserStruct = Struct.new('User', :id, :name)
|
6
|
+
|
7
|
+
Users = {
|
8
|
+
'1' => UserStruct.new('1', 'John Doe'),
|
9
|
+
'2' => UserStruct.new('2', 'Jane Smith')
|
10
|
+
}
|
11
|
+
|
12
|
+
PhotoStruct = Struct.new('Photo', :id, :width)
|
13
|
+
|
14
|
+
Photos = {
|
15
|
+
'3' => PhotoStruct.new('3', 300),
|
16
|
+
'4' => PhotoStruct.new('4', 400)
|
17
|
+
}
|
18
|
+
|
19
|
+
fetcher = lambda { |id, info|
|
20
|
+
if Users[id.to_s]
|
21
|
+
return Users[id.to_s]
|
22
|
+
end
|
23
|
+
if Photos[id.to_s]
|
24
|
+
return Photos[id.to_s]
|
25
|
+
end
|
26
|
+
}
|
27
|
+
|
28
|
+
resolver = lambda { |root|
|
29
|
+
if Users[root.id.to_s]
|
30
|
+
return UserType
|
31
|
+
end
|
32
|
+
|
33
|
+
if Photos[root.id.to_s]
|
34
|
+
return PhotoType
|
35
|
+
end
|
36
|
+
}
|
37
|
+
|
38
|
+
definitions = Relay::Node.definitions(fetcher, resolver)
|
39
|
+
|
40
|
+
UserType = GraphQL::GraphQLObjectType.new do
|
41
|
+
name 'User'
|
42
|
+
|
43
|
+
field :id, ! GraphQL::GraphQLID
|
44
|
+
field :name, GraphQL::GraphQLString
|
45
|
+
|
46
|
+
interface definitions[:interface]
|
47
|
+
end
|
48
|
+
|
49
|
+
PhotoType = GraphQL::GraphQLObjectType.new do
|
50
|
+
name 'Photo'
|
51
|
+
|
52
|
+
field :id, ! GraphQL::GraphQLID
|
53
|
+
field :width, GraphQL::GraphQLInt
|
54
|
+
|
55
|
+
interface definitions[:interface]
|
56
|
+
end
|
57
|
+
|
58
|
+
QueryType = GraphQL::GraphQLObjectType.new do
|
59
|
+
name 'Query'
|
60
|
+
|
61
|
+
field definitions[:field]
|
62
|
+
end
|
63
|
+
|
64
|
+
Schema = GraphQL::GraphQLSchema.new do
|
65
|
+
query QueryType
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Relay
|
2
|
+
module Node
|
3
|
+
module DataGlobal
|
4
|
+
|
5
|
+
UserStruct = Struct.new('UserGlobal', :id, :name)
|
6
|
+
|
7
|
+
Users = {
|
8
|
+
'1' => UserStruct.new('1', 'John Doe'),
|
9
|
+
'2' => UserStruct.new('2', 'Jane Smith')
|
10
|
+
}
|
11
|
+
|
12
|
+
PhotoStruct = Struct.new('PhotoGlobal', :photo_id, :width)
|
13
|
+
|
14
|
+
Photos = {
|
15
|
+
'3' => PhotoStruct.new('3', 300),
|
16
|
+
'4' => PhotoStruct.new('4', 400)
|
17
|
+
}
|
18
|
+
|
19
|
+
fetcher = lambda { |id, *args|
|
20
|
+
type, id = Relay::Node.from_global_id(id)
|
21
|
+
|
22
|
+
case type
|
23
|
+
when 'User'
|
24
|
+
Users[id]
|
25
|
+
when 'Photo'
|
26
|
+
Photos[id]
|
27
|
+
end
|
28
|
+
}
|
29
|
+
|
30
|
+
resolver = lambda { |root|
|
31
|
+
case root
|
32
|
+
when UserStruct
|
33
|
+
UserType
|
34
|
+
when PhotoStruct
|
35
|
+
PhotoType
|
36
|
+
end
|
37
|
+
}
|
38
|
+
|
39
|
+
definitions = Relay::Node.definitions(fetcher, resolver)
|
40
|
+
|
41
|
+
UserType = GraphQL::GraphQLObjectType.new do
|
42
|
+
name 'User'
|
43
|
+
|
44
|
+
global_id_field :id, type_name: 'User'
|
45
|
+
|
46
|
+
field :name, GraphQL::GraphQLString
|
47
|
+
|
48
|
+
interface definitions[:interface]
|
49
|
+
end
|
50
|
+
|
51
|
+
PhotoType = GraphQL::GraphQLObjectType.new do
|
52
|
+
name 'Photo'
|
53
|
+
|
54
|
+
global_id_field :id, type_name: 'Photo', resolve_id: -> (object) { object.photo_id }
|
55
|
+
|
56
|
+
field :width, GraphQL::GraphQLInt
|
57
|
+
|
58
|
+
interface definitions[:interface]
|
59
|
+
end
|
60
|
+
|
61
|
+
QueryType = GraphQL::GraphQLObjectType.new do
|
62
|
+
name 'Query'
|
63
|
+
|
64
|
+
field definitions[:field]
|
65
|
+
|
66
|
+
field :all, + definitions[:interface] do
|
67
|
+
resolve lambda { |*args|
|
68
|
+
Users.values.concat(Photos.values)
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
Schema = GraphQL::GraphQLSchema.new do
|
74
|
+
query QueryType
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'relay'
|
2
|
+
require_relative 'data_global'
|
3
|
+
|
4
|
+
RSpec.describe 'Relay Node with Global ID' do
|
5
|
+
|
6
|
+
def schema
|
7
|
+
Relay::Node::DataGlobal::Schema
|
8
|
+
end
|
9
|
+
|
10
|
+
def all_q1
|
11
|
+
%Q{{
|
12
|
+
all {
|
13
|
+
id
|
14
|
+
}
|
15
|
+
}}
|
16
|
+
end
|
17
|
+
|
18
|
+
def all_q2
|
19
|
+
%Q{{
|
20
|
+
user: node(id: "VXNlcjox") {
|
21
|
+
id
|
22
|
+
... on User {
|
23
|
+
name
|
24
|
+
}
|
25
|
+
}
|
26
|
+
photo: node(id: "UGhvdG86Mw==") {
|
27
|
+
id
|
28
|
+
... on Photo {
|
29
|
+
width
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}}
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
it "Should give different ids" do
|
37
|
+
expectations = {all: [{id: 'VXNlcjox'}, {id: 'VXNlcjoy'}, {id: 'UGhvdG86Mw=='}, {id: 'UGhvdG86NA=='}]}
|
38
|
+
document = GraphQL::Language.parse(all_q1)
|
39
|
+
executor = GraphQL::Executor.new(document, schema)
|
40
|
+
expect(executor.execute({})).to eq(expectations)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "Should refetch ids" do
|
44
|
+
expectations = {user: {id: 'VXNlcjox', name: 'John Doe'}, photo: {id: 'UGhvdG86Mw==', width: 300}}
|
45
|
+
document = GraphQL::Language.parse(all_q2)
|
46
|
+
executor = GraphQL::Executor.new(document, schema)
|
47
|
+
expect(executor.execute({})).to eq(expectations)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'relay'
|
2
|
+
|
3
|
+
RSpec.describe 'Relay Node Plural' do
|
4
|
+
|
5
|
+
UserType = GraphQL::GraphQLObjectType.new do
|
6
|
+
name 'User'
|
7
|
+
|
8
|
+
field :username, GraphQL::GraphQLString
|
9
|
+
field :url, GraphQL::GraphQLString
|
10
|
+
end
|
11
|
+
|
12
|
+
UserStruct = Struct.new('PluralUser', :username, :url)
|
13
|
+
|
14
|
+
QueryType = GraphQL::GraphQLObjectType.new do
|
15
|
+
name 'Query'
|
16
|
+
|
17
|
+
plural_identifying_root_field :usernames do
|
18
|
+
argument_name :usernames
|
19
|
+
input_type GraphQL::GraphQLString
|
20
|
+
output_type UserType
|
21
|
+
|
22
|
+
resolve_single_input lambda { |username, *args|
|
23
|
+
UserStruct.new(username, "www.facebook.com/#{username}")
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Schema = GraphQL::GraphQLSchema.new do
|
29
|
+
|
30
|
+
query QueryType
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def q1
|
35
|
+
%Q(
|
36
|
+
{
|
37
|
+
usernames(usernames: ["dschafer", "leebyron", "schrockn"]) {
|
38
|
+
username
|
39
|
+
url
|
40
|
+
}
|
41
|
+
}
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "Should allow fetching" do
|
46
|
+
document = GraphQL::Language.parse(q1)
|
47
|
+
executor = GraphQL::Executor.new(document, Schema)
|
48
|
+
puts executor.execute({})
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|