Skip to content

Commit ffc4af1

Browse files
committed
Iterate over paged data sets
Some APIs do not honor the per_page API (at least for large values) and require that we actually walk the links headers to pull all data.
1 parent 807498f commit ffc4af1

File tree

1 file changed

+36
-6
lines changed

1 file changed

+36
-6
lines changed

github.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
function _request(method, path, data, cb, raw) {
3737
function getURL() {
38-
var url = API_URL + path;
38+
var url = path.indexOf('//') >= 0 ? path : API_URL + path;
3939
return url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime();
4040
}
4141

@@ -46,9 +46,9 @@
4646
xhr.onreadystatechange = function () {
4747
if (this.readyState == 4) {
4848
if (this.status >= 200 && this.status < 300 || this.status === 304) {
49-
cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true);
49+
cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true, this);
5050
} else {
51-
cb({request: this, error: this.status});
51+
cb({path: path, request: this, error: this.status});
5252
}
5353
}
5454
};
@@ -66,14 +66,42 @@
6666
data ? xhr.send(JSON.stringify(data)) : xhr.send();
6767
}
6868

69+
function _requestAllPages(path, cb) {
70+
var results = [];
71+
(function iterate() {
72+
_request("GET", path, null, function(err, res, xhr) {
73+
if (err) {
74+
return cb(err);
75+
}
76+
77+
results.push.apply(results, res);
78+
79+
var links = (xhr.getResponseHeader('link') || '').split(/\s*,\s*/g),
80+
next = _.find(links, function(link) { return /rel="next"/.test(link); });
81+
82+
if (next) {
83+
next = (/<(.*)>/.exec(next) || [])[1];
84+
}
85+
86+
if (!next) {
87+
cb(err, results);
88+
} else {
89+
path = next;
90+
iterate();
91+
}
92+
});
93+
})();
94+
}
95+
6996

7097

7198
// User API
7299
// =======
73100

74101
Github.User = function() {
75102
this.repos = function(cb) {
76-
_request("GET", "/user/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
103+
// Github does not always honor the 1000 limit so we want to iterate over the data set.
104+
_requestAllPages("/user/repos?type=all&per_page=1000&sort=updated", function(err, res) {
77105
cb(err, res);
78106
});
79107
};
@@ -111,7 +139,8 @@
111139
// -------
112140

113141
this.userRepos = function(username, cb) {
114-
_request("GET", "/users/"+username+"/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
142+
// Github does not always honor the 1000 limit so we want to iterate over the data set.
143+
_requestAllPages("/users/"+username+"/repos?type=all&per_page=1000&sort=updated", function(err, res) {
115144
cb(err, res);
116145
});
117146
};
@@ -129,7 +158,8 @@
129158
// -------
130159

131160
this.orgRepos = function(orgname, cb) {
132-
_request("GET", "/orgs/"+orgname+"/repos?type=all&per_page=1000&sort=updated&direction=desc", null, function(err, res) {
161+
// Github does not always honor the 1000 limit so we want to iterate over the data set.
162+
_requestAllPages("/orgs/"+orgname+"/repos?type=all&&page_num=1000&sort=updated&direction=desc", function(err, res) {
133163
cb(err, res);
134164
});
135165
};

0 commit comments

Comments
 (0)