From 772afa7282ae1ef53ecde6279fda4f2169edc039 Mon Sep 17 00:00:00 2001 From: geirolz Date: Fri, 30 Jun 2023 17:46:56 +0200 Subject: [PATCH] Add Fly4s module --- build.sbt | 17 ++++- .../scala/com/geirolz/app/toolkit/App.scala | 28 +++---- docs/source/README.md | 74 +++++++++++++++++-- .../com/geirolz/example/app/AppMain.scala | 2 +- .../geirolz/example/app/AppWithFailures.scala | 2 +- .../geirolz/app/toolkit/fly4s/package.scala | 3 + .../geirolz/app/toolkit/fly4s/syntax.scala | 56 ++++++++++++++ .../app/toolkit/fly4s/Fly4sSupportSuite.scala | 38 ++++++++++ .../toolkit/fly4s/testing/TestConfig.scala | 8 ++ project/ProjectDependencies.scala | 9 ++- 10 files changed, 208 insertions(+), 29 deletions(-) create mode 100644 integrations/fly4s/src/main/scala/com/geirolz/app/toolkit/fly4s/package.scala create mode 100644 integrations/fly4s/src/main/scala/com/geirolz/app/toolkit/fly4s/syntax.scala create mode 100644 integrations/fly4s/src/test/scala/com/geirolz/app/toolkit/fly4s/Fly4sSupportSuite.scala create mode 100644 integrations/fly4s/src/test/scala/com/geirolz/app/toolkit/fly4s/testing/TestConfig.scala diff --git a/build.sbt b/build.sbt index 6f55485..3d3409d 100644 --- a/build.sbt +++ b/build.sbt @@ -35,13 +35,13 @@ lazy val root: Project = project .settings( copyReadMe := IO.copyFile(file("docs/compiled/README.md"), file("README.md")) ) - .aggregate(core, docs, config, testing, log4cats, odin, pureconfig) + .aggregate(core, docs, config, testing, log4cats, odin, pureconfig, fly4s) lazy val docs: Project = project .in(file("docs")) .enablePlugins(MdocPlugin) - .dependsOn(core, config, log4cats, odin, pureconfig) + .dependsOn(core, config, log4cats, odin, pureconfig, fly4s) .settings( baseSettings, noPublishSettings, @@ -142,13 +142,22 @@ lazy val odin: Project = libraryDependencies ++= ProjectDependencies.Integrations.Odin.dedicated ) -lazy val `pureconfig`: Project = +lazy val pureconfig: Project = module("pureconfig")( folder = s"$integrationsFolder/pureconfig", publishAs = Some(subProjectName("pureconfig")) ).dependsOn(core, config) .settings( - libraryDependencies ++= ProjectDependencies.Integrations.ConfigPureConfig.dedicated + libraryDependencies ++= ProjectDependencies.Integrations.Pureconfig.dedicated + ) + +lazy val fly4s: Project = + module("fly4s")( + folder = s"$integrationsFolder/fly4s", + publishAs = Some(subProjectName("fly4s")) + ).dependsOn(core) + .settings( + libraryDependencies ++= ProjectDependencies.Integrations.Fly4s.dedicated ) //=============================== MODULES UTILS =============================== diff --git a/core/src/main/scala/com/geirolz/app/toolkit/App.scala b/core/src/main/scala/com/geirolz/app/toolkit/App.scala index 2a4f199..d087182 100644 --- a/core/src/main/scala/com/geirolz/app/toolkit/App.scala +++ b/core/src/main/scala/com/geirolz/app/toolkit/App.scala @@ -22,7 +22,7 @@ class App[ val loggerBuilder: F[LOGGER_T[F]], val configLoader: F[CONFIG], val resourcesLoader: F[RESOURCES], - val beforeRunF: App.Dependencies[APP_INFO, LOGGER_T[F], CONFIG, DEPENDENCIES, RESOURCES] => F[Unit], + val beforeProvidingF: App.Dependencies[APP_INFO, LOGGER_T[F], CONFIG, DEPENDENCIES, RESOURCES] => F[Unit], val onFinalizeF: App.Dependencies[APP_INFO, LOGGER_T[F], CONFIG, DEPENDENCIES, RESOURCES] => F[Unit], val failureHandlerLoader: App.Resources[APP_INFO, LOGGER_T[F], CONFIG, RESOURCES] => FailureHandler[F, FAILURE], val dependenciesLoader: App.Resources[APP_INFO, LOGGER_T[F], CONFIG, RESOURCES] => Resource[F, FAILURE \/ DEPENDENCIES], @@ -60,15 +60,15 @@ class App[ def withMessages(messages: AppMessages): Self = copyWith(appMessages = messages) - def beforeRun( + def beforeProviding( f: App.Dependencies[APP_INFO, LOGGER_T[F], CONFIG, DEPENDENCIES, RESOURCES] => F[Unit] ): Self = - copyWith(beforeRunF = f) + copyWith(beforeProvidingF = beforeProvidingF >> f) def onFinalize( f: App.Dependencies[APP_INFO, LOGGER_T[F], CONFIG, DEPENDENCIES, RESOURCES] => F[Unit] ): Self = - copyWith(onFinalizeF = f) + copyWith(onFinalizeF = onFinalizeF >> f) private[toolkit] def _compile(appArgs: List[String]): Resource[F, FAILURE \/ F[NonEmptyList[FAILURE] \/ Unit]] = ( @@ -146,7 +146,7 @@ class App[ } yield maybeReducedFailures.toLeft(()) } yield { appLoggerF.info(appMessages.startingApp) >> - beforeRunF(appDependencies) >> + beforeProvidingF(appDependencies) >> appLogic .onCancel(appLoggerF.info(appMessages.appWasStopped)) .onError(e => appLoggerF.error(e)(appMessages.appEnErrorOccurred)) @@ -180,13 +180,13 @@ class App[ RES2, DEPS2 ]( - appInfo: APP_INFO2 = this.appInfo, - appMessages: AppMessages = this.appMessages, - loggerBuilder: G[LOGGER_T2[G]] = this.loggerBuilder, - configLoader: G[CONFIG2] = this.configLoader, - resourcesLoader: G[RES2] = this.resourcesLoader, - beforeRunF: App.Dependencies[APP_INFO2, LOGGER_T2[G], CONFIG2, DEPS2, RES2] => G[Unit] = this.beforeRunF, - onFinalizeF: App.Dependencies[APP_INFO2, LOGGER_T2[G], CONFIG2, DEPS2, RES2] => G[Unit] = this.onFinalizeF, + appInfo: APP_INFO2 = this.appInfo, + appMessages: AppMessages = this.appMessages, + loggerBuilder: G[LOGGER_T2[G]] = this.loggerBuilder, + configLoader: G[CONFIG2] = this.configLoader, + resourcesLoader: G[RES2] = this.resourcesLoader, + beforeProvidingF: App.Dependencies[APP_INFO2, LOGGER_T2[G], CONFIG2, DEPS2, RES2] => G[Unit] = this.beforeProvidingF, + onFinalizeF: App.Dependencies[APP_INFO2, LOGGER_T2[G], CONFIG2, DEPS2, RES2] => G[Unit] = this.onFinalizeF, failureHandlerLoader: App.Resources[APP_INFO2, LOGGER_T2[G], CONFIG2, RES2] => FailureHandler[ G, FAILURE2 @@ -205,7 +205,7 @@ class App[ loggerBuilder = loggerBuilder, configLoader = configLoader, resourcesLoader = resourcesLoader, - beforeRunF = beforeRunF, + beforeProvidingF = beforeProvidingF, onFinalizeF = onFinalizeF, failureHandlerLoader = failureHandlerLoader, dependenciesLoader = dependenciesLoader, @@ -434,7 +434,7 @@ object App extends AppSyntax { failureHandlerLoader = _ => FailureHandler.cancelAll, loggerBuilder = loggerBuilder, resourcesLoader = resourcesLoader, - beforeRunF = _ => ().pure[F], + beforeProvidingF = _ => ().pure[F], onFinalizeF = _ => ().pure[F], configLoader = configLoader, dependenciesLoader = dependenciesLoader, diff --git a/docs/source/README.md b/docs/source/README.md index 937ecd7..9b6287a 100644 --- a/docs/source/README.md +++ b/docs/source/README.md @@ -78,10 +78,10 @@ object Main extends IOApp { App[IO] .withInfo( SimpleAppInfo.string( - name = "toolkit", - version = "0.0.1", - scalaVersion = "2.13.10", - sbtVersion = "1.8.0" + name = "toolkit", + version = "0.0.1", + scalaVersion = "2.13.10", + sbtVersion = "1.8.0" ) ) .withLogger(ToolkitLogger.console[IO](_)) @@ -108,6 +108,10 @@ object Main extends IOApp { ```sbt libraryDependencies += "com.github.geirolz" %% "toolkit-pureconfig" % "@VERSION@" ``` +Import the syntax +```scala +import com.geirolz.app.toolkit.config.pureconfig.syntax.* +``` Which allows you to use `withPureConfigLoader` to load the config from a `ConfigSource.default` @@ -127,10 +131,10 @@ object TestConfig { App[IO] .withInfo( SimpleAppInfo.string( - name = "toolkit", - version = "0.0.1", - scalaVersion = "2.13.10", - sbtVersion = "1.8.0" + name = "toolkit", + version = "0.0.1", + scalaVersion = "2.13.10", + sbtVersion = "1.8.0" ) ) .withPureConfigLoader[TestConfig] @@ -149,4 +153,58 @@ libraryDependencies += "com.github.geirolz" %% "toolkit-log4cats" % "@VERSION@" ```sbt libraryDependencies += "com.github.geirolz" %% "toolkit-odin" % "@VERSION@" +``` + +#### fly4s + +```sbt +libraryDependencies += "com.github.geirolz" %% "toolkit-fly4s" % "@VERSION@" +``` + +Import the syntax +```scala +import com.geirolz.app.toolkit.fly4s.syntax.* +``` + +Which allows you to use `beforeProvidingMigrateDatabaseWithConfig` on `App` to migrate the database before running the +app. +To have access to the whole app dependencies you can use `beforeProvidingMigrateDatabaseWith` instead while to have +access to +the whole app dependencies to provide a custom `Fly4s` instance you can use `beforeProvidingMigrateDatabase`. + +```scala mdoc:silent + +import cats.Show +import com.geirolz.app.toolkit.fly4s.syntax.* + +case class TestConfig(dbUrl: String, dbUser: Option[String], dbPassword: Option[Array[Char]]) + +object TestConfig { + implicit val show: Show[TestConfig] = Show.fromToString +} + +App[IO] + .withInfo( + SimpleAppInfo.string( + name = "toolkit", + version = "0.0.1", + scalaVersion = "2.13.10", + sbtVersion = "1.8.0" + ) + ) + .withConfig( + TestConfig( + dbUrl = "jdbc:postgresql://localhost:5432/toolkit", + dbUser = Some("postgres"), + dbPassword = Some("postgres".toCharArray) + ) + ) + .withoutDependencies + .provideOne(_ => IO.unit) + .beforeProvidingMigrateDatabaseWithConfig( + url = _.dbUrl, + user = _.dbUser, + password = _.dbPassword + ) + .run_ ``` \ No newline at end of file diff --git a/example/src/main/scala-2/com/geirolz/example/app/AppMain.scala b/example/src/main/scala-2/com/geirolz/example/app/AppMain.scala index 8893e0c..aaa28dd 100644 --- a/example/src/main/scala-2/com/geirolz/example/app/AppMain.scala +++ b/example/src/main/scala-2/com/geirolz/example/app/AppMain.scala @@ -27,7 +27,7 @@ object AppMain extends IOApp { .drain ) ) - .beforeRun(_.logger.info("CUSTOM PRE-RUN")) + .beforeProviding(_.logger.info("CUSTOM PRE-RUN")) .onFinalize(_.logger.info("CUSTOM END")) .run(args) } diff --git a/example/src/main/scala-2/com/geirolz/example/app/AppWithFailures.scala b/example/src/main/scala-2/com/geirolz/example/app/AppWithFailures.scala index 69bca85..54ae760 100644 --- a/example/src/main/scala-2/com/geirolz/example/app/AppWithFailures.scala +++ b/example/src/main/scala-2/com/geirolz/example/app/AppWithFailures.scala @@ -27,7 +27,7 @@ object AppWithFailures extends IOApp { .drain ) ) - .beforeRun(_.logger.info("CUSTOM PRE-RUN")) + .beforeProviding(_.logger.info("CUSTOM PRE-RUN")) .onFinalize(_.logger.info("CUSTOM END")) .run(args) } diff --git a/integrations/fly4s/src/main/scala/com/geirolz/app/toolkit/fly4s/package.scala b/integrations/fly4s/src/main/scala/com/geirolz/app/toolkit/fly4s/package.scala new file mode 100644 index 0000000..93ad905 --- /dev/null +++ b/integrations/fly4s/src/main/scala/com/geirolz/app/toolkit/fly4s/package.scala @@ -0,0 +1,3 @@ +package com.geirolz.app.toolkit + +package object fly4s {} diff --git a/integrations/fly4s/src/main/scala/com/geirolz/app/toolkit/fly4s/syntax.scala b/integrations/fly4s/src/main/scala/com/geirolz/app/toolkit/fly4s/syntax.scala new file mode 100644 index 0000000..cdbc152 --- /dev/null +++ b/integrations/fly4s/src/main/scala/com/geirolz/app/toolkit/fly4s/syntax.scala @@ -0,0 +1,56 @@ +package com.geirolz.app.toolkit.fly4s + +import cats.effect.Resource +import cats.effect.kernel.Async +import com.geirolz.app.toolkit.{App, SimpleAppInfo} +import fly4s.core.Fly4s +import fly4s.core.data.Fly4sConfig + +object syntax extends AllSyntax +private[fly4s] sealed trait AllSyntax { + + import cats.syntax.all.* + + implicit class AppResourcesLoaderOps[F[+_]: Async, FAILURE, APP_INFO <: SimpleAppInfo[?], LOGGER_T[_[_]], CONFIG, RESOURCES, DEPENDENCIES]( + app: App[F, FAILURE, APP_INFO, LOGGER_T, CONFIG, RESOURCES, DEPENDENCIES] + ) { + + def beforeProvidingMigrateDatabaseWithConfig( + url: CONFIG => String, + user: CONFIG => Option[String] = _ => None, + password: CONFIG => Option[Array[Char]] = _ => None, + config: Fly4sConfig = Fly4sConfig.default, + classLoader: ClassLoader = Thread.currentThread.getContextClassLoader + ): App[F, FAILURE, APP_INFO, LOGGER_T, CONFIG, RESOURCES, DEPENDENCIES] = + beforeProvidingMigrateDatabaseWith( + url = d => url(d.config), + user = d => user(d.config), + password = d => password(d.config), + config = config, + classLoader = classLoader + ) + + def beforeProvidingMigrateDatabaseWith( + url: App.Dependencies[APP_INFO, LOGGER_T[F], CONFIG, DEPENDENCIES, RESOURCES] => String, + user: App.Dependencies[APP_INFO, LOGGER_T[F], CONFIG, DEPENDENCIES, RESOURCES] => Option[String] = _ => None, + password: App.Dependencies[APP_INFO, LOGGER_T[F], CONFIG, DEPENDENCIES, RESOURCES] => Option[Array[Char]] = _ => None, + config: Fly4sConfig = Fly4sConfig.default, + classLoader: ClassLoader = Thread.currentThread.getContextClassLoader + ): App[F, FAILURE, APP_INFO, LOGGER_T, CONFIG, RESOURCES, DEPENDENCIES] = + beforeProvidingMigrateDatabase(dep => + Fly4s + .make[F]( + url = url(dep), + user = user(dep), + password = password(dep), + config = config, + classLoader = classLoader + ) + ) + + def beforeProvidingMigrateDatabase( + f: App.Dependencies[APP_INFO, LOGGER_T[F], CONFIG, DEPENDENCIES, RESOURCES] => Resource[F, Fly4s[F]] + ): App[F, FAILURE, APP_INFO, LOGGER_T, CONFIG, RESOURCES, DEPENDENCIES] = + app.beforeProviding(dep => f(dep).use(_.migrate).void) + } +} diff --git a/integrations/fly4s/src/test/scala/com/geirolz/app/toolkit/fly4s/Fly4sSupportSuite.scala b/integrations/fly4s/src/test/scala/com/geirolz/app/toolkit/fly4s/Fly4sSupportSuite.scala new file mode 100644 index 0000000..44356c4 --- /dev/null +++ b/integrations/fly4s/src/test/scala/com/geirolz/app/toolkit/fly4s/Fly4sSupportSuite.scala @@ -0,0 +1,38 @@ +package com.geirolz.app.toolkit.fly4s + +import cats.effect.IO +import com.geirolz.app.toolkit.fly4s.syntax.* +import com.geirolz.app.toolkit.fly4s.testing.TestConfig +import com.geirolz.app.toolkit.{App, SimpleAppInfo} + +class Fly4sSupportSuite extends munit.CatsEffectSuite { + + test("Syntax works as expected") { + assertIO_( + App[IO] + .withInfo( + SimpleAppInfo.string( + name = "toolkit", + version = "0.0.1", + scalaVersion = "2.13.10", + sbtVersion = "1.8.0" + ) + ) + .withConfig( + TestConfig( + dbUrl = "jdbc:postgresql://localhost:5432/toolkit", + dbUser = Some("postgres"), + dbPassword = Some("postgres".toCharArray) + ) + ) + .withoutDependencies + .provideOne(_ => IO.unit) + .beforeProvidingMigrateDatabaseWithConfig( + url = _.dbUrl, + user = _.dbUser, + password = _.dbPassword + ) + .run_ + ) + } +} diff --git a/integrations/fly4s/src/test/scala/com/geirolz/app/toolkit/fly4s/testing/TestConfig.scala b/integrations/fly4s/src/test/scala/com/geirolz/app/toolkit/fly4s/testing/TestConfig.scala new file mode 100644 index 0000000..b8e204e --- /dev/null +++ b/integrations/fly4s/src/test/scala/com/geirolz/app/toolkit/fly4s/testing/TestConfig.scala @@ -0,0 +1,8 @@ +package com.geirolz.app.toolkit.fly4s.testing + +import cats.Show + +case class TestConfig(dbUrl: String, dbUser: Option[String], dbPassword: Option[Array[Char]]) +object TestConfig { + implicit val show: Show[TestConfig] = Show.fromToString +} diff --git a/project/ProjectDependencies.scala b/project/ProjectDependencies.scala index 6360a92..14258fc 100644 --- a/project/ProjectDependencies.scala +++ b/project/ProjectDependencies.scala @@ -10,6 +10,7 @@ object ProjectDependencies { private val circeVersion = "0.14.5" private val circeGenericExtraVersion = "0.14.3" private val pureConfigVersion = "0.17.4" + private val fly4sVersion = "0.0.18" private val munitVersion = "0.7.29" private val munitEffectVersion = "1.0.7" private val slf4Version = "2.0.7" @@ -96,11 +97,17 @@ object ProjectDependencies { ) } - object ConfigPureConfig { + object Pureconfig { lazy val dedicated: Seq[ModuleID] = List( "com.github.pureconfig" %% "pureconfig-core" % pureConfigVersion ) } + + object Fly4s { + lazy val dedicated: Seq[ModuleID] = List( + "com.github.geirolz" %% "fly4s-core" % fly4sVersion + ) + } } object Plugins {