diff --git a/github4s/src/main/scala/github4s/algebras/GitData.scala b/github4s/src/main/scala/github4s/algebras/GitData.scala index 1daaf218e..dcd687db0 100644 --- a/github4s/src/main/scala/github4s/algebras/GitData.scala +++ b/github4s/src/main/scala/github4s/algebras/GitData.scala @@ -215,6 +215,22 @@ trait GitData[F[_]] { headers: Map[String, String] = Map() ): F[GHResponse[TreeResult]] + /** + * Get a Tag by sha + * + * @param owner of the repo + * @param repo name of the repo + * @param sha the sha of the tree + * @param headers optional user headers to include in the request + * @return a GHResponse with the Tree + */ + def getTag( + owner: String, + repo: String, + sha: String, + headers: Map[String, String] = Map() + ): F[GHResponse[Tag]] + /** * Create a Tag * diff --git a/github4s/src/main/scala/github4s/algebras/Repositories.scala b/github4s/src/main/scala/github4s/algebras/Repositories.scala index f999d6dce..48bb69dca 100644 --- a/github4s/src/main/scala/github4s/algebras/Repositories.scala +++ b/github4s/src/main/scala/github4s/algebras/Repositories.scala @@ -278,7 +278,7 @@ trait Repositories[F[_]] { * @param owner of the repo * @param repo name of the repo * @param headers optional user headers to include in the request - * @return a GHResponse with List[Release] + * @return a GHResponse with Option[Release] */ def getRelease( releaseId: Int, @@ -287,6 +287,22 @@ trait Repositories[F[_]] { headers: Map[String, String] = Map() ): F[GHResponse[Option[Release]]] + /** + * Get release by tag name + * + * @param tagName of the release + * @param owner of the repo + * @param repo name of the repo + * @param headers optional user headers to include in the request + * @return a GHResponse with Option[Release] + */ + def getReleaseByTagName( + owner: String, + repo: String, + tagName: String, + headers: Map[String, String] = Map() + ): F[GHResponse[Option[Release]]] + /** * Latest release * diff --git a/github4s/src/main/scala/github4s/interpreters/GitDataInterpreter.scala b/github4s/src/main/scala/github4s/interpreters/GitDataInterpreter.scala index 7870fd868..df953bfd2 100644 --- a/github4s/src/main/scala/github4s/interpreters/GitDataInterpreter.scala +++ b/github4s/src/main/scala/github4s/interpreters/GitDataInterpreter.scala @@ -143,6 +143,18 @@ class GitDataInterpreter[F[_]](implicit client: HttpClient[F], accessToken: Opti NewTreeRequest(baseTree, treeDataList) ) + override def getTag( + owner: String, + repo: String, + sha: String, + headers: Map[String, String] + ): F[GHResponse[Tag]] = + client.get[Tag]( + accessToken, + s"repos/$owner/$repo/git/tags/$sha", + headers + ) + override def createTag( owner: String, repo: String, diff --git a/github4s/src/main/scala/github4s/interpreters/RepositoriesInterpreter.scala b/github4s/src/main/scala/github4s/interpreters/RepositoriesInterpreter.scala index f49e590ba..af07a9784 100644 --- a/github4s/src/main/scala/github4s/interpreters/RepositoriesInterpreter.scala +++ b/github4s/src/main/scala/github4s/interpreters/RepositoriesInterpreter.scala @@ -239,6 +239,20 @@ class RepositoriesInterpreter[F[_]](implicit client: HttpClient[F], accessToken: Map.empty ) + override def getReleaseByTagName( + owner: String, + repo: String, + tagName: String, + headers: Map[String, String] + ): F[GHResponse[Option[Release]]] = + client + .get[Option[Release]]( + accessToken, + s"repos/$owner/$repo/releases/tags/$tagName", + headers, + Map.empty + ) + override def listReleases( owner: String, repo: String, diff --git a/github4s/src/test/scala/github4s/integration/GitDataSpec.scala b/github4s/src/test/scala/github4s/integration/GitDataSpec.scala index 868339c35..5b81bdba3 100644 --- a/github4s/src/test/scala/github4s/integration/GitDataSpec.scala +++ b/github4s/src/test/scala/github4s/integration/GitDataSpec.scala @@ -109,6 +109,22 @@ trait GitDataSpec extends BaseIntegrationSpec { response.statusCode shouldBe okStatusCode } + it should "return one tag" taggedAs Integration in { + val response = clientResource + .use { client => + Github[IO](client, accessToken).gitData + .getTag(validRepoOwner, validRepoName, validTagTitle, headerUserAgent) + } + .unsafeRunSync() + + testIsRight[Tag]( + response, + r => r.tag shouldBe validTagTitle + ) + response.statusCode shouldBe okStatusCode + + } + it should "return the file tree recursively" taggedAs Integration in { val response = clientResource .use { client => diff --git a/github4s/src/test/scala/github4s/integration/ReposSpec.scala b/github4s/src/test/scala/github4s/integration/ReposSpec.scala index d296cd5f9..eb029ff48 100644 --- a/github4s/src/test/scala/github4s/integration/ReposSpec.scala +++ b/github4s/src/test/scala/github4s/integration/ReposSpec.scala @@ -23,6 +23,7 @@ import github4s.GHError.NotFoundError import github4s.domain._ import github4s.utils.{BaseIntegrationSpec, Integration} import github4s.{GHResponse, Github} +import org.scalatest.Assertion trait ReposSpec extends BaseIntegrationSpec { @@ -64,6 +65,30 @@ trait ReposSpec extends BaseIntegrationSpec { "Repos >> getRelease" should "return the expected repos when a valid org is provided" taggedAs Integration in { + def getReleaseIO(gh: Github[IO], release: Release): IO[GHResponse[Option[Release]]] = + gh.repos + .getRelease(release.id, validRepoOwner, validRepoName, headers = headerUserAgent) + + testAllReleases(getReleaseIO _) + } + + "Repos >> getReleaseByTagName" should "return the expected repos when a valid org is provided" taggedAs Integration in { + + def getReleaseByTagName(gh: Github[IO], release: Release): IO[GHResponse[Option[Release]]] = + gh.repos + .getReleaseByTagName( + validRepoOwner, + validRepoName, + release.tag_name, + headers = headerUserAgent + ) + + testAllReleases(getReleaseByTagName _) + } + + private def testAllReleases( + getReleaseIO: (Github[IO], Release) => IO[GHResponse[Option[Release]]] + ): Assertion = { val responseResource = for { client <- clientResource @@ -77,8 +102,7 @@ trait ReposSpec extends BaseIntegrationSpec { releasesAreFoundCheck: IO[List[(Release, GHResponse[Option[Release]])]] = releases.map { release => - val releaseIO = gh.repos - .getRelease(release.id, validRepoOwner, validRepoName, headers = headerUserAgent) + val releaseIO = getReleaseIO(gh, release) releaseIO.map(r => release -> r) }.sequence diff --git a/github4s/src/test/scala/github4s/unit/GitDataSpec.scala b/github4s/src/test/scala/github4s/unit/GitDataSpec.scala index 402cea4e3..1277a2942 100644 --- a/github4s/src/test/scala/github4s/unit/GitDataSpec.scala +++ b/github4s/src/test/scala/github4s/unit/GitDataSpec.scala @@ -216,6 +216,31 @@ class GitDataSpec extends BaseSpec { } + "GitData.getTag" should "call to httpClient.get with the right parameters" in { + + val response: IO[GHResponse[Tag]] = + IO( + GHResponse( + tag.asRight, + okStatusCode, + Map.empty + ) + ) + + implicit val httpClientMock = httpClientMockGet[Tag]( + url = s"repos/$validRepoOwner/$validRepoName/git/tags/$validCommitSha", + response = response + ) + val gitData = new GitDataInterpreter[IO] + + gitData.getTag( + validRepoOwner, + validRepoName, + validCommitSha, + headerUserAgent + ) + } + "GitData.createTag" should "call to httpClient.post with the right parameters" in { val response: IO[GHResponse[Tag]] = IO(GHResponse(tag.asRight, okStatusCode, Map.empty)) diff --git a/github4s/src/test/scala/github4s/unit/ReposSpec.scala b/github4s/src/test/scala/github4s/unit/ReposSpec.scala index 25bb9750b..6c0be4816 100644 --- a/github4s/src/test/scala/github4s/unit/ReposSpec.scala +++ b/github4s/src/test/scala/github4s/unit/ReposSpec.scala @@ -85,6 +85,25 @@ class ReposSpec extends BaseSpec { repos.getRelease(release.id, validRepoOwner, validRepoName, headers = headerUserAgent) } + "Repos.getReleaseByTagName" should "call to httpClient.get with the right parameters" in { + val response: IO[GHResponse[Option[Release]]] = + IO(GHResponse(Option(release).asRight, okStatusCode, Map.empty)) + + implicit val httpClientMock = httpClientMockGet[Option[Release]]( + url = s"repos/$validRepoOwner/$validRepoName/releases/tags/${release.tag_name}", + response = response + ) + + val repos = new RepositoriesInterpreter[IO] + + repos.getReleaseByTagName( + validRepoOwner, + validRepoName, + release.tag_name, + headers = headerUserAgent + ) + } + "Repos.listOrgRepos" should "call to httpClient.get with the right parameters" in { val response: IO[GHResponse[List[Repository]]] = IO(GHResponse(List(repo).asRight, okStatusCode, Map.empty)) diff --git a/microsite/docs/repository.md b/microsite/docs/repository.md index bdcdb8dda..f7320ed9d 100644 --- a/microsite/docs/repository.md +++ b/microsite/docs/repository.md @@ -25,6 +25,7 @@ with Github4s, you can interact with: - [Releases](#releases) - [List of releases](#list-of-releases) - [Get a single release](#get-a-single-release) + - [Get a release by tag name](#get-a-release-by-tag-name) - [The latest release](#the-latest-release) - [Create a release](#create-a-release) - [Statuses](#statuses) @@ -406,6 +407,29 @@ response.result match { The `result` on the right is the corresponding [Option[Release]][repository-scala]. +### Get a release by tag name + +You can get a release by tag name using `getReleaseByTagName`, it takes as arguments: + +- the repository coordinates (`owner` and `name` of the repository). +- the release coordinates (`tagName` of the release) + +Get a release by tag name: + +```scala mdoc:compile-only +val getRelease = + gh.repos.getReleaseByTagName( + "github4s", + "v0.24.0", + "47deg") +val response = getRelease.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 [Option[Release]][repository-scala]. ### The latest release