From 78a98b167ff7ddd343110e8b96c723110d2f6350 Mon Sep 17 00:00:00 2001 From: David Buchan-Swanson Date: Mon, 21 Nov 2016 06:48:16 +1000 Subject: [PATCH 01/61] update the link to the docs (#402) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7828852..17ba8db5 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ yahoo.listRepos(function(err, repos) { ``` [codecov]: https://codecov.io/github/michael/github?branch=master -[docs]: http://michael.github.io/github/ +[docs]: http://github-tools.github.io/github/ [gitter]: https://gitter.im/michael/github [npm-package]: https://www.npmjs.com/package/github-api/ [unpkg]: https://unpkg.com/github-api/ From 6ec1d0b5d1a8c52a3707a43a5dc5f0465f6bdf5a Mon Sep 17 00:00:00 2001 From: Mathieu Dutour Date: Wed, 30 Nov 2016 21:41:17 +0000 Subject: [PATCH 02/61] chore: fix tests and bump to 3.x (#401) * add getContributorStats * add labels methods * add updateStatus and updateRepository method * add project api and fix tests * improve release script * remove prepublish script * request all pages for cards * add getEmails * remove polyfill * drop support for node < 4 / test on node 4-6 * add coverage * remove clearRepo --- .gitignore | 3 +- .npmignore | 3 +- .travis.yml | 9 +- README.md | 116 +++++++--------- gulpfile.babel.js | 12 +- lib/GitHub.js | 10 ++ lib/Issue.js | 53 ++++++- lib/Markdown.js | 4 +- lib/Organization.js | 23 ++++ lib/Project.js | 236 ++++++++++++++++++++++++++++++++ lib/Repository.js | 114 +++++++++++---- lib/Requestable.js | 51 ++++--- lib/User.js | 10 ++ package.json | 53 ++++--- release.sh | 11 +- test/auth.spec.js | 6 +- test/gist.spec.js | 2 +- test/helpers/callbacks.js | 1 - test/helpers/helperFunctions.js | 43 ++++++ test/helpers/wait.js | 5 + test/issue.spec.js | 102 +++++++++++++- test/markdown.spec.js | 6 +- test/organization.spec.js | 53 +++++-- test/project.spec.js | 171 +++++++++++++++++++++++ test/rate-limit.spec.js | 2 +- test/repository.spec.js | 124 +++++++++++++---- test/search.spec.js | 12 +- test/team.spec.js | 71 ++++++++-- test/user.spec.js | 10 +- 29 files changed, 1079 insertions(+), 237 deletions(-) create mode 100644 lib/Project.js create mode 100644 test/helpers/helperFunctions.js create mode 100644 test/helpers/wait.js create mode 100644 test/project.spec.js 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..26f56aa3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,7 @@ node_js: - '6' - '5' - '4' - - '0.12' - + cache: directories: - node_modules @@ -14,10 +13,10 @@ before_script: - npm run lint # - npm run build # will need this when we do sauce testing of compiled files script: - - npm test + - npm run test-coverage # - 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 +after_success: + - npm run codecov before_deploy: - npm run build deploy: diff --git a/README.md b/README.md index 17ba8db5..919b5eb3 100644 --- a/README.md +++ b/README.md @@ -2,50 +2,19 @@ [![Downloads per month](https://img.shields.io/npm/dm/github-api.svg?maxAge=2592000)][npm-package] [![Latest version](https://img.shields.io/npm/v/github-api.svg?maxAge=3600)][npm-package] -[![Gitter](https://img.shields.io/gitter/room/michael/github.js.svg?maxAge=2592000)][gitter] -[![Travis](https://img.shields.io/travis/michael/github.svg?maxAge=60)][travis-ci] - +[![Gitter](https://img.shields.io/gitter/room/github-tools/github.js.svg?maxAge=2592000)][gitter] +[![Travis](https://img.shields.io/travis/github-tools/github.svg?maxAge=60)][travis-ci] +[![Codecov](https://img.shields.io/codecov/c/github/github-tools/github.svg?maxAge=2592000)][codecov] -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. +Github.js provides a minimal higher-level wrapper around Github's API. -## [Read the docs][docs] - -## Installation -Github.js is available from `npm` or [unpkg][unpkg]. - -```shell -npm install github-api -``` - -```html - - - - - -``` - -## Compatibility -Github.js is tested on Node: -* 6.x -* 5.x -* 4.x -* 0.12 - -## GitHub Tools - -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. - -## 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'; @@ -62,57 +31,66 @@ gist.create({ } }).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(); +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.listStarredRepos() - .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.listRepos(function(err, repos) { - // look at all the repos! -}) +```shell +npm install github-api ``` -[codecov]: https://codecov.io/github/michael/github?branch=master +```html + + + + + +``` + +## Compatibility +`Github.js` is tested on Node.js: +* 6.x + +Note: `Github.js` uses Promise, hence it will not work in Node.js < 4 without polyfill. + +[codecov]: https://codecov.io/github/github-tools/github?branch=master [docs]: http://github-tools.github.io/github/ -[gitter]: https://gitter.im/michael/github +[gitter]: https://gitter.im/github-tools/github [npm-package]: https://www.npmjs.com/package/github-api/ [unpkg]: https://unpkg.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 +[travis-ci]: https://travis-ci.org/github-tools/github 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/GitHub.js b/lib/GitHub.js index 944fc868..59cb94ff 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. @@ -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 {Markdown} + */ + 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 07fa2b1a..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,7 +184,7 @@ 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) { @@ -201,6 +201,51 @@ class Issue extends Requestable { 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..edc346cc 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 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 301e1230..fb200397 100644 --- a/lib/Repository.js +++ b/lib/Repository.js @@ -8,7 +8,7 @@ import Requestable from './Requestable'; import Utf8 from 'utf8'; import { - Base64 + Base64, } from 'js-base64'; import debug from 'debug'; const log = debug('github:repository'); @@ -28,7 +28,7 @@ class Repository extends Requestable { this.__fullname = fullname; this.__currentTree = { branch: null, - sha: null + sha: null, }; } @@ -266,21 +266,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 @@ -306,8 +306,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); @@ -324,7 +324,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); } @@ -341,7 +341,7 @@ class Repository extends Requestable { let data = { message, tree, - parents: [parent] + parents: [parent], }; return this._request('POST', `/repos/${this.__fullname}/git/commits`, data, cb) @@ -363,11 +363,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 @@ -384,6 +419,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); } @@ -421,7 +466,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); } @@ -435,7 +480,7 @@ class Repository extends Requestable { */ getReadme(ref, raw, cb) { return this._request('GET', `/repos/${this.__fullname}/readme`, { - ref + ref, }, cb, raw); } @@ -478,7 +523,7 @@ class Repository extends Requestable { let sha = response.data.object.sha; return this.createRef({ sha, - ref: `refs/heads/${newBranch}` + ref: `refs/heads/${newBranch}`, }, cb); }); } @@ -494,21 +539,6 @@ class Repository extends Requestable { return this._request('POST', `/repos/${this.__fullname}/pulls`, options, cb); } - /** - * Update a pull request - * @deprecated since version 2.4.0 - * @see https://developer.github.com/v3/pulls/#update-a-pull-request - * @param {number|string} number - the number of the pull request to update - * @param {Object} options - the pull request description - * @param {Requestable.callback} [cb] - will receive the pull request information - * @return {Promise} - the promise for the http request - */ - updatePullRequst(number, options, cb) { - log('Deprecated: This method contains a typo and it has been deprecated. It will be removed in next major version. Use updatePullRequest() instead.'); - - return this.updatePullRequest(number, options, cb); - } - /** * Update a pull request * @see https://developer.github.com/v3/pulls/#update-a-pull-request @@ -633,7 +663,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); }); @@ -694,7 +724,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) @@ -803,6 +833,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 95c23bbd..8d39c04f 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; } @@ -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); } @@ -236,7 +253,7 @@ 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) { @@ -279,7 +296,7 @@ function callbackErrorOrThrow(cb, path) { return function handler(object) { let error; if (object.hasOwnProperty('config')) { - const {status, statusText, config: {method, url}} = object; + 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)}`); diff --git a/lib/User.js b/lib/User.js index be86c4fc..3f3b4bb6 100644 --- a/lib/User.js +++ b/lib/User.js @@ -132,6 +132,16 @@ class User extends Requestable { return this._requestAllPages(this.__getScopedUrl('starred'), requestOptions, 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 diff --git a/package.json b/package.json index c942525d..f6ff43a1 100644 --- a/package.json +++ b/package.json @@ -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.15.2", "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,33 +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": "^8.0.0", + "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..bd0d8205 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -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,7 +83,7 @@ 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.status).to.be.equal(401, 'Return 401 status for bad auth'); expect(err.response.data.message).to.equal('Bad credentials'); done(); diff --git a/test/gist.spec.js b/test/gist.spec.js index 0fa476bd..c9c2e393 100644 --- a/test/gist.spec.js +++ b/test/gist.spec.js @@ -15,7 +15,7 @@ describe('Gist', function() { github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, - auth: 'basic' + auth: 'basic', }); }); diff --git a/test/helpers/callbacks.js b/test/helpers/callbacks.js index 112464ac..9c80ad98 100644 --- a/test/helpers/callbacks.js +++ b/test/helpers/callbacks.js @@ -7,7 +7,6 @@ export function assertSuccessful(done, cb) { try { expect(err).not.to.exist(err ? (err.response ? err.response.data : err) : 'No error'); expect(res).to.exist(); - expect(xhr).to.be.an.object(); if (cb) { setTimeout(function delay() { diff --git a/test/helpers/helperFunctions.js b/test/helpers/helperFunctions.js new file mode 100644 index 00000000..c35ed64f --- /dev/null +++ b/test/helpers/helperFunctions.js @@ -0,0 +1,43 @@ +export function getNextPage(linksHeader = '') { + const links = linksHeader.split(/\s*,\s*/); // splits and strips the urls + return links.reduce(function(nextUrl, link) { + if (link.search(/rel="next"/) !== -1) { + return (link.match(/<(.*)>/) || [])[1]; + } + + return nextUrl; + }, undefined); +} + +export function deleteRepo(repo, github) { + return github + .getRepo(repo.owner.login, repo.name) + .deleteRepo() + .then((removed) => { + if (removed) { + console.log(repo.full_name, 'deleted'); // eslint-disable-line + } + }); +} + +export function deleteTeam(team, github) { + return github + .getTeam(team.id) + .deleteTeam() + .then((removed) => { + if (removed) { + console.log('team', team.name, 'deleted'); //eslint-disable-line + } + }); +} + +export function deleteProject(project, github) { + return github + .getProject(project.id) + .deleteProject() + .then((removed) => { + if (removed) { + console.log('project', project.name, 'deleted'); //eslint-disable-line + } + }); +} diff --git a/test/helpers/wait.js b/test/helpers/wait.js new file mode 100644 index 00000000..680ea360 --- /dev/null +++ b/test/helpers/wait.js @@ -0,0 +1,5 @@ +export default function(delay = 1000) { + return () => new Promise((resolve) => { + setTimeout(() => resolve(), delay); + }); +} diff --git a/test/issue.spec.js b/test/issue.spec.js index e3d0bb78..5064c556 100644 --- a/test/issue.spec.js +++ b/test/issue.spec.js @@ -2,20 +2,43 @@ import expect from 'must'; import Github from '../lib/GitHub'; import testUser from './fixtures/user.json'; +import wait from './helpers/wait'; import {assertSuccessful} from './helpers/callbacks'; +import getTestRepoName from './helpers/getTestRepoName'; describe('Issue', function() { let github; + const testRepoName = getTestRepoName(); let remoteIssues; before(function() { github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, - auth: 'basic' + auth: 'basic', }); - remoteIssues = github.getIssues(testUser.USERNAME, 'TestRepo'); + return github + .getUser() + .createRepo({name: testRepoName}) + .then(wait(5000)) + .then(function() { + remoteIssues = github.getIssues(testUser.USERNAME, testRepoName); + return remoteIssues.createIssue({ + title: 'Test issue', + body: 'Test issue body', + }); + }) + .then(function() { + return remoteIssues.createMilestone({ + title: 'Default Milestone', + description: 'Test', + }); + }); + }); + + after(function() { + return github.getRepo(testUser.USERNAME, testRepoName).deleteRepo(); }); describe('reading', function() { @@ -69,6 +92,7 @@ describe('Issue', function() { let createdIssueId; let issueCommentId; let createdMilestoneId; + let createdLabel; // 200ms between tests so that Github has a chance to settle beforeEach(function(done) { @@ -78,7 +102,7 @@ describe('Issue', function() { it('should create issue', function(done) { const newIssue = { title: 'New issue', - body: 'New issue body' + body: 'New issue body', }; remoteIssues.createIssue(newIssue, assertSuccessful(done, function(err, issue) { @@ -94,7 +118,7 @@ describe('Issue', function() { it('should edit issue', function(done) { const newProps = { title: 'Edited title', - state: 'closed' + state: 'closed', }; remoteIssues.editIssue(createdIssueId, newProps, assertSuccessful(done, function(err, issue) { @@ -148,7 +172,7 @@ describe('Issue', function() { it('should create a milestone', function(done) { let milestone = { title: 'v42', - description: 'The ultimate version' + description: 'The ultimate version', }; remoteIssues.createMilestone(milestone) @@ -162,7 +186,7 @@ describe('Issue', function() { }); it('should update a milestone', function(done) { let milestone = { - description: 'Version 6 * 7' + description: 'Version 6 * 7', }; expect(createdMilestoneId).to.be.a.number(); @@ -182,5 +206,71 @@ describe('Issue', function() { done(); }).catch(done); }); + + it('should create a label', (done) => { + let label = { + name: 'test', + color: '123456', + }; + + remoteIssues.createLabel(label) + .then(({data: _createdLabel}) => { + expect(_createdLabel).to.have.own('name', label.name); + expect(_createdLabel).to.have.own('color', label.color); + + createdLabel = label.name; + done(); + }).catch(done); + }); + + it('should retrieve a single label', (done) => { + let label = { + name: 'test', + color: '123456', + }; + + remoteIssues.getLabel(label.name) + .then(({data: retrievedLabel}) => { + expect(retrievedLabel).to.have.own('name', label.name); + expect(retrievedLabel).to.have.own('color', label.color); + + done(); + }).catch(done); + }); + + it('should update a label', (done) => { + let label = { + color: '789abc', + }; + + expect(createdLabel).to.be.a.string(); + remoteIssues.editLabel(createdLabel, label) + .then(({data: updatedLabel}) => { + expect(updatedLabel).to.have.own('name', createdLabel); + expect(updatedLabel).to.have.own('color', label.color); + + done(); + }).catch(done); + }); + + it('should list labels', (done) => { + expect(createdLabel).to.be.a.string(); + + remoteIssues.listLabels({}, assertSuccessful(done, function(err, labels) { + expect(labels).to.be.an.array(); + const hasLabel = labels.some((label) => label.name === createdLabel); + expect(hasLabel).to.be.true(); + done(); + })); + }); + + it('should delete a label', (done) => { + expect(createdLabel).to.be.a.string(); + remoteIssues.deleteLabel(createdLabel) + .then(({status}) => { + expect(status).to.equal(204); + done(); + }).catch(done); + }); }); }); diff --git a/test/markdown.spec.js b/test/markdown.spec.js index 22f5d982..eef9fe08 100644 --- a/test/markdown.spec.js +++ b/test/markdown.spec.js @@ -11,7 +11,7 @@ describe('Markdown', function() { github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, - auth: 'basic' + auth: 'basic', }); markdown = github.getMarkdown(); @@ -19,7 +19,7 @@ describe('Markdown', function() { it('should convert markdown to html as plain Markdown', function(done) { const options = { - text: 'Hello world github/linguist#1 **cool**, and #1!' + text: 'Hello world github/linguist#1 **cool**, and #1!', }; markdown.render(options) @@ -33,7 +33,7 @@ describe('Markdown', function() { const options = { text: 'Hello world github/linguist#1 **cool**, and #1!', mode: 'gfm', - context: 'github/gollum' + context: 'github/gollum', }; markdown.render(options) .then(function({data: html}) { diff --git a/test/organization.spec.js b/test/organization.spec.js index 9d5f75f6..26fd3607 100644 --- a/test/organization.spec.js +++ b/test/organization.spec.js @@ -9,14 +9,19 @@ describe('Organization', function() { let github; const ORG_NAME = 'github-tools'; const MEMBER_NAME = 'clayreimann'; + let createdProject; before(function() { github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, - auth: 'basic' + auth: 'basic', }); + return; + }); + after(function() { + return github.getProject(createdProject.id).deleteProject(); }); describe('reading', function() { @@ -35,7 +40,7 @@ describe('Organization', function() { .then(function({data: members}) { expect(members).to.be.an.array(); - let hasClayReimann = members.reduce((found, member) => member.login === MEMBER_NAME || found, false); + const hasClayReimann = members.some((member) => member.login === MEMBER_NAME); expect(hasClayReimann).to.be.true(); done(); @@ -76,28 +81,48 @@ describe('Organization', function() { })); }); - // TODO: The longer this is in place the slower it will get if we don't cleanup random test teams + it('should create an organization team', function(done) { + const options = { + name: testRepoName, + description: 'Created by unit tests', + privacy: 'secret', + }; + + organization.createTeam(options, assertSuccessful(done, function(err, team) { + expect(team.name).to.equal(testRepoName); + expect(team.organization.login).to.equal(testUser.ORGANIZATION); // jscs:ignore + done(); + })); + }); + it('should list the teams in the organization', function() { return organization.getTeams() .then(({data}) => { - const hasTeam = data.reduce( - (found, member) => member.slug === 'fixed-test-team-1' || found, - false); + const hasTeam = data.some((member) => member.slug === testRepoName); expect(hasTeam).to.be.true(); }); }); - it('should create an organization team', function(done) { - const options = { + it('should create a project', function(done) { + organization.createProject({ name: testRepoName, - description: 'Created by unit tests', - privacy: 'secret' - }; + body: 'body', + }, assertSuccessful(done, function(err, project) { + createdProject = project; + expect(project).to.own('name', testRepoName); + expect(project).to.own('body', 'body'); + done(); + })); + }); - organization.createTeam(options, assertSuccessful(done, function(err, team) { - expect(team.name).to.equal(testRepoName); - expect(team.organization.login).to.equal(testUser.ORGANIZATION); // jscs:ignore + it('should list repo projects', function(done) { + organization.listProjects(assertSuccessful(done, function(err, projects) { + expect(projects).to.be.an.array(); + + const hasProject = projects.some((project) => project.name === testRepoName); + + expect(hasProject).to.be.true(); done(); })); }); diff --git a/test/project.spec.js b/test/project.spec.js new file mode 100644 index 00000000..cccf4e81 --- /dev/null +++ b/test/project.spec.js @@ -0,0 +1,171 @@ +import expect from 'must'; + +import Github from '../lib/GitHub'; +import wait from './helpers/wait'; +import testUser from './fixtures/user.json'; +import {assertSuccessful} from './helpers/callbacks'; +import getTestRepoName from './helpers/getTestRepoName'; + +describe('Project', function() { + let github; + const testRepoName = getTestRepoName(); + let project; + let columnId; + let cardId; + + before(function() { + github = new Github({ + username: testUser.USERNAME, + password: testUser.PASSWORD, + auth: 'basic', + }); + + return github + .getUser() + .createRepo({name: testRepoName}) + .then(wait(5000)) + .then(function() { + const remoteRepo = github.getRepo(testUser.USERNAME, testRepoName); + return remoteRepo.createProject({ + name: 'test-project', + body: 'body', + }); + }) + .then(function(_project) { + project = github.getProject(_project.data.id); + }); + }); + + after(function() { + return github.getRepo(testUser.USERNAME, testRepoName).deleteRepo(); + }); + + it('should get repo project', function(done) { + project.getProject(assertSuccessful(done, function(err, project) { + expect(project).to.own('name', 'test-project'); + done(); + })); + }); + + it('should update a project', function(done) { + project.updateProject({ + name: 'another-test-project', + body: 'another-body', + }, assertSuccessful(done, function(err, project) { + expect(project).to.own('name', 'another-test-project'); + expect(project).to.own('body', 'another-body'); + done(); + })); + }); + + it('should create a repo project column', function(done) { + project.createProjectColumn({ + name: 'test-column', + }, assertSuccessful(done, function(err, column) { + expect(column).to.own('name', 'test-column'); + columnId = column.id; + done(); + })); + }); + + it('should list repo project columns', function(done) { + project.listProjectColumns(assertSuccessful(done, function(err, columns) { + expect(columns).to.be.an.array(); + expect(columns.length).to.equal(1); + done(); + })); + }); + + it('should get repo project column', function(done) { + project.getProjectColumn(columnId, assertSuccessful(done, function(err, project) { + expect(project).to.own('name', 'test-column'); + done(); + })); + }); + + it('should update a repo project column', function(done) { + project.updateProjectColumn(columnId, { + name: 'another-test-column', + }, assertSuccessful(done, function(err, column) { + expect(column).to.own('name', 'another-test-column'); + done(); + })); + }); + + it('should create repo project card', function(done) { + project.createProjectCard(columnId, { + note: 'test-card', + }, assertSuccessful(done, function(err, card) { + expect(card).to.own('note', 'test-card'); + cardId = card.id; + done(); + })); + }); + + it('should list cards of a project', function(done) { + project.listProjectCards(assertSuccessful(done, function(err, cards) { + expect(cards).to.be.an.array(); + expect(cards.length).to.equal(1); + done(); + })); + }); + + it('should list cards of a column', function(done) { + project.listColumnCards(columnId, assertSuccessful(done, function(err, cards) { + expect(cards).to.be.an.array(); + expect(cards.length).to.equal(1); + done(); + })); + }); + + it('should get repo project card', function(done) { + project.getProjectCard(cardId, assertSuccessful(done, function(err, card) { + expect(card).to.own('note', 'test-card'); + done(); + })); + }); + + it('should update repo project card', function(done) { + project.updateProjectCard(cardId, { + note: 'another-test-card', + }, assertSuccessful(done, function(err, card) { + expect(card).to.own('note', 'another-test-card'); + done(); + })); + }); + + it('should move repo project card', function(done) { + project.moveProjectCard(cardId, 'top', columnId, assertSuccessful(done, function(err, result) { + expect(result).to.be(true); + done(); + })); + }); + + it('should move repo project column', function(done) { + project.moveProjectColumn(columnId, 'first', assertSuccessful(done, function(err, result) { + expect(result).to.be(true); + done(); + })); + }); + + it('should delete repo project card', function(done) { + project.deleteProjectCard(cardId, assertSuccessful(done, function(err, result) { + expect(result).to.be(true); + done(); + })); + }); + + it('should delete repo project column', function(done) { + project.deleteProjectColumn(columnId, assertSuccessful(done, function(err, result) { + expect(result).to.be(true); + done(); + })); + }); + + it('should delete repo project', function(done) { + project.deleteProject(assertSuccessful(done, function(err, result) { + expect(result).to.be(true); + done(); + })); + }); +}); diff --git a/test/rate-limit.spec.js b/test/rate-limit.spec.js index 23e12d14..c3a5b858 100644 --- a/test/rate-limit.spec.js +++ b/test/rate-limit.spec.js @@ -12,7 +12,7 @@ describe('RateLimit', function() { github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, - auth: 'basic' + auth: 'basic', }); rateLimit = github.getRateLimit(); diff --git a/test/repository.spec.js b/test/repository.spec.js index f747878d..ef5101cf 100644 --- a/test/repository.spec.js +++ b/test/repository.spec.js @@ -1,6 +1,7 @@ import expect from 'must'; import Github from '../lib/GitHub'; +import wait from './helpers/wait'; import testUser from './fixtures/user.json'; import loadImage from './fixtures/imageBlob'; import {assertSuccessful, assertFailure} from './helpers/callbacks'; @@ -8,18 +9,18 @@ import getTestRepoName from './helpers/getTestRepoName'; describe('Repository', function() { let github; - let remoteRepo; let user; let imageB64; let imageBlob; const testRepoName = getTestRepoName(); const v10SHA = '20fcff9129005d14cc97b9d59b8a3d37f4fb633b'; - const statusUrl = 'https://api.github.com/repos/michael/github/statuses/20fcff9129005d14cc97b9d59b8a3d37f4fb633b'; + const statusUrl = + 'https://api.github.com/repos/github-tools/github/statuses/20fcff9129005d14cc97b9d59b8a3d37f4fb633b'; before(function(done) { github = new Github({ username: testUser.USERNAME, - password: testUser.PASSWORD + password: testUser.PASSWORD, }); loadImage(function(b64, blob) { @@ -30,13 +31,15 @@ describe('Repository', function() { }); describe('reading', function() { + let remoteRepo; + before(function() { - remoteRepo = github.getRepo('michael', 'github'); + remoteRepo = github.getRepo('github-tools', 'github'); }); it('should get repo details', function(done) { remoteRepo.getDetails(assertSuccessful(done, function(err, repo) { - expect(repo).to.have.own('full_name', 'michael/github'); + expect(repo).to.have.own('full_name', 'github-tools/github'); done(); })); @@ -137,7 +140,7 @@ describe('Repository', function() { path: 'test', author: 'AurelioDeRosa', since, - until + until, }; remoteRepo.listCommits(options, assertSuccessful(done, function(err, commits) { @@ -166,7 +169,7 @@ describe('Repository', function() { it('should fail when null ref is passed', function(done) { remoteRepo.getSingleCommit(null, assertFailure(done, function(err) { - expect(err.status).to.be(404); + expect(err.response.status).to.be(404); done(); })); }); @@ -181,6 +184,23 @@ describe('Repository', function() { const contributor = contributors[0]; + expect(contributor).to.have.own('login'); + expect(contributor).to.have.own('contributions'); + + done(); + })); + }); + + it('should show repo contributor stats', function(done) { + remoteRepo.getContributorStats(assertSuccessful(done, function(err, contributors) { + if (!(contributors instanceof Array)) { + console.log(JSON.stringify(contributors, null, 2)); // eslint-disable-line + } + expect(contributors).to.be.an.array(); + expect(contributors.length).to.be.above(1); + + const contributor = contributors[0]; + expect(contributor).to.have.own('author'); expect(contributor).to.have.own('total'); expect(contributor).to.have.own('weeks'); @@ -224,10 +244,10 @@ describe('Repository', function() { }); it('should get a repo by fullname', function(done) { - const repoByName = github.getRepo('michael/github'); + const repoByName = github.getRepo('github-tools/github'); repoByName.getDetails(assertSuccessful(done, function(err, repo) { - expect(repo).to.have.own('full_name', 'michael/github'); + expect(repo).to.have.own('full_name', 'github-tools/github'); done(); })); @@ -265,6 +285,7 @@ describe('Repository', function() { const releaseBody = 'This is my 49 character long release description.'; let sha; let releaseId; + let remoteRepo; before(function() { user = github.getUser(); @@ -278,7 +299,7 @@ describe('Repository', function() { it('should create repo', function(done) { const repoDef = { - name: testRepoName + name: testRepoName, }; user.createRepo(repoDef, assertSuccessful(done, function(err, repo) { @@ -288,6 +309,22 @@ describe('Repository', function() { })); }); + it('should be able to edit repository information', function(done) { + const options = { + name: testRepoName, + description: 'New short description', + homepage: 'http://example.com', + }; + + remoteRepo.updateRepository(options, assertSuccessful(done, + function(err, repository) { + expect(repository).to.have.own('homepage', options.homepage); + expect(repository).to.have.own('description', options.description); + expect(repository).to.have.own('name', testRepoName); + done(); + })); + }); + it('should show repo collaborators', function(done) { remoteRepo.getCollaborators(assertSuccessful(done, function(err, collaborators) { if (!(collaborators instanceof Array)) { @@ -312,26 +349,27 @@ describe('Repository', function() { it('should write to repo', function(done) { remoteRepo.writeFile('master', fileName, initialText, initialMessage, assertSuccessful(done, function() { - remoteRepo.getContents('master', fileName, 'raw', assertSuccessful(done, function(err, fileText) { + wait()().then(() => remoteRepo.getContents('master', fileName, 'raw', + assertSuccessful(done, function(err, fileText) { expect(fileText).to.be(initialText); done(); - })); + }))); })); }); it('should rename files', function(done) { remoteRepo.writeFile('master', fileName, initialText, initialMessage, assertSuccessful(done, function() { - remoteRepo.move('master', fileName, 'new_name', assertSuccessful(done, function() { - remoteRepo.getContents('master', fileName, 'raw', assertFailure(done, function(err) { - expect(err.status).to.be(404); + wait()().then(() => remoteRepo.move('master', fileName, 'new_name', assertSuccessful(done, function() { + wait()().then(() => remoteRepo.getContents('master', fileName, 'raw', assertFailure(done, function(err) { + expect(err.response.status).to.be(404); remoteRepo.getContents('master', 'new_name', 'raw', assertSuccessful(done, function(err, fileText) { expect(fileText).to.be(initialText); done(); })); - })); - })); + }))); + }))); })); }); @@ -392,12 +430,30 @@ describe('Repository', function() { remoteRepo.getRef('heads/master', assertSuccessful(done, function(err, refSpec) { let newRef = { ref: 'refs/heads/new-test-branch', - sha: refSpec.object.sha + sha: refSpec.object.sha, }; remoteRepo.createRef(newRef, assertSuccessful(done)); })); }); + it('should update commit status', function(done) { + const status = { + state: 'success', + target_url: 'http://example.com', // eslint-disable-line camelcase + description: 'Build was successful!', + }; + remoteRepo.getRef('heads/master', assertSuccessful(done, function(err, refSpec) { + remoteRepo.updateStatus(refSpec.object.sha, status, assertSuccessful(done, + function(err, updated) { + expect(updated).to.have.own('state', status.state); + expect(updated).to.have.own('target_url', status.target_url); + expect(updated).to.have.own('description', status.description); + expect(updated).to.have.own('context', 'default'); + done(); + })); + })); + }); + it('should delete ref on repo', function(done) { remoteRepo.deleteRef('heads/new-test-branch', assertSuccessful(done)); }); @@ -424,7 +480,7 @@ describe('Repository', function() { }); it('should get pull requests on repo', function(done) { - const repo = github.getRepo('michael', 'github'); + const repo = github.getRepo('github-tools', 'github'); repo.getPullRequest(153, assertSuccessful(done, function(err, pr) { expect(pr).to.have.own('title'); @@ -444,7 +500,7 @@ describe('Repository', function() { it('should write author and committer to repo', function(done) { const options = { author: {name: 'Author Name', email: 'author@example.com'}, - committer: {name: 'Committer Name', email: 'committer@example.com'} + committer: {name: 'Committer Name', email: 'committer@example.com'}, }; remoteRepo.writeFile('dev', @@ -482,7 +538,7 @@ describe('Repository', function() { remoteRepo.getRef('heads/master', assertSuccessful(done, function(err, refSpec) { let newRef = { ref: 'refs/heads/testing-14', - sha: refSpec.object.sha + sha: refSpec.object.sha, }; remoteRepo.createRef(newRef, assertSuccessful(done, function() { @@ -503,7 +559,7 @@ describe('Repository', function() { it('should be able to write an image to the repo', function(done) { const opts = { - encode: false + encode: false, }; remoteRepo.writeFile('master', imageFileName, imageB64, initialMessage, opts, assertSuccessful(done, @@ -539,7 +595,7 @@ describe('Repository', function() { it('should fail on broken commit', function(done) { remoteRepo.commit('broken-parent-hash', 'broken-tree-hash', initialMessage, assertFailure(done, function(err) { - expect(err.status).to.be(422); + expect(err.response.status).to.be(422); done(); })); }); @@ -560,7 +616,7 @@ describe('Repository', function() { it('should edit a release', function(done) { const releaseDef = { name: releaseName, - body: releaseBody + body: releaseBody, }; remoteRepo.updateRelease(releaseId, releaseDef, assertSuccessful(done, function(err, release) { @@ -589,9 +645,29 @@ describe('Repository', function() { it('should delete a release', function(done) { remoteRepo.deleteRelease(releaseId, assertSuccessful(done)); }); + + it('should create a project', function(done) { + remoteRepo.createProject({ + name: 'test-project', + body: 'body', + }, assertSuccessful(done, function(err, project) { + expect(project).to.own('name', 'test-project'); + expect(project).to.own('body', 'body'); + done(); + })); + }); + + it('should list repo projects', function(done) { + remoteRepo.listProjects(assertSuccessful(done, function(err, projects) { + expect(projects).to.be.an.array(); + expect(projects.length).to.equal(1); + done(); + })); + }); }); describe('deleting', function() { + let remoteRepo; before(function() { remoteRepo = github.getRepo(testUser.USERNAME, testRepoName); }); diff --git a/test/search.spec.js b/test/search.spec.js index 93d852da..21a32935 100644 --- a/test/search.spec.js +++ b/test/search.spec.js @@ -5,14 +5,14 @@ import Github from '../lib/GitHub'; import testUser from './fixtures/user.json'; describe('Search', function() { - this.timeout(20 * 1000); + this.timeout(20 * 1000); // eslint-disable-line no-invalid-this let github; before(function() { github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, - auth: 'basic' + auth: 'basic', }); nock.load('test/fixtures/search.json'); }); @@ -22,7 +22,7 @@ describe('Search', function() { let search = github.search({ q: 'tetris language:assembly', sort: 'stars', - order: 'desc' + order: 'desc', }); return search.forRepositories(options) @@ -35,7 +35,7 @@ describe('Search', function() { it('should search code', function() { let options; let search = github.search({ - q: 'addClass in:file language:js repo:jquery/jquery' + q: 'addClass in:file language:js repo:jquery/jquery', }); return search.forCode(options) @@ -50,7 +50,7 @@ describe('Search', function() { let search = github.search({ q: 'windows pip label:bug language:python state:open ', sort: 'created', - order: 'asc' + order: 'asc', }); return search.forIssues(options) @@ -63,7 +63,7 @@ describe('Search', function() { it('should search users', function() { let options; let search = github.search({ - q: 'tom repos:>42 followers:>1000' + q: 'tom repos:>42 followers:>1000', }); return search.forUsers(options) diff --git a/test/team.spec.js b/test/team.spec.js index 1f712617..8a5ec8be 100644 --- a/test/team.spec.js +++ b/test/team.spec.js @@ -6,7 +6,7 @@ import {assertFailure} from './helpers/callbacks'; import getTestRepoName from './helpers/getTestRepoName'; const altUser = { - USERNAME: 'mtscout6-test' + USERNAME: 'mtscout6-test', }; function createTestTeam() { @@ -15,14 +15,14 @@ function createTestTeam() { const github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, - auth: 'basic' + auth: 'basic', }); const org = github.getOrganization(testUser.ORGANIZATION); return org.createTeam({ name, - privacy: 'closed' + privacy: 'closed', }).then(({data: result}) => { const team = github.getTeam(result.id); return {team, name}; @@ -37,10 +37,59 @@ describe('Team', function() { // Isolate tests that are based on a fixed team const github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, - auth: 'basic' + auth: 'basic', }); - team = github.getTeam(2027812); // github-api-tests/fixed-test-team-1 + const org = github.getOrganization(testUser.ORGANIZATION); + + /* eslint-disable no-console */ + // The code below add a fixed-test-repo-1 + let promiseRepo = new Promise((resolve) => { + org + .createRepo({name: 'fixed-test-repo-1'}) + .then(resolve, () => { + console.log('skiped fixed-test-repo-1 creation'); + resolve(); + }); + }); + + // The code below add a fixed-test-repo-1 + let promiseTeam = org + .createTeam({ + name: 'fixed-test-repo-1', + repo_names: [testUser.ORGANIZATION + '/fixed-test-repo-1'], // eslint-disable-line camelcase + }) + .then(({data: team}) => team) + .catch(() => { + console.log('skiped fixed-test-repo-1 creation'); + // Team already exists, fetch the team + return org.getTeams().then(({data: teams}) => { + let team = teams + .filter((team) => team.name === 'fixed-test-repo-1') + .pop(); + if (!team) { + throw new Error('missing fixed-test-repo-1'); + } + return team; + }); + }); + /* eslint-enable no-console */ + + return promiseRepo.then(() => { + return promiseTeam + .then((t) => { + team = github.getTeam(t.id); + return team; + }) + .then((team) => { + let setupTeam = [ + team.addMembership(altUser.USERNAME), + team.addMembership(testUser.USERNAME), + team.manageRepo(testUser.ORGANIZATION, 'fixed-test-repo-1'), + ]; + return Promise.all(setupTeam); + }); + }); }); it('should get membership for a given user', function() { @@ -56,10 +105,7 @@ describe('Team', function() { // Isolate tests that are based on a fixed team .then(function({data: members}) { expect(members).to.be.an.array(); - let hasTestUser = members.reduce( - (found, member) => member.login === testUser.USERNAME || found, - false - ); + const hasTestUser = members.some((member) => member.login === testUser.USERNAME); expect(hasTestUser).to.be.true(); }); @@ -68,10 +114,7 @@ describe('Team', function() { // Isolate tests that are based on a fixed team it('should get team repos', function() { return team.listRepos() .then(({data}) => { - const hasRepo = data.reduce( - (found, repo) => repo.name === 'fixed-test-repo-1' || found, - false - ); + const hasRepo = data.some((repo) => repo.name === 'fixed-test-repo-1'); expect(hasRepo).to.be.true(); }); @@ -80,7 +123,7 @@ describe('Team', function() { // Isolate tests that are based on a fixed team it('should get team', function() { return team.getTeam() .then(({data}) => { - expect(data.name).to.equal('Fixed Test Team 1'); + expect(data.name).to.equal('fixed-test-repo-1'); }); }); diff --git a/test/user.spec.js b/test/user.spec.js index 81181389..2296781c 100644 --- a/test/user.spec.js +++ b/test/user.spec.js @@ -10,7 +10,7 @@ describe('User', function() { github = new Github({ username: testUser.USERNAME, password: testUser.PASSWORD, - auth: 'basic' + auth: 'basic', }); user = github.getUser(); }); @@ -24,7 +24,7 @@ describe('User', function() { type: 'owner', sort: 'updated', per_page: 90, // eslint-disable-line - page: 10 + page: 10, }; user.listRepos(filterOpts, assertArray(done)); @@ -47,7 +47,7 @@ describe('User', function() { all: true, participating: true, since: '2015-01-01T00:00:00Z', - before: '2015-02-01T00:00:00Z' + before: '2015-02-01T00:00:00Z', }; user.listNotifications(filterOpts, assertArray(done)); @@ -68,4 +68,8 @@ describe('User', function() { it('should unfollow user', function(done) { user.unfollow('ingalls', assertSuccessful(done)); }); + + it('should list the email addresses of the user', function(done) { + user.getEmails(assertSuccessful(done)); + }); }); From 21fe6b6e3ee5fe8a1a133d7dadfd88adcac0bf39 Mon Sep 17 00:00:00 2001 From: Jeanette Head Date: Tue, 20 Dec 2016 14:51:50 -0700 Subject: [PATCH 03/61] feat: use the page option to fetch a single page Signed-off-by: Byron Anderson --- lib/Requestable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Requestable.js b/lib/Requestable.js index 8d39c04f..bad111ac 100644 --- a/lib/Requestable.js +++ b/lib/Requestable.js @@ -256,7 +256,7 @@ class Requestable { results.push(...thisGroup); const nextUrl = getNextPage(response.headers.link); - if (nextUrl) { + if (nextUrl && typeof options.page !== 'number') { log(`getting next page: ${nextUrl}`); return this._requestAllPages(nextUrl, options, cb, results); } From f075749ed4234c7d76f122879523fbca2eb01f86 Mon Sep 17 00:00:00 2001 From: Clay Reimann Date: Wed, 4 Jan 2017 08:54:30 -0600 Subject: [PATCH 04/61] 3.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6ff43a1..7c66bb60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "github-api", - "version": "2.4.0", + "version": "3.0.0", "license": "BSD-3-Clause-Clear", "description": "A higher-level wrapper around the Github API.", "main": "dist/components/GitHub.js", From 46dd1d7c20b6fa686c47d4219a1aff0ca53e6ea0 Mon Sep 17 00:00:00 2001 From: Felix Kling Date: Thu, 5 Jan 2017 08:56:15 -0800 Subject: [PATCH 05/61] Add methods to fetch gist's commit and specific revision (#412) Add support for: - https://developer.github.com/v3/gists/#list-gist-commits - https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist --- lib/Gist.js | 21 +++++++++++++++++++++ test/gist.spec.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) 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/test/gist.spec.js b/test/gist.spec.js index c9c2e393..af24f966 100644 --- a/test/gist.spec.js +++ b/test/gist.spec.js @@ -10,6 +10,7 @@ describe('Gist', function() { let gistId; let github; let commentId; + let revisionId; before(function() { github = new Github({ @@ -51,6 +52,7 @@ describe('Gist', function() { expect(gist).to.have.own('public', testGist.public); expect(gist).to.have.own('description', testGist.description); gistId = gist.id; + revisionId = gist.history[0].version; done(); })); @@ -99,6 +101,36 @@ describe('Gist', function() { it('should delete comment', function(done) { gist.deleteComment(commentId, assertSuccessful(done)); }); + + it('should update gist', function(done) { + const newGist = { + files: { + 'README.md': { + content: 'README updated', + }, + }, + }; + gist.update(newGist, assertSuccessful(done, function(err, gist) { + expect(gist.history.length).to.be(2); + expect(gist.files['README.md']).to.have.own('content', newGist.files['README.md'].content); + done(); + })); + }); + + it('should list commits', function(done) { + gist.listCommits(assertSuccessful(done, function(err, commits) { + expect(commits).to.be.an.array(); + done(); + })); + }); + + it('should get revision', function(done) { + gist.getRevision(revisionId, assertSuccessful(done, function(err, gist) { + expect(gist.history.length).to.be(1); + expect(gist.files['README.md']).to.have.own('content', testGist.files['README.md'].content); + done(); + })); + }); }); describe('deleting', function() { From bc37708b4ad245e65d2bb5f6f8a5140868a8d803 Mon Sep 17 00:00:00 2001 From: Clay Reimann Date: Wed, 11 Jan 2017 09:58:18 -0600 Subject: [PATCH 06/61] 3.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c66bb60..2ea2eb9a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "github-api", - "version": "3.0.0", + "version": "3.1.0", "license": "BSD-3-Clause-Clear", "description": "A higher-level wrapper around the Github API.", "main": "dist/components/GitHub.js", From 2deb74c29e9ced29caa3aa99730ed16531a02a7d Mon Sep 17 00:00:00 2001 From: Kairat Bakitow Date: Mon, 30 Jan 2017 23:51:09 +0600 Subject: [PATCH 07/61] Fix incorrect path for delete hook API (#418) --- lib/Repository.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Repository.js b/lib/Repository.js index fb200397..d338b261 100644 --- a/lib/Repository.js +++ b/lib/Repository.js @@ -603,7 +603,7 @@ class Repository extends Requestable { * @return {Promise} - the promise for the http request */ deleteHook(id, cb) { - return this._request('DELETE', `${this.__fullname}/hooks/${id}`, null, cb); + return this._request('DELETE', `/repos/${this.__fullname}/hooks/${id}`, null, cb); } /** From 22b889cd48cd281812b020d85f8ea502af69ddfd Mon Sep 17 00:00:00 2001 From: Julien Vanier Date: Fri, 24 Feb 2017 12:29:42 -0500 Subject: [PATCH 08/61] Docs fix for getProject (#428) --- lib/GitHub.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/GitHub.js b/lib/GitHub.js index 59cb94ff..c5cec4e2 100644 --- a/lib/GitHub.js +++ b/lib/GitHub.js @@ -117,7 +117,7 @@ class GitHub { /** * Create a new Project wrapper * @param {string} id - the id of the project - * @return {Markdown} + * @return {Project} */ getProject(id) { return new Project(id, this.__auth, this.__apiBase); From 1c18ef0eea29bb0aeb1b5dd79034ce3d1f77baf7 Mon Sep 17 00:00:00 2001 From: Andrii Lundiak Date: Fri, 8 Sep 2017 22:29:22 +0200 Subject: [PATCH 09/61] typo fix in GitHub.js --- lib/GitHub.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/GitHub.js b/lib/GitHub.js index c5cec4e2..fddd72ea 100644 --- a/lib/GitHub.js +++ b/lib/GitHub.js @@ -71,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} */ @@ -81,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} */ From 24223f7a51ba935f1c86ffd552dac20e8f3e359d Mon Sep 17 00:00:00 2001 From: Miyata Jumpei Date: Wed, 27 Sep 2017 01:15:56 +0900 Subject: [PATCH 10/61] chore: fix broken tests (#474) --- lib/Requestable.js | 2 +- test/organization.spec.js | 6 ++++-- test/team.spec.js | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Requestable.js b/lib/Requestable.js index bad111ac..44702b94 100644 --- a/lib/Requestable.js +++ b/lib/Requestable.js @@ -256,7 +256,7 @@ class Requestable { results.push(...thisGroup); const nextUrl = getNextPage(response.headers.link); - if (nextUrl && typeof options.page !== 'number') { + if (nextUrl && !(options && typeof options.page !== 'number')) { log(`getting next page: ${nextUrl}`); return this._requestAllPages(nextUrl, options, cb, results); } diff --git a/test/organization.spec.js b/test/organization.spec.js index 26fd3607..710ebbcf 100644 --- a/test/organization.spec.js +++ b/test/organization.spec.js @@ -17,11 +17,13 @@ describe('Organization', function() { password: testUser.PASSWORD, auth: 'basic', }); - return; + createdProject = undefined; }); after(function() { - return github.getProject(createdProject.id).deleteProject(); + if (createdProject) { + return github.getProject(createdProject.id).deleteProject(); + } }); describe('reading', function() { diff --git a/test/team.spec.js b/test/team.spec.js index 8a5ec8be..8f975384 100644 --- a/test/team.spec.js +++ b/test/team.spec.js @@ -159,7 +159,7 @@ describe('Team', function() { // Isolate tests that need a new team per test }); it('should add membership for a given user', function() { - return team.addMembership(testUser.USERNAME) + return team.addMembership(altUser.USERNAME) .then(({data}) => { const {state, role} = data; expect(state === 'active' || state === 'pending').to.be.true(); From 64a9223cc3817b583e387efb47f7e10e9e98dba7 Mon Sep 17 00:00:00 2001 From: Clay Jensen-Reimann Date: Sun, 31 Mar 2019 18:49:32 -0500 Subject: [PATCH 11/61] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 919b5eb3..d6c7c16c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +# Maintainers wanted +[Apply within](https://github.com/github-tools/github/issues/539) + # Github.js [![Downloads per month](https://img.shields.io/npm/dm/github-api.svg?maxAge=2592000)][npm-package] From 076229dac843f5519d4b51fcc723cd2705b3e8f8 Mon Sep 17 00:00:00 2001 From: Jared Rewerts Date: Fri, 5 Apr 2019 15:07:59 -0600 Subject: [PATCH 12/61] Fixed null ref test failure. --- test/repository.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/repository.spec.js b/test/repository.spec.js index ef5101cf..40d43397 100644 --- a/test/repository.spec.js +++ b/test/repository.spec.js @@ -169,7 +169,7 @@ describe('Repository', function() { it('should fail when null ref is passed', function(done) { remoteRepo.getSingleCommit(null, assertFailure(done, function(err) { - expect(err.response.status).to.be(404); + expect(err.response.status).to.be(422); done(); })); }); From dcc347289b047ff35d4af9d6f1c330487e9bac18 Mon Sep 17 00:00:00 2001 From: Jared Rewerts Date: Fri, 5 Apr 2019 14:49:55 -0600 Subject: [PATCH 13/61] Updated markdown GFM test. --- test/markdown.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/markdown.spec.js b/test/markdown.spec.js index eef9fe08..d3738d03 100644 --- a/test/markdown.spec.js +++ b/test/markdown.spec.js @@ -37,7 +37,7 @@ describe('Markdown', function() { }; markdown.render(options) .then(function({data: html}) { - expect(html).to.be('

Hello world github/linguist#1 cool, and #1!

'); // eslint-disable-line + expect(html).to.be('

Hello world github/linguist#1 cool, and gollum#1!

'); // eslint-disable-line done(); }).catch(done); }); From cd264468b459117ab7347b9069daf98a580f282f Mon Sep 17 00:00:00 2001 From: Kenshi Kamata Date: Tue, 9 Apr 2019 12:49:09 +0900 Subject: [PATCH 14/61] feature(User): Add List starred gists API (#475) * get list of starred gist api --- lib/User.js | 17 +++++++++++++++++ test/user.spec.js | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/lib/User.js b/lib/User.js index 3f3b4bb6..7e2660d5 100644 --- a/lib/User.js +++ b/lib/User.js @@ -132,6 +132,23 @@ 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 diff --git a/test/user.spec.js b/test/user.spec.js index 2296781c..b2ecc66e 100644 --- a/test/user.spec.js +++ b/test/user.spec.js @@ -61,6 +61,13 @@ describe('User', function() { user.listStarredRepos(assertArray(done)); }); + it('should show user\'s starred gists', function(done) { + const option = { + since: '2015-01-01T00:00:00Z', + }; + user.listStarredGists(option, assertArray(done)); + }); + it('should follow user', function(done) { user.follow('ingalls', assertSuccessful(done)); }); From cda24412632ac334387d27ec507d075b6b200b69 Mon Sep 17 00:00:00 2001 From: Alangi Derick Date: Thu, 17 Aug 2017 13:55:48 +0100 Subject: [PATCH 15/61] Adding styling to text Added styles to some portion of the text to make it more user friendly :). --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d6c7c16c..3be89eb5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![Travis](https://img.shields.io/travis/github-tools/github.svg?maxAge=60)][travis-ci] [![Codecov](https://img.shields.io/codecov/c/github/github-tools/github.svg?maxAge=2592000)][codecov] -Github.js provides a minimal higher-level wrapper around Github's API. +`Github.js` provides a minimal higher-level wrapper around Github's API. ## Usage @@ -71,7 +71,7 @@ clayreimann.listStarredRepos(function(err, repos) { should include updated JSDoc. ## Installation -Github.js is available from `npm` or [unpkg][unpkg]. +`Github.js` is available from `npm` or [unpkg][unpkg]. ```shell npm install github-api @@ -86,10 +86,10 @@ npm install github-api ``` ## Compatibility -`Github.js` is tested on Node.js: +`Github.js` is tested on `Node.js`: * 6.x -Note: `Github.js` uses Promise, hence it will not work in Node.js < 4 without polyfill. +Note: `Github.js` uses Promise, hence it will not work in `Node.js` < 4 without polyfill. [codecov]: https://codecov.io/github/github-tools/github?branch=master [docs]: http://github-tools.github.io/github/ From 943ac8a7d36dbac6c0848bee9a5ca64dc229f807 Mon Sep 17 00:00:00 2001 From: Andrii Lundiak Date: Sat, 9 Sep 2017 18:41:17 +0200 Subject: [PATCH 16/61] typo fix in Repository.js --- lib/Repository.js | 2 +- lib/Requestable.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Repository.js b/lib/Repository.js index d338b261..45ca3f94 100644 --- a/lib/Repository.js +++ b/lib/Repository.js @@ -14,7 +14,7 @@ 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 { /** diff --git a/lib/Requestable.js b/lib/Requestable.js index 44702b94..ab43d9d0 100644 --- a/lib/Requestable.js +++ b/lib/Requestable.js @@ -124,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) { @@ -235,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. */ From e26c2927a2a4f7abfe152cbd5233be48865435c4 Mon Sep 17 00:00:00 2001 From: Clay Jensen-Reimann Date: Tue, 9 Apr 2019 14:57:40 -0500 Subject: [PATCH 17/61] Update build versions, document in readme --- .travis.yml | 6 +++--- README.md | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 26f56aa3..ee525e3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ sudo: false language: node_js node_js: - - '6' - - '5' - - '4' + - '11' + - '10' + - '8' cache: directories: diff --git a/README.md b/README.md index 3be89eb5..bf021040 100644 --- a/README.md +++ b/README.md @@ -86,10 +86,7 @@ npm install github-api ``` ## Compatibility -`Github.js` is tested on `Node.js`: -* 6.x - -Note: `Github.js` uses Promise, hence it will not work in `Node.js` < 4 without polyfill. +`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/ From 9fb1f2f6fbb61009800bbf2a1dc19da7d94157b5 Mon Sep 17 00:00:00 2001 From: JongChan Choi Date: Tue, 3 Oct 2017 03:50:54 +0900 Subject: [PATCH 18/61] gist id is string --- lib/GitHub.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/GitHub.js b/lib/GitHub.js index fddd72ea..7c3ac24c 100644 --- a/lib/GitHub.js +++ b/lib/GitHub.js @@ -34,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) { From 38530fa329d636ff2233065f8048437b1ab4ef51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20P=C3=A9rez?= Date: Sat, 31 Mar 2018 10:09:46 +0200 Subject: [PATCH 19/61] Implementing methods to retrieve list of followers and following users. Github v3 provides the endpoints `followers` and `following` associated to the user entity. This change provides support for them and implements basic unit tests for them. --- lib/User.js | 20 ++++++++++++++++++++ test/user.spec.js | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/User.js b/lib/User.js index 7e2660d5..4848a1c6 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); + } + + /** + * List users followed by another user + * @see https://developer.github.com/v3/users/followers/#list-users-followed-by-another-user + * @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 diff --git a/test/user.spec.js b/test/user.spec.js index b2ecc66e..9b033ef1 100644 --- a/test/user.spec.js +++ b/test/user.spec.js @@ -34,6 +34,14 @@ describe('User', function() { user.listOrgs(assertArray(done)); }); + it('should get user followers', function(done) { + user.listFollowers(assertArray(done)); + }); + + it('should get user following list', function(done) { + user.listFollowing(assertArray(done)); + }); + it('should get user gists', function(done) { user.listGists(assertArray(done)); }); From 90fb78b2fb4b4e8e607d24a3dacc983caed2437e Mon Sep 17 00:00:00 2001 From: Jared Rewerts Date: Tue, 9 Apr 2019 20:51:40 -0600 Subject: [PATCH 20/61] feature: Add commit author (#547) --- lib/Repository.js | 12 +++++++++++- test/repository.spec.js | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/lib/Repository.js b/lib/Repository.js index 45ca3f94..f59cf0d0 100644 --- a/lib/Repository.js +++ b/lib/Repository.js @@ -334,16 +334,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], }; + 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 diff --git a/test/repository.spec.js b/test/repository.spec.js index 40d43397..62bb3ba6 100644 --- a/test/repository.spec.js +++ b/test/repository.spec.js @@ -600,6 +600,45 @@ describe('Repository', function() { })); }); + it('should succeed on proper commit', function(done) { + let parentSHA = ''; + let treeSHA = ''; + remoteRepo.getRef('heads/master').then((ref) => { + parentSHA = ref.data.object.sha; + return remoteRepo.getCommit(parentSHA); + }).then((commit) => { + treeSHA = commit.data.tree.sha; + return remoteRepo.commit(parentSHA, treeSHA, 'is this thing on?'); + }).then((commit) => { + expect(commit.data.author).to.have.own('name', 'Mike de Boer'); + expect(commit.data.author).to.have.own('email', 'mike@c9.io'); + done(); + }); + }); + + it('should allow commit to change author', function(done) { + let parentSHA = ''; + let treeSHA = ''; + remoteRepo.getRef('heads/master').then((ref) => { + parentSHA = ref.data.object.sha; + return remoteRepo.getCommit(parentSHA); + }).then((commit) => { + treeSHA = commit.data.tree.sha; + return remoteRepo.commit(parentSHA, treeSHA, 'Who made this commit?', { + author: { + name: 'Jimothy Halpert', + email: 'jim@dundermifflin.com', + }, + }); + }).then((commit) => { + expect(commit.data.author).to.have.own('name', 'Jimothy Halpert'); + expect(commit.data.author).to.have.own('email', 'jim@dundermifflin.com'); + done(); + }).catch((err) => { + throw err; + }); + }); + it('should create a release', function(done) { const releaseDef = { name: releaseName, From bf6b42a040a2f0461e5ab855496a5a457d3f0c71 Mon Sep 17 00:00:00 2001 From: CodyGramlich Date: Wed, 10 Apr 2019 22:24:26 -0600 Subject: [PATCH 21/61] Use username arg for follow and unfollow requests. --- lib/User.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/User.js b/lib/User.js index 3f3b4bb6..3b5cb653 100644 --- a/lib/User.js +++ b/lib/User.js @@ -150,7 +150,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); } /** @@ -161,7 +161,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); } /** From 5a5d87e94185baced8a8b6268717c5dfac8bcf8e Mon Sep 17 00:00:00 2001 From: Clay Jensen-Reimann Date: Tue, 16 Apr 2019 20:18:34 -0500 Subject: [PATCH 22/61] 3.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ea2eb9a..4f28fdb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "github-api", - "version": "3.1.0", + "version": "3.2.0", "license": "BSD-3-Clause-Clear", "description": "A higher-level wrapper around the Github API.", "main": "dist/components/GitHub.js", From ee64cd1e352ad0354ea6ce0e8abf676a7f57204d Mon Sep 17 00:00:00 2001 From: Clay Jensen-Reimann Date: Tue, 16 Apr 2019 20:45:52 -0500 Subject: [PATCH 23/61] Updated npm deployment token --- .travis.yml | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index ee525e3e..6b6bad91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,21 @@ sudo: false language: node_js node_js: - - '11' - - '10' - - '8' - +- '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 run test-coverage - # - npm run test-dist # test the compiled files +- npm run test-coverage after_success: - - npm run codecov +- npm run codecov before_deploy: - - npm run build +- npm run build deploy: provider: npm skip_cleanup: true @@ -26,4 +23,4 @@ deploy: tags: true email: clayreimann@gmail.com api_key: - secure: TZHqJ9Kh2Qf0GAVDjEOQ01Ez6rGMYHKwVLOKTbnb7nSzF7iiGNT4UwzvYawm0T9p1k7X1WOqW3l7OEbIwoKl7/9azT4BBJm7qUMRfB9Zio5cL3rKubJVz7+LEEIW4iBeDWLanhUDgy9BO2JKCt8bfp/U2tltgXtu9Fm/UFPALI8= + secure: WnLh1m02aF7NvFNILCZ8KsjPuDeSddQI87y8dwAixStr2FhQyz8FIKZN2Qj1N1Q9ZJvBETe5HWs1c9yOjTKBkD0d/eU2hlpnB9WXEFRJVDjiUuMnpAMMvuqTZwYg6kXq5N+of95PX58AYiBiV/qwsdUr/MgjEEYLt5UZgRYQRvE= From 09384e3981de3ef40caddff62fcefccf6aa89523 Mon Sep 17 00:00:00 2001 From: Jared Rewerts Date: Tue, 16 Apr 2019 18:54:09 -0700 Subject: [PATCH 24/61] Fix browser markdown (#548) Got the code from PR #505. Fixes #451. --- lib/Markdown.js | 2 +- test/dist.spec/index.html | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/Markdown.js b/lib/Markdown.js index edc346cc..ebfbb512 100644 --- a/lib/Markdown.js +++ b/lib/Markdown.js @@ -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/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 @@