graphql-relay 0.7.1 → 0.8.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 +31 -0
- data/lib/graphql/relay.rb +7 -1
- data/lib/graphql/relay/connection_field.rb +0 -3
- data/lib/graphql/relay/define.rb +20 -0
- data/lib/graphql/relay/global_node_identification.rb +42 -15
- data/lib/graphql/relay/mutation.rb +17 -3
- data/lib/graphql/relay/version.rb +1 -1
- data/spec/graphql/relay/global_node_identification_spec.rb +35 -0
- data/spec/support/star_wars_data.rb +2 -1
- data/spec/support/star_wars_schema.rb +11 -1
- metadata +7 -7
- data/lib/graphql/relay/monkey_patches/definition_config.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: e48e815c037e952c8d1e2a9f54379b5bfbce7ae8
|
4
|
+
data.tar.gz: 629bdf27a10511bdee0664304443b33f999581c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b4a352c2405b890bc4a9b727f80f65f7f3e9c332162848b7fb41560147b4cf57597da9cc1739e47d15441f59528aa20ddba763201c5b58ffd50e1a1cedfaf71
|
7
|
+
data.tar.gz: d630722d99ee1c605bf55df1554d23912ac3f3bb545ef89dd9a24bbd1bf267af25a6838dbf5e22a8d606d764422271aae2a931c3f7beec7050e78ae63d687b84
|
data/README.md
CHANGED
@@ -87,6 +87,32 @@ QueryType = GraphQL::ObjectType.define do
|
|
87
87
|
end
|
88
88
|
```
|
89
89
|
|
90
|
+
#### Custom UUID Generation
|
91
|
+
|
92
|
+
By default, `graphql-relay` uses `Base64.strict_encode64` to generate opaque global ids. You can modify this behavior by providing two configurations. They work together to encode and decode ids:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define do
|
96
|
+
# ...
|
97
|
+
|
98
|
+
# Return a string for re-fetching this object
|
99
|
+
to_global_id -> (type_name, id) {
|
100
|
+
"#{type_name.downcase}/#{id}"
|
101
|
+
}
|
102
|
+
|
103
|
+
# Based on the incoming string, extract the type_name and id
|
104
|
+
from_global_id -> (global_id) {
|
105
|
+
id_parts = global_id.split("/")
|
106
|
+
type_name = id_parts[0]
|
107
|
+
id = id_parts[1]
|
108
|
+
# Return *both*:
|
109
|
+
type_name, id
|
110
|
+
}
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
`graphql-relay` will use those procs for interacting with global ids.
|
115
|
+
|
90
116
|
### Connections
|
91
117
|
|
92
118
|
Connections provide pagination and `pageInfo` for `Array`s or `ActiveRecord::Relation`s.
|
@@ -318,6 +344,11 @@ https://medium.com/@gauravtiwari/graphql-and-relay-on-rails-first-relay-powered-
|
|
318
344
|
|
319
345
|
## Todo
|
320
346
|
|
347
|
+
- Allow custom defined ID scheme
|
348
|
+
- Allow custom edge fields (per connection type)
|
349
|
+
- `GlobalNodeIdentification.to_global_id` should receive the type name and _object_, not `id`. (Or, maintain the "`type_name, id` in, `type_name, id` out" pattern?)
|
350
|
+
- Make GlobalId a property of the schema, not a global
|
351
|
+
|
321
352
|
## More Resources
|
322
353
|
|
323
354
|
- [GraphQL Slack](http://graphql-slack.herokuapp.com), come join us in the `#ruby` channel!
|
data/lib/graphql/relay.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'base64'
|
2
2
|
require 'graphql'
|
3
3
|
# MONKEY PATCHES 😬
|
4
|
-
require 'graphql/relay/monkey_patches/definition_config'
|
5
4
|
require 'graphql/relay/monkey_patches/base_type'
|
6
5
|
|
6
|
+
require 'graphql/relay/define'
|
7
7
|
require 'graphql/relay/global_node_identification'
|
8
8
|
require 'graphql/relay/page_info'
|
9
9
|
require 'graphql/relay/edge'
|
@@ -13,3 +13,9 @@ require 'graphql/relay/relation_connection'
|
|
13
13
|
require 'graphql/relay/global_id_field'
|
14
14
|
require 'graphql/relay/mutation'
|
15
15
|
require 'graphql/relay/connection_field'
|
16
|
+
|
17
|
+
# Accept Relay-specific definitions
|
18
|
+
GraphQL::BaseType.accepts_definitions(
|
19
|
+
connection: GraphQL::Relay::Define::AssignConnection,
|
20
|
+
global_id_field: GraphQL::Relay::Define::AssignGlobalIdField,
|
21
|
+
)
|
@@ -45,9 +45,6 @@ module GraphQL
|
|
45
45
|
def self.get_connection_resolve(field_name, underlying_resolve, max_page_size: nil)
|
46
46
|
-> (obj, args, ctx) {
|
47
47
|
items = underlying_resolve.call(obj, args, ctx)
|
48
|
-
if items == GraphQL::Query::DEFAULT_RESOLVE
|
49
|
-
items = obj.public_send(field_name)
|
50
|
-
end
|
51
48
|
connection_class = GraphQL::Relay::BaseConnection.connection_for_items(items)
|
52
49
|
connection_class.new(items, args, max_page_size: max_page_size)
|
53
50
|
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Relay
|
3
|
+
module Define
|
4
|
+
module AssignConnection
|
5
|
+
def self.call(type_defn, name, type = nil, desc = nil, property: nil, max_page_size: nil, &block)
|
6
|
+
underlying_field = GraphQL::Define::AssignObjectField.call(type_defn, name, type, desc, property: property, &block)
|
7
|
+
connection_field = GraphQL::Relay::ConnectionField.create(underlying_field, max_page_size: max_page_size)
|
8
|
+
type_defn.fields[name.to_s] = connection_field
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module AssignGlobalIdField
|
13
|
+
def self.call(type_defn, field_name)
|
14
|
+
type_defn.name || raise("You must define the type's name before creating a GlobalIdField")
|
15
|
+
GraphQL::Define::AssignObjectField.call(type_defn, field_name, field: GraphQL::Relay::GlobalIdField.new(type_defn.name))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -3,20 +3,16 @@ module GraphQL
|
|
3
3
|
module Relay
|
4
4
|
# This object provides helpers for working with global IDs.
|
5
5
|
# It's assumed you'll only have 1!
|
6
|
+
#
|
6
7
|
# GlobalIdField depends on that, since it calls class methods
|
7
8
|
# which delegate to the singleton instance.
|
9
|
+
#
|
8
10
|
class GlobalNodeIdentification
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
self.id_separator = "-"
|
13
|
-
|
14
|
-
include GraphQL::DefinitionHelpers::DefinedByConfig
|
15
|
-
defined_by_config :object_from_id_proc, :type_from_object_proc
|
16
|
-
attr_accessor :object_from_id_proc, :type_from_object_proc
|
11
|
+
include GraphQL::Define::InstanceDefinable
|
12
|
+
accepts_definitions(:object_from_id, :type_from_object, :to_global_id, :from_global_id)
|
17
13
|
|
18
14
|
class << self
|
19
|
-
attr_accessor :instance
|
15
|
+
attr_accessor :instance, :id_separator
|
20
16
|
def new(*args, &block)
|
21
17
|
@instance = super
|
22
18
|
end
|
@@ -30,6 +26,13 @@ module GraphQL
|
|
30
26
|
end
|
31
27
|
end
|
32
28
|
|
29
|
+
self.id_separator = "-"
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@to_global_id_proc = DEFAULT_TO_GLOBAL_ID
|
33
|
+
@from_global_id_proc = DEFAULT_FROM_GLOBAL_ID
|
34
|
+
end
|
35
|
+
|
33
36
|
# Returns `NodeInterface`, which all Relay types must implement
|
34
37
|
def interface
|
35
38
|
@interface ||= begin
|
@@ -56,20 +59,36 @@ module GraphQL
|
|
56
59
|
end
|
57
60
|
end
|
58
61
|
|
62
|
+
DEFAULT_TO_GLOBAL_ID = -> (type_name, id) {
|
63
|
+
id_str = id.to_s
|
64
|
+
if type_name.include?(self.id_separator) || id_str.include?(self.id_separator)
|
65
|
+
raise "to_global_id(#{type_name}, #{id}) contains reserved characters `#{self.id_separator}`"
|
66
|
+
end
|
67
|
+
Base64.strict_encode64([type_name, id_str].join(self.id_separator))
|
68
|
+
}
|
69
|
+
|
70
|
+
DEFAULT_FROM_GLOBAL_ID = -> (global_id) {
|
71
|
+
Base64.decode64(global_id).split(self.id_separator)
|
72
|
+
}
|
73
|
+
|
59
74
|
# Create a global ID for type-name & ID
|
60
75
|
# (This is an opaque transform)
|
61
76
|
def to_global_id(type_name, id)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
77
|
+
@to_global_id_proc.call(type_name, id)
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_global_id=(proc)
|
81
|
+
@to_global_id_proc = proc
|
67
82
|
end
|
68
83
|
|
69
84
|
# Get type-name & ID from global ID
|
70
85
|
# (This reverts the opaque transform)
|
71
86
|
def from_global_id(global_id)
|
72
|
-
|
87
|
+
@from_global_id_proc.call(global_id)
|
88
|
+
end
|
89
|
+
|
90
|
+
def from_global_id=(proc)
|
91
|
+
@from_global_id_proc = proc
|
73
92
|
end
|
74
93
|
|
75
94
|
# Use the provided config to
|
@@ -84,11 +103,19 @@ module GraphQL
|
|
84
103
|
end
|
85
104
|
end
|
86
105
|
|
106
|
+
def type_from_object=(proc)
|
107
|
+
@type_from_object_proc = proc
|
108
|
+
end
|
109
|
+
|
87
110
|
# Use the provided config to
|
88
111
|
# get an object from a UUID
|
89
112
|
def object_from_id(id, ctx)
|
90
113
|
@object_from_id_proc.call(id, ctx)
|
91
114
|
end
|
115
|
+
|
116
|
+
def object_from_id=(proc)
|
117
|
+
@object_from_id_proc = proc
|
118
|
+
end
|
92
119
|
end
|
93
120
|
end
|
94
121
|
end
|
@@ -48,9 +48,23 @@ module GraphQL
|
|
48
48
|
# # }}
|
49
49
|
#
|
50
50
|
class Mutation
|
51
|
-
include GraphQL::
|
52
|
-
|
53
|
-
|
51
|
+
include GraphQL::Define::InstanceDefinable
|
52
|
+
accepts_definitions(
|
53
|
+
:name, :description, :resolve,
|
54
|
+
input_field: GraphQL::Define::AssignArgument,
|
55
|
+
return_field: GraphQL::Define::AssignObjectField,
|
56
|
+
)
|
57
|
+
attr_accessor :name, :definition
|
58
|
+
attr_reader :fields, :arguments
|
59
|
+
|
60
|
+
# For backwards compat, but do we need this separate API?
|
61
|
+
alias :return_fields :fields
|
62
|
+
alias :input_fields :arguments
|
63
|
+
|
64
|
+
def initialize
|
65
|
+
@fields = {}
|
66
|
+
@arguments = {}
|
67
|
+
end
|
54
68
|
|
55
69
|
def resolve=(proc)
|
56
70
|
@resolve_proc = proc
|
@@ -75,6 +75,41 @@ describe GraphQL::Relay::GlobalNodeIdentification do
|
|
75
75
|
}
|
76
76
|
assert_includes err.message, "to_global_id(Best-Thing, 234) contains reserved characters `-`"
|
77
77
|
end
|
78
|
+
|
79
|
+
describe "custom definitions" do
|
80
|
+
before do
|
81
|
+
@previous_global_node_id = GraphQL::Relay::GlobalNodeIdentification.instance
|
82
|
+
@new_node_id = GraphQL::Relay::GlobalNodeIdentification.define do
|
83
|
+
to_global_id -> (type_name, id) {
|
84
|
+
"#{type_name}/#{id}"
|
85
|
+
}
|
86
|
+
|
87
|
+
from_global_id -> (global_id) {
|
88
|
+
global_id.split("/")
|
89
|
+
}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
after do
|
94
|
+
GraphQL::Relay::GlobalNodeIdentification.instance = @previous_global_node_id
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "generating IDs" do
|
98
|
+
it "Applies custom-defined ID generation" do
|
99
|
+
result = query(%| { largestBase { id } }|)
|
100
|
+
generated_id = result["data"]["largestBase"]["id"]
|
101
|
+
assert_equal "Base/13", generated_id
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "fetching by ID" do
|
106
|
+
it "Deconstructs the ID by the custom proc" do
|
107
|
+
result = query(%| { node(id: "Base/11") { ... on Base { name } } }|)
|
108
|
+
base_name = result["data"]["node"]["name"]
|
109
|
+
assert_equal "Yavin", base_name
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
78
113
|
end
|
79
114
|
|
80
115
|
describe "type_from_object" do
|
@@ -8,13 +8,19 @@
|
|
8
8
|
# See global_node_identification.rb for the full API.
|
9
9
|
NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define do
|
10
10
|
object_from_id -> (id, ctx) do
|
11
|
-
|
11
|
+
# In a normal app, you could call `from_global_id` on your defined object
|
12
|
+
# type_name, id = NodeIdentification.from_global_id(id)
|
13
|
+
#
|
14
|
+
# But to support our testing setup, reach for the global:
|
15
|
+
type_name, id = GraphQL::Relay::GlobalNodeIdentification.from_global_id(id)
|
12
16
|
STAR_WARS_DATA[type_name][id]
|
13
17
|
end
|
14
18
|
|
15
19
|
type_from_object -> (object) do
|
16
20
|
if object == :test_error
|
17
21
|
:not_a_type
|
22
|
+
elsif object.is_a?(Base)
|
23
|
+
BaseType
|
18
24
|
else
|
19
25
|
STAR_WARS_DATA["Faction"].values.include?(object) ? Faction : Ship
|
20
26
|
end
|
@@ -133,6 +139,10 @@ QueryType = GraphQL::ObjectType.define do
|
|
133
139
|
resolve -> (obj, args, ctx) { STAR_WARS_DATA["Faction"]["2"]}
|
134
140
|
end
|
135
141
|
|
142
|
+
field :largestBase, BaseType do
|
143
|
+
resolve -> (obj, args, ctx) { Base.find(13) }
|
144
|
+
end
|
145
|
+
|
136
146
|
field :node, field: NodeIdentification.field
|
137
147
|
end
|
138
148
|
|
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.8.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: 2016-
|
11
|
+
date: 2016-03-20 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.12'
|
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.12'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -170,14 +170,14 @@ dependencies:
|
|
170
170
|
requirements:
|
171
171
|
- - "~>"
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version: '
|
173
|
+
version: '11.0'
|
174
174
|
type: :development
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version: '
|
180
|
+
version: '11.0'
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
182
|
name: sqlite3
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -205,11 +205,11 @@ files:
|
|
205
205
|
- lib/graphql/relay/array_connection.rb
|
206
206
|
- lib/graphql/relay/base_connection.rb
|
207
207
|
- lib/graphql/relay/connection_field.rb
|
208
|
+
- lib/graphql/relay/define.rb
|
208
209
|
- lib/graphql/relay/edge.rb
|
209
210
|
- lib/graphql/relay/global_id_field.rb
|
210
211
|
- lib/graphql/relay/global_node_identification.rb
|
211
212
|
- lib/graphql/relay/monkey_patches/base_type.rb
|
212
|
-
- lib/graphql/relay/monkey_patches/definition_config.rb
|
213
213
|
- lib/graphql/relay/mutation.rb
|
214
214
|
- lib/graphql/relay/page_info.rb
|
215
215
|
- lib/graphql/relay/relation_connection.rb
|
@@ -1,26 +0,0 @@
|
|
1
|
-
class GraphQL::DefinitionHelpers::DefinedByConfig::DefinitionConfig
|
2
|
-
# Wraps a field definition with a ConnectionField
|
3
|
-
def connection(name, type = nil, desc = nil, property: nil, max_page_size: nil, &block)
|
4
|
-
underlying_field = field(name, type, desc, property: property, &block)
|
5
|
-
connection_field = GraphQL::Relay::ConnectionField.create(underlying_field, max_page_size: max_page_size)
|
6
|
-
fields[name.to_s] = connection_field
|
7
|
-
end
|
8
|
-
|
9
|
-
alias :return_field :field
|
10
|
-
alias :return_fields :fields
|
11
|
-
|
12
|
-
def global_id_field(field_name)
|
13
|
-
name || raise("You must define the type's name before creating a GlobalIdField")
|
14
|
-
field(field_name, field: GraphQL::Relay::GlobalIdField.new(name))
|
15
|
-
end
|
16
|
-
|
17
|
-
# Support GlobalNodeIdentification
|
18
|
-
attr_accessor :object_from_id_proc, :type_from_object_proc
|
19
|
-
def object_from_id(proc)
|
20
|
-
@object_from_id_proc = proc
|
21
|
-
end
|
22
|
-
|
23
|
-
def type_from_object(proc)
|
24
|
-
@type_from_object_proc = proc
|
25
|
-
end
|
26
|
-
end
|