From e1233d123e7a427f338ba5be876b4f88936a7be7 Mon Sep 17 00:00:00 2001 From: bond15 Date: Fri, 13 Mar 2020 03:17:42 -0700 Subject: [PATCH] Add support for listing cards, fixes #361 (#364) --- docs/docs/project.md | 37 ++++++++++++++++++- .../src/main/scala/github4s/Decoders.scala | 1 + .../scala/github4s/algebras/Projects.scala | 17 +++++++++ .../main/scala/github4s/domain/Project.scala | 14 +++++++ .../interpreters/ProjectsInterpreter.scala | 17 ++++++++- .../github4s/integration/GHProjectsSpec.scala | 20 +++++++++- .../scala/github4s/unit/ProjectSpec.scala | 18 +++++++++ .../test/scala/github4s/utils/TestData.scala | 36 ++++++++++++++++++ 8 files changed, 156 insertions(+), 4 deletions(-) diff --git a/docs/docs/project.md b/docs/docs/project.md index cf2c3a956..47d3bfd3b 100644 --- a/docs/docs/project.md +++ b/docs/docs/project.md @@ -3,7 +3,7 @@ layout: docs title: Project API permalink: project --- - + # Project API Note: The Projects API is currently available for developers to preview. During the preview period, @@ -19,6 +19,8 @@ with Github4s, you can interact with: - [List projects](#list-projects) - [Columns](#columns) - [List project columns](#list-project-columns) + - [Cards](#cards) + - [List project cards](#list-project-cards-by-column) The following examples assume the following imports and token: @@ -124,4 +126,35 @@ The `result` on the right is the corresponding [List[Column]][column-scala]. See [the API doc](https://developer.github.com/v3/projects/columns/#list-project-columns) for full reference. -[column-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/Column.scala +[column-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/Project.scala + +### Cards + +#### List project cards by column + +You can list the cards for a particular column with `listCards`; it takes as arguments: + +- `column_id`: column id for which we want to retrieve the cards. +- `archived_state`: filters the project cards that are returned by the card's state. +Can be one of `all`,`archived`, or `not_archived`. Default: `not_archived`, optional. +- `pagination`: Limit and Offset for pagination, optional. +- `header`: headers to include in the request, optional. + +To list the columns for project_id `8271018`: + +```scala mdoc:compile-only +val listCards = Github[IO](accessToken).projects.listCards( + column_id = 8271018, + headers = Map("Accept" -> "application/vnd.github.inertia-preview+json")) +val response = listCards.unsafeRunSync() +response.result match { + case Left(e) => println(s"Something went wrong: ${e.getMessage}") + case Right(r) => println(r) +} +``` + +The `result` on the right is the corresponding [List[Card]][card-scala]. + +See [the API doc](https://developer.github.com/v3/projects/cards/#list-project-cards) for full reference. + +[card-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/Project.scala diff --git a/github4s/src/main/scala/github4s/Decoders.scala b/github4s/src/main/scala/github4s/Decoders.scala index 087c4261b..f28dda3bf 100644 --- a/github4s/src/main/scala/github4s/Decoders.scala +++ b/github4s/src/main/scala/github4s/Decoders.scala @@ -280,5 +280,6 @@ object Decoders { implicit val decodeMilestone: Decoder[Milestone] = deriveDecoder[Milestone] implicit val decodeProject: Decoder[Project] = deriveDecoder[Project] implicit val decodeColumn: Decoder[Column] = deriveDecoder[Column] + implicit val decodeCard: Decoder[Card] = deriveDecoder[Card] } diff --git a/github4s/src/main/scala/github4s/algebras/Projects.scala b/github4s/src/main/scala/github4s/algebras/Projects.scala index 22b4aaabe..b008b2d90 100644 --- a/github4s/src/main/scala/github4s/algebras/Projects.scala +++ b/github4s/src/main/scala/github4s/algebras/Projects.scala @@ -71,4 +71,21 @@ trait Projects[F[_]] { headers: Map[String, String] = Map() ): F[GHResponse[List[Column]]] + /** + * List the cards belonging to a specific column id + * + * @param column_id Column id for which we want to retrieve the cards + * @param archived_state Filters the project cards that are returned by the card's state. + * Can be one of all,archived, or not_archived. Default: not_archived + * @param pagination Limit and Offset for pagination + * @param headers Optional user headers to include in the request + * @return GHResponse with the list of cards belonging to this column id + */ + def listCards( + column_id: Int, + archived_state: Option[String] = None, + pagination: Option[Pagination] = None, + headers: Map[String, String] = Map() + ): F[GHResponse[List[Card]]] + } diff --git a/github4s/src/main/scala/github4s/domain/Project.scala b/github4s/src/main/scala/github4s/domain/Project.scala index c0073c287..694be4bd8 100644 --- a/github4s/src/main/scala/github4s/domain/Project.scala +++ b/github4s/src/main/scala/github4s/domain/Project.scala @@ -64,3 +64,17 @@ final case class Column( created_at: String, updated_at: String ) + +final case class Card( + url: String, + project_url: String, + id: Int, + node_id: String, + note: Option[String], + archived: Boolean, + creator: Creator, + created_at: String, + updated_at: String, + column_url: String, + content_url: Option[String] +) diff --git a/github4s/src/main/scala/github4s/interpreters/ProjectsInterpreter.scala b/github4s/src/main/scala/github4s/interpreters/ProjectsInterpreter.scala index b98550d81..46aea9c87 100644 --- a/github4s/src/main/scala/github4s/interpreters/ProjectsInterpreter.scala +++ b/github4s/src/main/scala/github4s/interpreters/ProjectsInterpreter.scala @@ -18,7 +18,7 @@ package github4s.interpreters import github4s.GithubResponses.GHResponse import github4s.algebras.Projects -import github4s.domain.{Column, Pagination, Project} +import github4s.domain.{Column, Card, Pagination, Project} import github4s.http.HttpClient import github4s.Decoders._ @@ -67,4 +67,19 @@ class ProjectsInterpreter[F[_]]( Map(), pagination ) + + override def listCards( + column_id: Int, + archived_state: Option[String], + pagination: Option[Pagination], + headers: Map[String, String] + ): F[GHResponse[List[Card]]] = + client.get[List[Card]]( + accessToken, + s"projects/columns/$column_id/cards", + headers, + archived_state.fold(Map.empty[String, String])(s => Map("archived_state" -> s)), + pagination + ) + } diff --git a/github4s/src/test/scala/github4s/integration/GHProjectsSpec.scala b/github4s/src/test/scala/github4s/integration/GHProjectsSpec.scala index c8474f603..e63518a89 100644 --- a/github4s/src/test/scala/github4s/integration/GHProjectsSpec.scala +++ b/github4s/src/test/scala/github4s/integration/GHProjectsSpec.scala @@ -18,7 +18,7 @@ package github4s.integration import cats.effect.IO import github4s.Github -import github4s.domain.{Column, Project} +import github4s.domain.{Card, Column, Project} import github4s.utils.{BaseIntegrationSpec, Integration} trait GHProjectsSpec extends BaseIntegrationSpec { @@ -103,4 +103,22 @@ trait GHProjectsSpec extends BaseIntegrationSpec { response.statusCode shouldBe notFoundStatusCode } + "Project >> ListCards" should "return the expected cards when a valid column id is provided" taggedAs Integration in { + val response = Github[IO](accessToken).projects + .listCards(validColumnId, headers = headerUserAgent ++ headerAccept) + .unsafeRunSync() + + testIsRight[List[Card]](response, r => r.nonEmpty shouldBe true) + response.statusCode shouldBe okStatusCode + } + + it should "return error when an invalid column id is passed" taggedAs Integration in { + val response = + Github[IO](accessToken).projects + .listCards(invalidColumnId, headers = headerUserAgent ++ headerAccept) + .unsafeRunSync() + + testIsLeft(response) + response.statusCode shouldBe notFoundStatusCode + } } diff --git a/github4s/src/test/scala/github4s/unit/ProjectSpec.scala b/github4s/src/test/scala/github4s/unit/ProjectSpec.scala index e7948c8e0..172a95a05 100644 --- a/github4s/src/test/scala/github4s/unit/ProjectSpec.scala +++ b/github4s/src/test/scala/github4s/unit/ProjectSpec.scala @@ -83,4 +83,22 @@ class ProjectSpec extends BaseSpec { projects.listColumns(validProjectId, None, headers = headerUserAgent ++ headerAccept) } + + "Project.listCards" should "call to httpClient.get with the right parameters" in { + + val response: IO[GHResponse[List[Card]]] = + IO(GHResponse(List(card).asRight, okStatusCode, Map.empty)) + + implicit val httpClientMock = httpClientMockGet[List[Card]]( + url = s"projects/columns/$validColumnId/cards", + headers = headerAccept, + response = response + ) + + val project = new ProjectsInterpreter[IO] + + project.listCards(validColumnId, None, None, headerUserAgent ++ headerAccept) + + } + } diff --git a/github4s/src/test/scala/github4s/utils/TestData.scala b/github4s/src/test/scala/github4s/utils/TestData.scala index 127916b73..f4cf689b4 100644 --- a/github4s/src/test/scala/github4s/utils/TestData.scala +++ b/github4s/src/test/scala/github4s/utils/TestData.scala @@ -486,4 +486,40 @@ trait TestData extends DummyGithubUrls { updated_at = "2019-07-04T09:39:01Z" ) + val validColumnId = 8271018 + val invalidColumnId = -32 + + val card = Card( + url = "https://api.github.com/projects/columns/cards/34323195", + project_url = "https://api.github.com/projects/4085286", + id = 34323195, + node_id = "MDExOlByb2plY3RDYXJkMzQzMjMxOTU=", + note = Some("Test Card"), + archived = false, + creator = Creator( + login = "calvellido", + id = 7753447, + node_id = "MDQ6VXNlcjc3NTM0NDc=", + avatar_url = "https://avatars0.githubusercontent.com/u/7753447?v=4", + gravatar_id = None, + url = "https://api.github.com/users/calvellido", + html_url = "https://github.com/calvellido", + followers_url = "https://api.github.com/users/calvellido/followers", + following_url = "https://api.github.com/users/calvellido/following{/other_user}", + gists_url = "https://api.github.com/users/calvellido/gists{/gist_id}", + starred_url = "https://api.github.com/users/calvellido/starred{/owner}{/repo}", + subscriptions_url = "https://api.github.com/users/calvellido/subscriptions", + organizations_url = "https://api.github.com/users/calvellido/orgs", + repos_url = "https://api.github.com/users/calvellido/repos", + events_url = "https://api.github.com/users/calvellido/events{/privacy}", + received_events_url = "https://api.github.com/users/calvellido/received_events", + `type` = "User", + site_admin = false + ), + created_at = "2018-11-02T09:36:28Z", + updated_at = "2019-07-04T09:39:01Z", + column_url = "https://api.github.com/projects/columns/8271018", + content_url = None + ) + }