From f0a7ac3f0f1c14d21061768ad7aabdd684e83648 Mon Sep 17 00:00:00 2001 From: Jakub Chrobasik Date: Fri, 23 Feb 2024 15:55:44 +0100 Subject: [PATCH] feat: using ducktape for object transformation (#31) --- build.sbt | 1 + .../io/renku/search/model/projects.scala | 7 ++++++ .../scala/io/renku/search/model/users.scala | 2 ++ .../io/renku/search/api/SearchApiImpl.scala | 23 ++++------------- .../search/provision/SearchProvisioner.scala | 25 ++++++------------- .../provision/SearchProvisionerSpec.scala | 2 +- project/Dependencies.scala | 5 ++++ 7 files changed, 29 insertions(+), 36 deletions(-) diff --git a/build.sbt b/build.sbt index d586e1b6..36bbc356 100644 --- a/build.sbt +++ b/build.sbt @@ -75,6 +75,7 @@ lazy val commons = project Dependencies.borer ++ Dependencies.catsCore ++ Dependencies.catsEffect ++ + Dependencies.ducktape ++ Dependencies.fs2Core ++ Dependencies.scodecBits ++ Dependencies.scribe, diff --git a/modules/commons/src/main/scala/io/renku/search/model/projects.scala b/modules/commons/src/main/scala/io/renku/search/model/projects.scala index 0a24005f..f341277f 100644 --- a/modules/commons/src/main/scala/io/renku/search/model/projects.scala +++ b/modules/commons/src/main/scala/io/renku/search/model/projects.scala @@ -21,6 +21,7 @@ package io.renku.search.model import cats.kernel.Order import io.bullet.borer.derivation.MapBasedCodecs.* import io.bullet.borer.{Codec, Decoder, Encoder} +import io.github.arainko.ducktape.* import io.renku.search.borer.codecs.all.given import java.time.Instant @@ -31,24 +32,28 @@ object projects: object Id: def apply(v: String): Id = v extension (self: Id) def value: String = self + given Transformer[String, Id] = apply given Codec[Id] = Codec.of[String] opaque type Name = String object Name: def apply(v: String): Name = v extension (self: Name) def value: String = self + given Transformer[String, Name] = apply given Codec[Name] = Codec.of[String] opaque type Slug = String object Slug: def apply(v: String): Slug = v extension (self: Slug) def value: String = self + given Transformer[String, Slug] = apply given Codec[Slug] = Codec.of[String] opaque type Repository = String object Repository: def apply(v: String): Repository = v extension (self: Repository) def value: String = self + given Transformer[String, Repository] = apply given Codec[Repository] = Codec.of[String] opaque type Description = String @@ -62,12 +67,14 @@ object projects: } } extension (self: Description) def value: String = self + given Transformer[String, Description] = apply given Codec[Description] = Codec.of[String] opaque type CreationDate = Instant object CreationDate: def apply(v: Instant): CreationDate = v extension (self: CreationDate) def value: Instant = self + given Transformer[Instant, CreationDate] = apply given Codec[CreationDate] = Codec.of[Instant] enum Visibility derives Codec: diff --git a/modules/commons/src/main/scala/io/renku/search/model/users.scala b/modules/commons/src/main/scala/io/renku/search/model/users.scala index 21b2d750..07369624 100644 --- a/modules/commons/src/main/scala/io/renku/search/model/users.scala +++ b/modules/commons/src/main/scala/io/renku/search/model/users.scala @@ -19,6 +19,7 @@ package io.renku.search.model import io.bullet.borer.Codec +import io.github.arainko.ducktape.Transformer object users: @@ -26,4 +27,5 @@ object users: object Id: def apply(v: String): Id = v extension (self: Id) def value: String = self + given Transformer[String, Id] = apply given Codec[Id] = Codec.bimap[String, Id](_.value, Id.apply) diff --git a/modules/search-api/src/main/scala/io/renku/search/api/SearchApiImpl.scala b/modules/search-api/src/main/scala/io/renku/search/api/SearchApiImpl.scala index 68f29cae..c4603a9e 100644 --- a/modules/search-api/src/main/scala/io/renku/search/api/SearchApiImpl.scala +++ b/modules/search-api/src/main/scala/io/renku/search/api/SearchApiImpl.scala @@ -20,12 +20,13 @@ package io.renku.search.api import cats.effect.Async import cats.syntax.all.* +import io.github.arainko.ducktape.* +import io.renku.search.api.data.* import io.renku.search.solr.client.SearchSolrClient -import io.renku.search.solr.documents.{Project as SolrProject, User as SolrUser} +import io.renku.search.solr.documents.Project as SolrProject +import io.renku.solr.client.QueryResponse import org.http4s.dsl.Http4sDsl import scribe.Scribe -import io.renku.search.api.data.* -import io.renku.solr.client.QueryResponse private class SearchApiImpl[F[_]: Async](solrClient: SearchSolrClient[F]) extends Http4sDsl[F] @@ -51,25 +52,11 @@ private class SearchApiImpl[F[_]: Async](solrClient: SearchSolrClient[F]) .as(message) .map(_.asLeft[SearchResult]) - private def toApiProject(p: SolrProject): SearchEntity = - def toUser(user: SolrUser): User = User(user.id) - Project( - p.id, - p.name, - p.slug, - p.repositories, - p.visibility, - p.description, - toUser(p.createdBy), - p.creationDate, - p.members.map(toUser) - ) - private def toApiResult(currentPage: PageDef)( solrResult: QueryResponse[SolrProject] ): SearchResult = val hasMore = solrResult.responseBody.docs.size > currentPage.limit val pageInfo = PageWithTotals(currentPage, solrResult.responseBody.numFound, hasMore) - val items = solrResult.responseBody.docs.map(toApiProject) + val items = solrResult.responseBody.docs.map(_.to[Project]) if (hasMore) SearchResult(items.init, pageInfo) else SearchResult(items, pageInfo) diff --git a/modules/search-provision/src/main/scala/io/renku/search/provision/SearchProvisioner.scala b/modules/search-provision/src/main/scala/io/renku/search/provision/SearchProvisioner.scala index 8cd3f198..1d4e543f 100644 --- a/modules/search-provision/src/main/scala/io/renku/search/provision/SearchProvisioner.scala +++ b/modules/search-provision/src/main/scala/io/renku/search/provision/SearchProvisioner.scala @@ -23,9 +23,11 @@ import cats.effect.{Async, Resource, Temporal} import cats.syntax.all.* import fs2.Chunk import fs2.io.net.Network +import io.github.arainko.ducktape.* import io.renku.avro.codec.AvroReader import io.renku.avro.codec.decoders.all.given -import io.renku.events.v1.ProjectCreated +import io.renku.events.v1 +import io.renku.events.v1.{ProjectCreated, Visibility} import io.renku.queue.client.* import io.renku.redis.client.{ClientId, QueueName, RedisConfig} import io.renku.search.model.* @@ -126,23 +128,12 @@ private class SearchProvisionerImpl[F[_]: Async]( .onError(markProcessedOnFailure(lastMessage, queueClient)) } + private given Transformer[String, User] = (from: String) => User(users.Id(from)) + private given Transformer[v1.Visibility, projects.Visibility] = + (from: v1.Visibility) => projects.Visibility.unsafeFromString(from.name()) + private lazy val toSolrDocuments: Seq[ProjectCreated] => Seq[Project] = - _.map { pc => - - def toUser(id: String): User = User(users.Id(id)) - - Project( - projects.Id(pc.id), - projects.Name(pc.name), - projects.Slug(pc.slug), - pc.repositories.map(projects.Repository(_)), - projects.Visibility.unsafeFromString(pc.visibility.name()), - pc.description.map(projects.Description(_)), - toUser(pc.createdBy), - projects.CreationDate(pc.creationDate), - pc.members.map(toUser) - ) - } + _.map(_.to[Project]) private def markProcessedOnFailure( message: QueueMessage, diff --git a/modules/search-provision/src/test/scala/io/renku/search/provision/SearchProvisionerSpec.scala b/modules/search-provision/src/test/scala/io/renku/search/provision/SearchProvisionerSpec.scala index e61f4c87..9344b503 100644 --- a/modules/search-provision/src/test/scala/io/renku/search/provision/SearchProvisionerSpec.scala +++ b/modules/search-provision/src/test/scala/io/renku/search/provision/SearchProvisionerSpec.scala @@ -132,4 +132,4 @@ class SearchProvisionerSpec extends CatsEffectSuite with QueueSpec with SearchSo ) override def munitFixtures: Seq[Fixture[_]] = - List(withQueueClient, withSearchSolrClient) + List(withRedisClient, withQueueClient, withSearchSolrClient) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f9f85339..c78fd420 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -13,6 +13,7 @@ object Dependencies { val catsParse = "1.0.0" val catsScalaCheck = "0.3.2" val ciris = "3.5.0" + val ducktape = "0.1.11" val fs2 = "3.9.4" val http4s = "0.23.25" val luceneQueryParser = "9.9.2" @@ -81,6 +82,10 @@ object Dependencies { "org.typelevel" %% "munit-cats-effect-3" % V.catsEffectMunit ) + val ducktape = Seq( + "io.github.arainko" %% "ducktape" % V.ducktape + ) + val fs2Core = Seq( "co.fs2" %% "fs2-core" % V.fs2 )