Skip to content

Commit 4268802

Browse files
committed
feature(Issue): add Milestone API
Closes #68 Closes #268
1 parent 3d761f5 commit 4268802

File tree

4 files changed

+153
-27
lines changed

4 files changed

+153
-27
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## master
44
### Features
5+
Added Milestone API
6+
* `Issue.listMilestones`
7+
* `Issue.getMilestone`
8+
* `Issue.editMilestone`
9+
* `Issue.deleteMilestone`
510

611
### Fixes
712

lib/Issue.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,62 @@ class Issue extends Requestable {
123123
getIssue(issue, cb) {
124124
return this._request('GET', `/repos/${this.__repository}/issues/${issue}`, null, cb);
125125
}
126+
127+
/**
128+
* List the milestones for the repository
129+
* @see https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
130+
* @param {Object} options - filtering options
131+
* @param {Requestable.callback} [cb] - will receive the array of milestones
132+
* @return {Promise} - the promise for the http request
133+
*/
134+
listMilestones(options, cb) {
135+
return this._request('GET', `/repos/${this.__repository}/milestones`, options, cb);
136+
}
137+
138+
/**
139+
* Get a milestone
140+
* @see https://developer.github.com/v3/issues/milestones/#get-a-single-milestone
141+
* @param {string} milestone - the id of the milestone to fetch
142+
* @param {Requestable.callback} [cb] - will receive the array of milestones
143+
* @return {Promise} - the promise for the http request
144+
*/
145+
getMilestone(milestone, cb) {
146+
return this._request('GET', `/repos/${this.__repository}/milestones/${milestone}`, null, cb);
147+
}
148+
149+
/**
150+
* Create a new milestone
151+
* @see https://developer.github.com/v3/issues/milestones/#create-a-milestone
152+
* @param {Object} milestoneData - the milestone definition
153+
* @param {Requestable.callback} [cb] - will receive the array of milestones
154+
* @return {Promise} - the promise for the http request
155+
*/
156+
createMilestone(milestoneData, cb) {
157+
return this._request('POST', `/repos/${this.__repository}/milestones`, milestoneData, cb);
158+
}
159+
160+
/**
161+
* Edit a milestone
162+
* @see https://developer.github.com/v3/issues/milestones/#update-a-milestone
163+
* @param {string} milestone - the id of the milestone to edit
164+
* @param {Object} milestoneData - the updates to make to the milestone
165+
* @param {Requestable.callback} [cb] - will receive the array of milestones
166+
* @return {Promise} - the promise for the http request
167+
*/
168+
editMilestone(milestone, milestoneData, cb) {
169+
return this._request('PATCH', `/repos/${this.__repository}/milestones/${milestone}`, milestoneData, cb);
170+
}
171+
172+
/**
173+
* Delete a milestone (this is distinct from closing a milestone)
174+
* @see https://developer.github.com/v3/issues/milestones/#delete-a-milestone
175+
* @param {string} milestone - the id of the milestone to delete
176+
* @param {Requestable.callback} [cb] - will receive the array of milestones
177+
* @return {Promise} - the promise for the http request
178+
*/
179+
deleteMilestone(milestone, cb) {
180+
return this._request('DELETE', `/repos/${this.__repository}/milestones/${milestone}`, null, cb);
181+
}
126182
}
127183

128184
module.exports = Issue;

