Skip to content

Commit

Permalink
Request can extract entities
Browse files Browse the repository at this point in the history
These needs testing and probably some refactoring
  • Loading branch information
noelwelsh committed Sep 22, 2023
1 parent 77e01b9 commit 9d4ef7e
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 23 deletions.
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ lazy val commonSettings = Seq(
Dependencies.http4sClient.value,
Dependencies.http4sServer.value,
Dependencies.http4sDsl.value,
Dependencies.http4sCirce.value,
Dependencies.endpoints4s.value,
Dependencies.endpoints4sOpenApi.value
)
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/scala/krop/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ object all {
export krop.Server
export krop.ServerBuilder
export krop.endpoints.KropEndpoints
export krop.route.Route

export org.http4s.EntityDecoder
export org.http4s.Method
}
82 changes: 61 additions & 21 deletions core/src/main/scala/krop/route/Request.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,78 @@ import cats.syntax.all.*
import org.http4s.Method
import org.http4s.Response
import org.http4s.{Request as Http4sRequest}
import org.http4s.EntityDecoder
import org.http4s.MediaRange
import org.http4s.DecodeResult
import org.http4s.Media
import scala.Tuple.Append

final case class Request[A <: Tuple](method: Method, path: Path[A]) {
def withMethod(method: Method): Request[A] =
this.copy(method = method)

def withPath[B <: Tuple](path: Path[B]): Request[B] =
this.copy(path = path)

def extract(request: Http4sRequest[IO]): Option[A] =
if request.method == method then path.extract(request.pathInfo)
else None
trait Request[A] {
def extract(request: Http4sRequest[IO]): IO[Option[A]]

def handle(f: A => IO[Response[IO]]): krop.Route =
krop.Route.liftRoutes(
Kleisli[OptionT[IO, *], Http4sRequest[IO], Response[IO]](request =>
OptionT(extract(request).traverse(f))
OptionT(extract(request).flatMap(maybePE => maybePE.traverse(f)))
)
)
}
object Request {
def delete: Request[EmptyTuple] =
Request(method = Method.DELETE, Path.root)
def delete: PathRequest[EmptyTuple] =
PathRequest(method = Method.DELETE, Path.root)

def get: PathRequest[EmptyTuple] =
PathRequest(method = Method.GET, Path.root)

def post: PathRequest[EmptyTuple] =
PathRequest(method = Method.POST, Path.root)

def put: PathRequest[EmptyTuple] =
PathRequest(method = Method.PUT, Path.root)

def method(method: Method): PathRequest[EmptyTuple] =
PathRequest(method = method, Path.root)
}

/** A [[krop.route.Request]] that only specifies a method and a
* [[krop.route.Path]]. The simplest possible [[krop.route.Request]].
*/
final case class PathRequest[P <: Tuple](
method: Method,
path: Path[P]
) extends Request[P] {
def withEntity[E](entity: EntityDecoder[IO, E]): PathEntityRequest[P, E] =
PathEntityRequest(this, entity)

def withMethod(method: Method): Request[P] =
this.copy(method = method)

def get: Request[EmptyTuple] =
Request(method = Method.GET, Path.root)
def withPath[P2 <: Tuple](path: Path[P2]): Request[P2] =
this.copy(path = path)

def post: Request[EmptyTuple] =
Request(method = Method.POST, Path.root)
def extract(request: Http4sRequest[IO]): IO[Option[P]] = {
IO.pure(
Option
.when(request.method == method)(())
.flatMap(_ => path.extract(request.pathInfo))
)
}

def put: Request[EmptyTuple] =
Request(method = Method.PUT, Path.root)
}

def method(method: Method): Request[EmptyTuple] =
Request(method = method, Path.root)
final case class PathEntityRequest[P <: Tuple, E](
pathRequest: PathRequest[P],
decoder: EntityDecoder[IO, E]
) extends Request[Tuple.Append[P, E]] {
def extract(request: Http4sRequest[IO]): IO[Option[Append[P, E]]] = {
given EntityDecoder[IO, E] = decoder
pathRequest
.extract(request)
.flatMap(maybePath =>
maybePath match {
case None => IO.pure(None)
case Some(value) => request.as[E].map(e => Some(value :* e))
}
)
}
}
4 changes: 2 additions & 2 deletions core/src/test/scala/route/RequestSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ class RequestSuite extends CatsEffectSuite {
val request =
Http4sRequest(method = Method.GET, uri = uri"http://example.org/")

assert(simpleRequest.extract(request).isDefined)
simpleRequest.extract(request).map(_.isDefined).assert
}

test("simple request doesn't match PUT /") {
val request =
Http4sRequest(method = Method.PUT, uri = uri"http://example.org/")

assert(simpleRequest.extract(request).isEmpty)
simpleRequest.extract(request).map(_.isEmpty).assert
}
}
1 change: 1 addition & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ object Dependencies {
val http4sServer =
Def.setting("org.http4s" %% "http4s-ember-server" % http4sVersion)
val http4sDsl = Def.setting("org.http4s" %% "http4s-dsl" % http4sVersion)
val http4sCirce = Def.setting("org.http4s" %% "http4s-circe" % http4sVersion)
val endpoints4s =
Def.setting("org.endpoints4s" %%% "algebra" % endpoints4sVersion)
val endpoints4sOpenApi =
Expand Down

0 comments on commit 9d4ef7e

Please sign in to comment.