Skip to content

Commit 421e8ef

Browse files
authored
update graphql-ruby subgraph with latest schema changes (#222)
Resolves: #195
1 parent 5cbd65f commit 421e8ef

File tree

9 files changed

+131
-60
lines changed

9 files changed

+131
-60
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,6 @@ bin
8080
obj
8181

8282
# ignore router executable
83-
router
83+
router
84+
85+
.bundle

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ The following open-source GraphQL server libraries and hosted subgraphs provide
123123
<tr><th width="300">Library</th><th>Federation 1 Support</th><th>Federation 2 Support</th></tr>
124124
</thead>
125125
<tbody>
126-
<tr><td><a href="https://graphql-ruby.org/">GraphQL Ruby</a></td><td><table><tr><th>_service</th><td>🟢</td></tr><tr><th>@key (single)</th><td>🟢</td></tr><tr><th>@key (multi)</th><td>🟢</td></tr><tr><th>@key (composite)</th><td>🟢</td></tr><tr><th>repeatable @key</th><td>🟢</td></tr><tr><th>@requires</th><td>🟢</td></tr><tr><th>@provides</th><td>🟢</td></tr><tr><th>federated tracing</th><td>🟢</td></tr></table></td><td><table><tr><th>@link</th><td></td></tr><tr><th>@shareable</th><td>🔲</td></tr><tr><th>@tag</th><td>🔲</td></tr><tr><th>@override</th><td>🔲</td></tr><tr><th>@inaccessible</th><td>🔲</td></tr></table></td></tr>
126+
<tr><td><a href="https://graphql-ruby.org/">GraphQL Ruby</a></td><td><table><tr><th>_service</th><td>🟢</td></tr><tr><th>@key (single)</th><td>🟢</td></tr><tr><th>@key (multi)</th><td>🟢</td></tr><tr><th>@key (composite)</th><td>🟢</td></tr><tr><th>repeatable @key</th><td>🟢</td></tr><tr><th>@requires</th><td>🟢</td></tr><tr><th>@provides</th><td>🟢</td></tr><tr><th>federated tracing</th><td>🟢</td></tr></table></td><td><table><tr><th>@link</th><td>🟢</td></tr><tr><th>@shareable</th><td>🟢</td></tr><tr><th>@tag</th><td>🔲</td></tr><tr><th>@override</th><td>🟢</td></tr><tr><th>@inaccessible</th><td>🔲</td></tr></table></td></tr>
127127
</tbody>
128128
</table>
129129

implementations/ruby/Gemfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
66

77
# gem "rails"
88

9-
gem "apollo-federation", "~> 2"
10-
gem "graphql", "~> 1.12"
9+
gem "apollo-federation", "~> 3"
10+
gem "graphql", "~> 2.0"
1111
gem "rack", "~> 2.2"
1212
gem "rake", "~> 13.0"
1313
gem "rerun", "~> 0.13.1"
14-
gem "sinatra", "~> 2.1"
14+
gem "sinatra", "~> 2.2"
1515
gem "thin", "~> 1.8"
1616

17-
gem "rb-kqueue", "~> 0.2.6"
17+
gem "rb-kqueue", "~> 0.2"

implementations/ruby/Gemfile.lock

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
GEM
22
remote: https://rubygems.org/
33
specs:
4-
apollo-federation (2.2.0)
4+
apollo-federation (3.3.0)
55
google-protobuf (~> 3.19)
66
graphql (>= 1.10.14)
77
daemons (1.4.0)
88
eventmachine (1.2.7)
99
ffi (1.15.3)
10-
google-protobuf (3.19.4)
11-
graphql (1.12.14)
10+
google-protobuf (3.21.5)
11+
graphql (2.0.13)
1212
listen (3.6.0)
1313
rb-fsevent (~> 0.10, >= 0.10.3)
1414
rb-inotify (~> 0.9, >= 0.9.10)
15-
mustermann (1.1.1)
15+
mustermann (2.0.2)
1616
ruby2_keywords (~> 0.0.1)
1717
rack (2.2.3)
18-
rack-protection (2.1.0)
18+
rack-protection (2.2.2)
1919
rack
2020
rake (13.0.6)
2121
rb-fsevent (0.11.0)
@@ -26,29 +26,29 @@ GEM
2626
rerun (0.13.1)
2727
listen (~> 3.0)
2828
ruby2_keywords (0.0.5)
29-
sinatra (2.1.0)
30-
mustermann (~> 1.0)
29+
sinatra (2.2.2)
30+
mustermann (~> 2.0)
3131
rack (~> 2.2)
32-
rack-protection (= 2.1.0)
32+
rack-protection (= 2.2.2)
3333
tilt (~> 2.0)
3434
thin (1.8.1)
3535
daemons (~> 1.0, >= 1.0.9)
3636
eventmachine (~> 1.0, >= 1.0.4)
3737
rack (>= 1, < 3)
38-
tilt (2.0.10)
38+
tilt (2.0.11)
3939

4040
PLATFORMS
4141
ruby
4242

4343
DEPENDENCIES
44-
apollo-federation (~> 2)
45-
graphql (~> 1.12)
44+
apollo-federation (~> 3)
45+
graphql (~> 2.0)
4646
rack (~> 2.2)
4747
rake (~> 13.0)
48-
rb-kqueue (~> 0.2.6)
48+
rb-kqueue (~> 0.2)
4949
rerun (~> 0.13.1)
50-
sinatra (~> 2.1)
50+
sinatra (~> 2.2)
5151
thin (~> 1.8)
5252

5353
BUNDLED WITH
54-
2.3.6
54+
2.3.21

implementations/ruby/products.graphql

Lines changed: 0 additions & 26 deletions
This file was deleted.

implementations/ruby/server.rb

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,69 @@ class ProductVariation < BaseObject
5656
end
5757

5858
class ProductDimension < BaseObject
59+
shareable
5960
field :size, String, null: true
6061
field :weight, Float, null: true
62+
# inaccessible is currently incorrectly namespaced
63+
field :unit, String, null: true, inaccessible: true
64+
end
65+
66+
class CaseStudy < BaseObject
67+
field :caseNumber, ID, null: false
68+
field :description, String, null: true
69+
end
70+
71+
class ProductResearch < BaseObject
72+
key fields: 'study { caseNumber }'
73+
74+
field :study, CaseStudy, null: false
75+
field :outcome, String, null: true
76+
77+
def self.resolve_reference(object, _context)
78+
RESEARCH.find { |research| research[:study][:caseNumber] == object[:study]["caseNumber"] }
79+
end
6180
end
6281

6382
class User < BaseObject
6483
extend_type
6584
key fields: 'email'
6685

86+
field :averageProductsCreatedPerYear, Int, null: true, requires: { fields: [:totalProductsCreated, :yearsOfEmployment] }
6787
field :email, ID, null: false, external: true
88+
field :name, String, null: true, override: { from: 'users' }
6889
field :total_products_created, Int, null: true, external: true
90+
field :yearsOfEmployment, Int, null: false, external: true
91+
92+
def averageProductsCreatedPerYear
93+
unless object[:totalProductsCreated].nil? and object[:yearsOfEmployment].nil?
94+
(object[:totalProductsCreated] + object[:yearsOfEmployment] / 2) / object[:yearsOfEmployment]
95+
else
96+
nil
97+
end
98+
end
99+
100+
def self.resolve_reference(object, _context)
101+
if object[:email] == '[email protected]'
102+
object.merge(DEFAULT_USER)
103+
else
104+
nil
105+
end
106+
end
107+
end
108+
109+
class DeprecatedProduct < BaseObject
110+
key fields: 'sku package'
111+
112+
field :sku, String, null: false
113+
field :package, String, null: false
114+
field :reason, String, null: true
115+
field :created_by, User, null: true
116+
117+
def self.resolve_reference(object, _context)
118+
if object[:sku] == DEPRECATED_PRODUCT[:sku] and object[:package] == DEPRECATED_PRODUCT[:package]
119+
DEPRECATED_PRODUCT
120+
end
121+
end
69122
end
70123

71124
class Product < BaseObject
@@ -79,6 +132,8 @@ class Product < BaseObject
79132
field :variation, ProductVariation, null: true
80133
field :dimensions, ProductDimension, null: true
81134
field :created_by, User, null: true, provides: { fields: 'totalProductsCreated' }
135+
field :notes, String, null: true
136+
field :research, [ProductResearch], null:false
82137

83138
def self.resolve_reference(object, _context)
84139
if object[:id]
@@ -93,37 +148,78 @@ def self.resolve_reference(object, _context)
93148
end
94149
end
95150

151+
DEFAULT_USER = {
152+
153+
name: "Jane Smith",
154+
total_products_created: 1337
155+
}
156+
157+
RESEARCH = [
158+
{
159+
study: {
160+
caseNumber: "1234",
161+
description: "Federation Study"
162+
}
163+
},
164+
{
165+
study: {
166+
caseNumber: "1235",
167+
description: "Studio Study"
168+
}
169+
}
170+
]
171+
172+
DEPRECATED_PRODUCT = {
173+
sku: "apollo-federation-v1",
174+
package: "@apollo/federation-v1",
175+
reason: "Migrate to Federation V2",
176+
createdBy: DEFAULT_USER
177+
}
178+
96179
PRODUCTS = [
97180
{
98181
id: "apollo-federation",
99182
sku: "federation",
100183
package: "@apollo/federation",
101184
variation: { id: "OSS" },
102-
dimensions: { size: "small", weight: 1 },
103-
created_by: { email: "[email protected]", total_products_created: 1337 }
185+
dimensions: { size: "small", weight: 1, unit: "kg" },
186+
created_by: DEFAULT_USER,
187+
research: [ RESEARCH[0] ]
104188
},
105189
{
106190
id: "apollo-studio",
107191
sku: "studio",
108192
package: "",
109193
variation: { id: "platform" },
110-
dimensions: { size: "small", weight: 1 },
111-
created_by: { email: "[email protected]", total_products_created: 1337 }
194+
dimensions: { size: "small", weight: 1, unit: "kg" },
195+
created_by: DEFAULT_USER,
196+
research: [ RESEARCH[1] ]
112197
},
113198
].freeze
114199

115200
class Query < BaseObject
116201
field :product, Product, null: true do
117202
argument :id, ID, required: true
118203
end
204+
field :deprecated_product, DeprecatedProduct, null: true do
205+
argument :sku, String, required: true
206+
argument :package, String, required: true
207+
end
119208

120209
def product(id:)
121210
PRODUCTS.find { |product| product[:id] == id }
122211
end
212+
213+
def deprecated_product(sku:, package:)
214+
if sku == DEPRECATED_PRODUCT[:sku] and package == DEPRECATED_PRODUCT[:package]
215+
DEPRECATED_PRODUCT
216+
end
217+
end
123218
end
124219

125220
class ProductSchema < GraphQL::Schema
126221
include ApolloFederation::Schema
222+
federation version: '2.0'
127223
use ApolloFederation::Tracing
128224

129225
query(Query)

src/tests/key.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe("@key single", () => {
99

1010
const { sdl } = serviceSDLQuery.data._service;
1111
const normalizedSDL = stripIgnoredCharacters(sdl);
12-
expect(normalizedSDL).toMatch(/type User(@extends)?@key\(fields:"email"( resolvable:true)?\)/);
12+
expect(normalizedSDL).toMatch(/type User(@extends|@federation__extends)?(@key|@federation__key)\(fields:"email"( resolvable:true)?\)/);
1313
});
1414

1515
test("resolves single field @key on User", async () => {
@@ -47,7 +47,7 @@ describe("@key multiple", () => {
4747

4848
const { sdl } = serviceSDLQuery.data._service;
4949
const normalizedSDL = stripIgnoredCharacters(sdl);
50-
expect(normalizedSDL).toContain("type DeprecatedProduct@key(fields:\"sku package\"");
50+
expect(normalizedSDL).toMatch(/type DeprecatedProduct(@key|@federation__key)\(fields:"sku package"/);
5151
});
5252

5353
test("resolves multiple field @key on DeprecatedProduct", async () => {
@@ -92,7 +92,7 @@ describe("@key composite", () => {
9292

9393
const { sdl } = serviceSDLQuery.data._service;
9494
const normalizedSDL = stripIgnoredCharacters(sdl);
95-
expect(normalizedSDL).toContain("type ProductResearch@key(fields:\"study { caseNumber }\"");
95+
expect(normalizedSDL).toMatch(/type ProductResearch(@key|@federation__key)\(fields:"study { caseNumber }"/);
9696
});
9797

9898
test("resolves composite object @key on ProductResearch", async () => {
@@ -139,10 +139,10 @@ describe("repeatable @key", () => {
139139

140140
const { sdl } = serviceSDLQuery.data._service;
141141
const normalizedSDL = stripIgnoredCharacters(sdl);
142-
expect(normalizedSDL).toMatch(/type Product.*@key\(fields:"id"( resolvable:true)?\).*\{/);
142+
expect(normalizedSDL).toMatch(/type Product.*(@key|@federation__key)\(fields:"id"( resolvable:true)?\).*\{/);
143143
// need to end regex with unique field in Product as otherwise we can match against DeprecatedProduct key
144-
expect(normalizedSDL).toMatch(/type Product.*@key\(fields:"sku package"( resolvable:true)?\).*variation/);
145-
expect(normalizedSDL).toMatch(/type Product.*@key\(fields:"sku variation { id }"( resolvable:true)?\).*\{/);
144+
expect(normalizedSDL).toMatch(/type Product.*(@key|@federation__key)\(fields:"sku package"( resolvable:true)?\).*variation/);
145+
expect(normalizedSDL).toMatch(/type Product.*(@key|@federation__key)\(fields:"sku variation { id }"( resolvable:true)?\).*\{/);
146146
});
147147

148148
test("resolves multiple @key directives on Product", async () => {

src/tests/override.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ describe("@override", () => {
88
});
99

1010
const { sdl } = response.data._service;
11-
expect(stripIgnoredCharacters(sdl)).toContain('@override(from:"users")');
11+
// expect(stripIgnoredCharacters(sdl)).toContain('@override(from:"users")');
12+
expect(stripIgnoredCharacters(sdl)).toMatch(/(@override|@federation__override)\(from:"users"\)/);
1213
});
1314

1415
it("should return overridden user name", async () => {

src/tests/shareable.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ describe("@shareable", () => {
88
});
99

1010
const { sdl } = response.data._service;
11-
expect(stripIgnoredCharacters(sdl)).toContain(
12-
"type ProductDimension@shareable"
13-
);
11+
expect(stripIgnoredCharacters(sdl)).toMatch(/type ProductDimension(@shareable|@federation__shareable)/);
1412
});
1513

1614
it("should be able to resolve @shareable ProductDimension types", async () => {

0 commit comments

Comments
 (0)