Skip to content

Commit

Permalink
monofunctor-tf example: run under both ZIO and Cats Effect
Browse files Browse the repository at this point in the history
  • Loading branch information
neko-kai committed Jun 20, 2024
1 parent cf1548a commit 3abe9fa
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 197 deletions.
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
package leaderboard.plugins

import distage.Scene
import distage.{ModuleDef, Scene}
import izumi.distage.docker.Docker.DockerPort
import izumi.distage.docker.bundled.PostgresDocker
import izumi.distage.docker.modules.DockerSupportModule
import izumi.distage.plugins.PluginDef
import izumi.reflect.TagKK
import leaderboard.config.PostgresPortCfg
import zio.Task
import zio.IO

object PostgresDockerPlugin extends PluginDef {
// only enable postgres docker when Scene axis is set to Managed
tag(Scene.Managed)
include(dockerModule[IO])

// add docker support dependencies
include(DockerSupportModule[Task])
def dockerModule[F[+_, +_]: TagKK]: ModuleDef = new ModuleDef {
// only enable postgres docker when Scene axis is set to Managed
tag(Scene.Managed)

// launch postgres docker for tests
make[PostgresDocker.Container]
.fromResource(PostgresDocker.make[Task])
// add docker support dependencies
include(DockerSupportModule[F[Throwable, _]])

// spawned docker container port is randomized
// to prevent conflicts, so make PostgresPortCfg
// point to the new port. This will also
// cause the container to start before
// integration check is performed
make[PostgresPortCfg].from {
(docker: PostgresDocker.Container) =>
val knownAddress = docker.availablePorts.first(DockerPort.TCP(5432))
PostgresPortCfg(knownAddress.hostString, knownAddress.port)
// launch postgres docker for tests
make[PostgresDocker.Container]
.fromResource(PostgresDocker.make[F[Throwable, _]])

// spawned docker container port is randomized
// to prevent conflicts, so make PostgresPortCfg
// point to the new port. This will also
// cause the container to start before
// integration check is performed
make[PostgresPortCfg].from {
(docker: PostgresDocker.Container) =>
val knownAddress = docker.availablePorts.first(DockerPort.TCP(5432))
PostgresPortCfg(knownAddress.hostString, knownAddress.port)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package leaderboard

import cats.Applicative
import cats.effect.Async
import distage.StandardAxis.Repo
import distage.plugins.PluginConfig
import distage.{Activation, Lifecycle, Module, ModuleDef}
import izumi.distage.model.definition.StandardAxis.Scene
import izumi.distage.modules.DefaultModule
import izumi.distage.roles.RoleAppMain
import izumi.distage.roles.bundled.{ConfigWriter, Help}
import izumi.distage.roles.model.{RoleDescriptor, RoleService}
import izumi.fundamentals.platform.IzPlatform
import izumi.fundamentals.platform.cli.model.raw.{RawEntrypointParams, RawRoleParams, RawValue}
import izumi.reflect.TagK
import logstage.LogIO
import leaderboard.api.{LadderApi, ProfileApi}
import leaderboard.http.HttpServer
import leaderboard.plugins.{LeaderboardPlugin, PostgresDockerPlugin}
import zio.interop.catz.asyncInstance
import zio.Task

import scala.annotation.unused

Expand Down Expand Up @@ -119,7 +120,8 @@ object LeaderboardRole extends RoleDescriptor {
* ./launcher -u repo:dummy :leaderboard
* }}}
*/
object MainDummy extends MainBase(Activation(Repo -> Repo.Dummy), Vector(RawRoleParams(LeaderboardRole.id)))
object MainDummyCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Dummy), Vector(RawRoleParams(LeaderboardRole.id)))
object MainDummyZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Dummy), Vector(RawRoleParams(LeaderboardRole.id)))

/**
* Launch with production configuration and setup the required postgres DB inside docker.
Expand All @@ -131,7 +133,8 @@ object MainDummy extends MainBase(Activation(Repo -> Repo.Dummy), Vector(RawRole
* ./launcher -u scene:managed :leaderboard
* }}}
*/
object MainProdDocker extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Scene.Managed), Vector(RawRoleParams(LeaderboardRole.id)))
object MainProdDockerCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Prod, Scene -> Scene.Managed), Vector(RawRoleParams(LeaderboardRole.id)))
object MainProdDockerZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Prod, Scene -> Scene.Managed), Vector(RawRoleParams(LeaderboardRole.id)))

/**
* Launch with production configuration and external, not dockerized, services.
Expand All @@ -148,7 +151,8 @@ object MainProdDocker extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Sc
* ./launcher :leaderboard
* }}}
*/
object MainProd extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(LeaderboardRole.id)))
object MainProdCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(LeaderboardRole.id)))
object MainProdZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(LeaderboardRole.id)))

