Skip to content

Commit

Permalink
fix: make namespace mandatory in search results
Browse files Browse the repository at this point in the history
  • Loading branch information
olevski committed Oct 14, 2024
1 parent 7764da8 commit dc4ef8a
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ private class SearchApiImpl[F[_]: Async](solrClient: SearchSolrClient[F])
FacetData(all)
}
.getOrElse(FacetData.empty)
val items = solrResult.responseBody.docs.map(EntityConverter.apply)
if (hasMore) SearchResult(items.init, facets, pageInfo)
else SearchResult(items, facets, pageInfo)
val allItems = solrResult.responseBody.docs.map(EntityConverter.apply)
// NOTE: Any entities which could not be converted to the response body properly
// are skipped and never returned. This is because the internal logic and models of the data service
// allows for some documents to be missing parts that are needed by the UI.
val successItems = allItems.filter(_.isSuccess).map(_.get)
if (hasMore) SearchResult(successItems.init, facets, pageInfo)
else SearchResult(successItems, facets, pageInfo)
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

package io.renku.search.api.data

import scala.util.{Success, Try}

import io.renku.search.solr.documents.{
Group as GroupDocument,
Project as ProjectDocument,
Expand All @@ -26,39 +28,48 @@ import io.renku.search.solr.documents.{
}

trait EntityConverter:
def project(p: ProjectDocument): SearchEntity.Project =
SearchEntity.Project(
id = p.id,
name = p.name,
slug = p.slug,
namespace = p.namespaceDetails.flatMap(_.docs.headOption).flatMap {
case u: UserDocument => Some(user(u))
case g: GroupDocument => Some(group(g))
case v =>
scribe.error(
s"Error converting project namespace due to incorrect data. A user or group was expected as the project namespace of ${p.id}/${p.slug}, but found: $v"
)
None
},
repositories = p.repositories,
visibility = p.visibility,
description = p.description,
createdBy = p.creatorDetails
.flatMap(_.docs.headOption)
.map(user)
.orElse(Some(SearchEntity.User(p.createdBy))),
creationDate = p.creationDate,
keywords = p.keywords,
score = p.score
def project(p: ProjectDocument): Try[SearchEntity.Project] =
Try(
SearchEntity.Project(
id = p.id,
name = p.name,
slug = p.slug,
namespace = p.namespaceDetails.flatMap(_.docs.headOption) match {
case Some(u: UserDocument) => user(u).get
case Some(g: GroupDocument) => group(g)
case _ =>
scribe.error(
s"Error converting project namespace due to incorrect data. A user or group was expected as the project namespace of ${p.id}/${p.slug}, but found nothing"
)
throw new Exception(
s"Missing namespace for project with id ${p.id} and slug ${p.slug}"
)
},
repositories = p.repositories,
visibility = p.visibility,
description = p.description,
createdBy =
p.creatorDetails.flatMap(_.docs.headOption).flatMap(u => Some(user(u).get)),
creationDate = p.creationDate,
keywords = p.keywords,
score = p.score
)
)

def user(u: UserDocument): SearchEntity.User =
SearchEntity.User(
id = u.id,
namespace = u.namespace,
firstName = u.firstName,
lastName = u.lastName,
score = u.score
def user(u: UserDocument): Try[SearchEntity.User] =
Try(
SearchEntity.User(
id = u.id,
namespace = u.namespace match {
case Some(x) => x
case None =>
scribe.error(s"Missing namespace for user with id ${u.id}")
throw new Exception(s"Missing namespace for user with id ${u.id}")
},
firstName = u.firstName,
lastName = u.lastName,
score = u.score
)
)

def group(g: GroupDocument): SearchEntity.Group =
Expand All @@ -70,11 +81,11 @@ trait EntityConverter:
score = g.score
)

def entity(e: EntityDocument): SearchEntity = e match
def entity(e: EntityDocument): Try[SearchEntity] = e match
case p: ProjectDocument => project(p)
case u: UserDocument => user(u)
case g: GroupDocument => group(g)
case g: GroupDocument => Success(group(g))
end EntityConverter

object EntityConverter extends EntityConverter:
def apply(e: EntityDocument): SearchEntity = entity(e)
def apply(e: EntityDocument): Try[SearchEntity] = entity(e)
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ object SearchEntity:
id: Id,
name: Name,
slug: Slug,
namespace: Option[UserOrGroup],
namespace: UserOrGroup,
repositories: Seq[Repository],
visibility: Visibility,
description: Option[Description] = None,
Expand All @@ -64,7 +64,7 @@ object SearchEntity:

final case class User(
id: Id,
namespace: Option[Namespace] = None,
namespace: Namespace,
firstName: Option[FirstName] = None,
lastName: Option[LastName] = None,
score: Option[Double] = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ object ApiSchema:

val exampleUser: SearchEntity.User = User(
Id("1CAF4C73F50D4514A041C9EDDB025A36"),
Some(Namespace("renku/renku")),
Namespace("renku/renku"),
Some(FirstName("Albert")),
Some(LastName("Einstein")),
Some(2.1)
Expand All @@ -167,7 +167,7 @@ object ApiSchema:
id = Id("01HRA7AZ2Q234CDQWGA052F8MK"),
name = Name("renku"),
slug = Slug("renku"),
namespace = Some(exampleGroup),
namespace = exampleGroup,
repositories = Seq(Repository("https://github.com/renku")),
visibility = Visibility.Public,
description = Some(Description("Renku project")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

package io.renku.search.api

import scala.util.Try

import cats.effect.IO
import cats.syntax.all.*

Expand All @@ -27,7 +29,11 @@ import io.renku.search.model.*
import io.renku.search.query.Query
import io.renku.search.solr.client.SearchSolrSuite
import io.renku.search.solr.client.SolrDocumentGenerators.*
import io.renku.search.solr.documents.{EntityDocument, User as SolrUser}
import io.renku.search.solr.documents.{
EntityDocument,
Group as SolrGroup,
User as SolrUser
}
import io.renku.solr.client.DocVersion
import io.renku.solr.client.ResponseBody
import munit.CatsEffectSuite
Expand All @@ -45,14 +51,22 @@ class SearchApiSpec extends CatsEffectSuite with SearchSolrSuite:
"matching",
"matching description",
Gen.const(None),
Gen.const(None),
Gen.const(Some(SolrUser(Id("nsId1"), namespace = Some(Namespace("namespace1"))))),
Gen.const(Visibility.Public)
).generateOne
val project2 = projectDocumentGen(
"disparate",
"disparate description",
Gen.const(None),
Gen.const(None),
Gen.const(
Some(
SolrGroup(
Id("nsId2"),
name = Name("some-group"),
namespace = Namespace("namespace2")
)
)
),
Gen.const(Visibility.Public)
).generateOne
for {
Expand All @@ -64,7 +78,7 @@ class SearchApiSpec extends CatsEffectSuite with SearchSolrSuite:
.map(_.fold(err => fail(s"Calling Search API failed with $err"), identity))

expected = toApiEntities(project1).toSet
obtained = results.items.map(scoreToNone).toSet
obtained = results.items.map(scoreToNone).map(Try.apply).toSet
} yield assert(
expected.diff(obtained).isEmpty,
s"Expected $expected, bot got $obtained"
Expand All @@ -77,7 +91,7 @@ class SearchApiSpec extends CatsEffectSuite with SearchSolrSuite:
"exclusive",
"exclusive description",
Gen.const(None),
Gen.const(None),
Gen.const(Some(SolrUser(Id("nsId1"), namespace = Some(Namespace("namespace1"))))),
Gen.const(Visibility.Public)
).generateOne.copy(createdBy = userId)
for {
Expand All @@ -92,7 +106,7 @@ class SearchApiSpec extends CatsEffectSuite with SearchSolrSuite:
project.copy(creatorDetails = ResponseBody.single(user).some),
user
).toSet
obtained = results.items.map(scoreToNone).toSet
obtained = results.items.map(scoreToNone).map(Try.apply).toSet
} yield assert(
expected.diff(obtained).isEmpty,
s"Expected $expected, bot got $obtained"
Expand Down

0 comments on commit dc4ef8a

Please sign in to comment.