graphql-relay 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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