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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4cb7e4a30922d4db833bf738f058336298533802
4
- data.tar.gz: 286be9492a0ada16bdeb48c37b70cc4f60c1ecd8
3
+ metadata.gz: e48e815c037e952c8d1e2a9f54379b5bfbce7ae8
4
+ data.tar.gz: 629bdf27a10511bdee0664304443b33f999581c9
5
5
  SHA512:
6
- metadata.gz: 56f104a6b5bd5505fa5dee4fe4baee4d981c14284bdfe2afb7357fdb72384fe213b02899b68129806aff1a5a1d5044b9f57fd23672cbb9faf26f8caf2aca2ce4
7
- data.tar.gz: 1be2fe49797910d14ff5dffa5a665a937c63c12a69136c6a9af3c94ccb42724e5c3d89a46740259a745ffc9302839612ba66a92de24d44123245534f1c205203
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!
@@ -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
- class << self
10
- attr_accessor :id_separator
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
- id_str = id.to_s
63
- if type_name.include?(self.class.id_separator) || id_str.include?(self.class.id_separator)
64
- raise "to_global_id(#{type_name}, #{id}) contains reserved characters `#{self.class.id_separator}`"
65
- end
66
- Base64.strict_encode64([type_name, id_str].join(self.class.id_separator))
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
- Base64.decode64(global_id).split(self.class.id_separator)
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::DefinitionHelpers::DefinedByConfig
52
- defined_by_config :name, :description, :return_fields, :input_fields, :resolve
53
- attr_accessor :name, :description, :return_fields, :input_fields
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
@@ -1,5 +1,5 @@
1
1
  module GraphQL
2
2
  module Relay
3
- VERSION = '0.7.1'
3
+ VERSION = '0.8.0'
4
4
  end
5
5
  end
@@ -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
@@ -59,7 +59,8 @@ STAR_WARS_DATA = {
59
59
  id = (idx + 1).to_s
60
60
  memo[id] = OpenStruct.new(name: name, id: id)
61
61
  memo
62
- end
62
+ end,
63
+ "Base" => Hash.new { |h, k| h[k] = Base.find(k) }
63
64
  }
64
65
 
65
66
  def STAR_WARS_DATA.create_ship(name, faction_id)
@@ -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
- type_name, id = NodeIdentification.from_global_id(id)
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.7.1
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-02-29 00:00:00.000000000 Z
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.8'
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.8'
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: '10.4'
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: '10.4'
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