Updated GitHub.js. Fixes #583
This commit is contained in:
parent
bfed1cfa45
commit
68fafcd11e
@ -1,59 +1,107 @@
|
|||||||
// Github.js 0.7.0
|
// Github.js 0.9.0
|
||||||
// (c) 2012 Michael Aufreiter, Development Seed
|
// (c) 2013 Michael Aufreiter, Development Seed
|
||||||
// Github.js is freely distributable under the MIT license.
|
// Github.js is freely distributable under the MIT license.
|
||||||
// For all details and documentation:
|
// For all details and documentation:
|
||||||
// http://substance.io/michael/github
|
// http://substance.io/michael/github
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var Github;
|
|
||||||
|
// Initial Setup
|
||||||
|
// -------------
|
||||||
|
|
||||||
|
var XMLHttpRequest, Base64, _;
|
||||||
|
if (typeof exports !== 'undefined') {
|
||||||
|
XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
|
||||||
|
_ = require('underscore');
|
||||||
|
Base64 = require('./lib/base64.js');
|
||||||
|
}else{
|
||||||
|
_ = window._;
|
||||||
|
Base64 = window.Base64;
|
||||||
|
}
|
||||||
|
//prefer native XMLHttpRequest always
|
||||||
|
if (typeof window !== 'undefined' && typeof window.XMLHttpRequest !== 'undefined'){
|
||||||
|
XMLHttpRequest = window.XMLHttpRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var API_URL = 'https://api.github.com';
|
var API_URL = 'https://api.github.com';
|
||||||
|
|
||||||
Github = window.Github = function(options) {
|
var Github = function(options) {
|
||||||
|
|
||||||
// HTTP Request Abstraction
|
// HTTP Request Abstraction
|
||||||
// =======
|
// =======
|
||||||
//
|
//
|
||||||
// I'm not proud of this and neither should you be if you were responsible for the XMLHttpRequest spec.
|
// I'm not proud of this and neither should you be if you were responsible for the XMLHttpRequest spec.
|
||||||
|
|
||||||
function _request(method, path, data, cb, raw) {
|
function _request(method, path, data, cb, raw, sync) {
|
||||||
function getURL() {
|
function getURL() {
|
||||||
var url = API_URL + path;
|
var url = path.indexOf('//') >= 0 ? path : API_URL + path;
|
||||||
return url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime();
|
return url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
if (!raw) {xhr.dataType = "json";}
|
if (!raw) {xhr.dataType = "json";}
|
||||||
|
|
||||||
xhr.open(method, getURL());
|
xhr.open(method, getURL(), !sync);
|
||||||
xhr.onreadystatechange = function () {
|
if (!sync) {
|
||||||
if (this.readyState == 4) {
|
xhr.onreadystatechange = function () {
|
||||||
if (this.status >= 200 && this.status < 300 || this.status === 304) {
|
if (this.readyState == 4) {
|
||||||
cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true);
|
if (this.status >= 200 && this.status < 300 || this.status === 304) {
|
||||||
} else {
|
cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true, this);
|
||||||
cb({request: this, error: this.status});
|
} else {
|
||||||
|
cb({path: path, request: this, error: this.status});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.setRequestHeader('Accept','application/vnd.github.raw');
|
xhr.setRequestHeader('Accept','application/vnd.github.v3.raw+json');
|
||||||
xhr.setRequestHeader('Content-Type','application/json');
|
xhr.setRequestHeader('Content-Type','application/json;charset=UTF-8');
|
||||||
if (
|
if ((options.token) || (options.username && options.password)) {
|
||||||
(options.auth == 'oauth' && options.token) ||
|
xhr.setRequestHeader('Authorization', options.token
|
||||||
(options.auth == 'basic' && options.username && options.password)
|
|
||||||
) {
|
|
||||||
xhr.setRequestHeader('Authorization',options.auth == 'oauth'
|
|
||||||
? 'token '+ options.token
|
? 'token '+ options.token
|
||||||
: 'Basic ' + Base64.encode(options.username + ':' + options.password)
|
: 'Basic ' + Base64.encode(options.username + ':' + options.password)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
data ? xhr.send(JSON.stringify(data)) : xhr.send();
|
data ? xhr.send(JSON.stringify(data)) : xhr.send();
|
||||||
|
if (sync) return xhr.response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _requestAllPages(path, cb) {
|
||||||
|
var results = [];
|
||||||
|
(function iterate() {
|
||||||
|
_request("GET", path, null, function(err, res, xhr) {
|
||||||
|
if (err) {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push.apply(results, res);
|
||||||
|
|
||||||
|
var links = (xhr.getResponseHeader('link') || '').split(/\s*,\s*/g),
|
||||||
|
next = _.find(links, function(link) { return /rel="next"/.test(link); });
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
next = (/<(.*)>/.exec(next) || [])[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!next) {
|
||||||
|
cb(err, results);
|
||||||
|
} else {
|
||||||
|
path = next;
|
||||||
|
iterate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// User API
|
// User API
|
||||||
// =======
|
// =======
|
||||||
|
|
||||||
Github.User = function() {
|
Github.User = function() {
|
||||||
this.repos = function(cb) {
|
this.repos = function(cb) {
|
||||||
_request("GET", "/user/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
|
// Github does not always honor the 1000 limit so we want to iterate over the data set.
|
||||||
|
_requestAllPages("/user/repos?type=all&per_page=1000&sort=updated", function(err, res) {
|
||||||
cb(err, res);
|
cb(err, res);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -76,6 +124,15 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// List authenticated user's unread notifications
|
||||||
|
// -------
|
||||||
|
|
||||||
|
this.notifications = function(cb) {
|
||||||
|
_request("GET", "/notifications", null, function(err, res) {
|
||||||
|
cb(err,res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Show user information
|
// Show user information
|
||||||
// -------
|
// -------
|
||||||
|
|
||||||
@ -91,7 +148,8 @@
|
|||||||
// -------
|
// -------
|
||||||
|
|
||||||
this.userRepos = function(username, cb) {
|
this.userRepos = function(username, cb) {
|
||||||
_request("GET", "/users/"+username+"/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
|
// Github does not always honor the 1000 limit so we want to iterate over the data set.
|
||||||
|
_requestAllPages("/users/"+username+"/repos?type=all&per_page=1000&sort=updated", function(err, res) {
|
||||||
cb(err, res);
|
cb(err, res);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -109,7 +167,8 @@
|
|||||||
// -------
|
// -------
|
||||||
|
|
||||||
this.orgRepos = function(orgname, cb) {
|
this.orgRepos = function(orgname, cb) {
|
||||||
_request("GET", "/orgs/"+orgname+"/repos?type=all&per_page=1000&sort=updated&direction=desc", null, function(err, res) {
|
// Github does not always honor the 1000 limit so we want to iterate over the data set.
|
||||||
|
_requestAllPages("/orgs/"+orgname+"/repos?type=all&&page_num=1000&sort=updated&direction=desc", function(err, res) {
|
||||||
cb(err, res);
|
cb(err, res);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -140,7 +199,7 @@
|
|||||||
Github.Repository = function(options) {
|
Github.Repository = function(options) {
|
||||||
var repo = options.name;
|
var repo = options.name;
|
||||||
var user = options.user;
|
var user = options.user;
|
||||||
|
|
||||||
var that = this;
|
var that = this;
|
||||||
var repoPath = "/repos/" + user + "/" + repo;
|
var repoPath = "/repos/" + user + "/" + repo;
|
||||||
|
|
||||||
@ -185,7 +244,7 @@
|
|||||||
|
|
||||||
// Delete a reference
|
// Delete a reference
|
||||||
// --------
|
// --------
|
||||||
//
|
//
|
||||||
// repo.deleteRef('heads/gh-pages')
|
// repo.deleteRef('heads/gh-pages')
|
||||||
// repo.deleteRef('tags/v1.0')
|
// repo.deleteRef('tags/v1.0')
|
||||||
|
|
||||||
@ -193,6 +252,60 @@
|
|||||||
_request("DELETE", repoPath + "/git/refs/"+ref, options, cb);
|
_request("DELETE", repoPath + "/git/refs/"+ref, options, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Create a repo
|
||||||
|
// -------
|
||||||
|
|
||||||
|
this.createRepo = function(options, cb) {
|
||||||
|
_request("POST", "/user/repos", options, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete a repo
|
||||||
|
// --------
|
||||||
|
|
||||||
|
this.deleteRepo = function(cb) {
|
||||||
|
_request("DELETE", repoPath, options, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
// List all tags of a repository
|
||||||
|
// -------
|
||||||
|
|
||||||
|
this.listTags = function(cb) {
|
||||||
|
_request("GET", repoPath + "/tags", null, function(err, tags) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
cb(null, tags);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// List all pull requests of a respository
|
||||||
|
// -------
|
||||||
|
|
||||||
|
this.listPulls = function(state, cb) {
|
||||||
|
_request("GET", repoPath + "/pulls" + (state ? '?state=' + state : ''), null, function(err, pulls) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
cb(null, pulls);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Gets details for a specific pull request
|
||||||
|
// -------
|
||||||
|
|
||||||
|
this.getPull = function(number, cb) {
|
||||||
|
_request("GET", repoPath + "/pulls/" + number, null, function(err, pull) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
cb(null, pull);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retrieve the changes made between base and head
|
||||||
|
// -------
|
||||||
|
|
||||||
|
this.compare = function(base, head, cb) {
|
||||||
|
_request("GET", repoPath + "/compare/" + base + "..." + head, null, function(err, diff) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
cb(null, diff);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// List all branches of a repository
|
// List all branches of a repository
|
||||||
// -------
|
// -------
|
||||||
|
|
||||||
@ -217,6 +330,7 @@
|
|||||||
// Just use head if path is empty
|
// Just use head if path is empty
|
||||||
if (path === "") return that.getRef("heads/"+branch, cb);
|
if (path === "") return that.getRef("heads/"+branch, cb);
|
||||||
that.getTree(branch+"?recursive=true", function(err, tree) {
|
that.getTree(branch+"?recursive=true", function(err, tree) {
|
||||||
|
if (err) return cb(err);
|
||||||
var file = _.select(tree, function(file) {
|
var file = _.select(tree, function(file) {
|
||||||
return file.path === path;
|
return file.path === path;
|
||||||
})[0];
|
})[0];
|
||||||
@ -243,7 +357,12 @@
|
|||||||
"content": content,
|
"content": content,
|
||||||
"encoding": "utf-8"
|
"encoding": "utf-8"
|
||||||
};
|
};
|
||||||
}
|
} else {
|
||||||
|
content = {
|
||||||
|
"content": btoa(String.fromCharCode.apply(null, new Uint8Array(content))),
|
||||||
|
"encoding": "base64"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
_request("POST", repoPath + "/git/blobs", content, function(err, res) {
|
_request("POST", repoPath + "/git/blobs", content, function(err, res) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
@ -290,14 +409,16 @@
|
|||||||
this.commit = function(parent, tree, message, cb) {
|
this.commit = function(parent, tree, message, cb) {
|
||||||
var data = {
|
var data = {
|
||||||
"message": message,
|
"message": message,
|
||||||
"author": {
|
|
||||||
"name": options.username
|
|
||||||
},
|
|
||||||
"parents": [
|
"parents": [
|
||||||
parent
|
parent
|
||||||
],
|
],
|
||||||
"tree": tree
|
"tree": tree
|
||||||
};
|
};
|
||||||
|
if(options.username) {
|
||||||
|
data.author = {
|
||||||
|
"name": options.username
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
_request("POST", repoPath + "/git/commits", data, function(err, res) {
|
_request("POST", repoPath + "/git/commits", data, function(err, res) {
|
||||||
currentTree.sha = res.sha; // update latest commit
|
currentTree.sha = res.sha; // update latest commit
|
||||||
@ -325,8 +446,8 @@
|
|||||||
// Get contents
|
// Get contents
|
||||||
// --------
|
// --------
|
||||||
|
|
||||||
this.contents = function(branch, path, cb) {
|
this.contents = function(branch, path, cb, sync) {
|
||||||
_request("GET", repoPath + "/contents?ref=" + branch, { path: path }, cb);
|
return _request("GET", repoPath + "/contents?ref=" + branch + (path ? "&path=" + path : ""), null, cb, 'raw', sync);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fork repository
|
// Fork repository
|
||||||
@ -336,6 +457,24 @@
|
|||||||
_request("POST", repoPath + "/forks", null, cb);
|
_request("POST", repoPath + "/forks", null, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Branch repository
|
||||||
|
// --------
|
||||||
|
|
||||||
|
this.branch = function(oldBranch,newBranch,cb) {
|
||||||
|
if(arguments.length === 2 && typeof arguments[1] === "function") {
|
||||||
|
cb = newBranch;
|
||||||
|
newBranch = oldBranch;
|
||||||
|
oldBranch = "master";
|
||||||
|
}
|
||||||
|
this.getRef("heads/" + oldBranch, function(err,ref) {
|
||||||
|
if(err && cb) return cb(err);
|
||||||
|
that.createRef({
|
||||||
|
ref: "refs/heads/" + newBranch,
|
||||||
|
sha: ref
|
||||||
|
},cb);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Create pull request
|
// Create pull request
|
||||||
// --------
|
// --------
|
||||||
|
|
||||||
@ -343,6 +482,41 @@
|
|||||||
_request("POST", repoPath + "/pulls", options, cb);
|
_request("POST", repoPath + "/pulls", options, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// List hooks
|
||||||
|
// --------
|
||||||
|
|
||||||
|
this.listHooks = function(cb) {
|
||||||
|
_request("GET", repoPath + "/hooks", null, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get a hook
|
||||||
|
// --------
|
||||||
|
|
||||||
|
this.getHook = function(id, cb) {
|
||||||
|
_request("GET", repoPath + "/hooks/" + id, null, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a hook
|
||||||
|
// --------
|
||||||
|
|
||||||
|
this.createHook = function(options, cb) {
|
||||||
|
_request("POST", repoPath + "/hooks", options, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Edit a hook
|
||||||
|
// --------
|
||||||
|
|
||||||
|
this.editHook = function(id, options, cb) {
|
||||||
|
_request("PATCH", repoPath + "/hooks/" + id, options, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete a hook
|
||||||
|
// --------
|
||||||
|
|
||||||
|
this.deleteHook = function(id, cb) {
|
||||||
|
_request("DELETE", repoPath + "/hooks/" + id, null, cb);
|
||||||
|
};
|
||||||
|
|
||||||
// Read file at given path
|
// Read file at given path
|
||||||
// -------
|
// -------
|
||||||
|
|
||||||
@ -377,7 +551,24 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Delete a file from the tree
|
||||||
|
// -------
|
||||||
|
|
||||||
|
this.delete = function(branch, path, cb) {
|
||||||
|
that.getSha(branch, path, function(err, sha) {
|
||||||
|
if (!sha) return cb("not found", null);
|
||||||
|
var delPath = repoPath + "/contents/" + path;
|
||||||
|
var params = {
|
||||||
|
"message": "Deleted " + path,
|
||||||
|
"sha": sha
|
||||||
|
};
|
||||||
|
delPath += "?message=" + encodeURIComponent(params.message);
|
||||||
|
delPath += "&sha=" + encodeURIComponent(params.sha);
|
||||||
|
_request("DELETE", delPath, null, cb);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Move a file to a new location
|
// Move a file to a new location
|
||||||
// -------
|
// -------
|
||||||
|
|
||||||
@ -419,6 +610,43 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// List commits on a repository. Takes an object of optional paramaters:
|
||||||
|
// sha: SHA or branch to start listing commits from
|
||||||
|
// path: Only commits containing this file path will be returned
|
||||||
|
// since: ISO 8601 date - only commits after this date will be returned
|
||||||
|
// until: ISO 8601 date - only commits before this date will be returned
|
||||||
|
// -------
|
||||||
|
|
||||||
|
this.getCommits = function(options, cb) {
|
||||||
|
options = options || {};
|
||||||
|
var url = repoPath + "/commits";
|
||||||
|
var params = [];
|
||||||
|
if (options.sha) {
|
||||||
|
params.push("sha=" + encodeURIComponent(options.sha));
|
||||||
|
}
|
||||||
|
if (options.path) {
|
||||||
|
params.push("path=" + encodeURIComponent(options.path));
|
||||||
|
}
|
||||||
|
if (options.since) {
|
||||||
|
var since = options.since;
|
||||||
|
if (since.constructor === Date) {
|
||||||
|
since = since.toISOString();
|
||||||
|
}
|
||||||
|
params.push("since=" + encodeURIComponent(since));
|
||||||
|
}
|
||||||
|
if (options.until) {
|
||||||
|
var until = options.until;
|
||||||
|
if (until.constructor === Date) {
|
||||||
|
until = until.toISOString();
|
||||||
|
}
|
||||||
|
params.push("until=" + encodeURIComponent(until));
|
||||||
|
}
|
||||||
|
if (params.length > 0) {
|
||||||
|
url += "?" + params.join("&");
|
||||||
|
}
|
||||||
|
_request("GET", url, null, cb);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Gists API
|
// Gists API
|
||||||
@ -448,7 +676,7 @@
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
this.create = function(options, cb){
|
this.create = function(options, cb){
|
||||||
_request("POST","/gists", options, cb);
|
_request("POST","/gists", options, cb);
|
||||||
};
|
};
|
||||||
@ -479,11 +707,55 @@
|
|||||||
cb(err,res);
|
cb(err,res);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Star a gist
|
||||||
|
// --------
|
||||||
|
|
||||||
|
this.star = function(cb) {
|
||||||
|
_request("PUT", gistPath+"/star", null, function(err,res) {
|
||||||
|
cb(err,res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Untar a gist
|
||||||
|
// --------
|
||||||
|
|
||||||
|
this.unstar = function(cb) {
|
||||||
|
_request("DELETE", gistPath+"/star", null, function(err,res) {
|
||||||
|
cb(err,res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if a gist is starred
|
||||||
|
// --------
|
||||||
|
|
||||||
|
this.isStarred = function(cb) {
|
||||||
|
_request("GET", gistPath+"/star", null, function(err,res) {
|
||||||
|
cb(err,res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Issues API
|
||||||
|
// ==========
|
||||||
|
|
||||||
|
Github.Issue = function(options) {
|
||||||
|
var path = "/repos/" + options.user + "/" + options.repo + "/issues";
|
||||||
|
|
||||||
|
this.list = function(options, cb) {
|
||||||
|
_request("GET", path, options, function(err, res) {
|
||||||
|
cb(err,res)
|
||||||
|
});
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Top Level API
|
// Top Level API
|
||||||
// -------
|
// -------
|
||||||
|
|
||||||
|
this.getIssues = function(user, repo) {
|
||||||
|
return new Github.Issue({user: user, repo: repo});
|
||||||
|
};
|
||||||
|
|
||||||
this.getRepo = function(user, repo) {
|
this.getRepo = function(user, repo) {
|
||||||
return new Github.Repository({user: user, name: repo});
|
return new Github.Repository({user: user, name: repo});
|
||||||
};
|
};
|
||||||
@ -496,4 +768,12 @@
|
|||||||
return new Github.Gist({id: id});
|
return new Github.Gist({id: id});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}).call(this);
|
|
||||||
|
|
||||||
|
if (typeof exports !== 'undefined') {
|
||||||
|
// Github = exports;
|
||||||
|
module.exports = Github;
|
||||||
|
} else {
|
||||||
|
window.Github = Github;
|
||||||
|
}
|
||||||
|
}).call(this);
|
||||||
|
Loading…
Reference in New Issue
Block a user