lib/Requestable.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,14 @@ module.exports = Requestable;
228228
// ////////////////////////// //
229229
// Private helper functions //
230230
// ////////////////////////// //
231-
function buildError(path, response) {
232-
return {
233-
path: path,
234-
request: response.config,
235-
response: response,
236-
status: response.status
237-
};
231+
class ResponseError extends Error {
232+
constructor(path, response) {
233+
super(`error making request ${response.config.method} ${response.config.url}`);
234+
this.path = path;
235+
this.request = response.config;
236+
this.response = response;
237+
this.status = response.status;
238+
}
238239
}
239240

240241
const METHODS_WITH_NO_BODY = ['GET', 'HEAD', 'DELETE'];
@@ -256,10 +257,12 @@ function getNextPage(linksHeader = '') {
256257
function callbackErrorOrThrow(cb, path) {
257258
return function handler(response) {
258259
log(`error making request ${response.config.method} ${response.config.url} ${JSON.stringify(response.data)}`);
259-
let error = buildError(path, response);
260+
let error = new ResponseError(path, response);
260261
if (cb) {
262+
log('going to error callback');
261263
cb(error);
262264
} else {
265+
log('throwing error');
263266
throw error;
264267
}
265268
};

test/issue.spec.js

Lines changed: 81 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import {assertSuccessful} from './helpers/callbacks';
77
describe('Issue', function() {
88
let github;
99
let remoteIssues;
10-
let remoteIssueId;
11-
let remoteIssueCommentId;
1210

1311
before(function() {
1412
github = new Github({
@@ -21,6 +19,9 @@ describe('Issue', function() {
2119
});
2220

2321
describe('reading', function() {
22+
let remoteIssueId;
23+
let milestoneId;
24+
2425
it('should list issues', function(done) {
2526
remoteIssues.listIssues({}, assertSuccessful(done, function(err, issues) {
2627
expect(issues).to.be.an.array();
@@ -37,9 +38,31 @@ describe('Issue', function() {
3738
done();
3839
}));
3940
});
41+
42+
it('should get all milestones', function(done) {
43+
remoteIssues.listMilestones()
44+
.then(({data: milestones}) => {
45+
expect(milestones).to.be.an.array();
46+
milestoneId = milestones[0].number;
47+
48+
done();
49+
}).catch(done);
50+
});
51+
52+
it('should get a single milestone', function(done) {
53+
remoteIssues.getMilestone(milestoneId)
54+
.then(({data: milestone}) => {
55+
expect(milestone).to.have.own('title', 'Default Milestone');
56+
done();
57+
}).catch(done);
58+
});
4059
});
4160

42-
describe('creating/modifiying/editing/deleting', function() {
61+
describe('creating/editing/deleting', function() {
62+
let createdIssueId;
63+
let issueCommentId;
64+
let createdMilestoneId;
65+
4366
// 200ms between tests so that Github has a chance to settle
4467
beforeEach(function(done) {
4568
setTimeout(done, 200);
@@ -52,6 +75,7 @@ describe('Issue', function() {
5275
};
5376

5477
remoteIssues.createIssue(newIssue, assertSuccessful(done, function(err, issue) {
78+
createdIssueId = issue.number;
5579
expect(issue).to.have.own('url');
5680
expect(issue).to.have.own('title', newIssue.title);
5781
expect(issue).to.have.own('body', newIssue.body);
@@ -60,56 +84,94 @@ describe('Issue', function() {
6084
}));
6185
});
6286

87+
it('should edit issue', function(done) {
88+
const newProps = {
89+
title: 'Edited title',
90+
state: 'closed'
91+
};
92+
93+
remoteIssues.editIssue(createdIssueId, newProps, assertSuccessful(done, function(err, issue) {
94+
expect(issue).to.have.own('title', newProps.title);
95+
96+
done();
97+
}));
98+
});
99+
63100
it('should post issue comment', function(done) {
64-
remoteIssues.createIssueComment(remoteIssueId, 'Comment test', assertSuccessful(done, function(err, issue) {
101+
remoteIssues.createIssueComment(createdIssueId, 'Comment test', assertSuccessful(done, function(err, issue) {
65102
expect(issue).to.have.own('body', 'Comment test');
66103

67104
done();
68105
}));
69106
});
70107

71108
it('should list issue comments', function(done) {
72-
remoteIssues.listIssueComments(remoteIssueId, assertSuccessful(done, function(err, comments) {
109+
remoteIssues.listIssueComments(createdIssueId, assertSuccessful(done, function(err, comments) {
73110
expect(comments).to.be.an.array();
74111
expect(comments[0]).to.have.own('body', 'Comment test');
75-
remoteIssueCommentId = comments[0].id
112+
issueCommentId = comments[0].id;
76113
done();
77114
}));
78115
});
79116

80117
it('should get a single issue comment', function(done) {
81-
remoteIssues.getIssueComment(remoteIssueCommentId, assertSuccessful(done, function(err, comment) {
118+
remoteIssues.getIssueComment(issueCommentId, assertSuccessful(done, function(err, comment) {
82119
expect(comment).to.have.own('body', 'Comment test');
83120
done();
84121
}));
85122
});
86123

87124
it('should edit issue comment', function(done) {
88-
remoteIssues.editIssueComment(remoteIssueCommentId, 'Comment test edited', assertSuccessful(done, function(err, comment) {
89-
expect(comment).to.have.own('body', 'Comment test edited');
125+
remoteIssues.editIssueComment(issueCommentId, 'Comment test edited',
126+
assertSuccessful(done, function(err, comment) {
127+
expect(comment).to.have.own('body', 'Comment test edited');
90128

91-
done();
92-
}));
129+
done();
130+
}));
93131
});
94132

95133
it('should delete issue comment', function(done) {
96-
remoteIssues.deleteIssueComment(remoteIssueCommentId, assertSuccessful(done, function(err, response) {
134+
remoteIssues.deleteIssueComment(issueCommentId, assertSuccessful(done, function(err, response) {
97135
expect(response).to.be.true;
98136

99137
done();
100138
}));
101139
});
102140

103-
it('should edit issues title', function(done) {
104-
const newProps = {
105-
title: 'Edited title'
141+
it('should create a milestone', function(done) {
142+
let milestone = {
143+
title: 'v42',
144+
description: 'The ultimate version'
106145
};
107146

108-
remoteIssues.editIssue(remoteIssueId, newProps, assertSuccessful(done, function(err, issue) {
109-
expect(issue).to.have.own('title', newProps.title);
147+
remoteIssues.createMilestone(milestone)
148+
.then(({data: createdMilestone}) => {
149+
expect(createdMilestone).to.have.own('number');
150+
expect(createdMilestone).to.have.own('title', milestone.title);
110151

111-
done();
112-
}));
152+
createdMilestoneId = createdMilestone.number;
153+
done();
154+
}).catch(done);
155+
});
156+
it('should update a milestone', function(done) {
157+
let milestone = {
158+
description: 'Version 6 * 7'
159+
};
160+
161+
remoteIssues.editMilestone(createdMilestoneId, milestone)
162+
.then(({data: createdMilestone}) => {
163+
expect(createdMilestone).to.have.own('number', createdMilestoneId);
164+
expect(createdMilestone).to.have.own('describe', milestone.description);
165+
166+
done();
167+
}).catch(done);
168+
});
169+
it('should delete a milestone', function(done) {
170+
remoteIssues.deleteMilestone(createdMilestoneId)
171+
.then(({status}) => {
172+
expect(status).to.equal(204);
173+
done();
174+
}).catch(done);
113175
});
114176
});
115177
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy