diff --git a/.gitignore b/.gitignore
index 6d462a19..db3cda2f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,8 @@ docs/
dist/
coverage/
node_modules/
-
+.nyc_output/
+/out/
.DS_Store
npm-debug.log
sauce.json
diff --git a/.npmignore b/.npmignore
index 15fff57d..fe969272 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,6 +1,7 @@
docs/
coverage/
node_modules/
-
+lib/
+.nyc_output/
.DS_Store
sauce.json
diff --git a/.travis.yml b/.travis.yml
index c00581ca..6b6bad91 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,25 +1,21 @@
sudo: false
language: node_js
node_js:
- - '6'
- - '5'
- - '4'
- - '0.12'
-
+- '11'
+- '10'
+- '8'
cache:
directories:
- - node_modules
+ - node_modules
before_install: npm install -g npm@latest
before_script:
- - npm run lint
- # - npm run build # will need this when we do sauce testing of compiled files
+- npm run lint
script:
- - npm test
- # - npm run test-dist # test the compiled files
-# after_success:
-# - npm run codecov # disabled temporarialy while I work out how to generate accurate coverage of ES2015 code
+- npm run test-coverage
+after_success:
+- npm run codecov
before_deploy:
- - npm run build
+- npm run build
deploy:
provider: npm
skip_cleanup: true
@@ -27,4 +23,4 @@ deploy:
tags: true
email: clayreimann@gmail.com
api_key:
- secure: TZHqJ9Kh2Qf0GAVDjEOQ01Ez6rGMYHKwVLOKTbnb7nSzF7iiGNT4UwzvYawm0T9p1k7X1WOqW3l7OEbIwoKl7/9azT4BBJm7qUMRfB9Zio5cL3rKubJVz7+LEEIW4iBeDWLanhUDgy9BO2JKCt8bfp/U2tltgXtu9Fm/UFPALI8=
+ secure: WnLh1m02aF7NvFNILCZ8KsjPuDeSddQI87y8dwAixStr2FhQyz8FIKZN2Qj1N1Q9ZJvBETe5HWs1c9yOjTKBkD0d/eU2hlpnB9WXEFRJVDjiUuMnpAMMvuqTZwYg6kXq5N+of95PX58AYiBiV/qwsdUr/MgjEEYLt5UZgRYQRvE=
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 925b11c1..035944d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Change Log
+## 2.4.0 - 2016/09/16
+### Features
+* add `Issue.createLabel`
+* add `Repository.createKey`
+* add `Repository.deleteKey`
+* add `Repository.getBranch`
+* add `Repository.listKeys`
+* add `Repository.getKey`
+* add `Repository.updatePullRequest`
+* deprecate `Repository.updatePullRequst`
+
+### Fixes
+* Request URL for deleting a hook (`Repository.deleteHook`)
+
## 2.3.0 - 2016/06/17
### Features
* add `Repository.mergePullRequest`
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..09827eb1
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at . All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/README.md b/README.md
index 28f61b90..5e5f527f 100644
--- a/README.md
+++ b/README.md
@@ -1,51 +1,23 @@
+# Maintainers wanted
+[Apply within](https://github.com/github-tools/github/issues/539)
+
# Github.js
[][npm-package]
[][npm-package]
-[][gitter]
-[][travis-ci]
-
-
-Github.js provides a minimal higher-level wrapper around Github's API. It was concieved in the context of
-[Prose][prose], a content editor for GitHub.
-
-## [Read the docs][docs]
-
-## Installation
-Github.js is available from `npm` or [npmcdn][npmcdn].
-
-```shell
-npm install github-api
-```
-
-```html
-
-
-
-
-
-```
-
-## Compatibility
-Github.js is tested on Node:
-* 6.x
-* 5.x
-* 4.x
-* 0.12
-
-## GitHub Tools
+[][gitter]
+[][travis-ci]
+[][codecov]
-The team behind Github.js has created a whole organization, called [GitHub Tools](https://github.com/github-tools),
-dedicated to GitHub and its API. In the near future this repository could be moved under the GitHub Tools organization
-as well. In the meantime, we recommend you to take a look at other projects of the organization.
+`Github.js` provides a minimal higher-level wrapper around Github's API.
-## Samples
+## Usage
```javascript
/*
Data can be retrieved from the API either using callbacks (as in versions < 1.0)
- or using a new promise-based API. For now the promise-based API just returns the
- raw HTTP request promise; this might change in the next version.
+ or using a new promise-based API. The promise-based API returns the raw Axios
+ request promise.
*/
import GitHub from 'github-api';
@@ -57,62 +29,98 @@ gist.create({
description: 'My first gist',
files: {
"file1.txt": {
- contents: "Aren't gists great!"
+ content: "Aren't gists great!"
}
}
}).then(function({data}) {
// Promises!
- let gistJson = data;
- gist.read(function(err, gist, xhr) {
- // if no error occurred then err == null
-
- // gistJson === httpResponse.data
-
- // xhr === httpResponse
- });
+ let createdGist = data;
+ return gist.read();
+}).then(function({data}) {
+ let retrievedGist = data;
+ // do interesting things
});
```
```javascript
-import GitHub from 'github-api';
+var GitHub = require('github-api');
// basic auth
-const gh = new GitHub({
+var gh = new GitHub({
username: 'FOO',
password: 'NotFoo'
+ /* also acceptable:
+ token: 'MY_OAUTH_TOKEN'
+ */
});
-const me = gh.getUser();
-me.listNotifications(function(err, notifcations) {
+var me = gh.getUser(); // no user specified defaults to the user for whom credentials were provided
+me.listNotifications(function(err, notifications) {
// do some stuff
});
-const clayreimann = gh.getUser('clayreimann');
-clayreimann.getStarredRepos()
- .then(function({data: reposJson}) {
- // do stuff with reposJson
- });
+var clayreimann = gh.getUser('clayreimann');
+clayreimann.listStarredRepos(function(err, repos) {
+ // look at all the starred repos!
+});
```
-```javascript
-var GitHub = require('github-api');
+## API Documentation
-// token auth
-var gh = new GitHub({
- token: 'MY_OAUTH_TOKEN'
-});
+[API documentation][docs] is hosted on github pages, and is generated from JSDoc; any contributions
+should include updated JSDoc.
+
+## Installation
+`Github.js` is available from `npm` or [unpkg][unpkg].
-var yahoo = gh.getOrganization('yahoo');
-yahoo.getRepos(function(err, repos) {
- // look at all the repos!
-})
+```shell
+npm install github-api
```
-[codecov]: https://codecov.io/github/michael/github?branch=master
-[docs]: http://michael.github.io/github/
-[gitter]: https://gitter.im/michael/github
+```html
+
+
+
+
+
+```
+
+## Compatibility
+`Github.js` is tested on node's LTS and current versions.
+
+[codecov]: https://codecov.io/github/github-tools/github?branch=master
+[docs]: http://github-tools.github.io/github/
+[gitter]: https://gitter.im/github-tools/github
[npm-package]: https://www.npmjs.com/package/github-api/
-[npmcdn]: https://npmcdn.com/github-api/
-[prose]: http://prose.io
-[travis-ci]: https://travis-ci.org/michael/github
-[xhr-link]: http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
+[unpkg]: https://unpkg.com/github-api/
+[travis-ci]: https://travis-ci.org/github-tools/github
+
+## Contributing
+
+We welcome contributions of all types! This section will guide you through setting up your development environment.
+
+### Setup
+
+1. [Install Node](https://nodejs.org/en/) version 8,10 or 11. It can often help to use a Node version switcher such as [NVM](https://github.com/nvm-sh/nvm).
+2. Fork this repo to your GitHub account.
+3. Clone the fork to your development machine (`git clone https://github.com/{YOUR_USERNAME}/github`).
+4. From the root of the cloned repo, run `npm install`.
+5. Email jaredrewerts@gmail.com with the subject **GitHub API - Personal Access Token Request**
+
+A personal access token for our test user, @github-tools-test, will be generated for you.
+
+6. Set the environment variable `GHTOOLS_USER` to `github-tools-test`.
+
+`export GHTOOLS_USER=github-tools-test`
+
+7. Set the environment variable `GHTOOLS_PASSWORD` to the personal access token that was generated for you.
+
+`export GHTOOLS_PASSWORD={YOUR_PAT}`
+
+**NOTE** Windows users can use [this guide](http://www.dowdandassociates.com/blog/content/howto-set-an-environment-variable-in-windows-command-line-and-registry/) to learn about setting environment variables on Windows.
+
+### Tests
+
+The main way we write code for `github-api` is using test-driven development. We use Mocha to run our tests. Given that the bulk of this library is just interacting with GitHub's API, nearly all of our tests are integration tests.
+
+To run the test suite, run `npm run test`.
diff --git a/gulpfile.babel.js b/gulpfile.babel.js
index c3d669f5..9965f7aa 100644
--- a/gulpfile.babel.js
+++ b/gulpfile.babel.js
@@ -13,7 +13,7 @@ import uglify from 'gulp-uglify';
const ALL_SOURCES = [
'*.js',
'lib/*.js',
- 'test/*.js'
+ 'test/*.js',
];
gulp.task('lint', function() {
@@ -33,13 +33,13 @@ gulp.task('build', [
'build:external:min',
'build:bundled:debug',
'build:external:debug',
- 'build:components'
+ 'build:components',
]);
const bundledConfig = {
debug: true,
entries: 'lib/GitHub.js',
- standalone: 'GitHub'
+ standalone: 'GitHub',
};
const externalConfig = {
debug: true,
@@ -50,9 +50,9 @@ const externalConfig = {
'js-base64',
'es6-promise',
'debug',
- 'utf8'
+ 'utf8',
],
- bundleExternal: false
+ bundleExternal: false,
};
gulp.task('build:bundled:min', function() {
return buildBundle(bundledConfig, '.bundle.min.js', true);
@@ -82,7 +82,7 @@ function buildBundle(options, extname, minify) {
.pipe(source('GitHub.js'))
.pipe(buffer())
.pipe(sourcemaps.init({
- loadMaps: true
+ loadMaps: true,
}));
if (minify) {
diff --git a/lib/Gist.js b/lib/Gist.js
index 14b71322..60f18cf5 100644
--- a/lib/Gist.js
+++ b/lib/Gist.js
@@ -108,6 +108,27 @@ class Gist extends Requestable {
return this._request204or404(`/gists/${this.__id}/star`, null, cb);
}
+ /**
+ * List the gist's commits
+ * @see https://developer.github.com/v3/gists/#list-gist-commits
+ * @param {Requestable.callback} [cb] - will receive the array of commits
+ * @return {Promise} - the Promise for the http request
+ */
+ listCommits(cb) {
+ return this._requestAllPages(`/gists/${this.__id}/commits`, null, cb);
+ }
+
+ /**
+ * Fetch one of the gist's revision.
+ * @see https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist
+ * @param {string} revision - the id of the revision
+ * @param {Requestable.callback} [cb] - will receive the revision
+ * @return {Promise} - the Promise for the http request
+ */
+ getRevision(revision, cb) {
+ return this._request('GET', `/gists/${this.__id}/${revision}`, null, cb);
+ }
+
/**
* List the gist's comments
* @see https://developer.github.com/v3/gists/comments/#list-comments-on-a-gist
diff --git a/lib/GitHub.js b/lib/GitHub.js
index 944fc868..7c3ac24c 100644
--- a/lib/GitHub.js
+++ b/lib/GitHub.js
@@ -15,6 +15,7 @@ import Repository from './Repository';
import Organization from './Organization';
import Team from './Team';
import Markdown from './Markdown';
+import Project from './Project';
/**
* GitHub encapsulates the functionality to create various API wrapper objects.
@@ -33,7 +34,7 @@ class GitHub {
/**
* Create a new Gist wrapper
- * @param {number} [id] - the id for the gist, leave undefined when creating a new gist
+ * @param {string} [id] - the id for the gist, leave undefined when creating a new gist
* @return {Gist}
*/
getGist(id) {
@@ -70,7 +71,7 @@ class GitHub {
/**
* Create a new Repository wrapper
- * @param {string} user - the user who owns the respository
+ * @param {string} user - the user who owns the repository
* @param {string} repo - the name of the repository
* @return {Repository}
*/
@@ -80,7 +81,7 @@ class GitHub {
/**
* Create a new Issue wrapper
- * @param {string} user - the user who owns the respository
+ * @param {string} user - the user who owns the repository
* @param {string} repo - the name of the repository
* @return {Issue}
*/
@@ -113,6 +114,15 @@ class GitHub {
return new Markdown(this.__auth, this.__apiBase);
}
+ /**
+ * Create a new Project wrapper
+ * @param {string} id - the id of the project
+ * @return {Project}
+ */
+ getProject(id) {
+ return new Project(id, this.__auth, this.__apiBase);
+ }
+
/**
* Computes the full repository name
* @param {string} user - the username (or the full name)
diff --git a/lib/Issue.js b/lib/Issue.js
index d5c03d4a..c0151b5f 100644
--- a/lib/Issue.js
+++ b/lib/Issue.js
@@ -150,7 +150,7 @@ class Issue extends Requestable {
* Get a milestone
* @see https://developer.github.com/v3/issues/milestones/#get-a-single-milestone
* @param {string} milestone - the id of the milestone to fetch
- * @param {Requestable.callback} [cb] - will receive the array of milestones
+ * @param {Requestable.callback} [cb] - will receive the milestone
* @return {Promise} - the promise for the http request
*/
getMilestone(milestone, cb) {
@@ -161,7 +161,7 @@ class Issue extends Requestable {
* Create a new milestone
* @see https://developer.github.com/v3/issues/milestones/#create-a-milestone
* @param {Object} milestoneData - the milestone definition
- * @param {Requestable.callback} [cb] - will receive the array of milestones
+ * @param {Requestable.callback} [cb] - will receive the milestone
* @return {Promise} - the promise for the http request
*/
createMilestone(milestoneData, cb) {
@@ -173,7 +173,7 @@ class Issue extends Requestable {
* @see https://developer.github.com/v3/issues/milestones/#update-a-milestone
* @param {string} milestone - the id of the milestone to edit
* @param {Object} milestoneData - the updates to make to the milestone
- * @param {Requestable.callback} [cb] - will receive the array of milestones
+ * @param {Requestable.callback} [cb] - will receive the updated milestone
* @return {Promise} - the promise for the http request
*/
editMilestone(milestone, milestoneData, cb) {
@@ -184,12 +184,68 @@ class Issue extends Requestable {
* Delete a milestone (this is distinct from closing a milestone)
* @see https://developer.github.com/v3/issues/milestones/#delete-a-milestone
* @param {string} milestone - the id of the milestone to delete
- * @param {Requestable.callback} [cb] - will receive the array of milestones
+ * @param {Requestable.callback} [cb] - will receive the status
* @return {Promise} - the promise for the http request
*/
deleteMilestone(milestone, cb) {
return this._request('DELETE', `/repos/${this.__repository}/milestones/${milestone}`, null, cb);
}
+
+ /**
+ * Create a new label
+ * @see https://developer.github.com/v3/issues/labels/#create-a-label
+ * @param {Object} labelData - the label definition
+ * @param {Requestable.callback} [cb] - will receive the object representing the label
+ * @return {Promise} - the promise for the http request
+ */
+ createLabel(labelData, cb) {
+ return this._request('POST', `/repos/${this.__repository}/labels`, labelData, cb);
+ }
+
+ /**
+ * List the labels for the repository
+ * @see https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository
+ * @param {Object} options - filtering options
+ * @param {Requestable.callback} [cb] - will receive the array of labels
+ * @return {Promise} - the promise for the http request
+ */
+ listLabels(options, cb) {
+ return this._request('GET', `/repos/${this.__repository}/labels`, options, cb);
+ }
+
+ /**
+ * Get a label
+ * @see https://developer.github.com/v3/issues/labels/#get-a-single-label
+ * @param {string} label - the name of the label to fetch
+ * @param {Requestable.callback} [cb] - will receive the label
+ * @return {Promise} - the promise for the http request
+ */
+ getLabel(label, cb) {
+ return this._request('GET', `/repos/${this.__repository}/labels/${label}`, null, cb);
+ }
+
+ /**
+ * Edit a label
+ * @see https://developer.github.com/v3/issues/labels/#update-a-label
+ * @param {string} label - the name of the label to edit
+ * @param {Object} labelData - the updates to make to the label
+ * @param {Requestable.callback} [cb] - will receive the updated label
+ * @return {Promise} - the promise for the http request
+ */
+ editLabel(label, labelData, cb) {
+ return this._request('PATCH', `/repos/${this.__repository}/labels/${label}`, labelData, cb);
+ }
+
+ /**
+ * Delete a label
+ * @see https://developer.github.com/v3/issues/labels/#delete-a-label
+ * @param {string} label - the name of the label to delete
+ * @param {Requestable.callback} [cb] - will receive the status
+ * @return {Promise} - the promise for the http request
+ */
+ deleteLabel(label, cb) {
+ return this._request('DELETE', `/repos/${this.__repository}/labels/${label}`, null, cb);
+ }
}
module.exports = Issue;
diff --git a/lib/Markdown.js b/lib/Markdown.js
index cb84851d..ebfbb512 100644
--- a/lib/Markdown.js
+++ b/lib/Markdown.js
@@ -8,11 +8,11 @@
import Requestable from './Requestable';
/**
- * RateLimit allows users to query their rate-limit status
+ * Renders html from Markdown text
*/
class Markdown extends Requestable {
/**
- * construct a RateLimit
+ * construct a Markdown
* @param {Requestable.auth} auth - the credentials to authenticate to GitHub
* @param {string} [apiBase] - the base Github API URL
* @return {Promise} - the promise for the http request
@@ -32,7 +32,7 @@ class Markdown extends Requestable {
* @return {Promise} - the promise for the http request
*/
render(options, cb) {
- return this._request('POST', '/markdown', options, cb);
+ return this._request('POST', '/markdown', options, cb, true);
}
}
diff --git a/lib/Organization.js b/lib/Organization.js
index 78354a8c..0a8177b4 100644
--- a/lib/Organization.js
+++ b/lib/Organization.js
@@ -93,6 +93,29 @@ class Organization extends Requestable {
createTeam(options, cb) {
return this._request('POST', `/orgs/${this.__name}/teams`, options, cb);
}
+
+ /**
+ * Get information about all projects
+ * @see https://developer.github.com/v3/projects/#list-organization-projects
+ * @param {Requestable.callback} [cb] - will receive the list of projects
+ * @return {Promise} - the promise for the http request
+ */
+ listProjects(cb) {
+ return this._requestAllPages(`/orgs/${this.__name}/projects`, {AcceptHeader: 'inertia-preview'}, cb);
+ }
+
+ /**
+ * Create a new project
+ * @see https://developer.github.com/v3/repos/projects/#create-a-project
+ * @param {Object} options - the description of the project
+ * @param {Requestable.callback} cb - will receive the newly created project
+ * @return {Promise} - the promise for the http request
+ */
+ createProject(options, cb) {
+ options = options || {};
+ options.AcceptHeader = 'inertia-preview';
+ return this._request('POST', `/orgs/${this.__name}/projects`, options, cb);
+ }
}
module.exports = Organization;
diff --git a/lib/Project.js b/lib/Project.js
new file mode 100644
index 00000000..ab31a078
--- /dev/null
+++ b/lib/Project.js
@@ -0,0 +1,236 @@
+/**
+ * @file
+ * @copyright 2013 Michael Aufreiter (Development Seed) and 2016 Yahoo Inc.
+ * @license Licensed under {@link https://spdx.org/licenses/BSD-3-Clause-Clear.html BSD-3-Clause-Clear}.
+ * Github.js is freely distributable.
+ */
+
+import Requestable from './Requestable';
+
+/**
+ * Project encapsulates the functionality to create, query, and modify cards and columns.
+ */
+class Project extends Requestable {
+ /**
+ * Create a Project.
+ * @param {string} id - the id of the project
+ * @param {Requestable.auth} [auth] - information required to authenticate to Github
+ * @param {string} [apiBase=https://api.github.com] - the base Github API URL
+ */
+ constructor(id, auth, apiBase) {
+ super(auth, apiBase, 'inertia-preview');
+ this.__id = id;
+ }
+
+ /**
+ * Get information about a project
+ * @see https://developer.github.com/v3/projects/#get-a-project
+ * @param {Requestable.callback} cb - will receive the project information
+ * @return {Promise} - the promise for the http request
+ */
+ getProject(cb) {
+ return this._request('GET', `/projects/${this.__id}`, null, cb);
+ }
+
+ /**
+ * Edit a project
+ * @see https://developer.github.com/v3/projects/#update-a-project
+ * @param {Object} options - the description of the project
+ * @param {Requestable.callback} cb - will receive the modified project
+ * @return {Promise} - the promise for the http request
+ */
+ updateProject(options, cb) {
+ return this._request('PATCH', `/projects/${this.__id}`, options, cb);
+ }
+
+ /**
+ * Delete a project
+ * @see https://developer.github.com/v3/projects/#delete-a-project
+ * @param {Requestable.callback} cb - will receive true if the operation is successful
+ * @return {Promise} - the promise for the http request
+ */
+ deleteProject(cb) {
+ return this._request('DELETE', `/projects/${this.__id}`, null, cb);
+ }
+
+ /**
+ * Get information about all columns of a project
+ * @see https://developer.github.com/v3/projects/columns/#list-project-columns
+ * @param {Requestable.callback} [cb] - will receive the list of columns
+ * @return {Promise} - the promise for the http request
+ */
+ listProjectColumns(cb) {
+ return this._requestAllPages(`/projects/${this.__id}/columns`, null, cb);
+ }
+
+ /**
+ * Get information about a column
+ * @see https://developer.github.com/v3/projects/columns/#get-a-project-column
+ * @param {string} colId - the id of the column
+ * @param {Requestable.callback} cb - will receive the column information
+ * @return {Promise} - the promise for the http request
+ */
+ getProjectColumn(colId, cb) {
+ return this._request('GET', `/projects/columns/${colId}`, null, cb);
+ }
+
+ /**
+ * Create a new column
+ * @see https://developer.github.com/v3/projects/columns/#create-a-project-column
+ * @param {Object} options - the description of the column
+ * @param {Requestable.callback} cb - will receive the newly created column
+ * @return {Promise} - the promise for the http request
+ */
+ createProjectColumn(options, cb) {
+ return this._request('POST', `/projects/${this.__id}/columns`, options, cb);
+ }
+
+ /**
+ * Edit a column
+ * @see https://developer.github.com/v3/projects/columns/#update-a-project-column
+ * @param {string} colId - the column id
+ * @param {Object} options - the description of the column
+ * @param {Requestable.callback} cb - will receive the modified column
+ * @return {Promise} - the promise for the http request
+ */
+ updateProjectColumn(colId, options, cb) {
+ return this._request('PATCH', `/projects/columns/${colId}`, options, cb);
+ }
+
+ /**
+ * Delete a column
+ * @see https://developer.github.com/v3/projects/columns/#delete-a-project-column
+ * @param {string} colId - the column to be deleted
+ * @param {Requestable.callback} cb - will receive true if the operation is successful
+ * @return {Promise} - the promise for the http request
+ */
+ deleteProjectColumn(colId, cb) {
+ return this._request('DELETE', `/projects/columns/${colId}`, null, cb);
+ }
+
+ /**
+ * Move a column
+ * @see https://developer.github.com/v3/projects/columns/#move-a-project-column
+ * @param {string} colId - the column to be moved
+ * @param {string} position - can be one of first, last, or after:,
+ * where is the id value of a column in the same project.
+ * @param {Requestable.callback} cb - will receive true if the operation is successful
+ * @return {Promise} - the promise for the http request
+ */
+ moveProjectColumn(colId, position, cb) {
+ return this._request(
+ 'POST',
+ `/projects/columns/${colId}/moves`,
+ {position: position},
+ cb
+ );
+ }
+
+ /**
+ * Get information about all cards of a project
+ * @see https://developer.github.com/v3/projects/cards/#list-project-cards
+ * @param {Requestable.callback} [cb] - will receive the list of cards
+ * @return {Promise} - the promise for the http request
+ */
+ listProjectCards(cb) {
+ return this.listProjectColumns()
+ .then(({data}) => {
+ return Promise.all(data.map((column) => {
+ return this._requestAllPages(`/projects/columns/${column.id}/cards`, null);
+ }));
+ }).then((cardsInColumns) => {
+ const cards = cardsInColumns.reduce((prev, {data}) => {
+ prev.push(...data);
+ return prev;
+ }, []);
+ if (cb) {
+ cb(null, cards);
+ }
+ return cards;
+ }).catch((err) => {
+ if (cb) {
+ cb(err);
+ return;
+ }
+ throw err;
+ });
+ }
+
+ /**
+ * Get information about all cards of a column
+ * @see https://developer.github.com/v3/projects/cards/#list-project-cards
+ * @param {string} colId - the id of the column
+ * @param {Requestable.callback} [cb] - will receive the list of cards
+ * @return {Promise} - the promise for the http request
+ */
+ listColumnCards(colId, cb) {
+ return this._requestAllPages(`/projects/columns/${colId}/cards`, null, cb);
+ }
+
+ /**
+ * Get information about a card
+ * @see https://developer.github.com/v3/projects/cards/#get-a-project-card
+ * @param {string} cardId - the id of the card
+ * @param {Requestable.callback} cb - will receive the card information
+ * @return {Promise} - the promise for the http request
+ */
+ getProjectCard(cardId, cb) {
+ return this._request('GET', `/projects/columns/cards/${cardId}`, null, cb);
+ }
+
+ /**
+ * Create a new card
+ * @see https://developer.github.com/v3/projects/cards/#create-a-project-card
+ * @param {string} colId - the column id
+ * @param {Object} options - the description of the card
+ * @param {Requestable.callback} cb - will receive the newly created card
+ * @return {Promise} - the promise for the http request
+ */
+ createProjectCard(colId, options, cb) {
+ return this._request('POST', `/projects/columns/${colId}/cards`, options, cb);
+ }
+
+ /**
+ * Edit a card
+ * @see https://developer.github.com/v3/projects/cards/#update-a-project-card
+ * @param {string} cardId - the card id
+ * @param {Object} options - the description of the card
+ * @param {Requestable.callback} cb - will receive the modified card
+ * @return {Promise} - the promise for the http request
+ */
+ updateProjectCard(cardId, options, cb) {
+ return this._request('PATCH', `/projects/columns/cards/${cardId}`, options, cb);
+ }
+
+ /**
+ * Delete a card
+ * @see https://developer.github.com/v3/projects/cards/#delete-a-project-card
+ * @param {string} cardId - the card to be deleted
+ * @param {Requestable.callback} cb - will receive true if the operation is successful
+ * @return {Promise} - the promise for the http request
+ */
+ deleteProjectCard(cardId, cb) {
+ return this._request('DELETE', `/projects/columns/cards/${cardId}`, null, cb);
+ }
+
+ /**
+ * Move a card
+ * @see https://developer.github.com/v3/projects/cards/#move-a-project-card
+ * @param {string} cardId - the card to be moved
+ * @param {string} position - can be one of top, bottom, or after:,
+ * where is the id value of a card in the same project.
+ * @param {string} colId - the id value of a column in the same project.
+ * @param {Requestable.callback} cb - will receive true if the operation is successful
+ * @return {Promise} - the promise for the http request
+ */
+ moveProjectCard(cardId, position, colId, cb) {
+ return this._request(
+ 'POST',
+ `/projects/columns/cards/${cardId}/moves`,
+ {position: position, column_id: colId}, // eslint-disable-line camelcase
+ cb
+ );
+ }
+}
+
+module.exports = Project;
diff --git a/lib/Repository.js b/lib/Repository.js
index de77ae48..74452dde 100644
--- a/lib/Repository.js
+++ b/lib/Repository.js
@@ -8,13 +8,13 @@
import Requestable from './Requestable';
import Utf8 from 'utf8';
import {
- Base64
+ Base64,
} from 'js-base64';
import debug from 'debug';
const log = debug('github:repository');
/**
- * Respository encapsulates the functionality to create, query, and modify files.
+ * Repository encapsulates the functionality to create, query, and modify files.
*/
class Repository extends Requestable {
/**
@@ -28,7 +28,7 @@ class Repository extends Requestable {
this.__fullname = fullname;
this.__currentTree = {
branch: null,
- sha: null
+ sha: null,
};
}
@@ -152,6 +152,17 @@ class Repository extends Requestable {
return this._request('GET', `/repos/${this.__fullname}/git/blobs/${sha}`, null, cb, 'raw');
}
+ /**
+ * Get a single branch
+ * @see https://developer.github.com/v3/repos/branches/#get-branch
+ * @param {string} branch - the name of the branch to fetch
+ * @param {Requestable.callback} cb - will receive the branch from the API
+ * @returns {Promise} - the promise for the http request
+ */
+ getBranch(branch, cb) {
+ return this._request('GET', `/repos/${this.__fullname}/branches/${branch}`, null, cb);
+ }
+
/**
* Get a commit from the repository
* @see https://developer.github.com/v3/repos/commits/#get-a-single-commit
@@ -177,13 +188,33 @@ class Repository extends Requestable {
*/
listCommits(options, cb) {
options = options || {};
-
+ if (typeof options === 'function') {
+ cb = options;
+ options = {};
+ }
options.since = this._dateToISO(options.since);
options.until = this._dateToISO(options.until);
return this._request('GET', `/repos/${this.__fullname}/commits`, options, cb);
}
+ /**
+ * List the commits on a pull request
+ * @see https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository
+ * @param {number|string} number - the number of the pull request to list the commits
+ * @param {Object} [options] - the filtering options for commits
+ * @param {Requestable.callback} [cb] - will receive the commits information
+ * @return {Promise} - the promise for the http request
+ */
+ listCommitsOnPR(number, options, cb) {
+ options = options || {};
+ if (typeof options === 'function') {
+ cb = options;
+ options = {};
+ }
+ return this._request('GET', `/repos/${this.__fullname}/pulls/${number}/commits`, options, cb);
+ }
+
/**
* Gets a single commit information for a repository
* @see https://developer.github.com/v3/repos/commits/#get-a-single-commit
@@ -220,6 +251,17 @@ class Repository extends Requestable {
return this._request('GET', `/repos/${this.__fullname}/commits/${sha}/statuses`, null, cb);
}
+ /**
+ * Get the combined view of commit statuses for a particular sha, branch, or tag
+ * @see https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref
+ * @param {string} sha - the sha, branch, or tag to get the combined status for
+ * @param {Requestable.callback} cb - will receive the combined status
+ * @returns {Promise} - the promise for the http request
+ */
+ getCombinedStatus(sha, cb) {
+ return this._request('GET', `/repos/${this.__fullname}/commits/${sha}/status`, null, cb);
+ }
+
/**
* Get a description of a git tree
* @see https://developer.github.com/v3/git/trees/#get-a-tree
@@ -255,21 +297,21 @@ class Repository extends Requestable {
log('contet is a string');
return {
content: Utf8.encode(content),
- encoding: 'utf-8'
+ encoding: 'utf-8',
};
} else if (typeof Buffer !== 'undefined' && content instanceof Buffer) {
log('We appear to be in Node');
return {
content: content.toString('base64'),
- encoding: 'base64'
+ encoding: 'base64',
};
} else if (typeof Blob !== 'undefined' && content instanceof Blob) {
log('We appear to be in the browser');
return {
content: Base64.encode(content),
- encoding: 'base64'
+ encoding: 'base64',
};
} else { // eslint-disable-line
@@ -295,8 +337,8 @@ class Repository extends Requestable {
path: path,
sha: blobSHA,
mode: '100644',
- type: 'blob'
- }]
+ type: 'blob',
+ }],
};
return this._request('POST', `/repos/${this.__fullname}/git/trees`, newTree, cb);
@@ -313,7 +355,7 @@ class Repository extends Requestable {
createTree(tree, baseSHA, cb) {
return this._request('POST', `/repos/${this.__fullname}/git/trees`, {
tree,
- base_tree: baseSHA // eslint-disable-line
+ base_tree: baseSHA, // eslint-disable-line camelcase
}, cb);
}
@@ -323,16 +365,26 @@ class Repository extends Requestable {
* @param {string} parent - the SHA of the parent commit
* @param {string} tree - the SHA of the tree for this commit
* @param {string} message - the commit message
+ * @param {Object} [options] - commit options
+ * @param {Object} [options.author] - the author of the commit
+ * @param {Object} [options.commiter] - the committer
* @param {Requestable.callback} cb - will receive the commit that is created
* @return {Promise} - the promise for the http request
*/
- commit(parent, tree, message, cb) {
+ commit(parent, tree, message, options, cb) {
+ if (typeof options === 'function') {
+ cb = options;
+ options = {};
+ }
+
let data = {
message,
tree,
- parents: [parent]
+ parents: [parent],
};
+ data = Object.assign({}, options, data);
+
return this._request('POST', `/repos/${this.__fullname}/git/commits`, data, cb)
.then((response) => {
this.__currentTree.sha = response.data.sha; // Update latest commit
@@ -352,11 +404,46 @@ class Repository extends Requestable {
updateHead(ref, commitSHA, force, cb) {
return this._request('PATCH', `/repos/${this.__fullname}/git/refs/${ref}`, {
sha: commitSHA,
- force: force
+ force: force,
}, cb);
}
/**
+ * Update commit status
+ * @see https://developer.github.com/v3/repos/statuses/
+ * @param {string} commitSHA - the SHA of the commit that should be updated
+ * @param {object} options - Commit status parameters
+ * @param {string} options.state - The state of the status. Can be one of: pending, success, error, or failure.
+ * @param {string} [options.target_url] - The target URL to associate with this status.
+ * @param {string} [options.description] - A short description of the status.
+ * @param {string} [options.context] - A string label to differentiate this status among CI systems.
+ * @param {Requestable.callback} cb - will receive the updated commit back
+ * @return {Promise} - the promise for the http request
+ */
+ updateStatus(commitSHA, options, cb) {
+ return this._request('POST', `/repos/${this.__fullname}/statuses/${commitSHA}`, options, cb);
+ }
+
+ /**
+ * Update repository information
+ * @see https://developer.github.com/v3/repos/#edit
+ * @param {object} options - New parameters that will be set to the repository
+ * @param {string} options.name - Name of the repository
+ * @param {string} [options.description] - A short description of the repository
+ * @param {string} [options.homepage] - A URL with more information about the repository
+ * @param {boolean} [options.private] - Either true to make the repository private, or false to make it public.
+ * @param {boolean} [options.has_issues] - Either true to enable issues for this repository, false to disable them.
+ * @param {boolean} [options.has_wiki] - Either true to enable the wiki for this repository, false to disable it.
+ * @param {boolean} [options.has_downloads] - Either true to enable downloads, false to disable them.
+ * @param {string} [options.default_branch] - Updates the default branch for this repository.
+ * @param {Requestable.callback} cb - will receive the updated repository back
+ * @return {Promise} - the promise for the http request
+ */
+ updateRepository(options, cb) {
+ return this._request('PATCH', `/repos/${this.__fullname}`, options, cb);
+ }
+
+ /**
* Get information about the repository
* @see https://developer.github.com/v3/repos/#get
* @param {Requestable.callback} cb - will receive the information about the repository
@@ -373,6 +460,16 @@ class Repository extends Requestable {
* @return {Promise} - the promise for the http request
*/
getContributors(cb) {
+ return this._request('GET', `/repos/${this.__fullname}/contributors`, null, cb);
+ }
+
+ /**
+ * List the contributor stats to the repository
+ * @see https://developer.github.com/v3/repos/#list-contributors
+ * @param {Requestable.callback} cb - will receive the list of contributors
+ * @return {Promise} - the promise for the http request
+ */
+ getContributorStats(cb) {
return this._request('GET', `/repos/${this.__fullname}/stats/contributors`, null, cb);
}
@@ -410,7 +507,7 @@ class Repository extends Requestable {
getContents(ref, path, raw, cb) {
path = path ? `${encodeURI(path)}` : '';
return this._request('GET', `/repos/${this.__fullname}/contents/${path}`, {
- ref
+ ref,
}, cb, raw);
}
@@ -424,7 +521,7 @@ class Repository extends Requestable {
*/
getReadme(ref, raw, cb) {
return this._request('GET', `/repos/${this.__fullname}/readme`, {
- ref
+ ref,
}, cb, raw);
}
@@ -438,6 +535,18 @@ class Repository extends Requestable {
return this._request('POST', `/repos/${this.__fullname}/forks`, null, cb);
}
+ /**
+ * Fork a repository to an organization
+ * @see https://developer.github.com/v3/repos/forks/#create-a-fork
+ * @param {String} org - organization where you'd like to create the fork.
+ * @param {Requestable.callback} cb - will receive the information about the newly created fork
+ * @return {Promise} - the promise for the http request
+ *
+ */
+ forkToOrg(org, cb) {
+ return this._request('POST', `/repos/${this.__fullname}/forks?organization=${org}`, null, cb);
+ }
+
/**
* List a repository's forks
* @see https://developer.github.com/v3/repos/forks/#list-forks
@@ -467,7 +576,7 @@ class Repository extends Requestable {
let sha = response.data.object.sha;
return this.createRef({
sha,
- ref: `refs/heads/${newBranch}`
+ ref: `refs/heads/${newBranch}`,
}, cb);
});
}
@@ -491,7 +600,7 @@ class Repository extends Requestable {
* @param {Requestable.callback} [cb] - will receive the pull request information
* @return {Promise} - the promise for the http request
*/
- updatePullRequst(number, options, cb) {
+ updatePullRequest(number, options, cb) {
return this._request('PATCH', `/repos/${this.__fullname}/pulls/${number}`, options, cb);
}
@@ -547,7 +656,50 @@ class Repository extends Requestable {
* @return {Promise} - the promise for the http request
*/
deleteHook(id, cb) {
- return this._request('DELETE', `${this.__repoPath}/hooks/${id}`, null, cb);
+ return this._request('DELETE', `/repos/${this.__fullname}/hooks/${id}`, null, cb);
+ }
+
+ /**
+ * List the deploy keys for the repository
+ * @see https://developer.github.com/v3/repos/keys/#list-deploy-keys
+ * @param {Requestable.callback} cb - will receive the list of deploy keys
+ * @return {Promise} - the promise for the http request
+ */
+ listKeys(cb) {
+ return this._request('GET', `/repos/${this.__fullname}/keys`, null, cb);
+ }
+
+ /**
+ * Get a deploy key for the repository
+ * @see https://developer.github.com/v3/repos/keys/#get-a-deploy-key
+ * @param {number} id - the id of the deploy key
+ * @param {Requestable.callback} cb - will receive the details of the deploy key
+ * @return {Promise} - the promise for the http request
+ */
+ getKey(id, cb) {
+ return this._request('GET', `/repos/${this.__fullname}/keys/${id}`, null, cb);
+ }
+
+ /**
+ * Add a new deploy key to the repository
+ * @see https://developer.github.com/v3/repos/keys/#add-a-new-deploy-key
+ * @param {Object} options - the configuration describing the new deploy key
+ * @param {Requestable.callback} cb - will receive the new deploy key
+ * @return {Promise} - the promise for the http request
+ */
+ createKey(options, cb) {
+ return this._request('POST', `/repos/${this.__fullname}/keys`, options, cb);
+ }
+
+ /**
+ * Delete a deploy key
+ * @see https://developer.github.com/v3/repos/keys/#remove-a-deploy-key
+ * @param {number} id - the id of the deploy key to be deleted
+ * @param {Requestable.callback} cb - will receive true if the call is successful
+ * @return {Promise} - the promise for the http request
+ */
+ deleteKey(id, cb) {
+ return this._request('DELETE', `/repos/${this.__fullname}/keys/${id}`, null, cb);
}
/**
@@ -564,7 +716,7 @@ class Repository extends Requestable {
const deleteCommit = {
message: `Delete the file at '${path}'`,
sha: response.data.sha,
- branch
+ branch,
};
return this._request('DELETE', `/repos/${this.__fullname}/contents/${path}`, deleteCommit, cb);
});
@@ -614,6 +766,7 @@ class Repository extends Requestable {
* @return {Promise} - the promise for the http request
*/
writeFile(branch, path, content, message, options, cb) {
+ options = options || {};
if (typeof options === 'function') {
cb = options;
options = {};
@@ -625,7 +778,7 @@ class Repository extends Requestable {
message,
author: options.author,
committer: options.committer,
- content: shouldEncode ? Base64.encode(content) : content
+ content: shouldEncode ? Base64.encode(content) : content,
};
return this.getSha(branch, filePath)
@@ -704,7 +857,7 @@ class Repository extends Requestable {
/**
* Get information about a release
* @see https://developer.github.com/v3/repos/releases/#get-a-single-release
- * @param {strign} id - the id of the release
+ * @param {string} id - the id of the release
* @param {Requestable.callback} cb - will receive the release information
* @return {Promise} - the promise for the http request
*/
@@ -734,6 +887,30 @@ class Repository extends Requestable {
mergePullRequest(number, options, cb) {
return this._request('PUT', `/repos/${this.__fullname}/pulls/${number}/merge`, options, cb);
}
+
+ /**
+ * Get information about all projects
+ * @see https://developer.github.com/v3/projects/#list-repository-projects
+ * @param {Requestable.callback} [cb] - will receive the list of projects
+ * @return {Promise} - the promise for the http request
+ */
+ listProjects(cb) {
+ return this._requestAllPages(`/repos/${this.__fullname}/projects`, {AcceptHeader: 'inertia-preview'}, cb);
+ }
+
+ /**
+ * Create a new project
+ * @see https://developer.github.com/v3/projects/#create-a-repository-project
+ * @param {Object} options - the description of the project
+ * @param {Requestable.callback} cb - will receive the newly created project
+ * @return {Promise} - the promise for the http request
+ */
+ createProject(options, cb) {
+ options = options || {};
+ options.AcceptHeader = 'inertia-preview';
+ return this._request('POST', `/repos/${this.__fullname}/projects`, options, cb);
+ }
+
}
module.exports = Repository;
diff --git a/lib/Requestable.js b/lib/Requestable.js
index fa26d9d6..4d6e8d9d 100644
--- a/lib/Requestable.js
+++ b/lib/Requestable.js
@@ -8,14 +8,9 @@
import axios from 'axios';
import debug from 'debug';
import {Base64} from 'js-base64';
-import {polyfill} from 'es6-promise';
const log = debug('github:request');
-if (typeof Promise === 'undefined') {
- polyfill();
-}
-
/**
* The error structure returned when a network call fails
*/
@@ -30,7 +25,7 @@ class ResponseError extends Error {
super(message);
this.path = path;
this.request = response.config;
- this.response = response;
+ this.response = (response || {}).response || response;
this.status = response.status;
}
}
@@ -51,14 +46,16 @@ class Requestable {
* @param {Requestable.auth} [auth] - the credentials to authenticate to Github. If auth is
* not provided request will be made unauthenticated
* @param {string} [apiBase=https://api.github.com] - the base Github API URL
+ * @param {string} [AcceptHeader=v3] - the accept header for the requests
*/
- constructor(auth, apiBase) {
+ constructor(auth, apiBase, AcceptHeader) {
this.__apiBase = apiBase || 'https://api.github.com';
this.__auth = {
token: auth.token,
username: auth.username,
- password: auth.password
+ password: auth.password,
};
+ this.__AcceptHeader = AcceptHeader || 'v3';
if (auth.token) {
this.__authorizationHeader = 'token ' + auth.token;
@@ -88,14 +85,20 @@ class Requestable {
* Compute the headers required for an API request.
* @private
* @param {boolean} raw - if the request should be treated as JSON or as a raw request
+ * @param {string} AcceptHeader - the accept header for the request
* @return {Object} - the headers to use in the request
*/
- __getRequestHeaders(raw) {
+ __getRequestHeaders(raw, AcceptHeader) {
let headers = {
- 'Accept': raw ? 'application/vnd.github.v3.raw+json' : 'application/vnd.github.v3+json',
- 'Content-Type': 'application/json;charset=UTF-8'
+ 'Content-Type': 'application/json;charset=UTF-8',
+ 'Accept': 'application/vnd.github.' + (AcceptHeader || this.__AcceptHeader),
};
+ if (raw) {
+ headers.Accept += '.raw';
+ }
+ headers.Accept += '+json';
+
if (this.__authorizationHeader) {
headers.Authorization = this.__authorizationHeader;
}
@@ -121,7 +124,7 @@ class Requestable {
/**
* if a `Date` is passed to this function it will be converted to an ISO string
- * @param {*} date - the object to attempt to cooerce into an ISO date string
+ * @param {*} date - the object to attempt to coerce into an ISO date string
* @return {string} - the ISO representation of `date` or whatever was passed in if it was not a date
*/
_dateToISO(date) {
@@ -152,7 +155,13 @@ class Requestable {
*/
_request(method, path, data, cb, raw) {
const url = this.__getURL(path);
- const headers = this.__getRequestHeaders(raw);
+
+ const AcceptHeader = (data || {}).AcceptHeader;
+ if (AcceptHeader) {
+ delete data.AcceptHeader;
+ }
+ const headers = this.__getRequestHeaders(raw, AcceptHeader);
+
let queryParams = {};
const shouldUseDataAsParams = data && (typeof data === 'object') && methodHasNoBody(method);
@@ -167,7 +176,7 @@ class Requestable {
headers: headers,
params: queryParams,
data: data,
- responseType: raw ? 'text' : 'json'
+ responseType: raw ? 'text' : 'json',
};
log(`${config.method} to ${config.url}`);
@@ -175,7 +184,15 @@ class Requestable {
if (cb) {
requestPromise.then((response) => {
- cb(null, response.data || true, response);
+ if (response.data && Object.keys(response.data).length > 0) {
+ // When data has results
+ cb(null, response.data, response);
+ } else if (config.method !== 'GET' && Object.keys(response.data).length < 1) {
+ // True when successful submit a request and receive a empty object
+ cb(null, (response.status < 300), response);
+ } else {
+ cb(null, response.data, response);
+ }
});
}
@@ -198,7 +215,7 @@ class Requestable {
}
return true;
}, function failure(response) {
- if (response.status === 404) {
+ if (response.response.status === 404) {
if (cb) {
cb(null, false, response);
}
@@ -218,7 +235,7 @@ class Requestable {
* @param {string} path - the path to request
* @param {Object} options - the query parameters to include
* @param {Requestable.callback} [cb] - the function to receive the data. The returned data will always be an array.
- * @param {Object[]} results - the partial results. This argument is intended for interal use only.
+ * @param {Object[]} results - the partial results. This argument is intended for internal use only.
* @return {Promise} - a promise which will resolve when all pages have been fetched
* @deprecated This will be folded into {@link Requestable#_request} in the 2.0 release.
*/
@@ -236,12 +253,23 @@ class Requestable {
let message = `cannot figure out how to append ${response.data} to the result set`;
throw new ResponseError(message, path, response);
}
- results.push.apply(results, thisGroup);
+ results.push(...thisGroup);
const nextUrl = getNextPage(response.headers.link);
- if (nextUrl) {
- log(`getting next page: ${nextUrl}`);
- return this._requestAllPages(nextUrl, options, cb, results);
+ if(nextUrl) {
+ if (!options) {
+ options = {};
+ }
+ options.page = parseInt(
+ nextUrl.match(/([&\?]page=[0-9]*)/g)
+ .shift()
+ .split('=')
+ .pop()
+ );
+ if (!(options && typeof options.page !== 'number')) {
+ log(`getting next page: ${nextUrl}`);
+ return this._requestAllPages(nextUrl, options, cb, results);
+ }
}
if (cb) {
@@ -276,10 +304,16 @@ function getNextPage(linksHeader = '') {
}
function callbackErrorOrThrow(cb, path) {
- return function handler(response) {
- let message = `error making request ${response.config.method} ${response.config.url}`;
- let error = new ResponseError(message, path, response);
- log(`${message} ${JSON.stringify(response.data)}`);
+ return function handler(object) {
+ let error;
+ if (object.hasOwnProperty('config')) {
+ const {response: {status, statusText}, config: {method, url}} = object;
+ let message = (`${status} error making request ${method} ${url}: "${statusText}"`);
+ error = new ResponseError(message, path, object);
+ log(`${message} ${JSON.stringify(object.data)}`);
+ } else {
+ error = object;
+ }
if (cb) {
log('going to error callback');
cb(error);
diff --git a/lib/User.js b/lib/User.js
index be86c4fc..a6f22324 100644
--- a/lib/User.js
+++ b/lib/User.js
@@ -81,6 +81,26 @@ class User extends Requestable {
return this._request('GET', this.__getScopedUrl('orgs'), null, cb);
}
+ /**
+ * List followers of a user
+ * @see https://developer.github.com/v3/users/followers/#list-followers-of-a-user
+ * @param {Requestable.callback} [cb] - will receive the list of followers
+ * @return {Promise} - the promise for the http request
+ */
+ listFollowers(cb) {
+ return this._request('GET', this.__getScopedUrl('followers'), null, cb);
+ }
+
+ /**
+ * Lists the people who the authenticated user follows.
+ * @see https://docs.github.com/en/rest/reference/users#list-the-people-the-authenticated-user-follows
+ * @param {Requestable.callback} [cb] - will receive the list of who a user is following
+ * @return {Promise} - the promise for the http request
+ */
+ listFollowing(cb) {
+ return this._request('GET', this.__getScopedUrl('following'), null, cb);
+ }
+
/**
* List the user's gists
* @see https://developer.github.com/v3/gists/#list-a-users-gists
@@ -132,6 +152,33 @@ class User extends Requestable {
return this._requestAllPages(this.__getScopedUrl('starred'), requestOptions, cb);
}
+ /**
+ * Gets the list of starred gists for the user
+ * @see https://developer.github.com/v3/gists/#list-starred-gists
+ * @param {Object} [options={}] - any options to refine the search
+ * @param {Requestable.callback} [cb] - will receive the list of gists
+ * @return {Promise} - the promise for the http request
+ */
+ listStarredGists(options, cb) {
+ options = options || {};
+ if (typeof options === 'function') {
+ cb = options;
+ options = {};
+ }
+ options.since = this._dateToISO(options.since);
+ return this._request('GET', '/gists/starred', options, cb);
+ }
+
+ /**
+ * List email addresses for a user
+ * @see https://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user
+ * @param {Requestable.callback} [cb] - will receive the list of emails
+ * @return {Promise} - the promise for the http request
+ */
+ getEmails(cb) {
+ return this._request('GET', '/user/emails', null, cb);
+ }
+
/**
* Have the authenticated user follow this user
* @see https://developer.github.com/v3/users/followers/#follow-a-user
@@ -140,7 +187,7 @@ class User extends Requestable {
* @return {Promise} - the promise for the http request
*/
follow(username, cb) {
- return this._request('PUT', `/user/following/${this.__user}`, null, cb);
+ return this._request('PUT', `/user/following/${username}`, null, cb);
}
/**
@@ -151,7 +198,7 @@ class User extends Requestable {
* @return {Promise} - the promise for the http request
*/
unfollow(username, cb) {
- return this._request('DELETE', `/user/following/${this.__user}`, null, cb);
+ return this._request('DELETE', `/user/following/${username}`, null, cb);
}
/**
diff --git a/mocha.opts b/mocha.opts
index 4944011d..0377ab2e 100644
--- a/mocha.opts
+++ b/mocha.opts
@@ -1,3 +1,3 @@
--compilers js:babel-register
---timeout 15000
+--timeout 20000
--slow 5000
diff --git a/package.json b/package.json
index 6f15d8f6..8e54341a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "github-api",
- "version": "2.3.0",
+ "version": "3.4.0",
"license": "BSD-3-Clause-Clear",
"description": "A higher-level wrapper around the Github API.",
"main": "dist/components/GitHub.js",
@@ -8,17 +8,20 @@
"Ændrew Rininsland (http://www.aendrew.com)",
"Aurelio De Rosa (http://www.audero.it/)",
"Clay Reimann (http://clayreimann.me)",
- "Michael Aufreiter (http://substance.io)"
+ "Michael Aufreiter (http://substance.io)",
+ "Mathieu Dutour (https://github.com/mathieudutour)"
],
"readmeFilename": "README.md",
"scripts": {
"clean": "gulp clean",
"build": "gulp build",
"test": "mocha --opts ./mocha.opts test/*.spec.js",
+ "test-coverage": "NODE_ENV=test nyc mocha --opts ./mocha.opts test/*.spec.js",
"test-verbose": "DEBUG=github* npm test",
"lint": "gulp lint",
"make-docs": "node_modules/.bin/jsdoc -c .jsdoc.json --verbose",
- "release": "./release.sh"
+ "release": "./release.sh",
+ "codecov": "nyc report --reporter=text-lcov > coverage.lcov && codecov"
},
"babel": {
"presets": [
@@ -26,33 +29,38 @@
],
"plugins": [
[
- "transform-es2015-modules-umd",
- {
- "globals": {
- "es6-promise": "Promise"
- }
- }
+ "add-module-exports",
+ "transform-es2015-modules-umd"
]
],
"env": {
"development": {
"sourceMaps": "inline"
+ },
+ "test": {
+ "plugins": [
+ "istanbul"
+ ]
}
}
},
+ "nyc": {
+ "sourceMap": false,
+ "instrument": false
+ },
"files": [
- "dist/*",
- "lib/*"
+ "dist/*"
],
"dependencies": {
- "axios": "^0.10.0",
+ "axios": "^0.21.1",
"debug": "^2.2.0",
- "es6-promise": "^3.0.2",
"js-base64": "^2.1.9",
"utf8": "^2.1.1"
},
"devDependencies": {
"babel-core": "^6.7.7",
+ "babel-plugin-add-module-exports": "^0.2.1",
+ "babel-plugin-istanbul": "3.0.0",
"babel-plugin-transform-es2015-modules-umd": "^6.5.0",
"babel-preset-es2015": "^6.5.0",
"babel-register": "^6.7.2",
@@ -60,32 +68,34 @@
"browserify": "^13.0.0",
"codecov": "^1.0.1",
"del": "^2.2.0",
- "eslint-config-google": "^0.5.0",
- "eslint-plugin-mocha": "^2.2.0",
+ "eslint-config-google": "^0.7.0",
+ "eslint-plugin-mocha": "^4.7.0",
"gulp": "^3.9.0",
"gulp-babel": "^6.1.2",
- "gulp-eslint": "^2.0.0",
- "gulp-jscs": "^3.0.2",
+ "gulp-eslint": "^3.0.1",
+ "gulp-jscs": "^4.0.0",
"gulp-jscs-stylish": "^1.3.0",
"gulp-rename": "^1.2.2",
- "gulp-sourcemaps": "^1.6.0",
- "gulp-uglify": "^1.5.1",
+ "gulp-sourcemaps": "^2.2.0",
+ "gulp-uglify": "^2.0.0",
"jsdoc": "^3.4.0",
"minami": "^1.1.1",
- "mocha": "^2.3.4",
+ "mocha": "^3.1.2",
"must": "^0.13.1",
+ "nock": "^9.0.2",
+ "nyc": "9.0.1",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
},
"repository": {
"type": "git",
- "url": "git://github.com/michael/github.git"
+ "url": "git://github.com/github-tools/github.git"
},
"keywords": [
"github",
"api"
],
"bugs": {
- "url": "https://github.com/michael/github/issues"
+ "url": "https://github.com/github-tools/github/issues"
}
}
diff --git a/release.sh b/release.sh
index 0b3a3c37..63853dae 100755
--- a/release.sh
+++ b/release.sh
@@ -1,15 +1,18 @@
#!/bin/bash
# This is the automated release script
-# make sure all our dependencies are installed so we can publish docs
-npm install
-
# guard against stupid
if [ -z "$1" ]; then
echo "You must specify a new version level: [patch, minor, major]";
exit 1;
fi
+# make sure all our dependencies are installed so we can publish docs
+npm install
+
+# try to build to make sure we don't publish something really broken
+npm run build
+
# bump the version
echo "npm version $1"
npm version $1
@@ -32,5 +35,5 @@ git checkout gh-pages
mv out/* docs/
echo $VERSION >> _data/versions.csv
git add .
-git co -m "adding docs for v$VERSION"
+git commit -m "adding docs for v$VERSION"
git push
diff --git a/test/auth.spec.js b/test/auth.spec.js
index 2c03d766..00842ee9 100644
--- a/test/auth.spec.js
+++ b/test/auth.spec.js
@@ -1,7 +1,7 @@
import expect from 'must';
import Github from '../lib/GitHub';
-import testUser from './fixtures/user.json';
+import testUser from './fixtures/user.js';
import {assertSuccessful, assertFailure} from './helpers/callbacks';
describe('Github', function() {
@@ -13,7 +13,7 @@ describe('Github', function() {
github = new Github({
username: testUser.USERNAME,
password: testUser.PASSWORD,
- auth: 'basic'
+ auth: 'basic',
});
user = github.getUser();
@@ -70,7 +70,7 @@ describe('Github', function() {
github = new Github({
username: testUser.USERNAME,
password: 'fake124',
- auth: 'basic'
+ auth: 'basic',
});
user = github.getUser();
@@ -83,8 +83,8 @@ describe('Github', function() {
it('should fail authentication and return err', function(done) {
user.listNotifications(assertFailure(done, function(err) {
- expect(err.status).to.be.equal(401, 'Return 401 status for bad auth');
- expect(err.response.data.message).to.equal('Bad credentials');
+ expect(err.response.status).to.be.equal(401, 'Return 401 status for bad auth');
+ expect(err.response.data.message).to.equal('Requires authentication');
done();
}));
diff --git a/test/dist.spec/index.html b/test/dist.spec/index.html
index da5a21de..b0a32d7b 100644
--- a/test/dist.spec/index.html
+++ b/test/dist.spec/index.html
@@ -12,7 +12,7 @@