From f90d94d761fa9d4ed4647dafa460da8ebe17e531 Mon Sep 17 00:00:00 2001 From: Mateusz Borek Date: Wed, 5 Jan 2022 09:30:40 +0100 Subject: [PATCH] Remove modules layer --- .../bootzooka/DependenciesFactory.scala | 67 +++++++++++++++++++ .../softwaremill/bootzooka/InitModule.scala | 17 ----- .../com/softwaremill/bootzooka/Main.scala | 57 ++++++++++------ .../softwaremill/bootzooka/MainModule.scala | 35 ---------- .../bootzooka/email/EmailModule.scala | 28 -------- .../bootzooka/infrastructure/DB.scala | 2 +- .../infrastructure/InfrastructureModule.scala | 14 ---- .../bootzooka/metrics/MetricsModule.scala | 12 ---- .../passwordreset/PasswordResetModule.scala | 25 ------- .../bootzooka/security/SecurityModule.scala | 17 ----- .../bootzooka/user/UserModule.scala | 24 ------- .../passwordreset/PasswordResetApiTest.scala | 33 +++------ .../bootzooka/test/AppDependencies.scala | 32 +++++++++ .../bootzooka/test/HttpTestSupport.scala | 3 - .../bootzooka/test/Requests.scala | 15 ++--- .../bootzooka/user/UserApiTest.scala | 22 ++---- 16 files changed, 157 insertions(+), 246 deletions(-) create mode 100644 backend/src/main/scala/com/softwaremill/bootzooka/DependenciesFactory.scala delete mode 100644 backend/src/main/scala/com/softwaremill/bootzooka/InitModule.scala delete mode 100644 backend/src/main/scala/com/softwaremill/bootzooka/MainModule.scala delete mode 100644 backend/src/main/scala/com/softwaremill/bootzooka/email/EmailModule.scala delete mode 100644 backend/src/main/scala/com/softwaremill/bootzooka/infrastructure/InfrastructureModule.scala delete mode 100644 backend/src/main/scala/com/softwaremill/bootzooka/metrics/MetricsModule.scala delete mode 100644 backend/src/main/scala/com/softwaremill/bootzooka/passwordreset/PasswordResetModule.scala delete mode 100644 backend/src/main/scala/com/softwaremill/bootzooka/security/SecurityModule.scala delete mode 100644 backend/src/main/scala/com/softwaremill/bootzooka/user/UserModule.scala create mode 100644 backend/src/test/scala/com/softwaremill/bootzooka/test/AppDependencies.scala diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/DependenciesFactory.scala b/backend/src/main/scala/com/softwaremill/bootzooka/DependenciesFactory.scala new file mode 100644 index 000000000..2038f2534 --- /dev/null +++ b/backend/src/main/scala/com/softwaremill/bootzooka/DependenciesFactory.scala @@ -0,0 +1,67 @@ +package com.softwaremill.bootzooka + +import cats.data.NonEmptyList +import cats.effect.{IO, Resource} +import com.softwaremill.bootzooka.config.Config +import com.softwaremill.bootzooka.email.{EmailConfig, EmailModel, EmailService} +import com.softwaremill.bootzooka.email.sender.{DummyEmailSender, EmailSender, MailgunEmailSender, SmtpEmailSender} +import com.softwaremill.bootzooka.http.{Http, HttpApi, HttpConfig} +import com.softwaremill.bootzooka.infrastructure.{DB, DBConfig} +import com.softwaremill.bootzooka.metrics.{MetricsApi, VersionApi} +import com.softwaremill.bootzooka.passwordreset.{PasswordResetApi, PasswordResetAuthToken, PasswordResetCodeModel} +import com.softwaremill.bootzooka.security.{ApiKeyAuthToken, ApiKeyModel} +import com.softwaremill.bootzooka.user.UserApi +import com.softwaremill.bootzooka.util.{Clock, DefaultIdGenerator, IdGenerator} +import com.softwaremill.macwire.autocats.autowire +import doobie.util.transactor.Transactor +import io.prometheus.client.CollectorRegistry +import sttp.client3.SttpBackend + +object DependenciesFactory { + private case class Modules(api: HttpApi, emailService: EmailService) + + def resource(config: Config, sttpBackend: Resource[IO, SttpBackend[IO, Any]], xa: Resource[IO, Transactor[IO]], clock: Clock): Resource[IO, (HttpApi, EmailService)] = { + lazy val collectorRegistry: CollectorRegistry = CollectorRegistry.defaultRegistry + lazy val idGenerator: IdGenerator = DefaultIdGenerator + + def buildHttpApi(http: Http, userApi: UserApi, passwordResetApi: PasswordResetApi, metricsApi: MetricsApi, versionApi: VersionApi, collectorRegistry: CollectorRegistry, cfg: HttpConfig) = + new HttpApi( + http, + userApi.endpoints concatNel passwordResetApi.endpoints, + NonEmptyList.of(metricsApi.metricsEndpoint, versionApi.versionEndpoint), + collectorRegistry, + cfg) + + def buildApiKeyAuthToken(apiKeyModel: ApiKeyModel): ApiKeyAuthToken = new ApiKeyAuthToken(apiKeyModel) + + def buildPasswordResetAuthToken(passwordResetCodeModel: PasswordResetCodeModel): PasswordResetAuthToken = new PasswordResetAuthToken(passwordResetCodeModel) + + def buildEmailSender(sttpBackend: SttpBackend[IO, Any], config: EmailConfig): EmailSender = if (config.mailgun.enabled) { + new MailgunEmailSender(config.mailgun, sttpBackend) + } else if (config.smtp.enabled) { + new SmtpEmailSender(config.smtp) + } else { + DummyEmailSender + } + + def buildEmailScheduler(emailModel: EmailModel, idGenerator: IdGenerator, emailSender: EmailSender, config: EmailConfig, xa: Transactor[IO]) = + new EmailService(emailModel, idGenerator, emailSender, config, xa) + + autowire[Modules]( + config.api, + config.user, + config.passwordReset, + config.email, + idGenerator, + clock, + collectorRegistry, + sttpBackend, + xa, + buildHttpApi _, + buildApiKeyAuthToken _, + buildEmailScheduler _, + buildEmailSender _, + buildPasswordResetAuthToken _ + ).map(modules => (modules.api, modules.emailService)) + } +} diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/InitModule.scala b/backend/src/main/scala/com/softwaremill/bootzooka/InitModule.scala deleted file mode 100644 index 83795cf88..000000000 --- a/backend/src/main/scala/com/softwaremill/bootzooka/InitModule.scala +++ /dev/null @@ -1,17 +0,0 @@ -package com.softwaremill.bootzooka - -import cats.effect.{IO, Resource} -import com.softwaremill.bootzooka.config.ConfigModule -import com.softwaremill.bootzooka.infrastructure.DB -import sttp.capabilities.WebSockets -import sttp.capabilities.fs2.Fs2Streams -import sttp.client3.SttpBackend -import sttp.client3.asynchttpclient.fs2.AsyncHttpClientFs2Backend - -/** Initialised resources needed by the application to start. - */ -trait InitModule extends ConfigModule { - lazy val db: DB = new DB(config.db) - lazy val baseSttpBackend: Resource[IO, SttpBackend[IO, Fs2Streams[IO] with WebSockets]] = - AsyncHttpClientFs2Backend.resource() -} diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/Main.scala b/backend/src/main/scala/com/softwaremill/bootzooka/Main.scala index ce4b671a9..3873e3d11 100644 --- a/backend/src/main/scala/com/softwaremill/bootzooka/Main.scala +++ b/backend/src/main/scala/com/softwaremill/bootzooka/Main.scala @@ -1,14 +1,28 @@ package com.softwaremill.bootzooka -import cats.effect.IO +import cats.data.NonEmptyList +import cats.effect.{IO, Resource} import cats.effect.unsafe.implicits.global -import com.softwaremill.bootzooka.config.Config -import com.softwaremill.bootzooka.infrastructure.CorrelationId -import com.softwaremill.bootzooka.metrics.Metrics +import com.softwaremill.bootzooka.config.ConfigModule +import com.softwaremill.bootzooka.email.{EmailConfig, EmailModel, EmailService} +import com.softwaremill.bootzooka.email.sender.{DummyEmailSender, EmailSender, MailgunEmailSender, SmtpEmailSender} +import com.softwaremill.bootzooka.http.{Http, HttpApi, HttpConfig} +import com.softwaremill.bootzooka.infrastructure.{CorrelationId, DB, DBConfig, SetCorrelationIdBackend} +import com.softwaremill.bootzooka.metrics.{Metrics, MetricsApi, VersionApi} +import com.softwaremill.bootzooka.passwordreset.{PasswordResetApi, PasswordResetAuthToken, PasswordResetCode, PasswordResetCodeModel} +import com.softwaremill.bootzooka.security.{ApiKey, ApiKeyAuthToken, ApiKeyModel, Auth} +import com.softwaremill.bootzooka.user.UserApi +import com.softwaremill.bootzooka.util.{Clock, DefaultClock, DefaultIdGenerator, IdGenerator} import com.typesafe.scalalogging.StrictLogging -import doobie.util.transactor import sttp.client3.SttpBackend import com.softwaremill.macwire.autocats._ +import doobie.util.transactor.Transactor +import io.prometheus.client.CollectorRegistry +import sttp.capabilities.WebSockets +import sttp.capabilities.fs2.Fs2Streams +import sttp.client3.asynchttpclient.fs2.AsyncHttpClientFs2Backend +import sttp.client3.logging.slf4j.Slf4jLoggingBackend +import sttp.client3.prometheus.PrometheusBackend object Main extends StrictLogging { def main(args: Array[String]): Unit = { @@ -16,31 +30,36 @@ object Main extends StrictLogging { Metrics.init() Thread.setDefaultUncaughtExceptionHandler((t, e) => logger.error("Uncaught exception in thread: " + t, e)) - val initModule = new InitModule {} + val configModule = new ConfigModule {} + configModule.logConfig() + val config = configModule.config - initModule.logConfig() + lazy val clock: Clock = DefaultClock - def buildMainModule(_xa: transactor.Transactor[IO], _baseSttpBackend: SttpBackend[IO, Any], _config: Config) = new MainModule { - override def xa: transactor.Transactor[IO] = _xa - override def baseSttpBackend: SttpBackend[IO, Any] = _baseSttpBackend - override def config: Config = _config - } + lazy val sttpBackend: Resource[IO, SetCorrelationIdBackend[Fs2Streams[IO] with WebSockets]] = + AsyncHttpClientFs2Backend.resource[IO]().map(baseSttpBackend => new SetCorrelationIdBackend( + Slf4jLoggingBackend(PrometheusBackend(baseSttpBackend), includeTiming = true) + )) + + lazy val xa = new DB(config.db).transactorResource - val mainTask = autowire[MainModule]( - initModule.db.transactorResource, - initModule.baseSttpBackend, - initModule.config, - buildMainModule _ - ).use { modules => + val mainTask = DependenciesFactory.resource( + config = config, + sttpBackend = sttpBackend, + xa = xa, + clock = clock + ).use { case (httpApi, emailService) => /* Sequencing two tasks using the >> operator: - the first starts the background processes (such as an email sender) - the second allocates the http api resource, and never releases it (so that the http server is available as long as our application runs) */ - modules.startBackgroundProcesses >> modules.httpApi.resource.use(_ => IO.never) + emailService.startProcesses().void >> httpApi.resource.use(_ => IO.never) } mainTask.unsafeRunSync() } } + +case class Modules(emailService: EmailService, httpApi: HttpApi) diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/MainModule.scala b/backend/src/main/scala/com/softwaremill/bootzooka/MainModule.scala deleted file mode 100644 index 5988693c0..000000000 --- a/backend/src/main/scala/com/softwaremill/bootzooka/MainModule.scala +++ /dev/null @@ -1,35 +0,0 @@ -package com.softwaremill.bootzooka - -import cats.data.NonEmptyList -import cats.effect.IO -import com.softwaremill.bootzooka.email.EmailModule -import com.softwaremill.bootzooka.http.{Http, HttpApi} -import com.softwaremill.bootzooka.infrastructure.InfrastructureModule -import com.softwaremill.bootzooka.metrics.MetricsModule -import com.softwaremill.bootzooka.passwordreset.PasswordResetModule -import com.softwaremill.bootzooka.security.SecurityModule -import com.softwaremill.bootzooka.user.UserModule -import com.softwaremill.bootzooka.util.{Clock, DefaultClock, DefaultIdGenerator, IdGenerator, ServerEndpoints} - -/** Main application module. Depends on resources initialised in [[InitModule]]. - */ -trait MainModule - extends SecurityModule - with EmailModule - with UserModule - with PasswordResetModule - with MetricsModule - with InfrastructureModule { - - override lazy val idGenerator: IdGenerator = DefaultIdGenerator - override lazy val clock: Clock = DefaultClock - - lazy val http: Http = new Http() - - private lazy val endpoints: ServerEndpoints = userApi.endpoints concatNel passwordResetApi.endpoints - private lazy val adminEndpoints: ServerEndpoints = NonEmptyList.of(metricsApi.metricsEndpoint, versionApi.versionEndpoint) - - lazy val httpApi: HttpApi = new HttpApi(http, endpoints, adminEndpoints, collectorRegistry, config.api) - - lazy val startBackgroundProcesses: IO[Unit] = emailService.startProcesses().void -} diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/email/EmailModule.scala b/backend/src/main/scala/com/softwaremill/bootzooka/email/EmailModule.scala deleted file mode 100644 index fcab28961..000000000 --- a/backend/src/main/scala/com/softwaremill/bootzooka/email/EmailModule.scala +++ /dev/null @@ -1,28 +0,0 @@ -package com.softwaremill.bootzooka.email - -import cats.effect.IO -import com.softwaremill.bootzooka.email.sender.{DummyEmailSender, EmailSender, MailgunEmailSender, SmtpEmailSender} -import com.softwaremill.bootzooka.util.BaseModule -import com.softwaremill.macwire._ -import sttp.client3.SttpBackend -import doobie.util.transactor.Transactor - -trait EmailModule extends BaseModule { - lazy val emailModel = new EmailModel - private lazy val emailConfig = config.email - lazy val emailService = wire[EmailService] - // the EmailService implements the EmailScheduler functionality - hence, creating an alias for this dependency - lazy val emailScheduler: EmailScheduler = emailService - lazy val emailTemplates = new EmailTemplates() - // depending on the configuration, creating the appropriate EmailSender instance - lazy val emailSender: EmailSender = if (config.email.mailgun.enabled) { - new MailgunEmailSender(config.email.mailgun, sttpBackend) - } else if (config.email.smtp.enabled) { - new SmtpEmailSender(config.email.smtp) - } else { - DummyEmailSender - } - - def xa: Transactor[IO] - def sttpBackend: SttpBackend[IO, Any] -} diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/infrastructure/DB.scala b/backend/src/main/scala/com/softwaremill/bootzooka/infrastructure/DB.scala index f7d99070e..4b4849e8d 100644 --- a/backend/src/main/scala/com/softwaremill/bootzooka/infrastructure/DB.scala +++ b/backend/src/main/scala/com/softwaremill/bootzooka/infrastructure/DB.scala @@ -9,9 +9,9 @@ import org.flywaydb.core.Flyway import scala.concurrent.duration._ import Doobie._ import com.softwaremill.bootzooka.config.Sensitive +import com.softwaremill.macwire.autocats.autowire import scala.concurrent.ExecutionContext -import com.softwaremill.macwire.autocats._ /** Configures the database, setting up the connection pool and performing migrations. */ diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/infrastructure/InfrastructureModule.scala b/backend/src/main/scala/com/softwaremill/bootzooka/infrastructure/InfrastructureModule.scala deleted file mode 100644 index 7609936d9..000000000 --- a/backend/src/main/scala/com/softwaremill/bootzooka/infrastructure/InfrastructureModule.scala +++ /dev/null @@ -1,14 +0,0 @@ -package com.softwaremill.bootzooka.infrastructure - -import cats.effect.IO -import sttp.client3.SttpBackend -import sttp.client3.prometheus.PrometheusBackend -import sttp.client3.logging.slf4j.Slf4jLoggingBackend - -trait InfrastructureModule { - implicit lazy val sttpBackend: SttpBackend[IO, Any] = new SetCorrelationIdBackend( - Slf4jLoggingBackend(PrometheusBackend(baseSttpBackend), includeTiming = true) - ) - - def baseSttpBackend: SttpBackend[IO, Any] -} diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/metrics/MetricsModule.scala b/backend/src/main/scala/com/softwaremill/bootzooka/metrics/MetricsModule.scala deleted file mode 100644 index ff994b179..000000000 --- a/backend/src/main/scala/com/softwaremill/bootzooka/metrics/MetricsModule.scala +++ /dev/null @@ -1,12 +0,0 @@ -package com.softwaremill.bootzooka.metrics - -import com.softwaremill.bootzooka.http.Http -import io.prometheus.client.CollectorRegistry - -trait MetricsModule { - lazy val metricsApi = new MetricsApi(http, collectorRegistry) - lazy val versionApi = new VersionApi(http) - lazy val collectorRegistry: CollectorRegistry = CollectorRegistry.defaultRegistry - - def http: Http -} diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/passwordreset/PasswordResetModule.scala b/backend/src/main/scala/com/softwaremill/bootzooka/passwordreset/PasswordResetModule.scala deleted file mode 100644 index 6d59313a9..000000000 --- a/backend/src/main/scala/com/softwaremill/bootzooka/passwordreset/PasswordResetModule.scala +++ /dev/null @@ -1,25 +0,0 @@ -package com.softwaremill.bootzooka.passwordreset - -import cats.effect.IO -import com.softwaremill.bootzooka.email.{EmailScheduler, EmailTemplates} -import com.softwaremill.bootzooka.http.Http -import com.softwaremill.bootzooka.security.Auth -import com.softwaremill.bootzooka.user.UserModel -import com.softwaremill.bootzooka.util.BaseModule -import com.softwaremill.bootzooka.infrastructure.Doobie._ -import com.softwaremill.macwire._ - -trait PasswordResetModule extends BaseModule { - lazy val passwordResetCodeModel = new PasswordResetCodeModel - private lazy val passwordResetConfig = config.passwordReset - lazy val passwordResetService = wire[PasswordResetService] - - lazy val passwordResetApi = wire[PasswordResetApi] - - def userModel: UserModel - def http: Http - def passwordResetCodeAuth: Auth[PasswordResetCode] - def emailScheduler: EmailScheduler - def emailTemplates: EmailTemplates - def xa: Transactor[IO] -} diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/security/SecurityModule.scala b/backend/src/main/scala/com/softwaremill/bootzooka/security/SecurityModule.scala deleted file mode 100644 index 6504ebf1e..000000000 --- a/backend/src/main/scala/com/softwaremill/bootzooka/security/SecurityModule.scala +++ /dev/null @@ -1,17 +0,0 @@ -package com.softwaremill.bootzooka.security - -import cats.effect.IO -import com.softwaremill.bootzooka.passwordreset.{PasswordResetAuthToken, PasswordResetCode, PasswordResetCodeModel} -import com.softwaremill.bootzooka.util.BaseModule -import com.softwaremill.macwire._ -import doobie.util.transactor.Transactor - -trait SecurityModule extends BaseModule { - lazy val apiKeyModel = new ApiKeyModel - lazy val apiKeyService = wire[ApiKeyService] - lazy val apiKeyAuth: Auth[ApiKey] = new Auth(new ApiKeyAuthToken(apiKeyModel), xa, clock) - lazy val passwordResetCodeAuth: Auth[PasswordResetCode] = new Auth(new PasswordResetAuthToken(passwordResetCodeModel), xa, clock) - - def passwordResetCodeModel: PasswordResetCodeModel - def xa: Transactor[IO] -} diff --git a/backend/src/main/scala/com/softwaremill/bootzooka/user/UserModule.scala b/backend/src/main/scala/com/softwaremill/bootzooka/user/UserModule.scala deleted file mode 100644 index c693bff84..000000000 --- a/backend/src/main/scala/com/softwaremill/bootzooka/user/UserModule.scala +++ /dev/null @@ -1,24 +0,0 @@ -package com.softwaremill.bootzooka.user - -import cats.effect.IO -import com.softwaremill.bootzooka.email.{EmailScheduler, EmailTemplates} -import com.softwaremill.bootzooka.http.Http -import com.softwaremill.bootzooka.security.{ApiKey, ApiKeyService, Auth} -import com.softwaremill.bootzooka.util.BaseModule -import com.softwaremill.macwire._ -import doobie.util.transactor.Transactor - -trait UserModule extends BaseModule { - lazy val userModel = new UserModel - private lazy val userConfig = config.user - - lazy val userService = wire[UserService] - lazy val userApi = wire[UserApi] - - def http: Http - def apiKeyAuth: Auth[ApiKey] - def emailScheduler: EmailScheduler - def emailTemplates: EmailTemplates - def apiKeyService: ApiKeyService - def xa: Transactor[IO] -} diff --git a/backend/src/test/scala/com/softwaremill/bootzooka/passwordreset/PasswordResetApiTest.scala b/backend/src/test/scala/com/softwaremill/bootzooka/passwordreset/PasswordResetApiTest.scala index fa2d21999..833f35b41 100644 --- a/backend/src/test/scala/com/softwaremill/bootzooka/passwordreset/PasswordResetApiTest.scala +++ b/backend/src/test/scala/com/softwaremill/bootzooka/passwordreset/PasswordResetApiTest.scala @@ -1,33 +1,16 @@ package com.softwaremill.bootzooka.passwordreset import cats.effect.IO -import com.softwaremill.bootzooka.MainModule -import com.softwaremill.bootzooka.config.Config import com.softwaremill.bootzooka.email.sender.DummyEmailSender -import com.softwaremill.bootzooka.infrastructure.Doobie._ import com.softwaremill.bootzooka.infrastructure.Json._ -import com.softwaremill.bootzooka.passwordreset.PasswordResetApi.{ - ForgotPassword_IN, - ForgotPassword_OUT, - PasswordReset_IN, - PasswordReset_OUT -} -import com.softwaremill.bootzooka.test.{BaseTest, Requests, TestConfig, TestEmbeddedPostgres} +import com.softwaremill.bootzooka.passwordreset.PasswordResetApi.{ForgotPassword_IN, ForgotPassword_OUT, PasswordReset_IN, PasswordReset_OUT} +import com.softwaremill.bootzooka.test.{AppDependencies, BaseTest, Requests} import org.http4s._ import org.http4s.syntax.all._ import org.scalatest.concurrent.Eventually -import sttp.client3.SttpBackend -import sttp.client3.asynchttpclient.fs2.AsyncHttpClientFs2Backend - -class PasswordResetApiTest extends BaseTest with TestEmbeddedPostgres with Eventually { - lazy val modules: MainModule = new MainModule { - override def xa: Transactor[IO] = currentDb.xa - - override lazy val baseSttpBackend: SttpBackend[IO, Any] = AsyncHttpClientFs2Backend.stub[IO] - override lazy val config: Config = TestConfig - } - val requests = new Requests(modules) +class PasswordResetApiTest extends BaseTest with Eventually with AppDependencies { + val requests = new Requests(httpApi) import requests._ @@ -110,18 +93,18 @@ class PasswordResetApiTest extends BaseTest with TestEmbeddedPostgres with Event val request = Request[IO](method = POST, uri = uri"/passwordreset/forgot") .withEntity(ForgotPassword_IN(loginOrEmail)) - modules.httpApi.mainRoutes(request).unwrap + httpApi.mainRoutes(request).unwrap } def resetPassword(code: String, password: String): Response[IO] = { val request = Request[IO](method = POST, uri = uri"/passwordreset/reset") .withEntity(PasswordReset_IN(code, password)) - modules.httpApi.mainRoutes(request).unwrap + httpApi.mainRoutes(request).unwrap } def codeSentToEmail(email: String): String = { - modules.emailService.sendBatch().unwrap + emailService.sendBatch().unwrap val emailData = DummyEmailSender .findSentEmail(email, "SoftwareMill Bootzooka password reset") @@ -132,7 +115,7 @@ class PasswordResetApiTest extends BaseTest with TestEmbeddedPostgres with Event } def codeWasNotSentToEmail(email: String): Unit = { - modules.emailService.sendBatch().unwrap + emailService.sendBatch().unwrap val maybeEmail = DummyEmailSender.findSentEmail(email, "SoftwareMill Bootzooka password reset") maybeEmail match { diff --git a/backend/src/test/scala/com/softwaremill/bootzooka/test/AppDependencies.scala b/backend/src/test/scala/com/softwaremill/bootzooka/test/AppDependencies.scala new file mode 100644 index 000000000..3f751e5ba --- /dev/null +++ b/backend/src/test/scala/com/softwaremill/bootzooka/test/AppDependencies.scala @@ -0,0 +1,32 @@ +package com.softwaremill.bootzooka.test + +import cats.effect.{IO, Resource} +import com.softwaremill.bootzooka.DependenciesFactory +import com.softwaremill.bootzooka.email.EmailService +import com.softwaremill.bootzooka.http.HttpApi +import org.scalatest.{BeforeAndAfterAll, Suite} +import sttp.client3.asynchttpclient.fs2.AsyncHttpClientFs2Backend + +trait AppDependencies extends BeforeAndAfterAll with TestEmbeddedPostgres { self: Suite with BaseTest => + + var httpApi: HttpApi = _ + var emailService: EmailService = _ + + override protected def beforeAll(): Unit = { + super.beforeAll() + + val deps = { + import cats.effect.unsafe.implicits.global + + DependenciesFactory.resource( + config = TestConfig, + sttpBackend = Resource.pure(AsyncHttpClientFs2Backend.stub[IO]), + xa = Resource.pure(currentDb.xa), + clock = testClock + ).allocated.unsafeRunSync()._1 + } + + httpApi = deps._1 + emailService = deps._2 + } +} diff --git a/backend/src/test/scala/com/softwaremill/bootzooka/test/HttpTestSupport.scala b/backend/src/test/scala/com/softwaremill/bootzooka/test/HttpTestSupport.scala index 0c74a392f..fcf5148b7 100644 --- a/backend/src/test/scala/com/softwaremill/bootzooka/test/HttpTestSupport.scala +++ b/backend/src/test/scala/com/softwaremill/bootzooka/test/HttpTestSupport.scala @@ -3,7 +3,6 @@ package com.softwaremill.bootzooka.test import cats.data.OptionT import cats.effect.kernel.Concurrent import cats.effect.{IO, Sync} -import com.softwaremill.bootzooka.MainModule import com.softwaremill.bootzooka.http.Error_OUT import com.softwaremill.bootzooka.infrastructure.Json._ import io.circe.{Decoder, Encoder} @@ -20,8 +19,6 @@ import scala.reflect.ClassTag trait HttpTestSupport extends Http4sDsl[IO] with Matchers { - val modules: MainModule - // in tests we are using the http4s client, hence we need http4s entity encoders/decoders to send/receive data implicit def entityEncoderFromCirce[F[_]: Sync, T: Encoder]: EntityEncoder[F, T] = { org.http4s.circe.jsonEncoderWithPrinterOf[F, T](noNullsPrinter) diff --git a/backend/src/test/scala/com/softwaremill/bootzooka/test/Requests.scala b/backend/src/test/scala/com/softwaremill/bootzooka/test/Requests.scala index afd55a60e..55c16d9ad 100644 --- a/backend/src/test/scala/com/softwaremill/bootzooka/test/Requests.scala +++ b/backend/src/test/scala/com/softwaremill/bootzooka/test/Requests.scala @@ -1,7 +1,7 @@ package com.softwaremill.bootzooka.test import cats.effect.IO -import com.softwaremill.bootzooka.MainModule +import com.softwaremill.bootzooka.http.HttpApi import com.softwaremill.bootzooka.infrastructure.Json._ import com.softwaremill.bootzooka.user.UserApi._ import org.http4s._ @@ -9,8 +9,7 @@ import org.http4s.syntax.all._ import scala.util.Random -class Requests(val modules: MainModule) extends HttpTestSupport { - +class Requests(httpApi: => HttpApi) extends HttpTestSupport { case class RegisteredUser(login: String, email: String, password: String, apiKey: String) private val random = new Random() @@ -22,7 +21,7 @@ class Requests(val modules: MainModule) extends HttpTestSupport { val request = Request[IO](method = POST, uri = uri"/user/register") .withEntity(Register_IN(login, email, password)) - modules.httpApi.mainRoutes(request).unwrap + httpApi.mainRoutes(request).unwrap } def newRegisteredUsed(): RegisteredUser = { @@ -35,26 +34,26 @@ class Requests(val modules: MainModule) extends HttpTestSupport { val request = Request[IO](method = POST, uri = uri"/user/login") .withEntity(Login_IN(loginOrEmail, password, apiKeyValidHours)) - modules.httpApi.mainRoutes(request).unwrap + httpApi.mainRoutes(request).unwrap } def getUser(apiKey: String): Response[IO] = { val request = Request[IO](method = GET, uri = uri"/user") - modules.httpApi.mainRoutes(authorizedRequest(apiKey, request)).unwrap + httpApi.mainRoutes(authorizedRequest(apiKey, request)).unwrap } def changePassword(apiKey: String, password: String, newPassword: String): Response[IO] = { val request = Request[IO](method = POST, uri = uri"/user/changepassword") .withEntity(ChangePassword_IN(password, newPassword)) - modules.httpApi.mainRoutes(authorizedRequest(apiKey, request)).unwrap + httpApi.mainRoutes(authorizedRequest(apiKey, request)).unwrap } def updateUser(apiKey: String, login: String, email: String): Response[IO] = { val request = Request[IO](method = POST, uri = uri"/user") .withEntity(UpdateUser_IN(login, email)) - modules.httpApi.mainRoutes(authorizedRequest(apiKey, request)).unwrap + httpApi.mainRoutes(authorizedRequest(apiKey, request)).unwrap } } diff --git a/backend/src/test/scala/com/softwaremill/bootzooka/user/UserApiTest.scala b/backend/src/test/scala/com/softwaremill/bootzooka/user/UserApiTest.scala index 7d48adf4c..20ffccf05 100644 --- a/backend/src/test/scala/com/softwaremill/bootzooka/user/UserApiTest.scala +++ b/backend/src/test/scala/com/softwaremill/bootzooka/user/UserApiTest.scala @@ -1,31 +1,17 @@ package com.softwaremill.bootzooka.user -import cats.effect.IO -import com.softwaremill.bootzooka.MainModule -import com.softwaremill.bootzooka.config.Config import com.softwaremill.bootzooka.email.sender.DummyEmailSender -import com.softwaremill.bootzooka.infrastructure.Doobie._ import com.softwaremill.bootzooka.infrastructure.Json._ -import com.softwaremill.bootzooka.test.{BaseTest, Requests, TestConfig, TestEmbeddedPostgres} +import com.softwaremill.bootzooka.test.{BaseTest, AppDependencies, Requests} import com.softwaremill.bootzooka.user.UserApi._ -import com.softwaremill.bootzooka.util.Clock import org.http4s.Status import org.scalatest.concurrent.Eventually -import sttp.client3.SttpBackend -import sttp.client3.asynchttpclient.fs2.AsyncHttpClientFs2Backend import scala.concurrent.duration._ -class UserApiTest extends BaseTest with TestEmbeddedPostgres with Eventually { +class UserApiTest extends BaseTest with Eventually with AppDependencies { + val requests = new Requests(httpApi) - lazy val modules: MainModule = new MainModule { - override def xa: Transactor[IO] = currentDb.xa - override lazy val baseSttpBackend: SttpBackend[IO, Any] = AsyncHttpClientFs2Backend.stub[IO] - override lazy val config: Config = TestConfig - override lazy val clock: Clock = testClock - } - - val requests = new Requests(modules) import requests._ "/user/register" should "register" in { @@ -94,7 +80,7 @@ class UserApiTest extends BaseTest with TestEmbeddedPostgres with Eventually { val RegisteredUser(login, email, _, _) = newRegisteredUsed() // then - modules.emailService.sendBatch().unwrap + emailService.sendBatch().unwrap DummyEmailSender.findSentEmail(email, s"registration confirmation for user $login").isDefined shouldBe true }