Skip to content

Commit e1233d1

Browse files
authored
Add support for listing cards, fixes #361 (#364)
1 parent 563f863 commit e1233d1

8 files changed

Lines changed: 156 additions & 4 deletions

File tree

docs/docs/project.md

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ layout: docs
33
title: Project API
44
permalink: project
55
---
6-
6+
77
# Project API
88

99
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:
1919
- [List projects](#list-projects)
2020
- [Columns](#columns)
2121
- [List project columns](#list-project-columns)
22+
- [Cards](#cards)
23+
- [List project cards](#list-project-cards-by-column)
2224

2325
The following examples assume the following imports and token:
2426

@@ -124,4 +126,35 @@ The `result` on the right is the corresponding [List[Column]][column-scala].
124126

125127
See [the API doc](https://developer.github.com/v3/projects/columns/#list-project-columns) for full reference.
126128

127-
[column-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/Column.scala
129+
[column-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/Project.scala
130+
131+
### Cards
132+
133+
#### List project cards by column
134+
135+
You can list the cards for a particular column with `listCards`; it takes as arguments:
136+
137+
- `column_id`: column id for which we want to retrieve the cards.
138+
- `archived_state`: filters the project cards that are returned by the card's state.
139+
Can be one of `all`,`archived`, or `not_archived`. Default: `not_archived`, optional.
140+
- `pagination`: Limit and Offset for pagination, optional.
141+
- `header`: headers to include in the request, optional.
142+
143+
To list the columns for project_id `8271018`:
144+
145+
```scala mdoc:compile-only
146+
val listCards = Github[IO](accessToken).projects.listCards(
147+
column_id = 8271018,
148+
headers = Map("Accept" -> "application/vnd.github.inertia-preview+json"))
149+
val response = listCards.unsafeRunSync()
150+
response.result match {
151+
case Left(e) => println(s"Something went wrong: ${e.getMessage}")
152+
case Right(r) => println(r)
153+
}
154+
```
155+
156+
The `result` on the right is the corresponding [List[Card]][card-scala].
157+
158+
See [the API doc](https://developer.github.com/v3/projects/cards/#list-project-cards) for full reference.
159+
160+
[card-scala]: https://github.com/47deg/github4s/blob/master/github4s/src/main/scala/github4s/domain/Project.scala

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,5 +280,6 @@ object Decoders {
280280
implicit val decodeMilestone: Decoder[Milestone] = deriveDecoder[Milestone]
281281
implicit val decodeProject: Decoder[Project] = deriveDecoder[Project]
282282
implicit val decodeColumn: Decoder[Column] = deriveDecoder[Column]
283+
implicit val decodeCard: Decoder[Card] = deriveDecoder[Card]
283284

284285
}

github4s/src/main/scala/github4s/algebras/Projects.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,21 @@ trait Projects[F[_]] {
7171
headers: Map[String, String] = Map()
7272
): F[GHResponse[List[Column]]]
7373

74+
/**
75+
* List the cards belonging to a specific column id
76+
*
77+
* @param column_id Column id for which we want to retrieve the cards
78+
* @param archived_state Filters the project cards that are returned by the card's state.
79+
* Can be one of all,archived, or not_archived. Default: not_archived
80+
* @param pagination Limit and Offset for pagination
81+
* @param headers Optional user headers to include in the request
82+
* @return GHResponse with the list of cards belonging to this column id
83+
*/
84+
def listCards(
85+
column_id: Int,
86+
archived_state: Option[String] = None,
87+
pagination: Option[Pagination] = None,
88+
headers: Map[String, String] = Map()
89+
): F[GHResponse[List[Card]]]
90+
7491
}

github4s/src/main/scala/github4s/domain/Project.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,17 @@ final case class Column(
6464
created_at: String,
6565
updated_at: String
6666
)
67+
68+
final case class Card(
69+
url: String,
70+
project_url: String,
71+
id: Int,
72+
node_id: String,
73+
note: Option[String],
74+
archived: Boolean,
75+
creator: Creator,
76+
created_at: String,
77+
updated_at: String,
78+
column_url: String,
79+
content_url: Option[String]
80+
)

github4s/src/main/scala/github4s/interpreters/ProjectsInterpreter.scala

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package github4s.interpreters
1818

1919
import github4s.GithubResponses.GHResponse
2020
import github4s.algebras.Projects
21-
import github4s.domain.{Column, Pagination, Project}
21+
import github4s.domain.{Column, Card, Pagination, Project}
2222
import github4s.http.HttpClient
2323
import github4s.Decoders._
2424

@@ -67,4 +67,19 @@ class ProjectsInterpreter[F[_]](
6767
Map(),
6868
pagination
6969
)
70+
71+
override def listCards(
72+
column_id: Int,
73+
archived_state: Option[String],
74+
pagination: Option[Pagination],
75+
headers: Map[String, String]
76+
): F[GHResponse[List[Card]]] =
77+
client.get[List[Card]](
78+
accessToken,
79+
s"projects/columns/$column_id/cards",
80+
headers,
81+
archived_state.fold(Map.empty[String, String])(s => Map("archived_state" -> s)),
82+
pagination
83+
)
84+
7085
}

github4s/src/test/scala/github4s/integration/GHProjectsSpec.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package github4s.integration
1818

1919
import cats.effect.IO
2020
import github4s.Github
21-
import github4s.domain.{Column, Project}
21+
import github4s.domain.{Card, Column, Project}
2222
import github4s.utils.{BaseIntegrationSpec, Integration}
2323

2424
trait GHProjectsSpec extends BaseIntegrationSpec {
@@ -103,4 +103,22 @@ trait GHProjectsSpec extends BaseIntegrationSpec {
103103
response.statusCode shouldBe notFoundStatusCode
104104
}
105105

106+
"Project >> ListCards" should "return the expected cards when a valid column id is provided" taggedAs Integration in {
107+
val response = Github[IO](accessToken).projects
108+
.listCards(validColumnId, headers = headerUserAgent ++ headerAccept)
109+
.unsafeRunSync()
110+
111+
testIsRight[List[Card]](response, r => r.nonEmpty shouldBe true)
112+
response.statusCode shouldBe okStatusCode
113+
}
114+
115+
it should "return error when an invalid column id is passed" taggedAs Integration in {
116+
val response =
117+
Github[IO](accessToken).projects
118+
.listCards(invalidColumnId, headers = headerUserAgent ++ headerAccept)
119+
.unsafeRunSync()
120+
121+
testIsLeft(response)
122+
response.statusCode shouldBe notFoundStatusCode
123+
}
106124
}

github4s/src/test/scala/github4s/unit/ProjectSpec.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,22 @@ class ProjectSpec extends BaseSpec {
8383
projects.listColumns(validProjectId, None, headers = headerUserAgent ++ headerAccept)
8484

8585
}
86+
87+
"Project.listCards" should "call to httpClient.get with the right parameters" in {
88+
89+
val response: IO[GHResponse[List[Card]]] =
90+
IO(GHResponse(List(card).asRight, okStatusCode, Map.empty))
91+
92+
implicit val httpClientMock = httpClientMockGet[List[Card]](
93+
url = s"projects/columns/$validColumnId/cards",
94+
headers = headerAccept,
95+
response = response
96+
)
97+
98+
val project = new ProjectsInterpreter[IO]
99+
100+
project.listCards(validColumnId, None, None, headerUserAgent ++ headerAccept)
101+
102+
}
103+
86104
}

github4s/src/test/scala/github4s/utils/TestData.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,4 +486,40 @@ trait TestData extends DummyGithubUrls {
486486
updated_at = "2019-07-04T09:39:01Z"
487487
)
488488

489+
val validColumnId = 8271018
490+
val invalidColumnId = -32
491+
492+
val card = Card(
493+
url = "https://api.github.com/projects/columns/cards/34323195",
494+
project_url = "https://api.github.com/projects/4085286",
495+
id = 34323195,
496+
node_id = "MDExOlByb2plY3RDYXJkMzQzMjMxOTU=",
497+
note = Some("Test Card"),
498+
archived = false,
499+
creator = Creator(
500+
login = "calvellido",
501+
id = 7753447,
502+
node_id = "MDQ6VXNlcjc3NTM0NDc=",
503+
avatar_url = "https://avatars0.githubusercontent.com/u/7753447?v=4",
504+
gravatar_id = None,
505+
url = "https://api.github.com/users/calvellido",
506+
html_url = "https://github.com/calvellido",
507+
followers_url = "https://api.github.com/users/calvellido/followers",
508+
following_url = "https://api.github.com/users/calvellido/following{/other_user}",
509+
gists_url = "https://api.github.com/users/calvellido/gists{/gist_id}",
510+
starred_url = "https://api.github.com/users/calvellido/starred{/owner}{/repo}",
511+
subscriptions_url = "https://api.github.com/users/calvellido/subscriptions",
512+
organizations_url = "https://api.github.com/users/calvellido/orgs",
513+
repos_url = "https://api.github.com/users/calvellido/repos",
514+
events_url = "https://api.github.com/users/calvellido/events{/privacy}",
515+
received_events_url = "https://api.github.com/users/calvellido/received_events",
516+
`type` = "User",
517+
site_admin = false
518+
),
519+
created_at = "2018-11-02T09:36:28Z",
520+
updated_at = "2019-07-04T09:39:01Z",
521+
column_url = "https://api.github.com/projects/columns/8271018",
522+
content_url = None
523+
)
524+
489525
}

0 commit comments

Comments
 (0)