From b6d12e7134dd75bdcdf72af832445d5e4020a4f4 Mon Sep 17 00:00:00 2001 From: Chris Davenport Date: Fri, 1 Nov 2019 09:43:31 -0700 Subject: [PATCH 1/2] Rate Limit Endpoint --- .../github/data/RateLimit.scala | 44 +++++++++++++ .../endpoints/miscellaneous/RateLimit.scala | 23 +++++++ .../miscellaneous.scala/RateLimitSpec.scala | 66 +++++++++++++++++++ .../scala/io/chrisdavenport/github/Main.scala | 12 ++-- 4 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 core/src/main/scala/io/chrisdavenport/github/data/RateLimit.scala create mode 100644 core/src/main/scala/io/chrisdavenport/github/endpoints/miscellaneous/RateLimit.scala create mode 100644 core/src/test/scala/io/chrisdavenport/github/endpoints/miscellaneous.scala/RateLimitSpec.scala diff --git a/core/src/main/scala/io/chrisdavenport/github/data/RateLimit.scala b/core/src/main/scala/io/chrisdavenport/github/data/RateLimit.scala new file mode 100644 index 0000000..008f544 --- /dev/null +++ b/core/src/main/scala/io/chrisdavenport/github/data/RateLimit.scala @@ -0,0 +1,44 @@ +package io.chrisdavenport.github.data + +import cats.implicits._ +import io.circe.Decoder +import io.circe.HCursor + +object RateLimit { + final case class Limits( + max: Int, + remaining: Int, + reset: Int + ) + object Limits { + implicit val decoder = new Decoder[Limits]{ + def apply(c: HCursor): Decoder.Result[Limits] = + ( + c.downField("limit").as[Int], + c.downField("remaining").as[Int], + c.downField("reset").as[Int] + ).mapN(Limits.apply) + } + } + + final case class RateLimit( + core: Limits, + search: Limits, + graphQL: Limits, + integrationManifest: Limits + ) + object RateLimit { + implicit val decoder = new Decoder[RateLimit]{ + def apply(c: HCursor): Decoder.Result[RateLimit] = { + val base = c.downField("resources") + ( + base.downField("core").as[Limits], + base.downField("search").as[Limits], + base.downField("graphql").as[Limits], + base.downField("integration_manifest").as[Limits] + ).mapN(RateLimit.apply) + } + } + + } +} \ No newline at end of file diff --git a/core/src/main/scala/io/chrisdavenport/github/endpoints/miscellaneous/RateLimit.scala b/core/src/main/scala/io/chrisdavenport/github/endpoints/miscellaneous/RateLimit.scala new file mode 100644 index 0000000..cacd5f1 --- /dev/null +++ b/core/src/main/scala/io/chrisdavenport/github/endpoints/miscellaneous/RateLimit.scala @@ -0,0 +1,23 @@ +package io.chrisdavenport.github.endpoints.miscellaneous + + +import cats.effect._ +import org.http4s._ +import org.http4s.implicits._ + +import io.chrisdavenport.github.data.{RateLimit => DRateLimit} +import io.chrisdavenport.github.Auth +import io.chrisdavenport.github.internals.GithubMedia._ +import io.chrisdavenport.github.internals.RequestConstructor + +object RateLimit { + + def rateLimit[F[_]: Sync]( + auth: Option[Auth] + ) = RequestConstructor.runRequestWithNoBody[F, DRateLimit.RateLimit]( + auth, + Method.GET, + uri"rate_limit" + ) + +} \ No newline at end of file diff --git a/core/src/test/scala/io/chrisdavenport/github/endpoints/miscellaneous.scala/RateLimitSpec.scala b/core/src/test/scala/io/chrisdavenport/github/endpoints/miscellaneous.scala/RateLimitSpec.scala new file mode 100644 index 0000000..8c78866 --- /dev/null +++ b/core/src/test/scala/io/chrisdavenport/github/endpoints/miscellaneous.scala/RateLimitSpec.scala @@ -0,0 +1,66 @@ +package io.chrisdavenport.github.endpoints.miscellaneous + +import org.specs2.mutable.Specification + +import cats.effect._ +import cats.effect.specs2.CatsEffect + + +import io.circe.literal._ +import org.http4s._ +import org.http4s.implicits._ +import org.http4s.client._ +import org.http4s.circe._ +import org.http4s.dsl.io._ + +class RateLimitSpec extends Specification with CatsEffect { + + "RateLimit" should { + + "return a valid rate-limit response" in { + RateLimit.rateLimit[IO](None) + .run(Client.fromHttpApp(rateLimit.orNotFound)) + .attempt + .map(_ must beRight) + } + + } + + val rateLimit : HttpRoutes[IO] = HttpRoutes.of { + case GET -> Root / "rate_limit" => + Ok( + json""" + { + "resources": { + "core": { + "limit": 5000, + "remaining": 4999, + "reset": 1372700873 + }, + "search": { + "limit": 30, + "remaining": 18, + "reset": 1372697452 + }, + "graphql": { + "limit": 5000, + "remaining": 4993, + "reset": 1372700389 + }, + "integration_manifest": { + "limit": 5000, + "remaining": 4999, + "reset": 1551806725 + } + }, + "rate": { + "limit": 5000, + "remaining": 4999, + "reset": 1372700873 + } + } + """ + ) + } + +} diff --git a/example/src/main/scala/io/chrisdavenport/github/Main.scala b/example/src/main/scala/io/chrisdavenport/github/Main.scala index a8055ad..58fb116 100644 --- a/example/src/main/scala/io/chrisdavenport/github/Main.scala +++ b/example/src/main/scala/io/chrisdavenport/github/Main.scala @@ -16,13 +16,11 @@ object Main extends IOApp { auth = OAuth(authLine) - // out <- Resource.liftF( - // Users.getAllUsers[IO](None, None) - // .run(c) - // .evalTap(s => IO(println(s))) - // .compile - // .drain - // ) + _ <- Resource.liftF( + endpoints.miscellaneous.RateLimit.rateLimit[IO](auth.some) + .run(c) + .flatTap(a => IO(println(a))) + ) // out <- liftPrint(endpoints.Users.userInfoAuthenticatedUser[IO](auth).run(c)) // out <- liftPrint(endpoints.Users.ownerInfoFor[IO]("http4s", auth.some).run(c)) // _ <- liftPrint(endpoints.Repositories.repository[IO]("http4s", "http4s", auth.some).run(c)) From 27b7de41911514ff782f449977b0b48669430c03 Mon Sep 17 00:00:00 2001 From: Chris Davenport Date: Fri, 1 Nov 2019 09:46:44 -0700 Subject: [PATCH 2/2] Documentation --- .../github/endpoints/miscellaneous/RateLimit.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/io/chrisdavenport/github/endpoints/miscellaneous/RateLimit.scala b/core/src/main/scala/io/chrisdavenport/github/endpoints/miscellaneous/RateLimit.scala index cacd5f1..5c561bf 100644 --- a/core/src/main/scala/io/chrisdavenport/github/endpoints/miscellaneous/RateLimit.scala +++ b/core/src/main/scala/io/chrisdavenport/github/endpoints/miscellaneous/RateLimit.scala @@ -11,7 +11,10 @@ import io.chrisdavenport.github.internals.GithubMedia._ import io.chrisdavenport.github.internals.RequestConstructor object RateLimit { - + + /** + * Get your current rate limit status + **/ def rateLimit[F[_]: Sync]( auth: Option[Auth] ) = RequestConstructor.runRequestWithNoBody[F, DRateLimit.RateLimit](