Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding client functionality #10

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions .scalafix.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
OrganizeImports {
groupedImports = Merge
coalesceToWildcardImportThreshold = 3
groups = ["re:javax?\\.", "scala.", "org.", "cats.", "sttp.", "io.", "skunk.", "pureconfig.", "monocle."]
removeUnused = true
blankLines = Auto
coalesceToWildcardImportThreshold = 5
expandRelative = false
groupExplicitlyImportedImplicitsSeparately = false
groupedImports = Merge
groups = ["re:javax?\\.", "scala.", "org.", "cats.", "sttp.", "io.", "skunk.", "pureconfig.", "monocle."]
importSelectorsOrder = Ascii
importsOrder = Ascii
preset = INTELLIJ_2020_3
removeUnused = true
}
60 changes: 34 additions & 26 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ThisBuild / version := "1.0.0"

ThisBuild / scalaVersion := "2.13.8"
ThisBuild / scalaVersion := "2.13.9"

ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.6.0"

Expand All @@ -9,22 +9,20 @@ val scalafixCommonSettings =

val Versions =
new {
val tapir = "1.0.0"
val log4jcats = "2.4.0"
val circe = "0.14.1"
val tapir = "1.1.2"
val log4cats = "2.5.0"
val circe = "0.14.2"
val http4s = "0.23.12"
val http4sCirce = "0.23.16"
}

lazy val commonSettings: Seq[Setting[_]] = Seq(
scalacOptions -= "-Xfatal-warnings",
// scalafixCommonSettings,
libraryDependencies ++= Seq(
"ch.qos.logback" % "logback-classic" % "1.2.11",
"org.typelevel" %% "log4cats-slf4j" % Versions.log4jcats,
"org.typelevel" %% "log4cats-noop" % Versions.log4jcats,
// "org.apache.logging.log4j" % "log4j-core" % Versions.log4j,
// "org.apache.logging.log4j" % "log4j-api" % Versions.log4j,
// "org.apache.logging.log4j" % "log4j-slf4j-impl" % Versions.log4j,
// "org.typelevel" %% "log4cats-core" % "2.4.0",
"ch.qos.logback" % "logback-classic" % "1.4.3",
"org.typelevel" %% "log4cats-slf4j" % Versions.log4cats,
"org.typelevel" %% "log4cats-noop" % Versions.log4cats,
"com.github.liancheng" %% "organize-imports" % "0.6.0"
)
)
Expand All @@ -45,8 +43,8 @@ lazy val database =
commonSettings,
// scalafixCommonSettings,
libraryDependencies ++= Seq(
"org.tpolecat" %% "skunk-core" % "0.3.1",
"org.tpolecat" %% "skunk-circe" % "0.3.1"
"org.tpolecat" %% "skunk-core" % "0.3.2",
"org.tpolecat" %% "skunk-circe" % "0.3.2"
)
)
.dependsOn(models, configs)
Expand All @@ -57,14 +55,26 @@ lazy val models =
commonSettings,
// scalafixCommonSettings,
libraryDependencies ++= Seq(
"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % Versions.tapir,
"io.circe" %% "circe-generic-extras" % Versions.circe,
"dev.optics" %% "monocle-core" % "3.1.0"
)
)

lazy val includeTestandIt = "it,test"

lazy val client =
project
.settings(
commonSettings,
libraryDependencies ++= Seq(
"org.http4s" %% "http4s-blaze-client" % Versions.http4s,
"io.circe" %% "circe-generic-extras" % Versions.circe,
"com.softwaremill.sttp.tapir" %% "tapir-http4s-client" % Versions.tapir,
"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % Versions.tapir
)
)
.dependsOn(configs)

lazy val server =
project
.configs(IntegrationTest)
Expand All @@ -74,28 +84,26 @@ lazy val server =
scalafixCommonSettings,
libraryDependencies ++= Seq(
"com.softwaremill.sttp.tapir" %% "tapir-http4s-server" % Versions.tapir,
"org.http4s" %% "http4s-blaze-server" % "0.23.12",
"com.softwaremill.sttp.tapir" %% "tapir-json-circe" % Versions.tapir,
"org.http4s" %% "http4s-blaze-server" % Versions.http4s,
"org.http4s" %% "http4s-circe" % Versions.http4sCirce,
"io.circe" %% "circe-generic-extras" % Versions.circe,
"com.softwaremill.sttp.tapir" %% "tapir-sttp-stub-server" % Versions.tapir % includeTestandIt,
"org.scalatest" %% "scalatest" % "3.2.12" % includeTestandIt,
"com.softwaremill.sttp.client3" %% "async-http-client-backend-cats" % "3.6.2" % includeTestandIt,
"com.softwaremill.sttp.client3" %% "circe" % "3.6.2" % includeTestandIt,
"org.mockito" %% "mockito-scala" % "1.17.7" % Test,
"org.tpolecat" %% "skunk-core" % "0.3.1" % IntegrationTest,
"org.scalatest" %% "scalatest" % "3.2.14" % includeTestandIt,
"com.softwaremill.sttp.client3" %% "async-http-client-backend-cats" % "3.8.2" % includeTestandIt,
"com.softwaremill.sttp.client3" %% "circe" % "3.8.2" % includeTestandIt,
"org.mockito" %% "mockito-scala" % "1.17.12" % Test,
"org.tpolecat" %% "skunk-core" % "0.3.2" % IntegrationTest,
"org.typelevel" %% "cats-effect-testing-specs2" % "1.4.0" % IntegrationTest
// "org.http4s" %% "http4s-testing" % "0.21.33" % Test
// "com.softwaremill.sttp.tapir" %% "tapir-server-tests" % "1.0.0",
// "com.softwaremill.sttp.tapir" %% "tapir-testing" % "1.0.0",
// "com.softwaremill.sttp.tapir" %% "tapir-tests" % "1.0.0"
)
)
.dependsOn(models, database)
.dependsOn(models, database, client)

