Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List milestone and List Labels in a project #359

Merged
merged 15 commits into from
Mar 5, 2020
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]

}
38 changes: 38 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,26 @@ trait Issues[F[_]] {
pagination: Option[Pagination] = None,
headers: Map[String, String] = Map()
): F[GHResponse[List[User]]]

/**
*
anamariamv marked this conversation as resolved.
Show resolved Hide resolved
* @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
anamariamv marked this conversation as resolved.
Show resolved Hide resolved
*/
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,20 @@ 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,
method = s"repos/$owner/$repo/labels",
headers,
Map(),
pagination
)
anamariamv marked this conversation as resolved.
Show resolved Hide resolved

override def listLabels(
owner: String,
repo: String,
Expand Down Expand Up @@ -183,4 +197,23 @@ 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 = state.fold(Map.empty[String, String])(s => Map("state" -> s)) ++
sort.fold(Map.empty[String, String])(s => Map("sort" -> s)) ++
direction.fold(Map.empty[String, String])(d => Map("direction" -> d))
anamariamv marked this conversation as resolved.
Show resolved Hide resolved
)
}
68 changes: 68 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,46 @@ 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,
validRepoNameWithMilestone,
None,
None,
None,
None,
headerUserAgent
)
.unsafeRunSync()

testIsRight[List[Milestone]](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
.listMilestones(
invalidRepoOwner,
validRepoNameWithMilestone,
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(milestone), okStatusCode, Map.empty)))

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

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

}
Loading