Skip to content

Commit 9bbd5e1

Browse files
author
Robert Mosolgo
authored
Merge pull request rmosolgo#241 from josh/definition-slice
Definition slicing
2 parents f9a8d77 + b23cdad commit 9bbd5e1

File tree

4 files changed

+259
-0
lines changed

4 files changed

+259
-0
lines changed

lib/graphql/language.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require "graphql/language/definition_slice"
12
require "graphql/language/generation"
23
require "graphql/language/lexer"
34
require "graphql/language/nodes"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module GraphQL
2+
module Language
3+
module DefinitionSlice
4+
extend self
5+
6+
def slice(document, name)
7+
definitions = {}
8+
document.definitions.each { |d| definitions[d.name] = d }
9+
names = find_definition_dependencies(definitions, name)
10+
definitions = document.definitions.select { |d| names.include?(d.name) }
11+
Nodes::Document.new(definitions: definitions)
12+
end
13+
14+
private
15+
16+
def find_definition_dependencies(definitions, name)
17+
names = Set.new([name])
18+
visitor = Visitor.new(definitions[name])
19+
visitor[Nodes::FragmentSpread] << -> (node, parent) {
20+
if fragment = definitions[node.name]
21+
names.merge(find_definition_dependencies(definitions, fragment.name))
22+
end
23+
}
24+
visitor.visit
25+
names
26+
end
27+
end
28+
end
29+
end

lib/graphql/language/nodes.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ class Document < AbstractNode
160160
def initialize_node(definitions: [])
161161
@definitions = definitions
162162
end
163+
164+
def slice_definition(name)
165+
GraphQL::Language::DefinitionSlice.slice(self, name)
166+
end
163167
end
164168

165169
# An enum value. The string is available as {#name}.
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
require "spec_helper"
2+
3+
describe GraphQL::Language::DefinitionSlice do
4+
let(:document) { GraphQL::Language::Parser.parse(query_string) }
5+
6+
describe "anonymous query with no dependencies" do
7+
let(:query_string) {%|
8+
{
9+
version
10+
}
11+
|}
12+
13+
it "is already the smallest slice" do
14+
assert_equal document.to_query_string,
15+
document.slice_definition(nil).to_query_string
16+
end
17+
end
18+
19+
describe "anonymous mutation with no dependencies" do
20+
let(:query_string) {%|
21+
mutation {
22+
ping {
23+
message
24+
}
25+
}
26+
|}
27+
28+
it "is already the smallest slice" do
29+
assert_equal document.to_query_string,
30+
document.slice_definition(nil).to_query_string
31+
end
32+
end
33+
34+
describe "anonymous fragment with no dependencies" do
35+
let(:query_string) {%|
36+
fragment on User {
37+
name
38+
}
39+
|}
40+
41+
it "is already the smallest slice" do
42+
assert_equal document.to_query_string,
43+
document.slice_definition(nil).to_query_string
44+
end
45+
end
46+
47+
describe "named query with no dependencies" do
48+
let(:query_string) {%|
49+
query getVersion {
50+
version
51+
}
52+
|}
53+
54+
it "is already the smallest slice" do
55+
assert_equal document.to_query_string,
56+
document.slice_definition("getVersion").to_query_string
57+
end
58+
end
59+
60+
describe "named fragment with no dependencies" do
61+
let(:query_string) {%|
62+
fragment profileFields on User {
63+
firstName
64+
lastName
65+
}
66+
|}
67+
68+
it "is already the smallest slice" do
69+
assert_equal document.to_query_string,
70+
document.slice_definition("profileFields").to_query_string
71+
end
72+
end
73+
74+
describe "document with multiple queries but no subdependencies" do
75+
let(:query_string) {%|
76+
query getVersion {
77+
version
78+
}
79+
80+
query getTime {
81+
time
82+
}
83+
|}
84+
85+
it "returns just the query definition" do
86+
assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[0]]).to_query_string,
87+
document.slice_definition("getVersion").to_query_string
88+
assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[1]]).to_query_string,
89+
document.slice_definition("getTime").to_query_string
90+
end
91+
end
92+
93+
describe "document with multiple fragments but no subdependencies" do
94+
let(:query_string) {%|
95+
fragment profileFields on User {
96+
firstName
97+
lastName
98+
}
99+
100+
fragment avatarFields on User {
101+
avatarURL(size: 80)
102+
}
103+
|}
104+
105+
it "returns just the fragment definition" do
106+
assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[0]]).to_query_string,
107+
document.slice_definition("profileFields").to_query_string
108+
assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[1]]).to_query_string,
109+
document.slice_definition("avatarFields").to_query_string
110+
end
111+
end
112+
113+
describe "query with missing spread" do
114+
let(:query_string) {%|
115+
query getUser {
116+
viewer {
117+
...profileFields
118+
}
119+
}
120+
|}
121+
122+
it "is ignored" do
123+
assert_equal document.to_query_string,
124+
document.slice_definition("getUser").to_query_string
125+
end
126+
end
127+
128+
describe "query and fragment subdependency" do
129+
let(:query_string) {%|
130+
query getUser {
131+
viewer {
132+
...profileFields
133+
}
134+
}
135+
136+
fragment profileFields on User {
137+
firstName
138+
lastName
139+
}
140+
|}
141+
142+
it "returns query and fragment dependency" do
143+
assert_equal document.to_query_string,
144+
document.slice_definition("getUser").to_query_string
145+
end
146+
end
147+
148+
describe "query and fragment nested subdependencies" do
149+
let(:query_string) {%|
150+
query getUser {
151+
viewer {
152+
...viewerInfo
153+
}
154+
}
155+
156+
fragment viewerInfo on User {
157+
...profileFields
158+
}
159+
160+
fragment profileFields on User {
161+
firstName
162+
lastName
163+
...avatarFields
164+
}
165+
166+
fragment avatarFields on User {
167+
avatarURL(size: 80)
168+
}
169+
|}
170+
171+
it "returns query and all fragment dependencies" do
172+
assert_equal document.to_query_string,
173+
document.slice_definition("getUser").to_query_string
174+
end
175+
end
176+
177+
describe "fragment subdependency referenced multiple times" do
178+
let(:query_string) {%|
179+
query getUser {
180+
viewer {
181+
...viewerInfo
182+
...moreViewerInfo
183+
}
184+
}
185+
186+
fragment viewerInfo on User {
187+
...profileFields
188+
}
189+
190+
fragment moreViewerInfo on User {
191+
...profileFields
192+
}
193+
194+
fragment profileFields on User {
195+
firstName
196+
lastName
197+
}
198+
|}
199+
200+
it "is only returned once" do
201+
assert_equal document.to_query_string,
202+
document.slice_definition("getUser").to_query_string
203+
end
204+
end
205+
206+
describe "query and unused fragment" do
207+
let(:query_string) {%|
208+
query getUser {
209+
viewer {
210+
id
211+
}
212+
}
213+
214+
fragment profileFields on User {
215+
firstName
216+
lastName
217+
}
218+
|}
219+
220+
it "returns just the query definition" do
221+
assert_equal GraphQL::Language::Nodes::Document.new(definitions: [document.definitions[0]]).to_query_string,
222+
document.slice_definition("getUser").to_query_string
223+
end
224+
end
225+
end

0 commit comments

Comments
 (0)