lazy val app =
project
.in(file("."))
.settings(publish := {}, publish / skip := true)
.aggregate(models, server, database)
.aggregate(models, server, database, client)

lazy val deleteBloop = taskKey[Unit]("Delete Existing Bloop Directory")
deleteBloop := {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.github.nullptr7
package client

case class ApiClients[F[_]](
transportServiceClient: TransportServiceClient[F]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.github.nullptr7
package client

import org.http4s._
import org.http4s.client.Client
import org.typelevel.ci.CIString
import org.typelevel.log4cats.Logger

import cats.effect.kernel.Async
import cats.implicits._

import configurations.types.ClientDetails
import exception.ServiceClientException

trait ServiceClient[F[_]] {

protected[client] val client: Client[F]

protected[client] val clientDetails: ClientDetails

// We can do unsafe here as we know this is already accepted by java.net.URI
final def sendAndReceive[Req, Res](
body: Option[Req]
)(implicit encoder: EntityEncoder[F, Req], decoder: EntityDecoder[F, Res], async: Async[F], logger: Logger[F]): F[Res] = {
val request: Request[F] = body match {
case None =>
Request[F](Method.GET, Uri.unsafeFromString(clientDetails.url.toString))
.withHeaders(Header.Raw(CIString("Authorization"), s"Bearer ${clientDetails.password.toString + clientDetails.password}"))
case Some(body) =>
Request[F](
method = Method.POST,
uri = Uri.unsafeFromString(clientDetails.url.toString),
headers = Headers(Header.Raw(CIString("x-mock-match-request-body"), "true"))
).withEntity(body)
}

client
.run(request)
.use(_.as[Res])
.handleErrorWith { t: Throwable =>
logger.error(t)("Service called failed") *> async.raiseError(ServiceClientException(t.getMessage))
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.github.nullptr7
package client

import org.http4s.client.Client

import configurations.types.TransportApiClientDetails

final class TransportServiceClient[F[_]](override val client: Client[F], override val clientDetails: TransportApiClientDetails)
extends ServiceClient[F]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.github.nullptr7
package client
package exception

sealed abstract class ClientException(msg: String) extends Exception {
override def getMessage: String = msg
}

case class ServiceClientException(msg: String) extends ClientException(msg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.github.nullptr7
package client
package module

import org.http4s.blaze.client.BlazeClientBuilder
import org.http4s.client.Client

import cats.effect.kernel.{Async, Resource}

import configurations.types.ClientConfig

sealed abstract class BlazeClientModule[F[_]] {

def make(clientConfig: ClientConfig): Resource[F, Client[F]]
}

object BlazeClientModule {

def apply[F[_]: BlazeClientModule]: BlazeClientModule[F] = implicitly

implicit def clientForAsync[F[_]: Async]: BlazeClientModule[F] =
new BlazeClientModule[F] {

override def make(clientConfig: ClientConfig): Resource[F, Client[F]] =
BlazeClientBuilder[F]
.withConnectTimeout(clientConfig.timeout)
.resource

}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.github.nullptr7
package configurations

import types.{DatabaseConfig, ServerConfig}
import types.{ClientConfig, DatabaseConfig, ServerConfig}

final case class ApplicationResources(
databaseConfig: DatabaseConfig,
serverConfig: ServerConfig
serverConfig: ServerConfig,
clientConfig: ClientConfig
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.github.nullptr7
package configurations

package types {
import scala.concurrent.duration.Duration

import java.net.URI

object types {

final case class Sensitive(value: String) extends AnyVal {
override def toString: String = "MASKED"
Expand All @@ -12,30 +16,51 @@ package types {

final case class Namespace(value: String) extends AnyVal

trait ConfigType {
sealed trait ConfigType {
val namespace: Namespace
}

object ConfigType {

implicit object Blaze extends ConfigType {
val namespace: Namespace = Namespace("server")
implicit object Server extends ConfigType {
override val namespace: Namespace = Namespace("server")
}

implicit object Postgres extends ConfigType {
val namespace: Namespace = Namespace("db")
override val namespace: Namespace = Namespace("db")
}

implicit object Client extends ConfigType {
override val namespace: Namespace = Namespace("client")
}

}

final case class ClientConfig(
timeout: Duration,
transport: TransportApiClientDetails
)

final case class ServerConfig(host: Hostname, port: Port)

final case class DatabaseConfig(
val host: Hostname,
val port: Port,
val user: String,
val database: String,
sealed trait ClientDetails {
val url: URI
val username: String
val password: Sensitive
}

final case class TransportApiClientDetails(
override val url: URI,
override val username: String,
override val password: Sensitive
) extends ClientDetails

final case class DatabaseConfig(
host: Hostname,
port: Port,
user: String,
database: String,
password: Sensitive
)

}
Loading