/**
* Launch just the `ladder` APIs with dummy repositories
Expand All @@ -158,7 +162,8 @@ object MainProd extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Scene.Pr
* ./launcher -u repo:dummy :ladder
* }}}
*/
object MainLadderDummy extends MainBase(Activation(Repo -> Repo.Dummy), Vector(RawRoleParams(LadderRole.id)))
object MainLadderDummyCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Dummy), Vector(RawRoleParams(LadderRole.id)))
object MainLadderDummyZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Dummy), Vector(RawRoleParams(LadderRole.id)))

/**
* Launch just the `ladder` APIs with postgres repositories and dockerized postgres service
Expand All @@ -168,7 +173,8 @@ object MainLadderDummy extends MainBase(Activation(Repo -> Repo.Dummy), Vector(R
* ./launcher -u scene:managed :ladder
* }}}
*/
object MainLadderProdDocker extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Scene.Managed), Vector(RawRoleParams(LadderRole.id)))
object MainLadderProdDockerCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Prod, Scene -> Scene.Managed), Vector(RawRoleParams(LadderRole.id)))
object MainLadderProdDockerZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Prod, Scene -> Scene.Managed), Vector(RawRoleParams(LadderRole.id)))

/**
* Launch just the `ladder` APIs with postgres repositories and external postgres service
Expand All @@ -180,7 +186,8 @@ object MainLadderProdDocker extends MainBase(Activation(Repo -> Repo.Prod, Scene
* ./launcher :ladder
* }}}
*/
object MainLadderProd extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(LadderRole.id)))
object MainLadderProdCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(LadderRole.id)))
object MainLadderProdZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(LadderRole.id)))

/**
* Launch just the `profile` APIs with dummy repositories
Expand All @@ -190,7 +197,8 @@ object MainLadderProd extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Sc
* ./launcher -u repo:dummy :profile
* }}}
*/
object MainProfileDummy extends MainBase(Activation(Repo -> Repo.Dummy), Vector(RawRoleParams(ProfileRole.id)))
object MainProfileDummyCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Dummy), Vector(RawRoleParams(ProfileRole.id)))
object MainProfileDummyZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Dummy), Vector(RawRoleParams(ProfileRole.id)))

/**
* Launch just the `profile` APIs with postgres repositories and dockerized postgres service
Expand All @@ -200,7 +208,8 @@ object MainProfileDummy extends MainBase(Activation(Repo -> Repo.Dummy), Vector(
* ./launcher -u scene:managed :profile
* }}}
*/
object MainProfileProdDocker extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Scene.Managed), Vector(RawRoleParams(ProfileRole.id)))
object MainProfileProdDockerCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Prod, Scene -> Scene.Managed), Vector(RawRoleParams(ProfileRole.id)))
object MainProfileProdDockerZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Prod, Scene -> Scene.Managed), Vector(RawRoleParams(ProfileRole.id)))

/**
* Launch just the `profile` APIs with postgres repositories and external postgres service
Expand All @@ -210,7 +219,8 @@ object MainProfileProdDocker extends MainBase(Activation(Repo -> Repo.Prod, Scen
* ./launcher :profile
* }}}
*/
object MainProfileProd extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(ProfileRole.id)))
object MainProfileProdCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(ProfileRole.id)))
object MainProfileProdZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(ProfileRole.id)))

/**
* Display help message with all available launcher arguments
Expand All @@ -221,7 +231,8 @@ object MainProfileProd extends MainBase(Activation(Repo -> Repo.Prod, Scene -> S
* ./launcher :help
* }}}
*/
object MainHelp extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(Help.id)))
object MainHelpCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(Help.id)))
object MainHelpZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector(RawRoleParams(Help.id)))

/**
* Write the default configuration files for each role into JSON files in `./config`.
Expand All @@ -236,8 +247,10 @@ object MainHelp extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Scene.Pr
* ./launcher :configwriter
* }}}
*/
object MainWriteReferenceConfigs
extends MainBase(
object MainWriteReferenceConfigsCats extends MainWriteReferenceConfigsBase[cats.effect.IO]
object MainWriteReferenceConfigsZIO extends MainWriteReferenceConfigsBase[zio.Task]
abstract class MainWriteReferenceConfigsBase[F[_]: TagK: Async: DefaultModule]
extends MainBase[F](
activation = {
Activation(Repo -> Repo.Prod, Scene -> Scene.Provided)
},
Expand Down Expand Up @@ -288,26 +301,23 @@ object MainWriteReferenceConfigs
*
* }}}
*/
object GenericLauncher extends MainBase(Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector.empty)
object GenericLauncherCats extends MainBase[cats.effect.IO](Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector.empty)
object GenericLauncherZIO extends MainBase[zio.Task](Activation(Repo -> Repo.Prod, Scene -> Scene.Provided), Vector.empty)

