Skip to content

Instantly share code, notes, and snippets.

@CJSmith-0141
Last active September 9, 2023 15:45
Show Gist options
  • Select an option

  • Save CJSmith-0141/6d7eee4a5d1cca092d0c1956fc54b59c to your computer and use it in GitHub Desktop.

Select an option

Save CJSmith-0141/6d7eee4a5d1cca092d0c1956fc54b59c to your computer and use it in GitHub Desktop.
package com.example.routes
import cats.Applicative
import cats.effect.Concurrent
import cats.syntax.all.*
import org.http4s.*
import org.http4s.circe.{jsonEncoderOf, jsonOf}
import org.http4s.dsl.Http4sDsl
import org.typelevel.ci.CIString
import io.circe.generic.semiauto.*
import io.circe.*
trait DataAccess[F[_]] {
def getSomething: F[Option[String]]
}
object DataAccess {
def impl[F[_]: Applicative]: DataAccess[F] = new DataAccess[F] {
override def getSomething: F[Option[String]] =
"something".some.pure[F] // go get this from the DB
}
}
case class CsvPayload(
csv: String,
filename: String
)
object CsvPayload {
implicit val decoder: Decoder[CsvPayload] = deriveDecoder
implicit val encoder: Encoder[CsvPayload] = deriveEncoder
}
object ValidationRoutes {
def ensureContentTypeJson[F[_]: Applicative](
request: Request[F]
): Either[F[Response[F]], Unit] = {
val dsl = new Http4sDsl[F] {}
import dsl.*
for {
content <- Either.fromOption(
request.headers.get(CIString("Content-Type")),
BadRequest("Content-Type header is required")
)
_ <- Either.cond(
content.head.value == "application/json",
(),
BadRequest("Content-Type must be application/json")
)
} yield ()
}
def chain[F[_]: Concurrent](
dataAccess: DataAccess[F]
): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl.*
implicit val csvD: EntityDecoder[F, CsvPayload] =
jsonOf[F, CsvPayload]
implicit val csvE: EntityEncoder[F, CsvPayload] = jsonEncoderOf[CsvPayload]
HttpRoutes.of[F] { case request @ POST -> Root / "csv" / "validate" =>
ensureContentTypeJson(request) match {
case Left(e) => e
case Right(_) => (for {
body <- request.as[CsvPayload].adaptError {
case e => new Throwable("Invalid JSON", e)
}
_ <- dataAccess.getSomething.flatMap(_.liftTo[F](new Throwable("was not able to get from DB")))
} yield body).attempt.flatMap {
case Left(e) => BadRequest(e.getMessage)
case Right(body) => Ok(body)
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment