Skip to content

Commit 01481b0

Browse files
CyberShadowGeod24
authored andcommitted
dlangbot.github_api: Fix GitHub paging
"prev" occurring before "next" was tripping up the old parser logic.
1 parent 22bc457 commit 01481b0

File tree

2 files changed

+90
-12
lines changed

2 files changed

+90
-12
lines changed

source/dlangbot/github_api.d

+54-12
Original file line numberDiff line numberDiff line change
@@ -114,24 +114,66 @@ auto ghSendRequest(T...)(HTTPMethod method, string url, T arg)
114114
// range-based page loader for the GH API
115115
private struct AllPages
116116
{
117-
private string url;
118-
private string link = "next";
119-
120-
// does not cache
121-
Json front() {
122-
scope req = ghGetRequest(url);
123-
link = req.headers.get("Link", null);
124-
return req.body;
117+
private static string[string] parseLinks(string s)
118+
{
119+
string[string] result;
120+
auto items = s.split(", "); // Hacky but should never occur inside an URL or "rel" value
121+
foreach (item; items)
122+
{
123+
auto parts = item.split("; "); // ditto
124+
string url; string[string] args;
125+
foreach (part; parts)
126+
{
127+
if (part.startsWith("<") && part.endsWith(">"))
128+
url = part[1..$-1];
129+
else
130+
{
131+
auto ps = part.findSplit("=");
132+
auto key = ps[0];
133+
auto value = ps[2];
134+
if (value.startsWith('"') && value.endsWith('"'))
135+
value = value[1..$-1];
136+
args[key] = value;
137+
}
138+
}
139+
result[args.get("rel", null)] = url;
140+
}
141+
return result;
142+
}
143+
144+
unittest
145+
{
146+
auto header = `<https://api.github.com/repositories/1257070/pulls?per_page=100&page=2>; rel="next", ` ~
147+
`<https://api.github.com/repositories/1257070/pulls?per_page=100&page=3>; rel="last"`;
148+
assert(parseLinks(header) == [
149+
"next" : "https://api.github.com/repositories/1257070/pulls?per_page=100&page=2",
150+
"last" : "https://api.github.com/repositories/1257070/pulls?per_page=100&page=3",
151+
]);
125152
}
153+
154+
private Result result;
155+
156+
this(string url)
157+
{
158+
result = ghGetRequest(url);
159+
}
160+
161+
Json front()
162+
{
163+
return result.body;
164+
}
165+
126166
void popFront()
127167
{
128-
import std.utf : byCodeUnit;
129-
if (link)
130-
url = link[1..$].byCodeUnit.until(">").array;
168+
if (auto pNext = "next" in parseLinks(result.headers.get("Link", null)))
169+
result = ghGetRequest(*pNext);
170+
else
171+
result = Result.init; // empty
131172
}
173+
132174
bool empty()
133175
{
134-
return !link.canFind("next");
176+
return result is Result.init;
135177
}
136178
}
137179

test/github.d

+36
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,39 @@ unittest
2323
assert(ghGetRequest(githubAPIURL ~ "/test").body == "hello");
2424
assert(ghGetRequest(githubAPIURL ~ "/test").body == "hello");
2525
}
26+
27+
@("github-multipage")
28+
unittest
29+
{
30+
setAPIExpectations(
31+
"/github/multipage-test",
32+
(scope HTTPServerRequest req, scope HTTPServerResponse res){
33+
res.headers["Link"] =
34+
`<` ~ githubAPIURL ~ `/multipage-test?page=2>; rel="next", ` ~
35+
`<` ~ githubAPIURL ~ `/multipage-test?page=3>; rel="last"`;
36+
res.writeJsonBody("D".Json);
37+
},
38+
"/github/multipage-test?page=2",
39+
(scope HTTPServerRequest req, scope HTTPServerResponse res){
40+
res.headers["Link"] =
41+
`<` ~ githubAPIURL ~ `/multipage-test?page=1>; rel="prev", ` ~
42+
`<` ~ githubAPIURL ~ `/multipage-test?page=3>; rel="next", ` ~
43+
`<` ~ githubAPIURL ~ `/multipage-test?page=3>; rel="last", ` ~
44+
`<` ~ githubAPIURL ~ `/multipage-test?page=1>; rel="first"`;
45+
res.writeJsonBody("is".Json);
46+
},
47+
"/github/multipage-test?page=3",
48+
(scope HTTPServerRequest req, scope HTTPServerResponse res){
49+
res.headers["Link"] =
50+
`<` ~ githubAPIURL ~ `/multipage-test?page=2>; rel="prev", ` ~
51+
`<` ~ githubAPIURL ~ `/multipage-test?page=1>; rel="first"`;
52+
res.writeJsonBody("awesome".Json);
53+
},
54+
);
55+
56+
auto pages = ghGetAllPages(githubAPIURL ~ "/multipage-test");
57+
string result;
58+
foreach (page; pages)
59+
result ~= page.get!string;
60+
assert(result == "D" ~ "is" ~ "awesome");
61+
}

0 commit comments

Comments
 (0)