sealed abstract class MainBase(
sealed abstract class MainBase[F[_]: TagK: Async: DefaultModule](
activation: Activation,
requiredRoles: Vector[RawRoleParams],
) extends RoleAppMain.LauncherCats[Task] {
) extends RoleAppMain.LauncherCats[F] {

override def requiredRoles(argv: RoleAppMain.ArgV): Vector[RawRoleParams] = {
requiredRoles
}

override def pluginConfig: PluginConfig = {
if (IzPlatform.isGraalNativeImage) {
// Only this would work reliably for NativeImage
PluginConfig.const(List(LeaderboardPlugin, PostgresDockerPlugin))
} else {
// Runtime discovery with PluginConfig.cached might be convenient for pure jvm projects during active development
// Once the project gets to the maintenance stage it's a good idea to switch to PluginConfig.const
PluginConfig.cached(pluginsPackage = "leaderboard.plugins")
}
override val pluginConfig: PluginConfig = {
// When our plugins are polymorphic, we can't use runtime discovery,
// we must instantiate them manually using PluginConfig.const.
// Also, only manual instantiation works reliably with NativeImage.
PluginConfig.const(List(LeaderboardPlugin[F], PostgresDockerPlugin[F]))
}

protected override def roleAppBootOverrides(argv: RoleAppMain.ArgV): Module = super.roleAppBootOverrides(argv) ++ new ModuleDef {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import distage.StandardAxis.Repo
import distage.config.ConfigModuleDef
import distage.{ModuleDef, Scene, TagK}
import doobie.util.transactor.Transactor
import izumi.distage.plugins.PluginDef
import izumi.distage.plugins.{PluginBase, PluginDef}
import izumi.distage.roles.bundled.BundledRolesModule
import izumi.distage.roles.model.definition.RoleModuleDef
import izumi.fundamentals.platform.integration.PortCheck
import zio.Task
import leaderboard.api.{HttpApi, LadderApi, ProfileApi}
import leaderboard.config.{PostgresCfg, PostgresPortCfg}
import leaderboard.http.HttpServer
Expand All @@ -20,13 +19,15 @@ import org.http4s.dsl.Http4sDsl

import scala.concurrent.duration.*

object LeaderboardPlugin extends PluginDef {
include(modules.roles[Task])
include(modules.api[Task])
include(modules.repoDummy[Task])
include(modules.repoProd[Task])
include(modules.configs)
include(modules.prodConfigs)
object LeaderboardPlugin {
def apply[F[_]: TagK]: PluginBase = new PluginDef {
include(modules.roles)
include(modules.api)
include(modules.repoDummy)
include(modules.repoProd)
include(modules.configs)
include(modules.prodConfigs)
}

object modules {
def roles[F[_]: TagK]: RoleModuleDef = new RoleModuleDef {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
package leaderboard.plugins

import distage.Scene
import distage.{ModuleDef, Scene}
import izumi.distage.docker.Docker.DockerPort
import izumi.distage.docker.bundled.PostgresDocker
import izumi.distage.docker.modules.DockerSupportModule
import izumi.distage.plugins.PluginDef
import izumi.distage.plugins.{PluginBase, PluginDef}
import izumi.reflect.TagK
import leaderboard.config.PostgresPortCfg
import zio.Task

object PostgresDockerPlugin extends PluginDef {
// only enable postgres docker when Scene axis is set to Managed
tag(Scene.Managed)
object PostgresDockerPlugin {
def apply[F[_]: TagK]: PluginBase = new PluginDef {
include(dockerModule[F])
}

def dockerModule[F[_]: TagK]: ModuleDef = new ModuleDef {
// only enable postgres docker when Scene axis is set to Managed
tag(Scene.Managed)

// add docker support dependencies
include(DockerSupportModule[Task])
// add docker support dependencies
include(DockerSupportModule[F])

// launch postgres docker for tests
make[PostgresDocker.Container]
.fromResource(PostgresDocker.make[Task])
// launch postgres docker for tests
make[PostgresDocker.Container]
.fromResource(PostgresDocker.make[F])

// spawned docker container port is randomized
// to prevent conflicts, so make PostgresPortCfg
// point to the new port. This will also
// cause the container to start before
// integration check is performed
make[PostgresPortCfg].from {
(docker: PostgresDocker.Container) =>
val knownAddress = docker.availablePorts.first(DockerPort.TCP(5432))
PostgresPortCfg(knownAddress.hostString, knownAddress.port)
// spawned docker container port is randomized
// to prevent conflicts, so make PostgresPortCfg
// point to the new port. This will also
// cause the container to start before
// integration check is performed
make[PostgresPortCfg].from {
(docker: PostgresDocker.Container) =>
val knownAddress = docker.availablePorts.first(DockerPort.TCP(5432))
PostgresPortCfg(knownAddress.hostString, knownAddress.port)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ package leaderboard

import izumi.distage.testkit.scalatest.SpecWiring

final class WiringTest extends SpecWiring(GenericLauncher)
final class WiringTestZIO extends SpecWiring(GenericLauncherZIO)
final class WiringTestCats extends SpecWiring(GenericLauncherCats)
Loading

0 comments on commit 3abe9fa

Please sign in to comment.