Skip to content

Instantly share code, notes, and snippets.

@zetashift
Last active October 29, 2022 15:20
Show Gist options
  • Save zetashift/779b0ad550e5e6899466008081d74829 to your computer and use it in GitHub Desktop.
Save zetashift/779b0ad550e5e6899466008081d74829 to your computer and use it in GitHub Desktop.
wooo
//> using scala "3.2"
//> using lib "org.http4s::http4s-dsl:1.0.0-M29"
//> using lib "org.http4s::http4s-ember-server:1.0.0-M29"
//> using lib "org.http4s::http4s-ember-client:1.0.0-M29"
//> using lib "org.http4s::http4s-circe:1.0.0-M29"
//> using lib "ch.qos.logback:logback-classic:1.2.6"
package zetashift.todo
import cats.effect._
import cats.effect.implicits.*
import cats.effect.kernel.Ref
import cats.effect.std.Random
import cats.implicits.*
import com.comcast.ip4s.host
import com.comcast.ip4s.ipv4
import com.comcast.ip4s.port
import io.circe.Encoder.*
import io.circe.*
import io.circe.syntax.*
import org.http4s.EntityDecoder
import org.http4s.EntityEncoder
import org.http4s._
import org.http4s.circe.CirceEntityDecoder.*
import org.http4s.circe.*
import org.http4s.dsl.io._
import org.http4s.ember.server.EmberServerBuilder
import org.http4s.implicits.*
import org.http4s.server.Router
import org.http4s.server.middleware.Logger
final case class Todo(id: Long, description: String) derives Codec.AsObject
object Todo:
given EntityEncoder[IO, Todo] =
jsonEncoderOf[IO, Todo]
trait Todos:
def get(id: Long): IO[Option[Todo]]
def put(description: String): IO[Todo]
final class InMemoryTodos(ref: Ref[IO, Map[Long, Todo]], rand: Random[IO])
extends Todos {
override def get(id: Long): IO[Option[Todo]] =
ref.get.map(db => db.get(key = id))
override def put(description: String): IO[Todo] =
rand.nextLong.flatMap(id =>
val todo = Todo(id, description)
ref
.update { db =>
db.updated(key = id, value = todo)
}
.as(todo)
)
}
object Todos:
def inMemory(seed: Long): IO[Todos] =
(
IO.ref(Map[Long, Todo](1L -> Todo(1L, "Test"))),
Random.scalaUtilRandomSeedLong[IO](seed)
)
.mapN { case (ref, rand) =>
new InMemoryTodos(ref, rand)
}
case class TodoRequest(description: String) derives Codec.AsObject
object Routes:
def todoRoutes(T: Todos) =
HttpRoutes.of[IO] {
case GET -> Root / "todo" / LongVar(id) =>
val todo = T.get(id)
todo.flatMap {
case Some(todo) => Ok(todo)
case None => NotFound()
}
case req @ POST -> Root / "todo" =>
for
todoR <- req.as[TodoRequest]
resp <- Ok(T.put(todoR.description))
yield resp
}
def helloRoutes = HttpRoutes
.of[IO] {
case GET -> Root => Ok("Hello World!")
case GET -> Root / "hello" / name => Ok(s"Hello, $name!")
}
.orNotFound
object HelloApp extends IOApp:
def run(args: List[String]): IO[ExitCode] =
(for {
todoAlg <- Todos.inMemory(seed = 123L).toResource
httpApp = (Routes.todoRoutes(todoAlg)).orNotFound
finalHttpApp = Logger.httpApp(
true,
true,
logAction = Some(payload => IO.println(payload))
)(
httpApp
)
_ <- EmberServerBuilder
.default[IO]
.withHost(ipv4"0.0.0.0")
.withPort(port"8080")
.withHttpApp(finalHttpApp)
.build
} yield ()).useForever
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment