Skip to content

Commit

Permalink
Make Dependencies a first-class concept
Browse files Browse the repository at this point in the history
  • Loading branch information
adamw committed Jan 20, 2022
1 parent 379f80c commit 37a38bb
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import doobie.util.transactor.Transactor
import io.prometheus.client.CollectorRegistry
import sttp.client3.SttpBackend

object DependenciesFactory {
private case class Modules(api: HttpApi, emailService: EmailService)
case class Dependencies(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)] = {
object Dependencies {
def wire(config: Config, sttpBackend: Resource[IO, SttpBackend[IO, Any]], xa: Resource[IO, Transactor[IO]], clock: Clock): Resource[IO, Dependencies] = {
def buildHttpApi(http: Http, userApi: UserApi, passwordResetApi: PasswordResetApi, metricsApi: MetricsApi, versionApi: VersionApi, collectorRegistry: CollectorRegistry, cfg: HttpConfig) =
new HttpApi(
http,
Expand All @@ -28,7 +28,7 @@ object DependenciesFactory {
collectorRegistry,
cfg)

autowire[Modules](
autowire[Dependencies](
config.api,
config.user,
config.passwordReset,
Expand All @@ -43,6 +43,6 @@ object DependenciesFactory {
new EmailService(_, _, _, _, _),
EmailSender.create _,
new PasswordResetAuthToken(_),
).map(modules => (modules.api, modules.emailService))
)
}
}
27 changes: 12 additions & 15 deletions backend/src/main/scala/com/softwaremill/bootzooka/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package com.softwaremill.bootzooka
import cats.effect.unsafe.implicits.global
import cats.effect.{IO, Resource}
import com.softwaremill.bootzooka.config.Config
import com.softwaremill.bootzooka.email.EmailService
import com.softwaremill.bootzooka.http.HttpApi
import com.softwaremill.bootzooka.infrastructure.DB
import com.softwaremill.bootzooka.metrics.Metrics
import com.softwaremill.bootzooka.util.DefaultClock
Expand All @@ -31,18 +29,17 @@ object Main extends StrictLogging {

val xa = new DB(config.db).transactorResource

val mainTask = DependenciesFactory.resource(config, sttpBackend, xa, DefaultClock).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)
*/
emailService.startProcesses().void >> httpApi.resource.use(_ => IO.never)
}

mainTask.unsafeRunSync()
Dependencies
.wire(config, sttpBackend, xa, DefaultClock)
.use { case Dependencies(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)
*/
emailService.startProcesses().void >> httpApi.resource.use(_ => IO.never)
}
.unsafeRunSync()
}
}

case class Modules(emailService: EmailService, httpApi: HttpApi)
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import cats.effect.IO
import com.softwaremill.bootzooka.email.sender.DummyEmailSender
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.{AppDependencies, BaseTest, Requests}
import com.softwaremill.bootzooka.test.{TestDependencies, BaseTest, Requests}
import org.http4s._
import org.http4s.syntax.all._
import org.scalatest.concurrent.Eventually

class PasswordResetApiTest extends BaseTest with Eventually with AppDependencies {
val requests = new Requests(httpApi)

class PasswordResetApiTest extends BaseTest with Eventually with TestDependencies {
lazy val requests = new Requests(dependencies.api)
import requests._

"/passwordreset" should "reset the password" in {
Expand Down Expand Up @@ -93,18 +92,18 @@ class PasswordResetApiTest extends BaseTest with Eventually with AppDependencies
val request = Request[IO](method = POST, uri = uri"/passwordreset/forgot")
.withEntity(ForgotPassword_IN(loginOrEmail))

httpApi.mainRoutes(request).unwrap
dependencies.api.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))

httpApi.mainRoutes(request).unwrap
dependencies.api.mainRoutes(request).unwrap
}

def codeSentToEmail(email: String): String = {
emailService.sendBatch().unwrap
dependencies.emailService.sendBatch().unwrap

val emailData = DummyEmailSender
.findSentEmail(email, "SoftwareMill Bootzooka password reset")
Expand All @@ -115,7 +114,7 @@ class PasswordResetApiTest extends BaseTest with Eventually with AppDependencies
}

def codeWasNotSentToEmail(email: String): Unit = {
emailService.sendBatch().unwrap
dependencies.emailService.sendBatch().unwrap

val maybeEmail = DummyEmailSender.findSentEmail(email, "SoftwareMill Bootzooka password reset")
maybeEmail match {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,25 @@
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 com.softwaremill.bootzooka.Dependencies
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 = _
trait TestDependencies extends BeforeAndAfterAll with TestEmbeddedPostgres { self: Suite with BaseTest =>
var dependencies: Dependencies = _

override protected def beforeAll(): Unit = {
super.beforeAll()

val deps = {
dependencies = {
import cats.effect.unsafe.implicits.global

DependenciesFactory.resource(
Dependencies.wire(
config = TestConfig,
sttpBackend = Resource.pure(AsyncHttpClientFs2Backend.stub[IO]),
xa = Resource.pure(currentDb.xa),
clock = testClock
).allocated.unsafeRunSync()._1
}

httpApi = deps._1
emailService = deps._2
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ package com.softwaremill.bootzooka.user

import com.softwaremill.bootzooka.email.sender.DummyEmailSender
import com.softwaremill.bootzooka.infrastructure.Json._
import com.softwaremill.bootzooka.test.{BaseTest, AppDependencies, Requests}
import com.softwaremill.bootzooka.test.{BaseTest, TestDependencies, Requests}
import com.softwaremill.bootzooka.user.UserApi._
import org.http4s.Status
import org.scalatest.concurrent.Eventually

import scala.concurrent.duration._

class UserApiTest extends BaseTest with Eventually with AppDependencies {
val requests = new Requests(httpApi)

class UserApiTest extends BaseTest with Eventually with TestDependencies {
lazy val requests = new Requests(dependencies.api)
import requests._

"/user/register" should "register" in {
Expand Down Expand Up @@ -80,7 +79,7 @@ class UserApiTest extends BaseTest with Eventually with AppDependencies {
val RegisteredUser(login, email, _, _) = newRegisteredUsed()

// then
emailService.sendBatch().unwrap
dependencies.emailService.sendBatch().unwrap
DummyEmailSender.findSentEmail(email, s"registration confirmation for user $login").isDefined shouldBe true
}

Expand Down

0 comments on commit 37a38bb

Please sign in to comment.