├── .gitignore ├── .travis.yml ├── package.json ├── .jshintrc ├── ghissues.js ├── LICENSE ├── README.md └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | ex.js 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - lts/* 4 | branches: 5 | only: 6 | - master 7 | notifications: 8 | email: 9 | - rod@vagg.org 10 | script: npm test 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ghissues", 3 | "version": "1.1.4", 4 | "description": "Interact with the GitHub users API", 5 | "main": "ghissues.js", 6 | "scripts": { 7 | "test": "node test.js | faucet" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/rvagg/ghissues.git" 12 | }, 13 | "keywords": [ 14 | "github", 15 | "issues" 16 | ], 17 | "author": "Rod Vagg ", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/rvagg/ghissues/issues" 21 | }, 22 | "homepage": "https://github.com/rvagg/ghissues", 23 | "dependencies": { 24 | "ghutils": "^4.0.0" 25 | }, 26 | "devDependencies": { 27 | "bl": "^1.2.3", 28 | "faucet": "0.0.1", 29 | "tape": "^5.0.1", 30 | "xtend": "^4.0.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ ] 3 | , "bitwise": false 4 | , "camelcase": false 5 | , "curly": false 6 | , "eqeqeq": false 7 | , "forin": false 8 | , "immed": false 9 | , "latedef": false 10 | , "noarg": true 11 | , "noempty": true 12 | , "nonew": true 13 | , "plusplus": false 14 | , "quotmark": true 15 | , "regexp": false 16 | , "undef": true 17 | , "unused": true 18 | , "strict": false 19 | , "trailing": true 20 | , "maxlen": 120 21 | , "asi": true 22 | , "boss": true 23 | , "debug": true 24 | , "eqnull": true 25 | , "esnext": true 26 | , "evil": true 27 | , "expr": true 28 | , "funcscope": false 29 | , "globalstrict": false 30 | , "iterator": false 31 | , "lastsemic": true 32 | , "laxbreak": true 33 | , "laxcomma": true 34 | , "loopfunc": true 35 | , "multistr": false 36 | , "onecase": false 37 | , "proto": false 38 | , "regexdash": false 39 | , "scripturl": true 40 | , "smarttabs": false 41 | , "shadow": false 42 | , "sub": true 43 | , "supernew": false 44 | , "validthis": true 45 | , "browser": true 46 | , "couch": false 47 | , "devel": false 48 | , "dojo": false 49 | , "mootools": false 50 | , "node": true 51 | , "nonstandard": true 52 | , "prototypejs": false 53 | , "rhino": false 54 | , "worker": true 55 | , "wsh": false 56 | , "nomen": false 57 | , "onevar": false 58 | , "passfail": false 59 | } -------------------------------------------------------------------------------- /ghissues.js: -------------------------------------------------------------------------------- 1 | const ghutils = require('ghutils') 2 | 3 | 4 | module.exports.list = function list (auth, org, repo, options, callback) { 5 | if (typeof options == 'function') { 6 | callback = options 7 | options = {} 8 | } 9 | 10 | var url = 'https://api.github.com/repos/' + org + '/' + repo + '/issues?page=1' 11 | ghutils.lister(auth, url, options, callback) 12 | } 13 | 14 | module.exports.get = function get (auth, org, repo, num, options, callback) { 15 | if (typeof options == 'function') { 16 | callback = options 17 | options = {} 18 | } 19 | 20 | var url = 'https://api.github.com/repos/' + org + '/' + repo + '/issues/' + num 21 | 22 | ghutils.ghget(auth, url, options, callback) 23 | } 24 | 25 | 26 | module.exports.create = function create (auth, org, repo, data, options, callback) { 27 | if (typeof options == 'function') { 28 | callback = options 29 | options = {} 30 | } 31 | 32 | var url = 'https://api.github.com/repos/' + org + '/' + repo + '/issues' 33 | 34 | ghutils.ghpost(auth, url, data, options, callback) 35 | } 36 | 37 | 38 | module.exports.listComments = function listComments (auth, org, repo, num, options, callback) { 39 | if (typeof options == 'function') { 40 | callback = options 41 | options = {} 42 | } 43 | 44 | var url = 'https://api.github.com/repos/' + org + '/' + repo + '/issues/' + num + '/comments?page=1' 45 | ghutils.lister(auth, url, options, callback) 46 | } 47 | 48 | 49 | module.exports.createComment = function createComment (auth, org, repo, num, body, options, callback) { 50 | if (typeof options == 'function') { 51 | callback = options 52 | options = {} 53 | } 54 | 55 | var url = 'https://api.github.com/repos/' + org + '/' + repo + '/issues/' + num + '/comments' 56 | 57 | ghutils.ghpost(auth, url, { body: body }, options, callback) 58 | } 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014, Rod Vagg (the "Original Author") 2 | All rights reserved. 3 | 4 | MIT +no-false-attribs License 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the "Software"), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | Distributions of all or part of the Software intended to be used 19 | by the recipients as they would use the unmodified Software, 20 | containing modifications that substantially alter, remove, or 21 | disable functionality of the Software, outside of the documented 22 | configuration mechanisms provided by the Software, shall be 23 | modified such that the Original Author's bug reporting email 24 | addresses and urls are either replaced with the contact information 25 | of the parties responsible for the changes, or removed entirely. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 29 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 30 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 31 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 32 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 33 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 34 | OTHER DEALINGS IN THE SOFTWARE. 35 | 36 | 37 | Except where noted, this license applies to any and all software 38 | programs and associated documentation files created by the 39 | Original Author, when distributed with the Software. 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ghissues 2 | 3 | [![Build Status](https://secure.travis-ci.org/rvagg/ghissues.png)](http://travis-ci.org/rvagg/ghissues) 4 | 5 | **A node library to interact with the GitHub issues API** 6 | 7 | [![NPM](https://nodei.co/npm/ghissues.png?mini=true)](https://nodei.co/npm/ghissues/) 8 | 9 | ## Example usage 10 | 11 | ```js 12 | const ghissues = require('ghissues') 13 | , authOptions = { user: 'rvagg', token: '24d5dee258c64aef38a66c0c5eca459c379901c2' } 14 | 15 | // list all issues in a repo 16 | ghissues.list(authOptions, 'rvagg', 'jsonist', function (err, issuelist) { 17 | // Array of issues data for 'rvagg/jsonist' 18 | console.log(issuelist) 19 | }) 20 | 21 | // get issue data by number (not internal GitHub id) 22 | ghissues.get(authOptions, 'rvagg', 'nan', 123, function (err, issue) { 23 | // object containing full issue #123 24 | console.log(issue) 25 | }) 26 | 27 | // create an issue 28 | var data = { 29 | title : 'New issue bro' 30 | , body : 'Pretty **slick** `markdown`' 31 | } 32 | ghissues.create(authOptions, 'rvagg', 'jsonist', data, function (err, issue) { 33 | // data for new issue 34 | console.log(issue) 35 | }) 36 | 37 | // list all comments in an issue 38 | ghissues.listComments(authOptions, 'rvagg', 'jsonist', 47, function (err, commentlist) { 39 | // Array of comment data for 'rvagg/jsonist#47' 40 | console.log(commentlist) 41 | }) 42 | 43 | // create a comment 44 | var body = 'Whoa dude, this is awesomesauce!! :+1:' 45 | ghissues.createComment(authOptions, 'rvagg', 'jsonist', 101, body, function (err, issue) { 46 | // data for new comment in 'rvagg/jsonist#101' 47 | console.log(issue) 48 | }) 49 | ``` 50 | 51 | 52 | The auth data is compatible with [ghauth](https://github.com/rvagg/ghauth) so you can just connect them together to make a simple command-line application: 53 | 54 | ```js 55 | const ghauth = require('ghauth') 56 | , ghissues = require('ghissues') 57 | , authOptions = { 58 | configName : 'issue-lister' 59 | , scopes : [ 'user' ] 60 | } 61 | 62 | ghauth(authOptions, function (err, authData) { 63 | ghissues.list(authData, 'rvagg', 'node-levelup', function (err, list) { 64 | console.log('Issues in rvagg/node-levelup:') 65 | list.forEach(function (i) { 66 | console.log('#%s: %s', i.number, i.title) 67 | }) 68 | }) 69 | }) 70 | ``` 71 | 72 | 73 | ## License 74 | 75 | **ghissues** is Copyright (c) 2014 Rod Vagg [@rvagg](https://github.com/rvagg) and licensed under the MIT licence. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE file for more details. 76 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const http = require('http') 2 | , ghutils = require('ghutils/test-util') 3 | , test = require('tape') 4 | , xtend = require('xtend') 5 | , bl = require('bl') 6 | , ghissues = require('./') 7 | 8 | 9 | test('test list issues', function (t) { 10 | t.plan(10) 11 | 12 | var auth = { user: 'authuser', token: 'authtoken' } 13 | , org = 'testorg' 14 | , repo = 'testrepo' 15 | , testData = [ 16 | { 17 | response : [ { test1: 'data1' }, { test2: 'data2' } ] 18 | , headers : { link: '; rel="next"' } 19 | } 20 | , { response: [] } 21 | ] 22 | , server 23 | 24 | server = ghutils.makeServer(testData) 25 | .on('ready', function () { 26 | ghissues.list(xtend(auth), org, repo, ghutils.verifyData(t, testData[0].response)) 27 | }) 28 | .on('request', ghutils.verifyRequest(t, auth)) 29 | .on('get', ghutils.verifyUrl(t, [ 30 | 'https://api.github.com/repos/testorg/testrepo/issues?page=1' 31 | , 'https://api.github.com/repos/testorg/testrepo/issues?page=2' 32 | ])) 33 | .on('close' , ghutils.verifyClose(t)) 34 | }) 35 | 36 | 37 | test('test list multi-page issues', function (t) { 38 | t.plan(13) 39 | 40 | var auth = { user: 'authuser', token: 'authtoken' } 41 | , org = 'testorg' 42 | , repo = 'testrepo' 43 | , testData = [ 44 | { 45 | response : [ { test1: 'data1' }, { test2: 'data2' } ] 46 | , headers : { link: '; rel="next"' } 47 | } 48 | , { 49 | response : [ { test1: 'data3' }, { test2: 'data4' } ] 50 | , headers : { link: '; rel="next"' } 51 | } 52 | , { response: [] } 53 | ] 54 | , server 55 | 56 | server = ghutils.makeServer(testData) 57 | .on('ready', function () { 58 | ghissues.list(xtend(auth), org, repo, ghutils.verifyData(t, testData[0].response.concat(testData[1].response))) 59 | }) 60 | .on('request', ghutils.verifyRequest(t, auth)) 61 | .on('get', ghutils.verifyUrl(t, [ 62 | 'https://api.github.com/repos/testorg/testrepo/issues?page=1' 63 | , 'https://api.github.com/repos/testorg/testrepo/issues?page=2' 64 | , 'https://api.github.com/repos/testorg/testrepo/issues?page=3' 65 | ])) 66 | .on('close' , ghutils.verifyClose(t)) 67 | }) 68 | 69 | 70 | test('test list no issues', function (t) { 71 | t.plan(7) 72 | 73 | var auth = { user: 'authuser', token: 'authtoken' } 74 | , org = 'testorg' 75 | , repo = 'testrepo' 76 | , testData = [ [] ] 77 | , server 78 | 79 | server = ghutils.makeServer(testData) 80 | .on('ready', function () { 81 | ghissues.list(xtend(auth), org, repo, ghutils.verifyData(t, [])) 82 | }) 83 | .on('request', ghutils.verifyRequest(t, auth)) 84 | .on('get', ghutils.verifyUrl(t, [ 85 | 'https://api.github.com/repos/testorg/testrepo/issues?page=1' 86 | ])) 87 | .on('close' , ghutils.verifyClose(t)) 88 | }) 89 | 90 | 91 | test('test get issue by id', function (t) { 92 | t.plan(7) 93 | 94 | var auth = { user: 'authuser', token: 'authtoken' } 95 | , org = 'testorg' 96 | , repo = 'testrepo' 97 | , num = 101 98 | , testData = { id: num, issue: 'body' } 99 | , server 100 | 101 | server = ghutils.makeServer(testData) 102 | .on('ready', function () { 103 | ghissues.get(xtend(auth), org, repo, num, ghutils.verifyData(t, testData)) 104 | }) 105 | .on('request', ghutils.verifyRequest(t, auth)) 106 | .on('get', ghutils.verifyUrl(t, [ 107 | 'https://api.github.com/repos/testorg/testrepo/issues/' + num 108 | ])) 109 | .on('close' , ghutils.verifyClose(t)) 110 | }) 111 | 112 | 113 | test('test create new issue', function (t) { 114 | t.plan(9) 115 | 116 | var auth = { user: 'authuser', token: 'authtoken' } 117 | , org = 'testorg' 118 | , repo = 'testrepo' 119 | , testData = { title: 'issue title', body: 'issue body' } 120 | , resp = 'derp' 121 | , server 122 | 123 | server = ghutils.makeServer(resp) 124 | .on('ready', function () { 125 | ghissues.create(xtend(auth), org, repo, testData, ghutils.verifyData(t, resp)) 126 | }) 127 | .on('request', ghutils.verifyRequest(t, auth)) 128 | .on('request', function (req) { 129 | req.pipe(bl(function (err, data) { 130 | t.notOk(err, 'no error') 131 | t.deepEqual(JSON.parse(data.toString()), testData, 'got expected post data') 132 | })) 133 | }) 134 | .on('post', ghutils.verifyUrl(t, [ 135 | 'https://api.github.com/repos/testorg/testrepo/issues' 136 | ])) 137 | .on('close' , ghutils.verifyClose(t)) 138 | }) 139 | 140 | 141 | test('test list issue comments', function (t) { 142 | t.plan(10) 143 | 144 | var auth = { user: 'authuser', token: 'authtoken' } 145 | , org = 'testorg' 146 | , repo = 'testrepo' 147 | , num = 48 148 | , testData = [ [ { test1: 'data1' }, { test2: 'data2' } ], [] ] 149 | , testData = [ 150 | { 151 | response : [ { test1: 'data1' }, { test2: 'data2' } ] 152 | , headers : { link: '; rel="next"' } 153 | } 154 | , { response: [] } 155 | ] 156 | , server 157 | 158 | server = ghutils.makeServer(testData) 159 | .on('ready', function () { 160 | ghissues.listComments(xtend(auth), org, repo, num, ghutils.verifyData(t, testData[0].response)) 161 | }) 162 | .on('request', ghutils.verifyRequest(t, auth)) 163 | .on('get', ghutils.verifyUrl(t, [ 164 | 'https://api.github.com/repos/testorg/testrepo/issues/' + num + '/comments?page=1' 165 | , 'https://api.github.com/repos/testorg/testrepo/issues/' + num + '/comments?page=2' 166 | ])) 167 | .on('close' , ghutils.verifyClose(t)) 168 | }) 169 | 170 | 171 | test('test list multi-page issue comments', function (t) { 172 | t.plan(13) 173 | 174 | var auth = { user: 'authuser', token: 'authtoken' } 175 | , org = 'testorg' 176 | , repo = 'testrepo' 177 | , num = 202 178 | , testData = [ 179 | { 180 | response : [ { test1: 'data1' }, { test2: 'data2' } ] 181 | , headers : { link: '; rel="next"' } 182 | } 183 | , { 184 | response : [ { test1: 'data3' }, { test2: 'data4' } ] 185 | , headers : { link: '; rel="next"' } 186 | } 187 | , { response: [] } 188 | ] 189 | , server 190 | 191 | server = ghutils.makeServer(testData) 192 | .on('ready', function () { 193 | ghissues.listComments(xtend(auth), org, repo, num, ghutils.verifyData(t, testData[0].response.concat(testData[1].response))) 194 | }) 195 | .on('request', ghutils.verifyRequest(t, auth)) 196 | .on('get', ghutils.verifyUrl(t, [ 197 | 'https://api.github.com/repos/testorg/testrepo/issues/' + num + '/comments?page=1' 198 | , 'https://api.github.com/repos/testorg/testrepo/issues/' + num + '/comments?page=2' 199 | , 'https://api.github.com/repos/testorg/testrepo/issues/' + num + '/comments?page=3' 200 | ])) 201 | .on('close' , ghutils.verifyClose(t)) 202 | }) 203 | 204 | 205 | test('test list no issue comments', function (t) { 206 | t.plan(7) 207 | 208 | var auth = { user: 'authuser', token: 'authtoken' } 209 | , org = 'testorg' 210 | , repo = 'testrepo' 211 | , num = 1 212 | , testData = [ [] ] 213 | , server 214 | 215 | server = ghutils.makeServer(testData) 216 | .on('ready', function () { 217 | ghissues.listComments(xtend(auth), org, repo, num, ghutils.verifyData(t, [])) 218 | }) 219 | .on('request', ghutils.verifyRequest(t, auth)) 220 | .on('get', ghutils.verifyUrl(t, [ 221 | 'https://api.github.com/repos/testorg/testrepo/issues/' + num + '/comments?page=1' 222 | ])) 223 | .on('close' , ghutils.verifyClose(t)) 224 | }) 225 | 226 | 227 | test('test create new comment', function (t) { 228 | t.plan(9) 229 | 230 | var auth = { user: 'authuser', token: 'authtoken' } 231 | , org = 'testorg' 232 | , repo = 'testrepo' 233 | , testData = 'comment body' 234 | , num = 303 235 | , resp = 'herpderp' 236 | , server 237 | 238 | server = ghutils.makeServer(resp) 239 | .on('ready', function () { 240 | ghissues.createComment(xtend(auth), org, repo, num, testData, ghutils.verifyData(t, resp)) 241 | }) 242 | .on('request', ghutils.verifyRequest(t, auth)) 243 | .on('request', function (req) { 244 | req.pipe(bl(function (err, data) { 245 | t.notOk(err, 'no error') 246 | t.deepEqual(JSON.parse(data.toString()), {body:testData}, 'got expected post data') 247 | })) 248 | }) 249 | .on('post', ghutils.verifyUrl(t, [ 250 | 'https://api.github.com/repos/testorg/testrepo/issues/' + num + '/comments' 251 | ])) 252 | .on('close' , ghutils.verifyClose(t)) 253 | }) 254 | --------------------------------------------------------------------------------