graphql-relay 0.2.0 → 0.3.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 +7 -11
- data/lib/graphql/definition_helpers/defined_by_config/definition_config.rb +27 -2
- data/lib/graphql/object_type.rb +7 -0
- data/lib/graphql/relay.rb +3 -2
- data/lib/graphql/relay/array_connection.rb +1 -0
- data/lib/graphql/relay/base_connection.rb +23 -3
- data/lib/graphql/relay/relation_connection.rb +3 -0
- data/lib/graphql/relay/version.rb +1 -1
- data/spec/graphql/relay/array_connection_spec.rb +18 -3
- data/spec/graphql/relay/relation_connection_spec.rb +10 -6
- data/spec/support/star_wars_schema.rb +10 -13
- metadata +3 -4
- data/lib/graphql/relay/connection_field.rb +0 -23
- data/lib/graphql/relay/connection_type.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a86533b3e1bc7030fe0016ee41e9492e25cc597
|
4
|
+
data.tar.gz: d80d3d7006d8f37a7f208b49c2fd3dc5f78081c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c0f624fba3f5e81724bdf73459cf72747f8f6f8ca9a569e15b99cd4391a72d25d098ec6f1c3932ce794d4d0db33b1ea2ec0e2292d11e7b79070989de2a0da09
|
7
|
+
data.tar.gz: 04e49ec547a0930ac9b26a54b5a7107dd5a61fe1f758449915c58b4ea7f4c2bbc4917a49e77fff80f0497a478b83624e1b97461dffc21694fec20d8fa6f55e03
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# graphql-relay
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/graphql-relay)
|
4
|
+
[](https://travis-ci.org/rmosolgo/graphql-relay-ruby)
|
4
5
|
[](https://codeclimate.com/github/rmosolgo/graphql-relay-ruby)
|
5
6
|
[](https://codeclimate.com/github/rmosolgo/graphql-relay-ruby/coverage)
|
6
7
|
|
@@ -24,24 +25,19 @@ Global Ids provide refetching & global identification for Relay.
|
|
24
25
|
|
25
26
|
You should implement an object that responds to `#object_from_id(global_id)` & `#type_from_object(object)`, then pass it to `GraphQL::Relay::Node.create(implementation)`. [Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/120b750cf86f1eb5c9997b588f022b2ef3a0012c/spec/support/star_wars_schema.rb#L4-L15)
|
26
27
|
|
27
|
-
Then, you can add global id fields to your types with `
|
28
|
+
Then, you can add global id fields to your types with `global_id_field` definition helper.
|
29
|
+
[Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L29)
|
28
30
|
|
29
31
|
### Connections
|
30
32
|
|
31
|
-
Connections will provide arguments, pagination and `pageInfo` for `Array`s or `ActiveRecord::Relation`s.
|
32
|
-
|
33
|
-
To create a connection, you should:
|
34
|
-
- create a connection type; then
|
35
|
-
- implement the field to return objects
|
36
|
-
|
37
|
-
To create a connection type, use either `GraphQL::Relay::ArrayConnection.create_type(base_type)` or for an `ActiveRecord::Relation`, use `GraphQL::Relay::RelationConnection.create_type(base_type)`. [Array example](https://github.com/rmosolgo/graphql-relay-ruby/blob/120b750cf86f1eb5c9997b588f022b2ef3a0012c/spec/support/star_wars_schema.rb#L27), [Relation example](https://github.com/rmosolgo/graphql-relay-ruby/blob/120b750cf86f1eb5c9997b588f022b2ef3a0012c/spec/support/star_wars_schema.rb#L39)
|
33
|
+
Connections will provide arguments, pagination and `pageInfo` for `Array`s or `ActiveRecord::Relation`s. You can use the `connection` definition helper.
|
38
34
|
|
39
35
|
Then, implement the field. It's different than a normal field:
|
40
36
|
- use the `connection` helper to define it, instead of `field`
|
41
|
-
-
|
37
|
+
- Call `#connection_type` on an `ObjectType` for the field's return type (eg, `ShipType.connection_type`)
|
42
38
|
- implement `resolve` to return an Array or an ActiveRecord::Relation, depending on the connection type.
|
43
39
|
|
44
|
-
[Example](https://github.com/rmosolgo/graphql-relay-ruby/blob/
|
40
|
+
[Example 1](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L39-L51), [Example 2](https://github.com/rmosolgo/graphql-relay-ruby/blob/master/spec/support/star_wars_schema.rb#L52-L58)
|
45
41
|
|
46
42
|
### Mutations
|
47
43
|
|
@@ -69,7 +65,7 @@ Examples:
|
|
69
65
|
|
70
66
|
## Todo
|
71
67
|
|
72
|
-
- [ ]
|
68
|
+
- [ ] Fix `Node.create` -- make it return one object which exposes useful info
|
73
69
|
|
74
70
|
## More Resources
|
75
71
|
|
@@ -3,14 +3,39 @@ module GraphQL
|
|
3
3
|
module DefinedByConfig
|
4
4
|
class DefinitionConfig
|
5
5
|
# Wraps a field definition with a ConnectionField
|
6
|
+
# - applies default fields
|
7
|
+
# - wraps the resolve proc to make a connection
|
8
|
+
#
|
6
9
|
def connection(name, type = nil, desc = nil, property: nil, &block)
|
7
|
-
|
8
|
-
|
10
|
+
# Wrap the given block to define the default args
|
11
|
+
definition_block = -> (config) {
|
12
|
+
argument :first, types.Int
|
13
|
+
argument :after, types.String
|
14
|
+
argument :last, types.Int
|
15
|
+
argument :before, types.String
|
16
|
+
argument :order, types.String
|
17
|
+
self.instance_eval(&block)
|
18
|
+
}
|
19
|
+
connection_field = field(name, type, desc, property: property, &definition_block)
|
20
|
+
# Wrap the defined resolve proc
|
21
|
+
# TODO: make a public API on GraphQL::Field to expose this proc
|
22
|
+
original_resolve = connection_field.instance_variable_get(:@resolve_proc)
|
23
|
+
connection_resolve = -> (obj, args, ctx) {
|
24
|
+
items = original_resolve.call(obj, args, ctx)
|
25
|
+
connection_class = GraphQL::Relay::BaseConnection.connection_for_items(items)
|
26
|
+
connection_class.new(items, args)
|
27
|
+
}
|
28
|
+
connection_field.resolve = connection_resolve
|
9
29
|
fields[name.to_s] = connection_field
|
10
30
|
end
|
11
31
|
|
12
32
|
alias :return_field :field
|
13
33
|
alias :return_fields :fields
|
34
|
+
|
35
|
+
def global_id_field(field_name)
|
36
|
+
name || raise("You must define the type's name before creating a GlobalIdField")
|
37
|
+
field(name, field: GraphQL::Relay::GlobalIdField.new(name))
|
38
|
+
end
|
14
39
|
end
|
15
40
|
end
|
16
41
|
end
|
data/lib/graphql/relay.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'base64'
|
2
|
+
# MONKEY PATCHES 😬
|
2
3
|
require 'graphql/definition_helpers/defined_by_config/definition_config'
|
4
|
+
require_relative './object_type.rb'
|
5
|
+
|
3
6
|
require 'graphql/relay/node'
|
4
7
|
require 'graphql/relay/page_info'
|
5
8
|
require 'graphql/relay/edge'
|
6
|
-
require 'graphql/relay/connection_type'
|
7
9
|
require 'graphql/relay/base_connection'
|
8
10
|
require 'graphql/relay/array_connection'
|
9
11
|
require 'graphql/relay/relation_connection'
|
10
12
|
require 'graphql/relay/global_id_field'
|
11
|
-
require 'graphql/relay/connection_field'
|
12
13
|
require 'graphql/relay/mutation'
|
@@ -13,21 +13,41 @@ module GraphQL
|
|
13
13
|
# Just to encode data in the cursor, use something that won't conflict
|
14
14
|
CURSOR_SEPARATOR = "---"
|
15
15
|
|
16
|
+
# Map of collection classes -> connection_classes
|
17
|
+
# eg Array -> ArrayConnection
|
18
|
+
CONNECTION_IMPLEMENTATIONS = {}
|
19
|
+
|
16
20
|
# Create a connection which exposes edges of this type
|
17
21
|
def self.create_type(wrapped_type)
|
18
22
|
edge_type = Edge.create_type(wrapped_type)
|
19
23
|
|
20
|
-
connection_type =
|
24
|
+
connection_type = ObjectType.define do
|
21
25
|
name("#{wrapped_type.name}Connection")
|
22
26
|
field :edges, types[edge_type]
|
23
27
|
field :pageInfo, PageInfo, property: :page_info
|
24
28
|
end
|
25
29
|
|
26
|
-
connection_type.connection_class = self
|
27
|
-
|
28
30
|
connection_type
|
29
31
|
end
|
30
32
|
|
33
|
+
# @return [subclass of BaseConnection] a connection wrapping `items`
|
34
|
+
def self.connection_for_items(items)
|
35
|
+
implementation = CONNECTION_IMPLEMENTATIONS.find do |items_class, connection_class|
|
36
|
+
items.is_a?(items_class)
|
37
|
+
end
|
38
|
+
if implementation.nil?
|
39
|
+
raise("No connection implementation to wrap #{items.class}")
|
40
|
+
else
|
41
|
+
implementation[1]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Add `connection_class` as the connection wrapper for `items_class`
|
46
|
+
# eg, `RelationConnection` is the implementation for `AR::Relation`
|
47
|
+
def self.register_connection_implementation(items_class, connection_class)
|
48
|
+
CONNECTION_IMPLEMENTATIONS[items_class] = connection_class
|
49
|
+
end
|
50
|
+
|
31
51
|
attr_reader :object, :arguments
|
32
52
|
|
33
53
|
def initialize(object, arguments)
|
@@ -5,11 +5,15 @@ describe GraphQL::Relay::ArrayConnection do
|
|
5
5
|
ships = result["data"]["rebels"]["ships"]["edges"]
|
6
6
|
names = ships.map { |e| e["node"]["name"] }
|
7
7
|
end
|
8
|
+
|
9
|
+
def get_last_cursor(result)
|
10
|
+
result["data"]["rebels"]["ships"]["edges"].last["cursor"]
|
11
|
+
end
|
8
12
|
describe "results" do
|
9
13
|
let(:query_string) {%|
|
10
|
-
query getShips($first: Int, $after: String, $last: Int, $before: String, $order: String){
|
14
|
+
query getShips($first: Int, $after: String, $last: Int, $before: String, $order: String, $nameIncludes: String){
|
11
15
|
rebels {
|
12
|
-
ships(first: $first, after: $after, last: $last, before: $before, order: $order) {
|
16
|
+
ships(first: $first, after: $after, last: $last, before: $before, order: $order, nameIncludes: $nameIncludes) {
|
13
17
|
edges {
|
14
18
|
cursor
|
15
19
|
node {
|
@@ -47,7 +51,7 @@ describe GraphQL::Relay::ArrayConnection do
|
|
47
51
|
assert_equal(["X-Wing", "Y-Wing", "A-Wing"], get_names(result))
|
48
52
|
|
49
53
|
# After the last result, find the next 2:
|
50
|
-
last_cursor = result
|
54
|
+
last_cursor = get_last_cursor(result)
|
51
55
|
|
52
56
|
result = query(query_string, "after" => last_cursor, "first" => 2)
|
53
57
|
assert_equal(["Millenium Falcon", "Home One"], get_names(result))
|
@@ -71,5 +75,16 @@ describe GraphQL::Relay::ArrayConnection do
|
|
71
75
|
result = query(query_string, "first" => 2, "order" => "-name")
|
72
76
|
assert_equal(["Y-Wing", "X-Wing"], get_names(result))
|
73
77
|
end
|
78
|
+
|
79
|
+
it 'applies custom arguments' do
|
80
|
+
result = query(query_string, "nameIncludes" => "Wing", "first" => 2)
|
81
|
+
names = get_names(result)
|
82
|
+
assert_equal(2, names.length)
|
83
|
+
|
84
|
+
after = get_last_cursor(result)
|
85
|
+
result = query(query_string, "nameIncludes" => "Wing", "after" => after)
|
86
|
+
names = get_names(result)
|
87
|
+
assert_equal(1, names.length)
|
88
|
+
end
|
74
89
|
end
|
75
90
|
end
|
@@ -15,18 +15,22 @@ describe GraphQL::Relay::RelationConnection do
|
|
15
15
|
query getShips($first: Int, $after: String, $last: Int, $before: String, $order: String){
|
16
16
|
empire {
|
17
17
|
bases(first: $first, after: $after, last: $last, before: $before, order: $order) {
|
18
|
-
|
19
|
-
cursor
|
20
|
-
node {
|
21
|
-
name
|
22
|
-
}
|
23
|
-
}
|
18
|
+
... basesConnection
|
24
19
|
pageInfo {
|
25
20
|
hasNextPage
|
26
21
|
}
|
27
22
|
}
|
28
23
|
}
|
29
24
|
}
|
25
|
+
|
26
|
+
fragment basesConnection on BaseConnection {
|
27
|
+
edges {
|
28
|
+
cursor
|
29
|
+
node {
|
30
|
+
name
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
30
34
|
|}
|
31
35
|
it 'limits the result' do
|
32
36
|
result = query(query_string, "first" => 2)
|
@@ -23,36 +23,33 @@ Ship = GraphQL::ObjectType.define do
|
|
23
23
|
field :name, types.String
|
24
24
|
end
|
25
25
|
|
26
|
-
# Define a connection which will wrap an array:
|
27
|
-
ShipConnection = GraphQL::Relay::ArrayConnection.create_type(Ship)
|
28
|
-
|
29
|
-
|
30
26
|
BaseType = GraphQL::ObjectType.define do
|
31
27
|
name "Base"
|
32
28
|
interfaces [NodeInterface]
|
33
|
-
|
29
|
+
global_id_field :id
|
34
30
|
field :name, types.String
|
35
31
|
field :planet, types.String
|
36
32
|
end
|
37
33
|
|
38
|
-
# Define a connection which will wrap an ActiveRecord::Relation:
|
39
|
-
BaseConnection = GraphQL::Relay::RelationConnection.create_type(BaseType)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
34
|
Faction = GraphQL::ObjectType.define do
|
44
35
|
name "Faction"
|
45
36
|
interfaces [NodeInterface]
|
46
37
|
field :id, field: GraphQL::Relay::GlobalIdField.new("Faction")
|
47
38
|
field :name, types.String
|
48
|
-
connection :ships,
|
39
|
+
connection :ships, Ship.connection_type do
|
49
40
|
# Resolve field should return an Array, the Connection
|
50
41
|
# will do the rest!
|
51
42
|
resolve -> (obj, args, ctx) {
|
52
|
-
obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
|
43
|
+
all_ships = obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
|
44
|
+
if args[:nameIncludes]
|
45
|
+
all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
|
46
|
+
end
|
47
|
+
all_ships
|
53
48
|
}
|
49
|
+
# You can define arguments here and use them in the connection
|
50
|
+
argument :nameIncludes, types.String
|
54
51
|
end
|
55
|
-
connection :bases,
|
52
|
+
connection :bases, BaseType.connection_type do
|
56
53
|
# Resolve field should return an Array, the Connection
|
57
54
|
# will do the rest!
|
58
55
|
resolve -> (obj, args, ctx) {
|
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.3.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-08-
|
11
|
+
date: 2015-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -188,11 +188,10 @@ extra_rdoc_files: []
|
|
188
188
|
files:
|
189
189
|
- README.md
|
190
190
|
- lib/graphql/definition_helpers/defined_by_config/definition_config.rb
|
191
|
+
- lib/graphql/object_type.rb
|
191
192
|
- lib/graphql/relay.rb
|
192
193
|
- lib/graphql/relay/array_connection.rb
|
193
194
|
- lib/graphql/relay/base_connection.rb
|
194
|
-
- lib/graphql/relay/connection_field.rb
|
195
|
-
- lib/graphql/relay/connection_type.rb
|
196
195
|
- lib/graphql/relay/edge.rb
|
197
196
|
- lib/graphql/relay/global_id_field.rb
|
198
197
|
- lib/graphql/relay/mutation.rb
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module GraphQL
|
2
|
-
module Relay
|
3
|
-
# The best way to make these is with the connection helper,
|
4
|
-
# @see {GraphQL::DefinitionHelpers::DefinedByConfig::DefinitionConfig}
|
5
|
-
class ConnectionField
|
6
|
-
def self.create(underlying_field)
|
7
|
-
field = GraphQL::Field.define do
|
8
|
-
argument :first, types.Int
|
9
|
-
argument :after, types.String
|
10
|
-
argument :last, types.Int
|
11
|
-
argument :before, types.String
|
12
|
-
argument :order, types.String
|
13
|
-
|
14
|
-
type(-> { underlying_field.type })
|
15
|
-
resolve -> (obj, args, ctx) {
|
16
|
-
items = underlying_field.resolve(obj, args, ctx)
|
17
|
-
underlying_field.type.connection_class.new(items, args)
|
18
|
-
}
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,10 +0,0 @@
|
|
1
|
-
module GraphQL
|
2
|
-
module Relay
|
3
|
-
# An ObjectType which also stores its {#connection_class}
|
4
|
-
class ConnectionType < GraphQL::ObjectType
|
5
|
-
defined_by_config :name, :fields, :interfaces
|
6
|
-
# @return [Class] A subclass of {BaseConnection}
|
7
|
-
attr_accessor :connection_class
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|