From 7645ad3f10d5958fc38364fb6b37886f31531758 Mon Sep 17 00:00:00 2001 From: JohnJoe Friedmann Date: Mon, 5 Feb 2024 20:37:15 +0900 Subject: [PATCH 1/2] Add new endpoint - List Emails --- .../src/main/scala/github4s/Decoders.scala | 2 ++ .../src/main/scala/github4s/Encoders.scala | 2 ++ .../main/scala/github4s/algebras/Users.scala | 12 ++++++++++ .../main/scala/github4s/domain/Email.scala | 24 +++++++++++++++++++ .../interpreters/UsersInterpreter.scala | 7 ++++++ .../github4s/integration/UsersSpec.scala | 10 ++++++++ .../test/scala/github4s/unit/UserSpec.scala | 11 +++++++++ .../test/scala/github4s/utils/TestData.scala | 3 +++ microsite/docs/user.md | 19 +++++++++++++++ 9 files changed, 90 insertions(+) create mode 100644 github4s/shared/src/main/scala/github4s/domain/Email.scala diff --git a/github4s/shared/src/main/scala/github4s/Decoders.scala b/github4s/shared/src/main/scala/github4s/Decoders.scala index d39cdd78..d3c11731 100644 --- a/github4s/shared/src/main/scala/github4s/Decoders.scala +++ b/github4s/shared/src/main/scala/github4s/Decoders.scala @@ -424,4 +424,6 @@ object Decoders { deriveDecoder[SearchCodeResult] implicit val decodeSearchCodeResultItem: Decoder[SearchCodeResultItem] = deriveDecoder[SearchCodeResultItem] + + implicit val decoderEmail: Decoder[Email] = deriveDecoder[Email] } diff --git a/github4s/shared/src/main/scala/github4s/Encoders.scala b/github4s/shared/src/main/scala/github4s/Encoders.scala index 76b2cbed..ab2c1c20 100644 --- a/github4s/shared/src/main/scala/github4s/Encoders.scala +++ b/github4s/shared/src/main/scala/github4s/Encoders.scala @@ -293,4 +293,6 @@ object Encoders { deriveEncoder[SearchCodeResult] implicit val encodeSearchCodeResultItem: Encoder[SearchCodeResultItem] = deriveEncoder[SearchCodeResultItem] + + implicit val encoderEmail: Encoder[Email] = deriveEncoder[Email] } diff --git a/github4s/shared/src/main/scala/github4s/algebras/Users.scala b/github4s/shared/src/main/scala/github4s/algebras/Users.scala index aa8da521..8960efb4 100644 --- a/github4s/shared/src/main/scala/github4s/algebras/Users.scala +++ b/github4s/shared/src/main/scala/github4s/algebras/Users.scala @@ -64,4 +64,16 @@ trait Users[F[_]] { pagination: Option[Pagination] = None, headers: Map[String, String] = Map() ): F[GHResponse[List[User]]] + + /** + * Get information for an authenticated user's associated email addresses + * + * @param pagination Limit and Offset for pagination + * @param headers optional user headers to include in the request + * @return GHResponse[Email] Email details + */ + def getEmails( + pagination: Option[Pagination] = None, + headers: Map[String, String] = Map() + ): F[GHResponse[List[Email]]] } diff --git a/github4s/shared/src/main/scala/github4s/domain/Email.scala b/github4s/shared/src/main/scala/github4s/domain/Email.scala new file mode 100644 index 00000000..26a08dc3 --- /dev/null +++ b/github4s/shared/src/main/scala/github4s/domain/Email.scala @@ -0,0 +1,24 @@ +/* + * Copyright 2016-2023 47 Degrees Open Source + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github4s.domain + +final case class Email( + email: String, + primary: Boolean, + verified: Boolean, + visibility: Option[String] +) diff --git a/github4s/shared/src/main/scala/github4s/interpreters/UsersInterpreter.scala b/github4s/shared/src/main/scala/github4s/interpreters/UsersInterpreter.scala index 4359754b..aa074cfe 100644 --- a/github4s/shared/src/main/scala/github4s/interpreters/UsersInterpreter.scala +++ b/github4s/shared/src/main/scala/github4s/interpreters/UsersInterpreter.scala @@ -45,4 +45,11 @@ class UsersInterpreter[F[_]](implicit client: HttpClient[F]) extends Users[F] { ): F[GHResponse[List[User]]] = client .get[List[User]](s"users/$username/following", headers, pagination = pagination) + + override def getEmails( + pagination: Option[Pagination], + headers: Map[String, String] + ): F[GHResponse[List[Email]]] = + client + .get[List[Email]]("user/emails", headers, pagination = pagination) } diff --git a/github4s/shared/src/test/scala/github4s/integration/UsersSpec.scala b/github4s/shared/src/test/scala/github4s/integration/UsersSpec.scala index b2f35a15..45cd6ebe 100644 --- a/github4s/shared/src/test/scala/github4s/integration/UsersSpec.scala +++ b/github4s/shared/src/test/scala/github4s/integration/UsersSpec.scala @@ -106,4 +106,14 @@ trait UsersSpec extends BaseIntegrationSpec { response.statusCode shouldBe notFoundStatusCode } + "Users >> GetEmails" should "return error on Left when no accessToken is provided" taggedAs Integration in { + val response = + clientResource + .use(client => GithubClient[IO](client).users.getEmails(None, headerUserAgent)) + .unsafeRunSync() + + testIsLeft[GHError.UnauthorizedError, List[Email]](response) + response.statusCode shouldBe unauthorizedStatusCode + } + } diff --git a/github4s/shared/src/test/scala/github4s/unit/UserSpec.scala b/github4s/shared/src/test/scala/github4s/unit/UserSpec.scala index 6c764a8a..3fb16f76 100644 --- a/github4s/shared/src/test/scala/github4s/unit/UserSpec.scala +++ b/github4s/shared/src/test/scala/github4s/unit/UserSpec.scala @@ -72,4 +72,15 @@ class UserSpec extends BaseSpec { users.getFollowing(validUsername, None, headerUserAgent).shouldNotFail } + "User.getEmails" should "call to httpClient.get with the right parameters" in { + + implicit val httpClientMock: HttpClient[IO] = httpClientMockGet[List[Email]]( + url = s"user/emails", + response = IO.pure(List(email)) + ) + + val users = new UsersInterpreter[IO] + users.getEmails(None, headerUserAgent).shouldNotFail + } + } diff --git a/github4s/shared/src/test/scala/github4s/utils/TestData.scala b/github4s/shared/src/test/scala/github4s/utils/TestData.scala index daf7afc8..cd92c55d 100644 --- a/github4s/shared/src/test/scala/github4s/utils/TestData.scala +++ b/github4s/shared/src/test/scala/github4s/utils/TestData.scala @@ -637,4 +637,7 @@ trait TestData { val validReviewers: ReviewersRequest = ReviewersRequest(List(validUsername), List(validSlug)) + + val email: Email = + Email("developer@47deg.com", primary = true, verified = true, Some("public")) } diff --git a/microsite/docs/user.md b/microsite/docs/user.md index 0345a703..e18f6681 100644 --- a/microsite/docs/user.md +++ b/microsite/docs/user.md @@ -112,4 +112,23 @@ The `result` on the right is the corresponding [List[User]][user-scala]. See [the API doc](https://developer.github.com/v3/users/followers/#list-users-followed-by-another-use) for full reference. +### List email addresses for the authenticated user + +You can get a list of emails associated with the authenticated user using `getEmails`, it takes as argument: + +- `pagination`: Limit and Offset for pagination, optional. + +```scala mdoc:compile-only +val getEmails = gh.users.getEmails() +getEmails.flatMap(_.result match { + case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}") + case Right(r) => IO.println(r) +}) +``` + +The `result` on the right is the corresponding [List[Email]][email-scala]. + +See [the API doc](https://developer.github.com/v3/users/emails/#list-email-addresses-for-the-authenticated-user) for full reference. + [user-scala]: https://github.com/47degrees/github4s/blob/main/github4s/shared/src/main/scala/github4s/domain/User.scala +[email-scala]: https://github.com/47degrees/github4s/blob/main/github4s/shared/src/main/scala/github4s/domain/Email.scala From 7a05c550610f01764e0148d77e37652174683ed2 Mon Sep 17 00:00:00 2001 From: JohnJoe Friedmann Date: Tue, 6 Feb 2024 19:08:48 +0900 Subject: [PATCH 2/2] Adding problem filter --- build.sbt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index dbacdf60..d5c215b7 100644 --- a/build.sbt +++ b/build.sbt @@ -42,7 +42,8 @@ lazy val github4s = (crossProject(JSPlatform, JVMPlatform)) Test / scalacOptions -= "-Wnonunit-statement", mimaPreviousArtifacts := Set("com.47deg" %% "github4s" % "0.32.1"), mimaBinaryIssueFilters ++= Seq( - ProblemFilters.exclude[IncompatibleMethTypeProblem]("github4s.http.HttpClient.this") + ProblemFilters.exclude[IncompatibleMethTypeProblem]("github4s.http.HttpClient.this"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("github4s.algebras.Users.getEmails") ) )