package org.amitayh.invoices.web import java.util.UUID import cats.effect.{ExitCode, IO, IOApp} import cats.syntax.functor._ import fs2.Stream import fs2.concurrent.Topic import org.amitayh.invoices.common.Config.Topics import org.amitayh.invoices.common.domain.{Command, CommandResult, InvoiceSnapshot} import org.amitayh.invoices.dao.{InvoiceList, MySqlInvoiceList} import org.amitayh.invoices.web.PushEvents._ import org.http4s.implicits._ import org.http4s.server.Router import org.http4s.server.blaze.BlazeServerBuilder object InvoicesServer extends IOApp { override def run(args: List[String]): IO[ExitCode] = stream.compile.drain.as(ExitCode.Success) private val stream: Stream[IO, ExitCode] = for { invoiceList <- Stream.resource(MySqlInvoiceList.resource[IO]) producer <- Stream.resource(Kafka.producer[IO, UUID, Command](Topics.Commands)) commandResultsTopic <- Stream.eval(Topic[IO, CommandResultRecord](None)) invoiceUpdatesTopic <- Stream.eval(Topic[IO, InvoiceSnapshotRecord](None)) server <- httpServer(invoiceList, producer, commandResultsTopic, invoiceUpdatesTopic) concurrently commandResults.through(commandResultsTopic.publish) concurrently invoiceUpdates.through(invoiceUpdatesTopic.publish) } yield server private def commandResults: Stream[IO, CommandResultRecord] = Kafka.subscribe[IO, UUID, CommandResult]( topic = Topics.CommandResults, groupId = "invoices.websocket.command-results").map(Some(_)) private def invoiceUpdates: Stream[IO, InvoiceSnapshotRecord] = Kafka.subscribe[IO, UUID, InvoiceSnapshot]( topic = Topics.Snapshots, groupId = "invoices.websocket.snapshots").map(Some(_)) private def httpServer(invoiceList: InvoiceList[IO], producer: Kafka.Producer[IO, UUID, Command], commandResultsTopic: Topic[IO, CommandResultRecord], invoiceUpdatesTopic: Topic[IO, InvoiceSnapshotRecord]): Stream[IO, ExitCode] = BlazeServerBuilder[IO] .bindHttp(8080, "0.0.0.0") .withHttpApp( Router( "/api" -> InvoicesApi[IO].service(invoiceList, producer, commandResultsTopic), "/events" -> PushEvents[IO].service(commandResultsTopic, invoiceUpdatesTopic), "/" -> Statics[IO].service).orNotFound) .serve }