diff --git a/README.md b/README.md index 43e3e548..a06c04e0 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ repo.show(function(err, repo) {}); Get contents at a particular path. ```js -repo.contents("path/to/dir", function(err, contents) {}); +repo.contents("master", "path/to/dir", function(err, contents) {}); ``` Fork repository. This operation runs asynchronously. You may want to poll for `repo.contents` until the forked repo is ready. @@ -169,7 +169,13 @@ user.orgs(function(err, orgs) {}); List authenticated user's gists. ```js -user.gists(username, function(err, gists) {}); +user.gists(function(err, gists) {}); +``` + +List unread notifications for the authenticated user. + +```js +user.notifications(function(err, notifications) {}); ``` Show user information for a particular username. Also works for organizations. diff --git a/github.js b/github.js index 19fe1a74..40dd0e12 100644 --- a/github.js +++ b/github.js @@ -35,7 +35,7 @@ function _request(method, path, data, cb, raw) { function getURL() { - var url = API_URL + path; + var url = path.indexOf('//') >= 0 ? path : API_URL + path; return url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime(); } @@ -46,19 +46,16 @@ xhr.onreadystatechange = function () { if (this.readyState == 4) { if (this.status >= 200 && this.status < 300 || this.status === 304) { - cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true); + cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true, this); } else { - cb({request: this, error: this.status}); + cb({path: path, request: this, error: this.status}); } } }; - xhr.setRequestHeader('Accept','application/vnd.github.raw'); + xhr.setRequestHeader('Accept','application/json'); xhr.setRequestHeader('Content-Type','application/json'); - if ( - (options.auth == 'oauth' && options.token) || - (options.auth == 'basic' && options.username && options.password) - ) { - xhr.setRequestHeader('Authorization',options.auth == 'oauth' + if ( (options.token) || (options.username && options.password)) { + xhr.setRequestHeader('Authorization', options.token ? 'token '+ options.token : 'Basic ' + Base64.encode(options.username + ':' + options.password) ); @@ -66,6 +63,33 @@ data ? xhr.send(JSON.stringify(data)) : xhr.send(); } + 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 @@ -73,7 +97,8 @@ Github.User = function() { 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); }); }; @@ -96,6 +121,15 @@ }); }; + // List authenticated user's unread notifications + // ------- + + this.notifications = function(cb) { + _request("GET", "/notifications", null, function(err, res) { + cb(err,res); + }); + }; + // Show user information // ------- @@ -111,7 +145,8 @@ // ------- 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); }); }; @@ -129,7 +164,8 @@ // ------- 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); }); }; @@ -151,6 +187,17 @@ cb(err, res); }); }; + + // Create a repo + // ------- + this.createRepo = function(options, cb) { + _request("POST", "/user/repos", options, cb); + }; + + + + + }; @@ -169,6 +216,14 @@ "sha": null }; + + // Delete a repo + // -------- + + this.deleteRepo = function(cb) { + _request("DELETE", repoPath, options, cb); + }; + // Uses the cache if branch has not been changed // ------- @@ -223,6 +278,71 @@ }); }; + // 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) { + if (_.isFunction(state)) { + cb = state; + state = undefined; + } + + _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); + }); + }; + + // List all issues of a respository + // ------- + + this.listIssues = function(options, cb) { + if (_.isFunction(options)) { + cb = options; + options = undefined; + } + + var params = _.map(options, function(value, key) { + return encodeURIComponent(key) + '=' + encodeURIComponent(value); + }).join('&'); + + _request("GET", repoPath + "/issues" + (params ? "?" + params : ""), null, function(err, issues) { + if (err) return cb(err); + cb(null, issues); + }); + }, + + + // 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); + }); + }; + // Retrieve the contents of a blob // ------- @@ -234,13 +354,10 @@ // ------- this.getSha = function(branch, path, cb) { - // Just use head if path is empty - if (path === "") return that.getRef("heads/"+branch, cb); - that.getTree(branch+"?recursive=true", function(err, tree) { - var file = _.select(tree, function(file) { - return file.path === path; - })[0]; - cb(null, file ? file.sha : null); + if (!path || path === "") return that.getRef("heads/"+branch, cb); + _request("GET", repoPath + "/contents/"+path, {ref: branch}, function(err, pathContent) { + if (err) return cb(err); + cb(null, pathContent.sha); }); }; @@ -345,8 +462,8 @@ // Get contents // -------- - this.contents = function(branch, path, cb) { - _request("GET", repoPath + "/contents?ref=" + branch, { path: path }, cb); + this.contents = function(ref, path, cb) { + _request("GET", repoPath + "/contents/"+path, { ref: ref }, cb); }; // Fork repository @@ -363,38 +480,68 @@ _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 // ------- this.read = function(branch, path, cb) { - that.getSha(branch, path, function(err, sha) { - if (!sha) return cb("not found", null); - that.getBlob(sha, function(err, content) { - cb(err, content, sha); - }); + _request("GET", repoPath + "/contents/"+path, {ref: branch}, function(err, obj) { + if (err && err.error === 404) return cb("not found", null, null); + + if (err) return cb(err); + var sha = obj.sha + , content = Base64.decode(obj.content); + + cb(null, content, sha); }); }; - // Remove a file from the tree + + // Remove a file // ------- this.remove = function(branch, path, cb) { - updateTree(branch, function(err, latestCommit) { - that.getTree(latestCommit+"?recursive=true", function(err, tree) { - // Update Tree - var newTree = _.reject(tree, function(ref) { return ref.path === path; }); - _.each(newTree, function(ref) { - if (ref.type === "tree") delete ref.sha; - }); - - that.postTree(newTree, function(err, rootTree) { - that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) { - that.updateHead(branch, commit, function(err) { - cb(err); - }); - }); - }); - }); + that.getSha(branch, path, function(err, sha) { + if (err) return cb(err); + _request("DELETE", repoPath + "/contents/" + path, { + message: path + " is removed", + sha: sha, + branch: branch + }, cb); }); }; @@ -425,20 +572,17 @@ // ------- this.write = function(branch, path, content, message, cb) { - updateTree(branch, function(err, latestCommit) { - if (err) return cb(err); - that.postBlob(content, function(err, blob) { - if (err) return cb(err); - that.updateTree(latestCommit, path, blob, function(err, tree) { - if (err) return cb(err); - that.commit(latestCommit, tree, message, function(err, commit) { - if (err) return cb(err); - that.updateHead(branch, commit, cb); - }); - }); - }); + that.getSha(branch, path, function(err, sha) { + if (err && err.error!=404) return cb(err); + _request("PUT", repoPath + "/contents/" + path, { + message: message, + content: Base64.encode(content), + branch: branch, + sha: sha + }, cb); }); }; + }; // Gists API @@ -499,6 +643,33 @@ 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); + }); + }; }; // Top Level API diff --git a/package.json b/package.json index 0ca8adc3..1ae32b47 100644 --- a/package.json +++ b/package.json @@ -36,63 +36,14 @@ "testling": { "files": "test/*_test.js", "browsers": [ - "iexplore/10.0", - "iexplore/9.0", - "chrome/6.0", - "chrome/7.0", - "chrome/8.0", - "chrome/9.0", - "chrome/10.0", - "chrome/11.0", - "chrome/12.0", - "chrome/13.0", - "chrome/14.0", - "chrome/15.0", - "chrome/16.0", - "chrome/17.0", - "chrome/18.0", - "chrome/19.0", - "chrome/20.0", - "chrome/21.0", - "chrome/22.0", - "chrome/23.0", - "chrome/24.0", - "chrome/25.0", - "firefox/3.0", - "firefox/3.5", - "firefox/3.6", - "firefox/4.0", - "firefox/5.0", - "firefox/6.0", - "firefox/7.0", - "firefox/8.0", - "firefox/9.0", - "firefox/10.0", - "firefox/11.0", - "firefox/12.0", - "firefox/13.0", - "firefox/14.0", - "firefox/15.0", - "firefox/16.0", - "firefox/17.0", - "firefox/18.0", - "firefox/19.0", - "opera/10.0", - "opera/10.5", - "opera/11.0", - "opera/11.5", - "opera/11.6", - "opera/12.0", - "safari/4.0", - "safari/5.0.5", - "safari/5.1", - "firefox/nightly", - "opera/next", - "chrome/canary", - "iphone/6.0", - "ipad/6.0", - "safari/6.0", - "android-browser/4.2" + "iexplore/9.0..latest", + "chrome/18.0..latest", + "firefox/15.0..latest", + "opera/11.0..latest", + "safari/5.0.5..latest", + "iphone/6.0..latest", + "ipad/6.0..latest", + "android-browser/4.2..latest" ], "harness": "mocha" } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy