Skip to content

Commit

Permalink
Merge pull request #44 from narrative-io/samidalouche/sc-25633/update…
Browse files Browse the repository at this point in the history
…-typelevel-ecosystem-cats-effect-3

Update to CE3
  • Loading branch information
samidalouche authored Sep 4, 2023
2 parents c9a7d72 + 683a6e2 commit 60c40f7
Show file tree
Hide file tree
Showing 18 changed files with 168 additions and 191 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.narrative.connectors.facebook

import cats.effect.{Blocker, ContextShift, IO, Resource}
import cats.effect.{IO, Resource}
import com.amazonaws.auth.{AWSCredentialsProvider, DefaultAWSCredentialsProviderChain}
import com.amazonaws.regions.Regions
import com.amazonaws.services.kms.{AWSKMS, AWSKMSClientBuilder}
Expand All @@ -11,39 +11,31 @@ import doobie.Transactor
import doobie.hikari.HikariTransactor
import doobie.util.ExecutionContexts
import io.narrative.common.ssm.SSMResources
import org.http4s.blaze.client.BlazeClientBuilder
import org.http4s.ember.client.EmberClientBuilder
import org.http4s.client.Client

import scala.concurrent.ExecutionContext

final case class Resources(
awsCredentials: AWSCredentialsProvider,
blocker: Blocker,
client: Client[IO],
kms: AWSKMS,
serverEC: ExecutionContext,
ssm: AWSSimpleSystemsManagement,
xa: Transactor[IO]
) {
def resolve(value: Config.Value)(implicit contextShift: ContextShift[IO]): IO[String] =
Resources.resolve(blocker, ssm, value)
def resolve(value: Config.Value): IO[String] =
Resources.resolve(ssm, value)
}
object Resources extends LazyLogging {
def apply(config: Config)(implicit contextShift: ContextShift[IO]): Resource[IO, Resources] =
def apply(config: Config): Resource[IO, Resources] =
for {
blocker <- Blocker[IO]
awsCredentials = new DefaultAWSCredentialsProviderChain()
client <- BlazeClientBuilder[IO](blocker.blockingContext).resource
serverEC <- ExecutionContexts.fixedThreadPool[IO](64)
ssm <- SSMResources.ssmClient(logger.underlying, blocker, awsCredentials)
awsCredentials <- Resource.eval(IO.blocking(new DefaultAWSCredentialsProviderChain()))
client <- EmberClientBuilder.default[IO].build
ssm <- SSMResources.ssmClient(logger.underlying, awsCredentials)
kms <- awsKms(awsCredentials)
xa <- transactor(blocker, ssm, config.database)
xa <- transactor(ssm, config.database)
} yield Resources(
awsCredentials = awsCredentials,
blocker = blocker,
client = client,
kms = kms,
serverEC = serverEC,
ssm = ssm,
xa = xa
)
Expand All @@ -53,15 +45,12 @@ object Resources extends LazyLogging {
IO(AWSKMSClientBuilder.standard().withRegion(Regions.US_EAST_1).withCredentials(awsCredentials).build())
)(kms => IO(kms.shutdown()))

def transactor(blocker: Blocker, ssm: AWSSimpleSystemsManagement, db: Config.Database)(implicit
contextShift: ContextShift[IO]
): Resource[IO, Transactor[IO]] = for {
username <- Resource.eval(resolve(blocker, ssm, db.username))
password <- Resource.eval(resolve(blocker, ssm, db.password))
jdbcUrl <- Resource.eval(resolve(blocker, ssm, db.jdbcUrl))
def transactor(ssm: AWSSimpleSystemsManagement, db: Config.Database): Resource[IO, Transactor[IO]] = for {
username <- Resource.eval(resolve(ssm, db.username))
password <- Resource.eval(resolve(ssm, db.password))
jdbcUrl <- Resource.eval(resolve(ssm, db.jdbcUrl))
connectEC <- ExecutionContexts.fixedThreadPool[IO](32)
xa <- HikariTransactor.newHikariTransactor[IO](
blocker = blocker,
connectEC = connectEC,
driverClassName = "org.postgresql.Driver",
pass = password,
Expand All @@ -70,15 +59,12 @@ object Resources extends LazyLogging {
)
} yield xa

def resolve(blocker: Blocker, ssm: AWSSimpleSystemsManagement, value: Config.Value)(implicit
contextShift: ContextShift[IO]
): IO[String] =
def resolve(ssm: AWSSimpleSystemsManagement, value: Config.Value): IO[String] =
value match {
case Config.Literal(value) =>
IO.pure(value)
case Config.SsmParam(value, encrypted) =>
blocker
.blockOn(IO(ssm.getParameter(new GetParameterRequest().withName(value).withWithDecryption(encrypted))))
IO.blocking(ssm.getParameter(new GetParameterRequest().withName(value).withWithDecryption(encrypted)))
.map(_.getParameter.getValue)
.attempt
.map {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.narrative.connectors.facebook

import cats.effect.{IO, IOApp, Resource}
import com.comcast.ip4s.{Host, Port}
import com.typesafe.scalalogging.LazyLogging
import io.narrative.connectors.facebook.routes.Logging
import io.narrative.connectors.facebook.routes.profiles.ProfileRoutes
Expand All @@ -15,7 +16,7 @@ import io.narrative.connectors.facebook.services.{
}
import io.narrative.connectors.facebook.stores.ProfileStore
import org.http4s.{HttpApp, Uri}
import org.http4s.blaze.server.BlazeServerBuilder
import org.http4s.ember.server.EmberServerBuilder
import org.http4s.server.Router
import org.http4s.server.middleware.CORS

Expand All @@ -25,10 +26,20 @@ object Server extends IOApp.Simple with LazyLogging {
config <- Resource.eval(Config())
resources <- Resources(config)
routes <- Resource.eval(router(config, resources))
server <- BlazeServerBuilder[IO](resources.serverEC)
.bindHttp(config.server.port.value.toInt, "0.0.0.0")
server <- EmberServerBuilder
.default[IO]
.withHost(
Host
.fromString("0.0.0.0")
.getOrElse(throw new RuntimeException("Programming error: host 0.0.0.0 is not valid"))
)
.withPort(
Port
.fromString(config.server.port.value)
.getOrElse(throw new RuntimeException(s"Programming error: port ${config.server.port.value} is not valid"))
)
.withHttpApp(routes)
.resource
.build
} yield server

server.use(_ => IO.never).void
Expand All @@ -41,11 +52,11 @@ object Server extends IOApp.Simple with LazyLogging {
kmsKeyId <- resources.resolve(config.kms.tokenEncryptionKeyId).map(KmsKeyId.apply)
apiBaseUri <- resources.resolve(config.narrativeApi.baseUri).map(Uri.unsafeFromString)
apiClient = new ApiClient(baseUri = apiBaseUri, client = resources.client)
fbClient = new FacebookClient(FacebookApp(appId, appSecret), blocker = resources.blocker)
fbClient = new FacebookClient(FacebookApp(appId, appSecret))
profileService = new ProfileService(
apiClient,
fbClient,
new TokenEncryptionService(resources.blocker, kmsKeyId, resources.kms),
new TokenEncryptionService(kmsKeyId, resources.kms),
ProfileStore(resources.xa)
)
http = CORS.policy.withAllowCredentials(false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package io.narrative.connectors.facebook.routes

import cats.data.{Kleisli, OptionT}
import cats.effect.IO
import cats.syntax.applicativeError._
import com.typesafe.scalalogging.LazyLogging
import io.narrative.connectors.facebook.services.BearerToken
import org.http4s.client.UnexpectedStatus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.http4s.{HttpApp, Response}
import org.http4s.server.middleware._

object Logging extends LazyLogging {
def apply(routes: HttpApp[IO])(implicit cs: ContextShift[IO]): HttpApp[IO] = {
def apply(routes: HttpApp[IO]): HttpApp[IO] = {
// The order that these middleware are stacked matters. Any other combination and the X-Request-ID header won't
// propagate to the response when the server throws an exception.
errorResponseLogging {
Expand All @@ -28,7 +28,7 @@ object Logging extends LazyLogging {
resp <- service(req)
_ <- resp.status.code match {
case code if code >= 400 =>
org.http4s.internal.Logger.logMessageWithBodyText[IO, Response[IO]](resp)(
org.http4s.internal.Logger.logMessageWithBodyText[IO](resp)(
logHeaders = true,
logBodyText = truncateBody(32768) _
)(logWarn)
Expand All @@ -40,5 +40,5 @@ object Logging extends LazyLogging {
}

private def truncateBody(maxBytes: Int)(bytes: fs2.Stream[IO, Byte]): Option[IO[String]] =
bytes.take(maxBytes.toLong).through(fs2.text.utf8Decode).compile.last.map(_.getOrElse("")).some
bytes.take(maxBytes.toLong).through(fs2.text.utf8.decode).compile.last.map(_.getOrElse("")).some
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.narrative.connectors.facebook.routes.profiles

import cats.data.{EitherT, OptionT}
import cats.effect.{ContextShift, IO}
import cats.effect.IO
import cats.syntax.show._
import com.typesafe.scalalogging.LazyLogging
import io.narrative.connectors.facebook.domain.Profile
Expand All @@ -11,9 +11,7 @@ import org.http4s._
import org.http4s.circe.CirceEntityCodec._
import org.http4s.dsl.io._

class ProfileRoutes(service: ProfileService.Ops[IO])(implicit
contextShift: ContextShift[IO]
) extends LazyLogging {
class ProfileRoutes(service: ProfileService.Ops[IO]) extends LazyLogging {

val routes: HttpRoutes[IO] = Auth.auth {
case GET -> Root as auth => profiles(auth)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.narrative.connectors.facebook.routes.tokens

import cats.effect.{ContextShift, IO}
import cats.effect.IO
import com.typesafe.scalalogging.LazyLogging
import io.narrative.connectors.facebook.routes.Auth
import io.narrative.connectors.facebook.services.{ProfileService, TokenMetaRequest}
import org.http4s._
import org.http4s.circe.CirceEntityCodec._
import org.http4s.dsl.io._

class TokenRoutes(service: ProfileService.Ops[IO])(implicit contextShift: ContextShift[IO]) extends LazyLogging {
class TokenRoutes(service: ProfileService.Ops[IO]) extends LazyLogging {

val routes: HttpRoutes[IO] = Auth.noauth { case req @ POST -> Root / "metadata" =>
tokenMeta(req)
Expand Down
5 changes: 3 additions & 2 deletions backend/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ ThisBuild / publishTo := {
}
ThisBuild / exportPipelining := false
ThisBuild / usePipelining := true
//ThisBuild / scalacOptions += "-P:semanticdb:synthetics:on"

name := "narrative-facebook-connector"

Expand Down Expand Up @@ -75,7 +76,7 @@ lazy val `api` = project
Http4s.`http4s-dsl`,
Http4s.`http4s-circe`,
Http4s.`http4s-server`,
Http4s.`http4s-blaze-server`,
Http4s.`http4s-ember-server`,
NarrativeBackend.`narrative-common-ssm`,
NarrativeBackend.`narrative-microframework-config`,
ScalaTest.`scalatest` % "test"
Expand Down Expand Up @@ -127,7 +128,7 @@ lazy val `services` = project
libraryDependencies ++= Seq(
Aws.`aws-java-sdk-kms`,
Facebook.`facebook-java-business-sdk`,
Http4s.`http4s-blaze-client`,
Http4s.`http4s-ember-client`,
Http4s.`http4s-circe`,
NarrativeBackend.`narrative-common-catsretry`,
NarrativeBackend.`narrative-microframework-config`,
Expand Down
27 changes: 14 additions & 13 deletions backend/project/LibraryDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import sbt._

object LibraryDependencies {
object NarrativeBackend {
val version = "20.0.1"
val version = "20.0.21"
val `narrative-common-ssm` = "io.narrative" %% "common-ssm" % version
val `narrative-common-catsretry` = "io.narrative" %% "common-catsretry" % version
val `narrative-common-doobie-testkit` = "io.narrative" %% "common-doobie-testkit" % version
Expand All @@ -15,7 +15,7 @@ object LibraryDependencies {
}

object NarrativeConnectorFramework {
val version = "0.1.0"
val version = "0.1.1"

val `connector-framework-core` =
"io.narrative" %% "connector-framework-core" % version
Expand All @@ -29,31 +29,32 @@ object LibraryDependencies {
}

object Cats {
val `cats-core` = "org.typelevel" %% "cats-core" % "2.8.0"
val `cats-effect` = "org.typelevel" %% "cats-effect" % "2.5.4"
val `cats-core` = "org.typelevel" %% "cats-core" % "2.9.0"
val `cats-effect` = "org.typelevel" %% "cats-effect" % "3.5.1"
}

object CatsRetry {
val version = "2.1.1"
val version = "3.1.0"
val `cats-retry` = "com.github.cb372" %% "cats-retry" % version
}

object Circe {
val version = "0.14.2"
val version = "0.14.5"
val `circe-core` = "io.circe" %% "circe-core" % version
val `circe-generic` = "io.circe" %% "circe-generic" % version
val `circe-generic-extras` = "io.circe" %% "circe-generic-extras" % version
val `circe-generic-extras` = "io.circe" %% "circe-generic-extras" % "0.14.3"
val `circe-literal` = "io.circe" %% "circe-literal" % version
val `circe-parser` = "io.circe" %% "circe-parser" % version
}

object CirceFs2 {
val version = "0.13.0"
// TODO: migrate to https://fs2-data.gnieh.org/documentation/json/libraries/#circe
val version = "0.14.1"
val `circe-fs2` = "io.circe" %% "circe-fs2" % version
}

object Doobie {
val version = "0.9.0"
val version = "1.0.0-RC4"
val `doobie-core` = "org.tpolecat" %% "doobie-core" % version
val `doobie-postgres` = "org.tpolecat" %% "doobie-postgres" % version
val `doobie-hikari` = "org.tpolecat" %% "doobie-hikari" % version
Expand All @@ -65,19 +66,19 @@ object LibraryDependencies {
}

object Fs2 {
val version = "2.4.4"
val version = "3.7.0"
val `fs2-core` = "co.fs2" %% "fs2-core" % version
val `fs2-io` = "co.fs2" %% "fs2-io" % version
}

object Http4s {
val version = "0.22.7"
val version = "0.23.22"
val `http4s-core` = "org.http4s" %% "http4s-dsl" % version
val `http4s-dsl` = "org.http4s" %% "http4s-dsl" % version
val `http4s-circe` = "org.http4s" %% "http4s-circe" % version
val `http4s-server` = "org.http4s" %% "http4s-server" % version
val `http4s-blaze-server` = "org.http4s" %% "http4s-blaze-server" % version
val `http4s-blaze-client` = "org.http4s" %% "http4s-blaze-client" % version
val `http4s-ember-server` = "org.http4s" %% "http4s-ember-server" % version
val `http4s-ember-client` = "org.http4s" %% "http4s-ember-client" % version
}

object Jackson {
Expand Down
1 change: 1 addition & 0 deletions backend/project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ addSbtPlugin("io.narrative" % "common-build" % "4.0.1")
addDependencyTreePlugin

addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.11.0")
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package io.narrative.connectors.facebook.services

import cats.{Eq, Show}
import cats.effect.{ContextShift, IO}
import cats.syntax.applicativeError._
import cats.syntax.show._
import cats.effect.IO
import cats.implicits._
import io.circe.{Decoder, Encoder}
import io.circe.generic.extras.semiauto.{deriveConfiguredDecoder, deriveConfiguredEncoder}
import org.http4s.circe.CirceEntityCodec._
Expand All @@ -13,7 +12,7 @@ import org.http4s.headers.Authorization
import io.narrative.connectors.facebook.domain.Profile

/** todo(mbabic) scaladocs */
class ApiClient(baseUri: Uri, client: Client[IO])(implicit contextShift: ContextShift[IO]) extends ApiClient.Ops[IO] {
class ApiClient(baseUri: Uri, client: Client[IO]) extends ApiClient.Ops[IO] {
import ApiClient._

override def company(auth: BearerToken): IO[ApiCompany] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package io.narrative.connectors.facebook.services

import cats.{Eq, Show}
import cats.effect.IO
import cats.effect.concurrent.Ref
import cats.syntax.applicativeError._
import cats.syntax.option._
import cats.implicits._
import com.typesafe.scalalogging.LazyLogging
import fs2.Stream
import io.circe.{Decoder, Encoder}
Expand All @@ -14,6 +12,7 @@ import org.http4s.{AuthScheme, BasicCredentials, Credentials, Method, Request, S
import org.http4s.client.{Client, UnexpectedStatus}
import org.http4s.circe.CirceEntityCodec._
import org.http4s.headers.Authorization
import cats.effect.Ref

/** todo(mbabic) scaladocs */
class AppApiClient private (
Expand Down
Loading

0 comments on commit 60c40f7

Please sign in to comment.