Skip to content

Commit

Permalink
List milestone and List Labels in a project (#359)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
anamariamv and BenFradet authored Mar 5, 2020
1 parent 51f1f71 commit db70e5b
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 3 deletions.
55 changes: 55 additions & 0 deletions docs/docs/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ with Github4s, you can interact with:
- [Edit a comment](#edit-a-comment)
- [Delete a comment](#delete-a-comment)
- [Labels](#labels)
- [List labels for this repository](#list-labels-for-this-repository)
- [List labels](#list-labels)
- [Add labels](#add-labels)
- [Remove a label](#remove-a-label)
- [Assignees](#assignees)
- [List available assignees](#list-available-assignees)
- [Milestones](#milestones)
- [List milestones for a respository](#list-milestones-for-a-repository)

The following examples assume the following imports and token:

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

## Labels

### List labels for this repository

You can list labels for an issue with the following parameters:

- the repository coordinates (`owner` and `name` of the repository).
- `pagination`: Limit and Offset for pagination, optional.

To list labels:

```scala mdoc:compile-only
val labelListRepository = Github[IO](accessToken).issues.listLabelsRepository("47deg", "github4s")
labelListRepository.unsafeRunSync match {
case Left(e) => println(s"Something went wrong: ${e.getMessage}")
case Right(r) => println(r.result)
}
```

The `result` on the right is the corresponding [List[Label]][issue-scala]

See [the API doc](https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository) for full reference.


### List labels

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

[issue-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/Issue.scala
[user-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/User.scala

## Milestones

### List milestones for a repository

You can list the milestone for a particular organization and repository with `listMilestones`; it takes arguments:

- `owner`: name of the owner for which we want to retrieve the milestones.
- `repo`: name of the repository for which we want to retrieve the milestones.
- `state`: filter projects returned by their state. Can be either `open`, `closed`, `all`. Default: `open`, optional
- `sort`: what to sort results by. Either `due_on` or `completeness`. Default: `due_on`, optional
- `direction` the direction of the sort. Either `asc` or `desc`. Default: `asc`, optional
- `pagination`: Limit and Offset for pagination, optional.
- `header`: headers to include in the request, optional.

To list the milestone for owner `47deg` and repository `github4s`:

```scala mdoc:compile-only
val milestones = Github[IO](accessToken).issues.listMilestones("47deg", "github4s", Some("open"), None, None)
milestones.unsafeRunSync match {
case Left(e) => println(s"Something went wrong: ${e.getMessage}")
case Right(r) => println(r.result)
}
```

The `result` on the right is the corresponding [List[Milestone]][milestone-scala]

See [the API doc](https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository) for full reference.

[milestone-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/Milestone.scala
7 changes: 4 additions & 3 deletions github4s/src/main/scala/github4s/Decoders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,9 @@ object Decoders {
)
)

implicit val decodeTeam: Decoder[Team] = deriveDecoder[Team]
implicit val decodeProject: Decoder[Project] = deriveDecoder[Project]
implicit val decodeColumn: Decoder[Column] = deriveDecoder[Column]
implicit val decodeTeam: Decoder[Team] = deriveDecoder[Team]
implicit val decodeMilestone: Decoder[Milestone] = deriveDecoder[Milestone]
implicit val decodeProject: Decoder[Project] = deriveDecoder[Project]
implicit val decodeColumn: Decoder[Column] = deriveDecoder[Column]

}
39 changes: 39 additions & 0 deletions github4s/src/main/scala/github4s/algebras/Issues.scala
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,22 @@ trait Issues[F[_]] {
headers: Map[String, String] = Map()
): F[GHResponse[Unit]]

/**
* List the labels assigned to a Repository
*
* @param owner of the repo
* @param repo name of the repo
* @param headers optional user headers to include in the request
* @param pagination Limit and Offset for pagination, optional.
* @return a GHResponse with the list of labels for the Repository.
*/
def listLabelsRepository(
owner: String,
repo: String,
headers: Map[String, String] = Map(),
pagination: Option[Pagination] = None
): F[GHResponse[List[Label]]]

/**
* List the labels assigned to an Issue
*
Expand Down Expand Up @@ -275,4 +291,27 @@ trait Issues[F[_]] {
pagination: Option[Pagination] = None,
headers: Map[String, String] = Map()
): F[GHResponse[List[User]]]

/**
* List milestone in specified repository
*
* @param owner repo owner
* @param repo repo name
* @param state filter milestones returned by their state. Can be either `open`, `closed`, `all`. Default: `open`
* @param sort What to sort results by. Either due_on or completeness. Default: due_on
* @param direction The direction of the sort. Either asc or desc. Default: asc
* @param pagination Limit and Offset for pagination
* @param headers optional user headers to include in the request
* @return a GHResponse with the list of milestones in specified repository
*/
def listMilestones(
owner: String,
repo: String,
state: Option[String],
sort: Option[String],
direction: Option[String],
pagination: Option[Pagination] = None,
headers: Map[String, String] = Map()
): F[GHResponse[List[Milestone]]]

}
19 changes: 19 additions & 0 deletions github4s/src/main/scala/github4s/domain/Issue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,22 @@ case class Comment(
)

case class CommentData(body: String)

case class Milestone(
url: String,
html_url: String,
labels_url: String,
id: Int,
node_id: String,
number: Int,
state: String,
title: String,
description: String,
creator: Creator,
open_issues: Int,
closed_issues: Int,
created_at: String,
updated_at: String,
closed_at: Option[String],
due_on: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,19 @@ class IssuesInterpreter[F[_]](implicit client: HttpClient[F], accessToken: Optio
): F[GHResponse[Unit]] =
client.delete(accessToken, s"repos/$owner/$repo/issues/comments/$id", headers)

override def listLabelsRepository(
owner: String,
repo: String,
headers: Map[String, String],
pagination: Option[Pagination]
): F[GHResponse[List[Label]]] =
client.get[List[Label]](
accessToken,
s"repos/$owner/$repo/labels",
headers = headers,
pagination = pagination
)

override def listLabels(
owner: String,
repo: String,
Expand Down Expand Up @@ -183,4 +196,22 @@ class IssuesInterpreter[F[_]](implicit client: HttpClient[F], accessToken: Optio
headers,
pagination = pagination
)

override def listMilestones(
owner: String,
repo: String,
state: Option[String],
sort: Option[String],
direction: Option[String],
pagination: Option[Pagination],
headers: Map[String, String]
): F[GHResponse[List[Milestone]]] =
client.get[List[Milestone]](
accessToken,
s"repos/$owner/$repo/milestones",
headers,
pagination = pagination,
params =
List(state.map("state" -> _), sort.map("sort" -> _), direction.map("direction" -> _)).flatten.toMap
)
}
65 changes: 65 additions & 0 deletions github4s/src/test/scala/github4s/integration/GHIssuesSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,32 @@ trait GHIssuesSpec extends BaseIntegrationSpec {
})
}

"Issues >> listLabelsRepository" should "return a list of labels" taggedAs Integration in {
val response = Github[IO](accessToken).issues
.listLabelsRepository(validRepoOwner, validRepoName, headerUserAgent, None)
.unsafeRunSync()

testIsRight[List[Label]](response, { r =>
r.result.nonEmpty shouldBe true
r.statusCode shouldBe okStatusCode
})
}
it should "return error for an invalid repo owner" taggedAs Integration in {
val response = Github[IO](accessToken).issues
.listLabelsRepository(invalidRepoOwner, validRepoName, headerUserAgent, None)
.unsafeRunSync()

testIsLeft(response)
}

it should "return error for an invalid repo name" taggedAs Integration in {
val response = Github[IO](accessToken).issues
.listLabelsRepository(validRepoOwner, invalidRepoName, headerUserAgent, None)
.unsafeRunSync()

testIsLeft(response)
}

"Issues >> RemoveLabel" should "return a list of removed labels" taggedAs Integration in {
val response = Github[IO](accessToken).issues
.removeLabel(
Expand Down Expand Up @@ -159,4 +185,43 @@ trait GHIssuesSpec extends BaseIntegrationSpec {
testIsLeft(response)
}

"GHIssues >> ListMilestones" should "return a list of milestones" taggedAs Integration in {
val response = Github[IO](accessToken).issues
.listMilestones(
validRepoOwner,
validRepoName,
None,
None,
None,
None,
headerUserAgent
)
.unsafeRunSync()

testIsRight[List[Milestone]](response, r => r.statusCode shouldBe okStatusCode)
}

it should "return error for an invalid repo owner" taggedAs Integration in {
val response = Github[IO](accessToken).issues
.listMilestones(
invalidRepoOwner,
validRepoName,
None,
None,
None,
None,
headerUserAgent
)
.unsafeRunSync()

testIsLeft(response)
}

it should "return error for an invalid repo name" taggedAs Integration in {
val response = Github[IO](accessToken).issues
.listMilestones(validRepoOwner, invalidRepoName, None, None, None, None, headerUserAgent)
.unsafeRunSync()
testIsLeft(response)
}

}
34 changes: 34 additions & 0 deletions github4s/src/test/scala/github4s/unit/IssuesSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,19 @@ class IssuesSpec extends BaseSpec {
issues.deleteComment(validRepoOwner, validRepoName, validCommentId, headerUserAgent)
}

"Issues.ListLabelsRepository" should "call httpClient.get with the right parameters" in {
val response: IO[GHResponse[List[Label]]] =
IO(Right(GHResult(List(label), okStatusCode, Map.empty)))

implicit val httpClientMock = httpClientMockGet[List[Label]](
url = s"repos/$validRepoOwner/$validRepoName/labels",
response = response
)

val issues = new IssuesInterpreter[IO]
issues.listLabelsRepository(validRepoOwner, validRepoName, headerUserAgent)
}

"Issues.ListLabels" should "call httpClient.get with the right parameters" in {
val response: IO[GHResponse[List[Label]]] =
IO(Right(GHResult(List(label), okStatusCode, Map.empty)))
Expand Down Expand Up @@ -276,4 +289,25 @@ class IssuesSpec extends BaseSpec {
)
}

"Issues.listMilestone" should "call httpClient.get with the right parameters" in {
val response: IO[GHResponse[List[Milestone]]] =
IO(Right(GHResult(List(), okStatusCode, Map.empty)))

implicit val httpClientMock = httpClientMockGet[List[Milestone]](
url = s"repos/$validRepoOwner/$validRepoName/milestones",
response = response
)

val issues = new IssuesInterpreter[IO]
issues.listMilestones(
validRepoOwner,
validRepoName,
None,
None,
None,
Some(Pagination(validPage, validPerPage)),
headerUserAgent
)
}

}

0 comments on commit db70e5b

Please sign in to comment.