Skip to content

Commit

Permalink
chore: prepare 2.46.0 (#1791)
Browse files Browse the repository at this point in the history
  • Loading branch information
jachro authored Nov 15, 2023
2 parents 4bf40b4 + b909c7c commit 9f30008
Show file tree
Hide file tree
Showing 52 changed files with 953 additions and 354 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import io.renku.graph.model.projects.Role
import io.renku.graph.model.testentities._
import io.renku.http.client.UrlEncoder._
import org.http4s.Status.Ok
import org.scalacheck.Gen.alphaLowerChar
import tooling.{AcceptanceSpec, ApplicationServices}

class CrossEntitiesSearchSpec extends AcceptanceSpec with ApplicationServices with TSProvisioning {
Expand All @@ -41,7 +42,7 @@ class CrossEntitiesSearchSpec extends AcceptanceSpec with ApplicationServices wi

val user = authUsers.generateOne

val commonPhrase = nonBlankStrings(minLength = 5).generateOne
val commonPhrase = nonBlankStrings(minLength = 5, charsGenerator = alphaLowerChar).generateOne
val testProject =
renkuProjectEntities(visibilityPublic, creatorGen = cliShapedPersons)
.modify(replaceProjectName(sentenceContaining(commonPhrase).generateAs[projects.Name]))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ object Criteria {
final case class Filters(maybeQuery: Option[Filters.Query] = None,
entityTypes: Set[Filters.EntityType] = Set.empty,
creators: Set[persons.Name] = Set.empty,
roles: Set[projects.Role] = Set.empty,
visibilities: Set[projects.Visibility] = Set.empty,
namespaces: Set[projects.Namespace] = Set.empty,
maybeSince: Option[Filters.Since] = None,
Expand All @@ -53,7 +54,9 @@ object Criteria {
final class Query private (val value: String) extends AnyVal with StringTinyType
object Query extends TinyTypeFactory[Query](new Query(_)) with NonBlank[Query]

sealed trait EntityType extends StringTinyType with Product with Serializable
final case class Owned(userId: persons.GitLabId)

sealed trait EntityType extends StringTinyType with Product
object EntityType extends TinyTypeFactory[EntityType](EntityTypeApply) {

final case object Project extends EntityType { override val value: String = "project" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import io.renku.entities.search.Criteria.Filters
import io.renku.entities.search.model.{Entity, MatchingScore}
import io.renku.entities.searchgraphs.concatSeparator
import io.renku.graph.model._
import io.renku.graph.model.projects.Visibility
import io.renku.http.server.security.model.AuthUser
import io.renku.projectauth.util.SparqlSnippets
import io.renku.triplesstore.client.sparql.{Fragment, LuceneQuery, VarName}
Expand All @@ -34,22 +33,23 @@ import io.renku.triplesstore.client.syntax._
object DatasetsQuery extends EntityQuery[Entity.Dataset] {
override val entityType: Filters.EntityType = Filters.EntityType.Dataset

val matchingScoreVar = VarName("matchingScore")
val nameVar = VarName("name")
val idsSlugsVisibilitiesVar = VarName("idsSlugsVisibilities")
val sameAsVar = VarName("sameAs")
val maybeDateCreatedVar = VarName("maybeDateCreated")
val maybeDatePublishedVar = VarName("maybeDatePublished")
val maybeDateModified = VarName("maybeDateModified")
val dateVar = VarName("date")
val creatorsNamesVar = VarName("creatorsNames")
val maybeDescriptionVar = VarName("maybeDescription")
val keywordsVar = VarName("keywords")
val imagesVar = VarName("images")
private val matchingScoreVar = VarName("matchingScore")
private val nameVar = VarName("name")
private val idsSlugsVisibilitiesVar = VarName("idsSlugsVisibilities")
private val sameAsVar = VarName("sameAs")
private val maybeDateCreatedVar = VarName("maybeDateCreated")
private val maybeDatePublishedVar = VarName("maybeDatePublished")
private val maybeDateModified = VarName("maybeDateModified")
private val dateVar = VarName("date")
private val creatorsNamesVar = VarName("creatorsNames")
private val maybeDescriptionVar = VarName("maybeDescription")
private val keywordsVar = VarName("keywords")
private val imagesVar = VarName("images")

private val authSnippets = SparqlSnippets(VarName("projId"))
private val projIdVar = VarName("projId")
private val authSnippets = SparqlSnippets(projIdVar)

override val selectVariables = Set(
override val selectVariables: Set[String] = Set(
entityTypeVar,
matchingScoreVar,
nameVar,
Expand All @@ -65,7 +65,7 @@ object DatasetsQuery extends EntityQuery[Entity.Dataset] {
imagesVar
).map(_.name)

override def query(criteria: Criteria): Option[String] =
override def query(criteria: Criteria): Option[Fragment] =
criteria.filters.whenRequesting(entityType) {
fr"""{
|SELECT DISTINCT $entityTypeVar
Expand Down Expand Up @@ -111,7 +111,7 @@ object DatasetsQuery extends EntityQuery[Entity.Dataset] {
| ${namespacesPart(criteria.filters.namespaces)}
|
| # access restriction
| ${accessRightsAndVisibility(criteria.maybeUser, criteria.filters.visibilities)}
| ${accessRightsAndVisibility(criteria.maybeUser, criteria.filters)}
| }
| }
| }# end sub select
Expand All @@ -130,14 +130,26 @@ object DatasetsQuery extends EntityQuery[Entity.Dataset] {
| }
|}
|}
|""".stripMargin.sparql
|""".stripMargin
}

private def accessRightsAndVisibility(maybeUser: Option[AuthUser], visibilities: Set[Visibility]): Fragment =
sparql"""
|?projId renku:projectVisibility ?visibility .
|${authSnippets.visibleProjects(maybeUser.map(_.id), visibilities)}
"""
private def accessRightsAndVisibility(maybeUser: Option[AuthUser], filters: Criteria.Filters): Fragment =
maybeUser.map(_.id) -> filters.roles match {
case Some(userId) -> roles if roles.nonEmpty =>
fr"""|$projIdVar renku:projectVisibility ?visibility .
|${visibilitiesPart(filters.visibilities)}
|${authSnippets.projectsWithRoles(userId, roles)}
|""".stripMargin
case maybeUserId -> _ =>
fr"""|$projIdVar renku:projectVisibility ?visibility .
|${authSnippets.visibleProjects(maybeUserId, filters.visibilities)}
|""".stripMargin
}

private lazy val visibilitiesPart: Set[projects.Visibility] => Fragment = {
case vis if vis.nonEmpty => fr"""VALUES (?visibility) {${vis.map(_.value)}}."""
case _ => Fragment.empty
}

private def resolveProject: Fragment =
fr"""| $sameAsVar a renku:DiscoverableDataset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private class EntitiesFinderImpl[F[_]: Async: NonEmptyParallel: Logger: SparqlQu
Prefixes of (prov -> "prov", renku -> "renku", schema -> "schema", text -> "text", xsd -> "xsd"),
s"""|SELECT ${entityQueries.map(_.selectVariables).combineAll.mkString(" ")}
|WHERE {
| ${entityQueries.flatMap(_.query(criteria)).mkString(" UNION ")}
| ${entityQueries.flatMap(_.query(criteria)).map(_.sparql).mkString(" UNION ")}
|}
|${`ORDER BY`(criteria.sorting)}
|""".stripMargin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ package io.renku.entities.search
import io.circe.Decoder
import io.renku.entities.search.Criteria.Filters.EntityType
import io.renku.triplesstore.ResultsDecoder
import io.renku.triplesstore.client.sparql.VarName
import io.renku.triplesstore.client.sparql.{Fragment, VarName}

private[entities] trait EntityQuery[+E <: model.Entity] extends ResultsDecoder with EntityQueryVars {
val entityType: EntityType
val selectVariables: Set[String]
def query(criteria: Criteria): Option[String]
def query(criteria: Criteria): Option[Fragment]
def decoder[EE >: E]: Decoder[EE]

def getDecoder[EE >: E](entityType: EntityType): Option[Decoder[EE]] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,43 +21,66 @@ package io.renku.entities.search
import io.circe.Decoder
import io.renku.entities.search.Criteria.Filters.EntityType
import io.renku.entities.search.model.{Entity, MatchingScore}
import io.renku.graph.model.{GraphClass, persons}
import io.renku.triplesstore.client.sparql.VarName
import io.renku.graph.model.entities.Person.gitLabSameAsAdditionalType
import io.renku.graph.model.{GraphClass, persons, projects}
import io.renku.http.server.security.model.AuthUser
import io.renku.triplesstore.client.sparql.{Fragment, VarName}
import io.renku.triplesstore.client.syntax._

private case object PersonsQuery extends EntityQuery[model.Entity.Person] {

override val entityType: EntityType = EntityType.Person

override val selectVariables = Set("?entityType", "?matchingScore", "?name")
override val selectVariables: Set[String] = Set("?entityType", "?matchingScore", "?name")

override def query(criteria: Criteria) =
override def query(criteria: Criteria): Option[Fragment] =
(criteria.filters whenRequesting (entityType, criteria.filters.withNoOrPublicVisibility, criteria.filters.namespaces.isEmpty, criteria.filters.maybeSince.isEmpty, criteria.filters.maybeUntil.isEmpty)) {
import criteria._
// format: off
s"""|{
| SELECT DISTINCT ?entityType ?matchingScore ?name
| WHERE {
| {
| SELECT (SAMPLE(?id) AS ?personId) ?name (MAX(?score) AS ?matchingScore)
| WHERE {
| ${filters.onQuery(
s"""(?id ?score) text:query (schema:name '${filters.query.query}').""",
matchingScoreVariableName = "?score")}
| GRAPH <${GraphClass.Persons.id}> {
| ?id a schema:Person;
| schema:name ?name
| }
| ${filters.maybeOnCreatorName(VarName("name")).sparql}
| }
| GROUP BY ?name
| }
| BIND ('person' AS ?entityType)
| }
|}
|""".stripMargin
// format: on
sparql"""|{
| SELECT DISTINCT ?entityType ?matchingScore ?name
| WHERE {
| {
| SELECT (SAMPLE(?id) AS ?personId) ?name (MAX(?score) AS ?matchingScore)
| WHERE {
| ${textPart(criteria.filters)}
|
| GRAPH ${GraphClass.Persons.id} {
| ?id a schema:Person;
| schema:name ?name.
| ${filterOnRoles(criteria)}
| }
| ${filters.maybeOnCreatorName(VarName("name"))}
| }
| GROUP BY ?name
| }
| BIND ('person' AS ?entityType)
| }
|}
|""".stripMargin
}

private def textPart(filters: Criteria.Filters) =
filters.onQuery(
snippet = fr"""(?id ?score) text:query (schema:name ${filters.query}).""",
matchingScoreVariableName = VarName("score")
)

private def filterOnRoles(criteria: Criteria): Fragment = criteria.maybeUser -> criteria.filters.roles match {
case Some(AuthUser(id, _)) -> roles if roles contains projects.Role.Owner =>
fr"""|?id schema:sameAs ?sameAsId.
|?sameAsId schema:additionalType ${gitLabSameAsAdditionalType.asTripleObject};
| schema:identifier ${id.asObject}.
|""".stripMargin
case _ -> roles if roles.nonEmpty =>
// this is a hack to filter out all the persons
// the assumption is that the caller cannot have an explicit Maintainer or Reader role on a Person
fr"""|FILTER EXISTS {
| ?id renku:nonexisting true.
|}
|""".stripMargin
case _ => Fragment.empty
}

override def decoder[EE >: Entity.Person]: Decoder[EE] = { implicit cursor =>
import io.renku.tinytypes.json.TinyTypeDecoders._

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private case object ProjectsQuery extends EntityQuery[model.Entity.Project] {
imagesVar
).map(_.name)

override def query(criteria: Criteria): Option[String] = (criteria.filters whenRequesting entityType) {
override def query(criteria: Criteria): Option[Fragment] = (criteria.filters whenRequesting entityType) {
import criteria._
sparql"""|{
| SELECT DISTINCT $entityTypeVar $matchingScoreVar $nameVar $slugVar $visibilityVar
Expand All @@ -87,7 +87,7 @@ private case object ProjectsQuery extends EntityQuery[model.Entity.Project] {
|
| ${filters.maybeOnDateCreated(maybeDateCreatedVar)}
|
| ${accessRightsAndVisibility(criteria.maybeUser, criteria.filters.visibilities)}
| ${accessRightsAndVisibility(criteria.maybeUser, criteria.filters)}
|
| GRAPH $projectIdVar {
| ${namespacesPart(criteria.filters.namespaces)}
Expand All @@ -107,7 +107,7 @@ private case object ProjectsQuery extends EntityQuery[model.Entity.Project] {
| }
| }
|}
|""".stripMargin.sparql
|""".stripMargin
}

private def textQueryPart: Option[Criteria.Filters.Query] => Fragment = {
Expand Down Expand Up @@ -138,11 +138,23 @@ private case object ProjectsQuery extends EntityQuery[model.Entity.Project] {
|""".stripMargin
}

private def accessRightsAndVisibility(maybeUser: Option[AuthUser], visibilities: Set[projects.Visibility]): Fragment =
sparql"""
|$projectIdVar renku:projectVisibility $visibilityVar .
|${authSnippets.visibleProjects(maybeUser.map(_.id), visibilities)}
""".stripMargin
private def accessRightsAndVisibility(maybeUser: Option[AuthUser], filters: Criteria.Filters): Fragment =
maybeUser.map(_.id) -> filters.roles match {
case Some(userId) -> roles if roles.nonEmpty =>
fr"""|$projectIdVar renku:projectVisibility $visibilityVar .
|${visibilitiesPart(filters.visibilities)}
|${authSnippets.projectsWithRoles(userId, roles)}
|""".stripMargin
case maybeUserId -> _ =>
fr"""|$projectIdVar renku:projectVisibility $visibilityVar .
|${authSnippets.visibleProjects(maybeUserId, filters.visibilities)}
|""".stripMargin
}

private lazy val visibilitiesPart: Set[projects.Visibility] => Fragment = {
case vis if vis.nonEmpty => fr"""VALUES ($visibilityVar) {${vis.map(_.value)}}."""
case _ => Fragment.empty
}

private def namespacesPart(ns: Set[projects.Namespace]): Fragment = {
val matchFrag =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,20 @@ private case object WorkflowsQuery extends EntityQuery[model.Entity.Workflow] {

override val entityType: EntityType = EntityType.Workflow

override val selectVariables = Set("?entityType",
"?matchingScore",
"?wkId",
"?name",
"?date",
"?maybeDescription",
"?keywords",
"?projectIdVisibilities",
"?workflowTypes"
override val selectVariables: Set[String] = Set("?entityType",
"?matchingScore",
"?wkId",
"?name",
"?date",
"?maybeDescription",
"?keywords",
"?projectIdVisibilities",
"?workflowTypes"
)

private val authSnippets = SparqlSnippets(VarName("projectId"))

override def query(criteria: Criteria) = (criteria.filters whenRequesting entityType) {
override def query(criteria: Criteria): Option[Fragment] = (criteria.filters whenRequesting entityType) {
import criteria._
// format: off
sparql"""|{
Expand Down Expand Up @@ -86,7 +86,7 @@ private case object WorkflowsQuery extends EntityQuery[model.Entity.Workflow] {
| FILTER (IF (BOUND(?childProjectsIds), !CONTAINS(STR(?childProjectsIds), STR(?projectId)), true))
| FILTER (IF (BOUND(?projectsIdsWhereInvalidated), !CONTAINS(STR(?projectsIdsWhereInvalidated), STR(?projectId)), true))
|
| ${accessRightsAndVisibility(criteria.maybeUser, criteria.filters.visibilities)}
| ${accessRightsAndVisibility(criteria.maybeUser, criteria.filters)}
|
| GRAPH ?projectId {
| ?wkId a prov:Plan;
Expand All @@ -96,6 +96,7 @@ private case object WorkflowsQuery extends EntityQuery[model.Entity.Workflow] {
| ^renku:hasPlan ?projectId.
| ?projectId renku:projectNamespace ?namespace.
| ?projectId renku:projectVisibility ?visibility.
| ${maybeFilterOnVisibilities(filters.visibilities)}
| BIND (CONCAT(STR(?projectId), STR('::'), STR(?visibility)) AS ?projectIdVisibility)
| ${filters.maybeOnNamespace(VarName("namespace"))}
| ${filters.maybeOnDateCreated(VarName("date"))}
Expand All @@ -108,12 +109,22 @@ private case object WorkflowsQuery extends EntityQuery[model.Entity.Workflow] {
| BIND ('workflow' AS ?entityType)
| }
|}
|""".stripMargin.sparql
|""".stripMargin
// format: on
}

private def accessRightsAndVisibility(maybeUser: Option[AuthUser], visibilities: Set[projects.Visibility]): Fragment =
authSnippets.visibleProjects(maybeUser.map(_.id), visibilities)
private def accessRightsAndVisibility(maybeUser: Option[AuthUser], filters: Criteria.Filters): Fragment =
maybeUser.map(_.id) -> filters.roles match {
case Some(userId) -> roles if roles.nonEmpty =>
authSnippets.projectsWithRoles(userId, roles)
case maybeUserId -> _ =>
authSnippets.visibleProjects(maybeUserId, filters.visibilities)
}

private lazy val maybeFilterOnVisibilities: Set[projects.Visibility] => Fragment = {
case vis if vis.nonEmpty => fr"""VALUES (?visibility) {${vis.map(_.value)}}."""
case _ => Fragment.empty
}

private def withoutQuerySnippet: Fragment =
sparql"""
Expand All @@ -124,7 +135,7 @@ private case object WorkflowsQuery extends EntityQuery[model.Entity.Workflow] {
|}
|""".stripMargin

def withQuerySnippet(query: LuceneQuery) =
private def withQuerySnippet(query: LuceneQuery) =
sparql"""
|{
| SELECT ?wkId (MAX(?score) AS ?matchingScore) (SAMPLE(?projId) AS ?projectId)
Expand Down Expand Up @@ -186,7 +197,7 @@ private case object WorkflowsQuery extends EntityQuery[model.Entity.Workflow] {
} yield Entity.Workflow(matchingScore, name, visibility, dateCreated, keywords, maybeDescription, workflowTypes)
}

def workflowTypeFromString(str: String): Either[String, WorkflowType] =
private def workflowTypeFromString(str: String): Either[String, WorkflowType] =
str match {
case _ if str.contains(CompositePlan.Ontology.typeDef.clazz.id.show) =>
Right(WorkflowType.Composite)
Expand Down
Loading

0 comments on commit 9f30008

Please sign in to comment.