graphql-relay 0.3.6 → 0.4.0
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/README.md +14 -8
- data/lib/graphql/relay.rb +1 -1
- data/lib/graphql/relay/global_id_field.rb +1 -1
- data/lib/graphql/relay/global_node_identification.rb +86 -0
- data/lib/graphql/relay/monkey_patches/definition_config.rb +10 -0
- data/lib/graphql/relay/version.rb +1 -1
- data/spec/graphql/relay/global_node_identification_spec.rb +36 -0
- data/spec/graphql/relay/mutation_spec.rb +1 -1
- data/spec/support/star_wars_schema.rb +15 -11
- metadata +7 -7
- data/lib/graphql/relay/node.rb +0 -64
- data/spec/graphql/relay/node_spec.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4efd1cd2ad74d644082dec208d0f59fee5c43973
|
4
|
+
data.tar.gz: 2c31b7d7c3f3661e54ec02103e622328f5c25a51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7475507b89ca0adf9a061be156f06ce2a8ff96dc6b09a78f23cd1c796c862e8490feed2a0afa1a60802ded7d3384b7aedafd2a2f48a13990baafe18edc5b350d
|
7
|
+
data.tar.gz: 9c38cc95c267429b0047b1ee140307cef4e08bde2abe832eff76cc29e3e867b7d6b5bffad51459671bebfcec0093dbc1e6cef3f99eaeeb27e0f1d194aeb37bd1
|
data/README.md
CHANGED
@@ -23,10 +23,11 @@ bundle install
|
|
23
23
|
|
24
24
|
Global Ids provide refetching & global identification for Relay.
|
25
25
|
|
26
|
-
You should
|
26
|
+
You should create `GraphQL::Relay::GlobalNodeIdentification` helper by defining `object_from_id(global_id)` & `type_from_object(object)`. The resulting object provides ID resultion methods, a find-by-global-id field and a node interface. [Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L9-L18)
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
ObjectTypes should implement that interface with the `global_id_field` helper: [Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L30-L31)
|
29
|
+
|
30
|
+
You should attach the field to your query type: [Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L121)
|
30
31
|
|
31
32
|
### Connections
|
32
33
|
|
@@ -35,9 +36,13 @@ Connections will provide arguments, pagination and `pageInfo` for `Array`s or `A
|
|
35
36
|
Then, implement the field. It's different than a normal field:
|
36
37
|
- use the `connection` helper to define it, instead of `field`
|
37
38
|
- Call `#connection_type` on an `ObjectType` for the field's return type (eg, `ShipType.connection_type`)
|
38
|
-
- implement `resolve` to return an Array or an ActiveRecord::Relation, depending on the connection type.
|
39
39
|
|
40
|
-
|
40
|
+
Examples:
|
41
|
+
|
42
|
+
- [Connection with custom arguments](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L51-L63)
|
43
|
+
- [Connection with a different name than the underlying property](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L77)
|
44
|
+
|
45
|
+
You can also add custom fields to connection objects: [Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L36-L43)
|
41
46
|
|
42
47
|
### Mutations
|
43
48
|
|
@@ -60,14 +65,15 @@ The resolve proc:
|
|
60
65
|
- Must return a hash with keys matching your defined `return_field`s
|
61
66
|
|
62
67
|
Examples:
|
63
|
-
- Definition: [example](https://github.com/rmosolgo/graphql-relay-ruby/blob/
|
64
|
-
- Mount on mutation type: [example](https://github.com/rmosolgo/graphql-relay-ruby/blob/
|
68
|
+
- Definition: [example](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L90)
|
69
|
+
- Mount on mutation type: [example](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L127)
|
65
70
|
|
66
71
|
## Todo
|
67
72
|
|
68
|
-
-
|
73
|
+
- Show how to replace default connection implementations with custom ones
|
69
74
|
|
70
75
|
## More Resources
|
71
76
|
|
77
|
+
- [GraphQL Slack](graphql-slack.herokuapp.com), come join us in the `#ruby` channel!
|
72
78
|
- [`graphql`](https://github.com/rmosolgo/graphql-ruby) Ruby gem
|
73
79
|
- [`graphql-relay-js`](https://github.com/graphql/graphql-relay-js) JavaScript helpers for GraphQL and Relay
|
data/lib/graphql/relay.rb
CHANGED
@@ -4,7 +4,7 @@ require 'graphql'
|
|
4
4
|
require 'graphql/relay/monkey_patches/definition_config'
|
5
5
|
require 'graphql/relay/monkey_patches/object_type'
|
6
6
|
|
7
|
-
require 'graphql/relay/
|
7
|
+
require 'graphql/relay/global_node_identification'
|
8
8
|
require 'graphql/relay/page_info'
|
9
9
|
require 'graphql/relay/edge'
|
10
10
|
require 'graphql/relay/base_connection'
|
@@ -10,7 +10,7 @@ module GraphQL
|
|
10
10
|
self.arguments = {}
|
11
11
|
self.type = !GraphQL::ID_TYPE
|
12
12
|
self.resolve = -> (obj, args, ctx) {
|
13
|
-
|
13
|
+
GraphQL::Relay::GlobalNodeIdentification.to_global_id(type_name, obj.public_send(property))
|
14
14
|
}
|
15
15
|
end
|
16
16
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
module GraphQL
|
3
|
+
module Relay
|
4
|
+
# This object provides helpers for working with global IDs.
|
5
|
+
# It's assumed you'll only have 1!
|
6
|
+
# GlobalIdField depends on that, since it calls class methods
|
7
|
+
# which delegate to the singleton instance.
|
8
|
+
class GlobalNodeIdentification
|
9
|
+
include GraphQL::DefinitionHelpers::DefinedByConfig
|
10
|
+
defined_by_config :object_from_id_proc, :type_from_object_proc
|
11
|
+
attr_accessor :object_from_id_proc, :type_from_object_proc
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def new(*args, &block)
|
15
|
+
if @instance.nil?
|
16
|
+
@instance = super
|
17
|
+
else
|
18
|
+
raise("Can't make a second global identifier!")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def instance
|
23
|
+
@instance
|
24
|
+
end
|
25
|
+
|
26
|
+
def from_global_id(id)
|
27
|
+
@instance.from_global_id(id)
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_global_id(type_name, id)
|
31
|
+
@instance.to_global_id(type_name, id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns `NodeInterface`, which all Relay types must implement
|
36
|
+
def interface
|
37
|
+
@interface ||= begin
|
38
|
+
ident = self
|
39
|
+
GraphQL::InterfaceType.define do
|
40
|
+
name "Node"
|
41
|
+
field :id, !types.ID
|
42
|
+
resolve_type -> (obj) {
|
43
|
+
ident.type_from_object(obj)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a field for finding objects from a global ID, which Relay needs
|
50
|
+
def field
|
51
|
+
ident = self
|
52
|
+
GraphQL::Field.define do
|
53
|
+
type(ident.interface)
|
54
|
+
argument :id, !types.ID
|
55
|
+
resolve -> (obj, args, ctx) {
|
56
|
+
ident.object_from_id(args[:id])
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Create a global ID for type-name & ID
|
62
|
+
# (This is an opaque transform)
|
63
|
+
def to_global_id(type_name, id)
|
64
|
+
Base64.strict_encode64("#{type_name}-#{id}")
|
65
|
+
end
|
66
|
+
|
67
|
+
# Get type-name & ID from global ID
|
68
|
+
# (This reverts the opaque transform)
|
69
|
+
def from_global_id(global_id)
|
70
|
+
Base64.decode64(global_id).split("-")
|
71
|
+
end
|
72
|
+
|
73
|
+
# Use the provided config to
|
74
|
+
# get a type for a given object
|
75
|
+
def type_from_object(object)
|
76
|
+
@type_from_object_proc.call(object)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Use the provided config to
|
80
|
+
# get an object from a UUID
|
81
|
+
def object_from_id(id)
|
82
|
+
@object_from_id_proc.call(id)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -38,4 +38,14 @@ class GraphQL::DefinitionHelpers::DefinedByConfig::DefinitionConfig
|
|
38
38
|
name || raise("You must define the type's name before creating a GlobalIdField")
|
39
39
|
field(field_name, field: GraphQL::Relay::GlobalIdField.new(name))
|
40
40
|
end
|
41
|
+
|
42
|
+
# Support GlobalNodeIdentification
|
43
|
+
attr_accessor :object_from_id_proc, :type_from_object_proc
|
44
|
+
def object_from_id(proc)
|
45
|
+
@object_from_id_proc = proc
|
46
|
+
end
|
47
|
+
|
48
|
+
def type_from_object(proc)
|
49
|
+
@type_from_object_proc = proc
|
50
|
+
end
|
41
51
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::Relay::GlobalNodeIdentification do
|
4
|
+
let(:node_identification) { NodeIdentification }
|
5
|
+
describe 'NodeField' do
|
6
|
+
it 'finds objects by id' do
|
7
|
+
global_id = node_identification.to_global_id("Ship", "1")
|
8
|
+
result = query(%|{node(id: "#{global_id}") { id, ... on Ship { name } }}|)
|
9
|
+
expected = {"data" => {
|
10
|
+
"node" => {
|
11
|
+
"id" => global_id,
|
12
|
+
"name" => "X-Wing"
|
13
|
+
}
|
14
|
+
}}
|
15
|
+
assert_equal(expected, result)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'to_global_id / from_global_id ' do
|
20
|
+
it 'Converts typename and ID to and from ID' do
|
21
|
+
global_id = node_identification.to_global_id("SomeType", "123")
|
22
|
+
type_name, id = node_identification.from_global_id(global_id)
|
23
|
+
assert_equal("SomeType", type_name)
|
24
|
+
assert_equal("123", id)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'making a second instance' do
|
29
|
+
it 'raises an error' do
|
30
|
+
err = assert_raises(RuntimeError) do
|
31
|
+
GraphQL::Relay::GlobalNodeIdentification.define {}
|
32
|
+
end
|
33
|
+
assert_includes(err.message, "Can't make a second")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -25,7 +25,7 @@ describe GraphQL::Relay::Mutation do
|
|
25
25
|
"clientMutationId" => "1234",
|
26
26
|
"ship" => {
|
27
27
|
"name" => "Bagel",
|
28
|
-
"id" =>
|
28
|
+
"id" => NodeIdentification.to_global_id("Ship", "9"),
|
29
29
|
},
|
30
30
|
"faction" => {"name" => STAR_WARS_DATA["Faction"]["1"].name }
|
31
31
|
}
|
@@ -1,29 +1,33 @@
|
|
1
|
-
#
|
1
|
+
# Adapted from graphql-relay-js
|
2
2
|
# https://github.com/graphql/graphql-relay-js/blob/master/src/__tests__/starWarsSchema.js
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
# This object exposes helpers for working with global IDs:
|
5
|
+
# - global id creation & "decrypting"
|
6
|
+
# - a find-object-by-global ID field
|
7
|
+
# - an interface for Relay ObjectTypes to implement
|
8
|
+
# See global_node_identification.rb for the full API.
|
9
|
+
NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define do
|
10
|
+
object_from_id -> (id) do
|
11
|
+
type_name, id = NodeIdentification.from_global_id(id)
|
7
12
|
STAR_WARS_DATA[type_name][id]
|
8
13
|
end
|
9
14
|
|
10
|
-
|
15
|
+
type_from_object -> (object) do
|
11
16
|
STAR_WARS_DATA["Faction"].values.include?(object) ? Faction : Ship
|
12
17
|
end
|
13
18
|
end
|
14
19
|
|
15
|
-
NodeInterface, NodeField = GraphQL::Relay::Node.create(NodeImplementation.new)
|
16
|
-
|
17
20
|
Ship = GraphQL::ObjectType.define do
|
18
21
|
name "Ship"
|
19
|
-
interfaces [
|
22
|
+
interfaces [NodeIdentification.interface]
|
23
|
+
# Explict alternative to `global_id_field` helper:
|
20
24
|
field :id, field: GraphQL::Relay::GlobalIdField.new("Ship")
|
21
25
|
field :name, types.String
|
22
26
|
end
|
23
27
|
|
24
28
|
BaseType = GraphQL::ObjectType.define do
|
25
29
|
name "Base"
|
26
|
-
interfaces [
|
30
|
+
interfaces [NodeIdentification.interface]
|
27
31
|
global_id_field :id
|
28
32
|
field :name, types.String
|
29
33
|
field :planet, types.String
|
@@ -41,7 +45,7 @@ end
|
|
41
45
|
|
42
46
|
Faction = GraphQL::ObjectType.define do
|
43
47
|
name "Faction"
|
44
|
-
interfaces [
|
48
|
+
interfaces [NodeIdentification.interface]
|
45
49
|
field :id, field: GraphQL::Relay::GlobalIdField.new("Faction")
|
46
50
|
field :name, types.String
|
47
51
|
connection :ships, Ship.connection_type do
|
@@ -114,7 +118,7 @@ QueryType = GraphQL::ObjectType.define do
|
|
114
118
|
resolve -> (obj, args, ctx) { STAR_WARS_DATA["Faction"]["2"]}
|
115
119
|
end
|
116
120
|
|
117
|
-
field :node, field:
|
121
|
+
field :node, field: NodeIdentification.field
|
118
122
|
end
|
119
123
|
|
120
124
|
MutationType = GraphQL::ObjectType.define do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-relay
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.8'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.8'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -192,16 +192,16 @@ files:
|
|
192
192
|
- lib/graphql/relay/base_connection.rb
|
193
193
|
- lib/graphql/relay/edge.rb
|
194
194
|
- lib/graphql/relay/global_id_field.rb
|
195
|
+
- lib/graphql/relay/global_node_identification.rb
|
195
196
|
- lib/graphql/relay/monkey_patches/definition_config.rb
|
196
197
|
- lib/graphql/relay/monkey_patches/object_type.rb
|
197
198
|
- lib/graphql/relay/mutation.rb
|
198
|
-
- lib/graphql/relay/node.rb
|
199
199
|
- lib/graphql/relay/page_info.rb
|
200
200
|
- lib/graphql/relay/relation_connection.rb
|
201
201
|
- lib/graphql/relay/version.rb
|
202
202
|
- spec/graphql/relay/array_connection_spec.rb
|
203
|
+
- spec/graphql/relay/global_node_identification_spec.rb
|
203
204
|
- spec/graphql/relay/mutation_spec.rb
|
204
|
-
- spec/graphql/relay/node_spec.rb
|
205
205
|
- spec/graphql/relay/relation_connection_spec.rb
|
206
206
|
- spec/spec_helper.rb
|
207
207
|
- spec/support/star_wars_data.rb
|
@@ -232,8 +232,8 @@ specification_version: 4
|
|
232
232
|
summary: Relay helpers for GraphQL
|
233
233
|
test_files:
|
234
234
|
- spec/graphql/relay/array_connection_spec.rb
|
235
|
+
- spec/graphql/relay/global_node_identification_spec.rb
|
235
236
|
- spec/graphql/relay/mutation_spec.rb
|
236
|
-
- spec/graphql/relay/node_spec.rb
|
237
237
|
- spec/graphql/relay/relation_connection_spec.rb
|
238
238
|
- spec/spec_helper.rb
|
239
239
|
- spec/support/star_wars_data.rb
|
data/lib/graphql/relay/node.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require 'singleton'
|
2
|
-
module GraphQL
|
3
|
-
module Relay
|
4
|
-
# To get a `NodeField` and `NodeInterface`,
|
5
|
-
# define an object that responds to:
|
6
|
-
# - object_from_id
|
7
|
-
# - type_from_object
|
8
|
-
# and pass it to `Node.create`
|
9
|
-
#
|
10
|
-
class Node
|
11
|
-
include Singleton
|
12
|
-
|
13
|
-
# Allows you to call methods on the class
|
14
|
-
def self.method_missing(method_name, *args, &block)
|
15
|
-
if instance.respond_to?(method_name)
|
16
|
-
instance.send(method_name, *args, &block)
|
17
|
-
else
|
18
|
-
super
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Return interface and field using implementation
|
23
|
-
def create(implementation)
|
24
|
-
interface = create_interface(implementation)
|
25
|
-
field = create_field(implementation, interface)
|
26
|
-
[interface, field]
|
27
|
-
end
|
28
|
-
|
29
|
-
# Create a global ID for type-name & ID
|
30
|
-
# (This is an opaque transform)
|
31
|
-
def to_global_id(type_name, id)
|
32
|
-
Base64.strict_encode64("#{type_name}-#{id}")
|
33
|
-
end
|
34
|
-
|
35
|
-
# Get type-name & ID from global ID
|
36
|
-
# (This reverts the opaque transform)
|
37
|
-
def from_global_id(global_id)
|
38
|
-
Base64.decode64(global_id).split("-")
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def create_interface(implementation)
|
44
|
-
GraphQL::InterfaceType.define do
|
45
|
-
name "Node"
|
46
|
-
field :id, !types.ID
|
47
|
-
resolve_type -> (obj) {
|
48
|
-
implementation.type_from_object(obj)
|
49
|
-
}
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def create_field(implementation, interface)
|
54
|
-
GraphQL::Field.define do
|
55
|
-
type(interface)
|
56
|
-
argument :id, !types.ID
|
57
|
-
resolve -> (obj, args, ctx) {
|
58
|
-
implementation.object_from_id(args[:id])
|
59
|
-
}
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe GraphQL::Relay::Node do
|
4
|
-
describe 'NodeField' do
|
5
|
-
it 'finds objects by id' do
|
6
|
-
global_id = GraphQL::Relay::Node.to_global_id("Ship", "1")
|
7
|
-
result = query(%|{node(id: "#{global_id}") { id, ... on Ship { name } }}|)
|
8
|
-
expected = {"data" => {
|
9
|
-
"node" => {
|
10
|
-
"id" => global_id,
|
11
|
-
"name" => "X-Wing"
|
12
|
-
}
|
13
|
-
}}
|
14
|
-
assert_equal(expected, result)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
describe 'to_global_id / from_global_id ' do
|
19
|
-
it 'Converts typename and ID to and from ID' do
|
20
|
-
global_id = GraphQL::Relay::Node.to_global_id("SomeType", "123")
|
21
|
-
type_name, id = GraphQL::Relay::Node.from_global_id(global_id)
|
22
|
-
assert_equal("SomeType", type_name)
|
23
|
-
assert_equal("123", id)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|