From aaeddfcc4099ff5399d0c27b2cdd389898d84159 Mon Sep 17 00:00:00 2001 From: Thanh Le Date: Thu, 9 May 2024 13:55:52 +0700 Subject: [PATCH] Derive clients --- build.sbt | 50 +++++++++++-------- modules/client/src/main/scala/Client.scala | 37 ++++++++++++++ modules/client/src/main/scala/IOClient.scala | 19 ------- .../client/src/main/scala/PlayClient.scala | 42 ++++++++++++++++ project/Dependencies.scala | 7 ++- 5 files changed, 112 insertions(+), 43 deletions(-) create mode 100644 modules/client/src/main/scala/Client.scala delete mode 100644 modules/client/src/main/scala/IOClient.scala create mode 100644 modules/client/src/main/scala/PlayClient.scala diff --git a/build.sbt b/build.sbt index f48389af..4c366215 100644 --- a/build.sbt +++ b/build.sbt @@ -2,18 +2,18 @@ import org.typelevel.scalacoptions.ScalacOption import org.typelevel.scalacoptions.ScalacOptions import Dependencies.* -lazy val scala213 = "2.13.14" -lazy val scala3 = "3.4.1" +lazy val scala213 = "2.13.14" +lazy val scala3 = "3.4.1" lazy val supportedScalaVersions = List(scala213, scala3) inThisBuild( Seq( - scalaVersion := scala213, + scalaVersion := scala213, versionScheme := Some("early-semver"), - version := "3.0.0-SNAPSHOT", - run / fork := true, + version := "3.0.0-SNAPSHOT", + run / fork := true, run / javaOptions += "-Dconfig.override_with_env_vars=true", - Compile / doc / sources := Seq.empty, + Compile / doc / sources := Seq.empty, Compile / packageDoc / publishArtifact := false, Compile / packageSrc / publishArtifact := false, resolvers += "lila-maven" at "https://raw.githubusercontent.com/ornicar/lila-maven/master" @@ -32,8 +32,8 @@ lazy val core = project name := "lila-search-core", libraryDependencies ++= Seq( "com.sksamuel.elastic4s" %% "elastic4s-client-esjava" % "8.11.5", - "org.typelevel" %% "cats-core" % "2.10.0", - "joda-time" % "joda-time" % "2.12.7", + "org.typelevel" %% "cats-core" % "2.10.0", + "joda-time" % "joda-time" % "2.12.7" ) ) @@ -46,9 +46,9 @@ lazy val play = project tpolecatExcludeOptions += ScalacOptions.fatalWarnings, name := "lila-search", libraryDependencies ++= Seq( - "com.github.ornicar" %% "scalalib" % "7.1.0", - "com.typesafe.play" %% "play-json" % "2.9.4", - "com.typesafe.play" %% "play-json-joda" % "2.9.4" + "com.github.ornicar" %% "scalalib" % "7.1.0", + "com.typesafe.play" %% "play-json" % "2.9.4", + "com.typesafe.play" %% "play-json-joda" % "2.9.4" ), // Play provides two styles of routers, one expects its actions to be injected, the // other, legacy style, accesses its actions statically. @@ -59,8 +59,8 @@ lazy val play = project lazy val api = (project in file("modules/api")) .enablePlugins(Smithy4sCodegenPlugin) .settings( - scalaVersion := scala3, - name := "lila-search-api", + scalaVersion := scala3, + name := "lila-search-api", smithy4sWildcardArgument := "?", libraryDependencies ++= Seq( "com.disneystreaming.smithy4s" %% "smithy4s-core" % smithy4sVersion.value @@ -71,11 +71,14 @@ lazy val client = (project in file("modules/client")) .enablePlugins(Smithy4sCodegenPlugin) .settings( scalaVersion := scala3, - name := "lila-search-client", + name := "lila-search-client", libraryDependencies ++= Seq( - "com.disneystreaming.smithy4s" %% "smithy4s-http4s" % smithy4sVersion.value + "com.disneystreaming.smithy4s" %% "smithy4s-http4s" % smithy4sVersion.value, + "com.typesafe.play" %% "play-ahc-ws-standalone" % "2.2.7", + http4sEmberClient ) - ).dependsOn(api) + ) + .dependsOn(api) lazy val app = (project in file("modules/app")) .enablePlugins(Smithy4sCodegenPlugin) @@ -86,7 +89,7 @@ lazy val app = (project in file("modules/app")) libraryDependencies ++= Seq( "com.disneystreaming.smithy4s" %% "smithy4s-http4s" % smithy4sVersion.value, "com.disneystreaming.smithy4s" %% "smithy4s-http4s-swagger" % smithy4sVersion.value, - "com.sksamuel.elastic4s" %% "elastic4s-effect-cats" % "8.11.5", + "com.sksamuel.elastic4s" %% "elastic4s-effect-cats" % "8.11.5", catsCore, catsEffect, ducktape, @@ -96,8 +99,15 @@ lazy val app = (project in file("modules/app")) cirisHtt4s, logbackX ), - excludeDependencies ++= Seq("org.typelevel" % "cats-core_2.13", "org.typelevel" % "cats-kernel_2.13", "com.sksamuel.elastic4s" % "elastic4s-core_2.13", "com.sksamuel.elastic4s" % "elastic4s-domain_2.13", "com.sksamuel.elastic4s" % "elastic4s-http_2.13", "com.fasterxml.jackson.module" % "jackson-module-scala_2.13"), - Compile / run / fork := true, + excludeDependencies ++= Seq( + "org.typelevel" % "cats-core_2.13", + "org.typelevel" % "cats-kernel_2.13", + "com.sksamuel.elastic4s" % "elastic4s-core_2.13", + "com.sksamuel.elastic4s" % "elastic4s-domain_2.13", + "com.sksamuel.elastic4s" % "elastic4s-http_2.13", + "com.fasterxml.jackson.module" % "jackson-module-scala_2.13" + ), + Compile / run / fork := true ) .enablePlugins(JavaAppPackaging) .dependsOn(api, core) @@ -105,4 +115,4 @@ lazy val app = (project in file("modules/app")) lazy val root = project .in(file(".")) .settings(publish := {}, publish / skip := true) - .aggregate(core, play, api, app) + .aggregate(core, play, api, app, client) diff --git a/modules/client/src/main/scala/Client.scala b/modules/client/src/main/scala/Client.scala new file mode 100644 index 00000000..18bafc14 --- /dev/null +++ b/modules/client/src/main/scala/Client.scala @@ -0,0 +1,37 @@ +package lila.search +package client + +import smithy4s.http4s.* +import org.http4s.Uri +import org.http4s.client.Client +import cats.effect.{ IO, Resource } +import org.http4s.ember.client.EmberClientBuilder +import lila.search.spec.* +import scala.concurrent.Future +import cats.effect.unsafe.implicits.global + +object Client: + + def apply(uri: Uri): Resource[IO, FutureClient] = + instance(uri).map(FutureClient(_)) + + def instance(uri: Uri): Resource[IO, SearchService[IO]] = + makeClient.flatMap(makeIO(uri)) + + private def makeClient: Resource[IO, Client[IO]] = + EmberClientBuilder.default[IO].build + + def makeIO(uri: Uri)(client: Client[IO]): Resource[IO, SearchService[IO]] = + SimpleRestJsonBuilder + .apply(SearchService) + .client(client) + .uri(uri) + .resource + +class FutureClient(io: SearchService[IO]) extends SearchService[Future]: + + override def countForum(text: String, troll: Boolean): Future[CountResponse] = + io.countForum(text, troll).unsafeToFuture() + + override def searchForum(body: ForumInputBody, from: Int, size: Int): Future[SearchResponse] = + io.searchForum(body, from, size).unsafeToFuture() diff --git a/modules/client/src/main/scala/IOClient.scala b/modules/client/src/main/scala/IOClient.scala deleted file mode 100644 index c89e426d..00000000 --- a/modules/client/src/main/scala/IOClient.scala +++ /dev/null @@ -1,19 +0,0 @@ -package lila.search -package client - -import smithy4s.http4s.* -import org.http4s.Uri -import org.http4s.client.Client -import cats.effect.IO -import cats.effect.Resource - -// the package under which the scala code was generated -import lila.search.spec.* - -object Clients: - def apply(http4sClient: Client[IO], uri: Uri): Resource[IO, SearchService[IO]] = - SimpleRestJsonBuilder - .apply(SearchService) - .client(http4sClient) - .uri(uri) - .resource diff --git a/modules/client/src/main/scala/PlayClient.scala b/modules/client/src/main/scala/PlayClient.scala new file mode 100644 index 00000000..1e4d2fd1 --- /dev/null +++ b/modules/client/src/main/scala/PlayClient.scala @@ -0,0 +1,42 @@ +package lila.search +package client + +import akka.util.ByteString +import com.github.plokhotnyuk.jsoniter_scala.core._ +import lila.search.spec.* +import play.api.libs.ws.BodyWritable +import play.api.libs.ws.InMemoryBody +import play.api.libs.ws.StandaloneWSClient +import scala.concurrent.ExecutionContext +import scala.concurrent.Future +import smithy4s.json.Json.given +import smithy4s.schema.Schema +import play.api.libs.ws.BodyReadable + +object implicits: + + given [A](using JsonCodec[A]): BodyWritable[A] = + BodyWritable(a => InMemoryBody(ByteString.fromArrayUnsafe(writeToArray(a))), "application/json") + + given [A](using JsonCodec[A]): BodyReadable[A] = + BodyReadable(res => readFromArray(res.bodyAsBytes.toArray)) + + def apply(client: StandaloneWSClient, url: String)(using ExecutionContext): SearchService[Future] = + PlayClient(client, url) + +class PlayClient(client: StandaloneWSClient, baseUrl: String)(using ExecutionContext) + extends SearchService[Future]: + + import implicits.given + + override def countForum(text: String, troll: Boolean): Future[CountResponse] = + request(s"$baseUrl/count/forum", ForumInputBody(text, troll)) + + override def searchForum(body: ForumInputBody, from: Int, size: Int): Future[SearchResponse] = + request(s"$baseUrl/search/forum/{from}/{int}", body) + + private def request[D: Schema, R: Schema](url: String, data: D): Future[R] = + client.url(url).post(data).flatMap { + case res if res.status == 200 => Future(res.body[R]) + case res => Future.failed(Exception(s"$url ${res.status}")) + } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f9ee767c..6c9ac5ee 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -15,8 +15,8 @@ object Dependencies { val catsCore = "org.typelevel" %% "cats-core" % "2.10.0" val catsEffect = "org.typelevel" %% "cats-effect" % V.catsEffect - val fs2 = "co.fs2" %% "fs2-core" % V.fs2 - val fs2IO = "co.fs2" %% "fs2-io" % V.fs2 + val fs2 = "co.fs2" %% "fs2-core" % V.fs2 + val fs2IO = "co.fs2" %% "fs2-io" % V.fs2 val cirisCore = "is.cir" %% "ciris" % V.ciris val cirisHtt4s = "is.cir" %% "ciris-http4s" % V.ciris @@ -28,11 +28,10 @@ object Dependencies { val http4sEmberClient = http4s("ember-client") val log4Cats = "org.typelevel" %% "log4cats-slf4j" % "2.7.0" - val logbackX = "ch.qos.logback" % "logback-classic" % "1.5.6" + val logbackX = "ch.qos.logback" % "logback-classic" % "1.5.6" val ducktape = "io.github.arainko" %% "ducktape" % "0.2.0" - val testContainers = "com.dimafeng" %% "testcontainers-scala-postgresql" % "0.41.3" % Test val weaver = "com.disneystreaming" %% "weaver-cats" % "0.8.4" % Test val weaverScalaCheck = "com.disneystreaming" %% "weaver-scalacheck" % "0.8.4" % Test