Skip to content

Commit db70e5b

Browse files
List milestone and List Labels in a project (#359)
* GET list milestones * added test * updated documentation listMilestone * added GET list labels repository * updated docu * merge with master * fix Travis * updated doc * Update github4s/src/main/scala/github4s/interpreters/IssuesInterpreter.scala Co-Authored-By: Ben Fradet <benjamin.fradet@gmail.com> * Address PR comments * remove val * fix test Co-authored-by: Ben Fradet <benjamin.fradet@gmail.com>
1 parent 51f1f71 commit db70e5b

7 files changed

Lines changed: 247 additions & 3 deletions

File tree

docs/docs/issue.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ with Github4s, you can interact with:
2121
- [Edit a comment](#edit-a-comment)
2222
- [Delete a comment](#delete-a-comment)
2323
- [Labels](#labels)
24+
- [List labels for this repository](#list-labels-for-this-repository)
2425
- [List labels](#list-labels)
2526
- [Add labels](#add-labels)
2627
- [Remove a label](#remove-a-label)
2728
- [Assignees](#assignees)
2829
- [List available assignees](#list-available-assignees)
30+
- [Milestones](#milestones)
31+
- [List milestones for a respository](#list-milestones-for-a-repository)
2932

3033
The following examples assume the following imports and token:
3134

@@ -268,6 +271,28 @@ See [the API doc](https://developer.github.com/v3/issues/comments/#delete-a-comm
268271

269272
## Labels
270273

274+
### List labels for this repository
275+
276+
You can list labels for an issue with the following parameters:
277+
278+
- the repository coordinates (`owner` and `name` of the repository).
279+
- `pagination`: Limit and Offset for pagination, optional.
280+
281+
To list labels:
282+
283+
```scala mdoc:compile-only
284+
val labelListRepository = Github[IO](accessToken).issues.listLabelsRepository("47deg", "github4s")
285+
labelListRepository.unsafeRunSync match {
286+
case Left(e) => println(s"Something went wrong: ${e.getMessage}")
287+
case Right(r) => println(r.result)
288+
}
289+
```
290+
291+
The `result` on the right is the corresponding [List[Label]][issue-scala]
292+
293+
See [the API doc](https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository) for full reference.
294+
295+
271296
### List labels
272297

273298
You can list labels for an issue with the following parameters:
@@ -362,3 +387,33 @@ As a result, if you'd like to see a feature supported, feel free to create an is
362387

363388
[issue-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/Issue.scala
364389
[user-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/User.scala
390+
391+
## Milestones
392+
393+
### List milestones for a repository
394+
395+
You can list the milestone for a particular organization and repository with `listMilestones`; it takes arguments:
396+
397+
- `owner`: name of the owner for which we want to retrieve the milestones.
398+
- `repo`: name of the repository for which we want to retrieve the milestones.
399+
- `state`: filter projects returned by their state. Can be either `open`, `closed`, `all`. Default: `open`, optional
400+
- `sort`: what to sort results by. Either `due_on` or `completeness`. Default: `due_on`, optional
401+
- `direction` the direction of the sort. Either `asc` or `desc`. Default: `asc`, optional
402+
- `pagination`: Limit and Offset for pagination, optional.
403+
- `header`: headers to include in the request, optional.
404+
405+
To list the milestone for owner `47deg` and repository `github4s`:
406+
407+
```scala mdoc:compile-only
408+
val milestones = Github[IO](accessToken).issues.listMilestones("47deg", "github4s", Some("open"), None, None)
409+
milestones.unsafeRunSync match {
410+
case Left(e) => println(s"Something went wrong: ${e.getMessage}")
411+
case Right(r) => println(r.result)
412+
}
413+
```
414+
415+
The `result` on the right is the corresponding [List[Milestone]][milestone-scala]
416+
417+
See [the API doc](https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository) for full reference.
418+
419+
[milestone-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/Milestone.scala

github4s/src/main/scala/github4s/Decoders.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,9 @@ object Decoders {
276276
)
277277
)
278278

279-
implicit val decodeTeam: Decoder[Team] = deriveDecoder[Team]
280-
implicit val decodeProject: Decoder[Project] = deriveDecoder[Project]
281-
implicit val decodeColumn: Decoder[Column] = deriveDecoder[Column]
279+
implicit val decodeTeam: Decoder[Team] = deriveDecoder[Team]
280+
implicit val decodeMilestone: Decoder[Milestone] = deriveDecoder[Milestone]
281+
implicit val decodeProject: Decoder[Project] = deriveDecoder[Project]
282+
implicit val decodeColumn: Decoder[Column] = deriveDecoder[Column]
282283

283284
}

github4s/src/main/scala/github4s/algebras/Issues.scala

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,22 @@ trait Issues[F[_]] {
208208
headers: Map[String, String] = Map()
209209
): F[GHResponse[Unit]]
210210

211+
/**
212+
* List the labels assigned to a Repository
213+
*
214+
* @param owner of the repo
215+
* @param repo name of the repo
216+
* @param headers optional user headers to include in the request
217+
* @param pagination Limit and Offset for pagination, optional.
218+
* @return a GHResponse with the list of labels for the Repository.
219+
*/
220+
def listLabelsRepository(
221+
owner: String,
222+
repo: String,
223+
headers: Map[String, String] = Map(),
224+
pagination: Option[Pagination] = None
225+
): F[GHResponse[List[Label]]]
226+
211227
/**
212228
* List the labels assigned to an Issue
213229
*
@@ -275,4 +291,27 @@ trait Issues[F[_]] {
275291
pagination: Option[Pagination] = None,
276292
headers: Map[String, String] = Map()
277293
): F[GHResponse[List[User]]]
294+
295+
/**
296+
* List milestone in specified repository
297+
*
298+
* @param owner repo owner
299+
* @param repo repo name
300+
* @param state filter milestones returned by their state. Can be either `open`, `closed`, `all`. Default: `open`
301+
* @param sort What to sort results by. Either due_on or completeness. Default: due_on
302+
* @param direction The direction of the sort. Either asc or desc. Default: asc
303+
* @param pagination Limit and Offset for pagination
304+
* @param headers optional user headers to include in the request
305+
* @return a GHResponse with the list of milestones in specified repository
306+
*/
307+
def listMilestones(
308+
owner: String,
309+
repo: String,
310+
state: Option[String],
311+
sort: Option[String],
312+
direction: Option[String],
313+
pagination: Option[Pagination] = None,
314+
headers: Map[String, String] = Map()
315+
): F[GHResponse[List[Milestone]]]
316+
278317
}

github4s/src/main/scala/github4s/domain/Issue.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,22 @@ case class Comment(
8484
)
8585

8686
case class CommentData(body: String)
87+
88+
case class Milestone(
89+
url: String,
90+
html_url: String,
91+
labels_url: String,
92+
id: Int,
93+
node_id: String,
94+
number: Int,
95+
state: String,
96+
title: String,
97+
description: String,
98+
creator: Creator,
99+
open_issues: Int,
100+
closed_issues: Int,
101+
created_at: String,
102+
updated_at: String,
103+
closed_at: Option[String],
104+
due_on: String
105+
)

github4s/src/main/scala/github4s/interpreters/IssuesInterpreter.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,19 @@ class IssuesInterpreter[F[_]](implicit client: HttpClient[F], accessToken: Optio
136136
): F[GHResponse[Unit]] =
137137
client.delete(accessToken, s"repos/$owner/$repo/issues/comments/$id", headers)
138138

139+
override def listLabelsRepository(
140+
owner: String,
141+
repo: String,
142+
headers: Map[String, String],
143+
pagination: Option[Pagination]
144+
): F[GHResponse[List[Label]]] =
145+
client.get[List[Label]](
146+
accessToken,
147+
s"repos/$owner/$repo/labels",
148+
headers = headers,
149+
pagination = pagination
150+
)
151+
139152
override def listLabels(
140153
owner: String,
141154
repo: String,
@@ -183,4 +196,22 @@ class IssuesInterpreter[F[_]](implicit client: HttpClient[F], accessToken: Optio
183196
headers,
184197
pagination = pagination
185198
)
199+
200+
override def listMilestones(
201+
owner: String,
202+
repo: String,
203+
state: Option[String],
204+
sort: Option[String],
205+
direction: Option[String],
206+
pagination: Option[Pagination],
207+
headers: Map[String, String]
208+
): F[GHResponse[List[Milestone]]] =
209+
client.get[List[Milestone]](
210+
accessToken,
211+
s"repos/$owner/$repo/milestones",
212+
headers,
213+
pagination = pagination,
214+
params =
215+
List(state.map("state" -> _), sort.map("sort" -> _), direction.map("direction" -> _)).flatten.toMap
216+
)
186217
}

github4s/src/test/scala/github4s/integration/GHIssuesSpec.scala

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,32 @@ trait GHIssuesSpec extends BaseIntegrationSpec {
104104
})
105105
}
106106

107+
"Issues >> listLabelsRepository" should "return a list of labels" taggedAs Integration in {
108+
val response = Github[IO](accessToken).issues
109+
.listLabelsRepository(validRepoOwner, validRepoName, headerUserAgent, None)
110+
.unsafeRunSync()
111+
112+
testIsRight[List[Label]](response, { r =>
113+
r.result.nonEmpty shouldBe true
114+
r.statusCode shouldBe okStatusCode
115+
})
116+
}
117+
it should "return error for an invalid repo owner" taggedAs Integration in {
118+
val response = Github[IO](accessToken).issues
119+
.listLabelsRepository(invalidRepoOwner, validRepoName, headerUserAgent, None)
120+
.unsafeRunSync()
121+
122+
testIsLeft(response)
123+
}
124+
125+
it should "return error for an invalid repo name" taggedAs Integration in {
126+
val response = Github[IO](accessToken).issues
127+
.listLabelsRepository(validRepoOwner, invalidRepoName, headerUserAgent, None)
128+
.unsafeRunSync()
129+
130+
testIsLeft(response)
131+
}
132+
107133
"Issues >> RemoveLabel" should "return a list of removed labels" taggedAs Integration in {
108134
val response = Github[IO](accessToken).issues
109135
.removeLabel(
@@ -159,4 +185,43 @@ trait GHIssuesSpec extends BaseIntegrationSpec {
159185
testIsLeft(response)
160186
}
161187

188+
"GHIssues >> ListMilestones" should "return a list of milestones" taggedAs Integration in {
189+
val response = Github[IO](accessToken).issues
190+
.listMilestones(
191+
validRepoOwner,
192+
validRepoName,
193+
None,
194+
None,
195+
None,
196+
None,
197+
headerUserAgent
198+
)
199+
.unsafeRunSync()
200+
201+
testIsRight[List[Milestone]](response, r => r.statusCode shouldBe okStatusCode)
202+
}
203+
204+
it should "return error for an invalid repo owner" taggedAs Integration in {
205+
val response = Github[IO](accessToken).issues
206+
.listMilestones(
207+
invalidRepoOwner,
208+
validRepoName,
209+
None,
210+
None,
211+
None,
212+
None,
213+
headerUserAgent
214+
)
215+
.unsafeRunSync()
216+
217+
testIsLeft(response)
218+
}
219+
220+
it should "return error for an invalid repo name" taggedAs Integration in {
221+
val response = Github[IO](accessToken).issues
222+
.listMilestones(validRepoOwner, invalidRepoName, None, None, None, None, headerUserAgent)
223+
.unsafeRunSync()
224+
testIsLeft(response)
225+
}
226+
162227
}

github4s/src/test/scala/github4s/unit/IssuesSpec.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,19 @@ class IssuesSpec extends BaseSpec {
203203
issues.deleteComment(validRepoOwner, validRepoName, validCommentId, headerUserAgent)
204204
}
205205

206+
"Issues.ListLabelsRepository" should "call httpClient.get with the right parameters" in {
207+
val response: IO[GHResponse[List[Label]]] =
208+
IO(Right(GHResult(List(label), okStatusCode, Map.empty)))
209+
210+
implicit val httpClientMock = httpClientMockGet[List[Label]](
211+
url = s"repos/$validRepoOwner/$validRepoName/labels",
212+
response = response
213+
)
214+
215+
val issues = new IssuesInterpreter[IO]
216+
issues.listLabelsRepository(validRepoOwner, validRepoName, headerUserAgent)
217+
}
218+
206219
"Issues.ListLabels" should "call httpClient.get with the right parameters" in {
207220
val response: IO[GHResponse[List[Label]]] =
208221
IO(Right(GHResult(List(label), okStatusCode, Map.empty)))
@@ -276,4 +289,25 @@ class IssuesSpec extends BaseSpec {
276289
)
277290
}
278291

292+
"Issues.listMilestone" should "call httpClient.get with the right parameters" in {
293+
val response: IO[GHResponse[List[Milestone]]] =
294+
IO(Right(GHResult(List(), okStatusCode, Map.empty)))
295+
296+
implicit val httpClientMock = httpClientMockGet[List[Milestone]](
297+
url = s"repos/$validRepoOwner/$validRepoName/milestones",
298+
response = response
299+
)
300+
301+
val issues = new IssuesInterpreter[IO]
302+
issues.listMilestones(
303+
validRepoOwner,
304+
validRepoName,
305+
None,
306+
None,
307+
None,
308+
Some(Pagination(validPage, validPerPage)),
309+
headerUserAgent
310+
)
311+
}
312+
279313
}

0 commit comments

Comments
 (0)