From 8a6236ca03a30a2b7e43eda8a4d173e433c57d8f Mon Sep 17 00:00:00 2001 From: Oliver <20188437+olivergrabinski@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:53:19 +0100 Subject: [PATCH 1/3] Attempt to fix flaky test in `ElasticSearchClientSpec` (#4812) --- .../client/ElasticSearchClientSpec.scala | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/delta/plugins/elasticsearch/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/client/ElasticSearchClientSpec.scala b/delta/plugins/elasticsearch/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/client/ElasticSearchClientSpec.scala index 0796c0030c..9765471399 100644 --- a/delta/plugins/elasticsearch/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/client/ElasticSearchClientSpec.scala +++ b/delta/plugins/elasticsearch/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/client/ElasticSearchClientSpec.scala @@ -4,6 +4,7 @@ import akka.actor.ActorSystem import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.Uri.Query import akka.testkit.TestKit +import cats.effect.IO import cats.implicits._ import ch.epfl.bluebrain.nexus.delta.kernel.search.Pagination.FromPagination import ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch.ScalaTestElasticSearchClientSetup @@ -23,7 +24,7 @@ import ch.epfl.bluebrain.nexus.testkit.CirceLiteral import ch.epfl.bluebrain.nexus.testkit.elasticsearch.ElasticSearchDocker import ch.epfl.bluebrain.nexus.testkit.scalatest.ce.CatsEffectSpec import io.circe.{Json, JsonObject} -import org.scalatest.DoNotDiscover +import org.scalatest.{Assertion, DoNotDiscover} import org.scalatest.concurrent.Eventually import scala.concurrent.duration._ @@ -260,23 +261,25 @@ class ElasticSearchClientSpec(override val docker: ElasticSearchDocker) ElasticSearchAction.Update(index, "1", json"""{ "doc" : {"field2" : "value2"} }""") ) + def theCountShouldBe(count: Long): IO[Assertion] = + esClient.count(index.value).map(_ shouldEqual count) + { for { // Indexing and checking count - _ <- esClient.bulk(operations) - _ <- esClient.refresh(index) - original <- esClient.count(index.value) - _ = original shouldEqual 2L + _ <- esClient.bulk(operations) + _ <- esClient.refresh(index) + _ <- eventually { theCountShouldBe(2L) } // Deleting document matching the given query - query = jobj"""{"query": {"bool": {"must": {"term": {"field1": 3} } } } }""" - _ <- esClient.deleteByQuery(query, index) + query = jobj"""{"query": {"bool": {"must": {"term": {"field1": 3} } } } }""" + _ <- esClient.deleteByQuery(query, index) // Checking docs again - newCount <- esClient.count(index.value) - _ = newCount shouldEqual 1L - doc1 <- esClient.getSource[Json](index, "1").attemptNarrow[HttpClientError] - _ = doc1.rightValue - doc2 <- esClient.getSource[Json](index, "2").attemptNarrow[HttpClientError] - _ = doc2.leftValue.errorCode.value shouldEqual StatusCodes.NotFound + _ <- esClient.refresh(index) + _ <- eventually { theCountShouldBe(1L) } + doc1 <- esClient.getSource[Json](index, "1").attemptNarrow[HttpClientError] + _ = doc1.rightValue + doc2 <- esClient.getSource[Json](index, "2").attemptNarrow[HttpClientError] + _ = doc2.leftValue.errorCode.value shouldEqual StatusCodes.NotFound } yield () }.accepted } From 6534494a040bef83e7392836322d5bef124d99b0 Mon Sep 17 00:00:00 2001 From: Oliver <20188437+olivergrabinski@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:05:18 +0100 Subject: [PATCH 2/3] Remove ship dependency on acls (#4811) --- .../bluebrain/nexus/ship/ContextWiring.scala | 13 ++--- .../bluebrain/nexus/ship/acls/AclWiring.scala | 49 +++++++++++++------ .../nexus/ship/resources/ResourceWiring.scala | 6 +-- .../nexus/ship/schemas/SchemaWiring.scala | 6 +-- 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/ContextWiring.scala b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/ContextWiring.scala index e060fbe719..8501981bc9 100644 --- a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/ContextWiring.scala +++ b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/ContextWiring.scala @@ -2,22 +2,20 @@ package ch.epfl.bluebrain.nexus.ship import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.kernel.utils.ClasspathResourceLoader +import ch.epfl.bluebrain.nexus.delta.plugins.blazegraph.model.{contexts => bgContexts} +import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.model.{contexts => compositeViewContexts} +import ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch.model.{contexts => esContexts} import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.contexts import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.JsonLdApi import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteContextResolution} -import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverContextResolution import ch.epfl.bluebrain.nexus.delta.sdk.resources.FetchResource import ch.epfl.bluebrain.nexus.delta.sourcing.Transactors import ch.epfl.bluebrain.nexus.delta.sourcing.config.EventLogConfig -import ch.epfl.bluebrain.nexus.ship.acls.AclWiring +import ch.epfl.bluebrain.nexus.ship.acls.AclWiring.alwaysAuthorize import ch.epfl.bluebrain.nexus.ship.resolvers.ResolverWiring -import ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch.model.{contexts => esContexts} -import ch.epfl.bluebrain.nexus.delta.plugins.blazegraph.model.{contexts => bgContexts} -import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.model.{contexts => compositeViewContexts} - object ContextWiring { implicit private val loader: ClasspathResourceLoader = ClasspathResourceLoader.withContext(getClass) @@ -53,12 +51,11 @@ object ContextWiring { clock: EventClock, xas: Transactors )(implicit jsonLdApi: JsonLdApi): IO[ResolverContextResolution] = { - val aclCheck = AclCheck(AclWiring.acls(config, clock, xas)) val resolvers = ResolverWiring.resolvers(fetchContext, config, clock, xas) for { rcr <- remoteContextResolution - } yield ResolverContextResolution(aclCheck, resolvers, rcr, fetchResource) + } yield ResolverContextResolution(alwaysAuthorize, resolvers, rcr, fetchResource) } } diff --git a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/acls/AclWiring.scala b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/acls/AclWiring.scala index 967962ff5d..a36bfcd50c 100644 --- a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/acls/AclWiring.scala +++ b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/acls/AclWiring.scala @@ -1,23 +1,44 @@ package ch.epfl.bluebrain.nexus.ship.acls -import cats.effect.{Clock, IO} -import ch.epfl.bluebrain.nexus.delta.sdk.acls.{Acls, AclsImpl} +import cats.effect.IO +import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck +import ch.epfl.bluebrain.nexus.delta.sdk.acls.model.AclAddress +import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.permissions.model.Permission -import ch.epfl.bluebrain.nexus.delta.sourcing.Transactors -import ch.epfl.bluebrain.nexus.delta.sourcing.config.EventLogConfig +import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity + +import scala.collection.immutable object AclWiring { - def acls(config: EventLogConfig, clock: Clock[IO], xas: Transactors): Acls = { - val permissionSet = Set(Permission.unsafe("resources/read")) - AclsImpl( - IO.pure(permissionSet), - AclsImpl.findUnknownRealms(xas), - permissionSet, - config, - xas, - clock - ) + def alwaysAuthorize: AclCheck = new AclCheck { + override def authorizeForOr[E <: Throwable](path: AclAddress, permission: Permission, identities: Set[Identity])( + onError: => E + ): IO[Unit] = IO.unit + + override def authorizeFor(path: AclAddress, permission: Permission, identities: Set[Identity]): IO[Boolean] = + IO.pure(true) + + override def authorizeForEveryOr[E <: Throwable](path: AclAddress, permissions: Set[Permission])(onError: => E)( + implicit caller: Caller + ): IO[Unit] = IO.unit + + override def mapFilterOrRaise[A, B]( + values: immutable.Iterable[A], + extractAddressPermission: A => (AclAddress, Permission), + onAuthorized: A => B, + onFailure: AclAddress => IO[Unit] + )(implicit caller: Caller): IO[Set[B]] = + IO.pure(values.map(onAuthorized).toSet) + + override def mapFilterAtAddressOrRaise[A, B]( + values: immutable.Iterable[A], + address: AclAddress, + extractPermission: A => Permission, + onAuthorized: A => B, + onFailure: AclAddress => IO[Unit] + )(implicit caller: Caller): IO[Set[B]] = + IO.pure(values.map(onAuthorized).toSet) } } diff --git a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/ResourceWiring.scala b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/ResourceWiring.scala index 52f4ba533c..801c26c5f6 100644 --- a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/ResourceWiring.scala +++ b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/resources/ResourceWiring.scala @@ -2,7 +2,6 @@ package ch.epfl.bluebrain.nexus.ship.resources import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.JsonLdApi import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContextResolution -import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResourceResolution import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources.ResourceLog @@ -11,7 +10,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.schemas.FetchSchema import ch.epfl.bluebrain.nexus.delta.sourcing.config.EventLogConfig import ch.epfl.bluebrain.nexus.delta.sourcing.{ScopedEventLog, Transactors} import ch.epfl.bluebrain.nexus.ship.EventClock -import ch.epfl.bluebrain.nexus.ship.acls.AclWiring +import ch.epfl.bluebrain.nexus.ship.acls.AclWiring.alwaysAuthorize import ch.epfl.bluebrain.nexus.ship.resolvers.ResolverWiring object ResourceWiring { @@ -27,10 +26,9 @@ object ResourceWiring { ): (ResourceLog, FetchResource) = { val rcr = RemoteContextResolution.never // TODO: Use correct RemoteContextResolution val detectChange = DetectChange(false) - val aclCheck = AclCheck(AclWiring.acls(config, clock, xas)) val resolvers = ResolverWiring.resolvers(fetchContext, config, clock, xas) val resourceResolution = - ResourceResolution.schemaResource(aclCheck, resolvers, fetchSchema, excludeDeprecated = false) + ResourceResolution.schemaResource(alwaysAuthorize, resolvers, fetchSchema, excludeDeprecated = false) val validate = ValidateResource(resourceResolution)(rcr) val resourceDef = Resources.definition(validate, detectChange, clock) diff --git a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/schemas/SchemaWiring.scala b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/schemas/SchemaWiring.scala index ba0f0b2a79..1bb6c4aea6 100644 --- a/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/schemas/SchemaWiring.scala +++ b/ship/src/main/scala/ch/epfl/bluebrain/nexus/ship/schemas/SchemaWiring.scala @@ -3,14 +3,13 @@ package ch.epfl.bluebrain.nexus.ship.schemas import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.JsonLdApi import ch.epfl.bluebrain.nexus.delta.rdf.shacl.ShaclShapesGraph -import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext import ch.epfl.bluebrain.nexus.delta.sdk.resources.FetchResource import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas.SchemaLog import ch.epfl.bluebrain.nexus.delta.sdk.schemas.{FetchSchema, SchemaImports, Schemas, ValidateSchema} import ch.epfl.bluebrain.nexus.delta.sourcing.config.EventLogConfig import ch.epfl.bluebrain.nexus.delta.sourcing.{ScopedEventLog, Transactors} -import ch.epfl.bluebrain.nexus.ship.acls.AclWiring +import ch.epfl.bluebrain.nexus.ship.acls.AclWiring.alwaysAuthorize import ch.epfl.bluebrain.nexus.ship.resolvers.ResolverWiring import ch.epfl.bluebrain.nexus.ship.{ContextWiring, EventClock} @@ -31,9 +30,8 @@ object SchemaWiring { )(implicit jsonLdApi: JsonLdApi ): SchemaImports = { - val aclCheck = AclCheck(AclWiring.acls(config, clock, xas)) val resolvers = ResolverWiring.resolvers(fetchContext, config, clock, xas) - SchemaImports(aclCheck, resolvers, fetchSchema, fetchResource) + SchemaImports(alwaysAuthorize, resolvers, fetchSchema, fetchResource) } private def validateSchema(implicit api: JsonLdApi): IO[ValidateSchema] = From 32f30d6139c4f79a09f4fee6034bcfd169b00d87 Mon Sep 17 00:00:00 2001 From: Oliver <20188437+olivergrabinski@users.noreply.github.com> Date: Thu, 28 Mar 2024 17:01:02 +0100 Subject: [PATCH 3/3] Allow resolvers with same priority as deprecated resolver (#4805) --- .../delta/sdk/resolvers/ResolversImpl.scala | 7 ++++++- .../sdk/resolvers/ResolversImplSpec.scala | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolversImpl.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolversImpl.scala index 3329efceaf..033963d0c2 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolversImpl.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolversImpl.scala @@ -165,7 +165,12 @@ object ResolversImpl { uuidF: UUIDF ): Resolvers = { def priorityAlreadyExists(ref: ProjectRef, self: Iri, priority: Priority): IO[Unit] = { - sql"SELECT id FROM scoped_states WHERE type = ${Resolvers.entityType} AND org = ${ref.organization} AND project = ${ref.project} AND id != $self AND (value->'value'->'priority')::int = ${priority.value} " + sql"""SELECT id FROM scoped_states + WHERE type = ${Resolvers.entityType} + AND org = ${ref.organization} AND project = ${ref.project} + AND id != $self + AND (value->'deprecated')::boolean = false + AND (value->'value'->'priority')::int = ${priority.value}""" .query[Iri] .option .transact(xas.read) diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolversImplSpec.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolversImplSpec.scala index 7c14789283..11af18e0e1 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolversImplSpec.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolversImplSpec.scala @@ -8,7 +8,6 @@ import ch.epfl.bluebrain.nexus.delta.kernel.utils.UUIDF import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{contexts, nxv, schema} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdJavaApi} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContextResolution -import ch.epfl.bluebrain.nexus.delta.sdk.ConfigFixtures import ch.epfl.bluebrain.nexus.delta.sdk.generators.ProjectGen import ch.epfl.bluebrain.nexus.delta.sdk.generators.ResolverGen.{resolverResourceFor, sourceFrom, sourceWithoutId} import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller @@ -24,6 +23,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverRejection.{Inco import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverValue.{CrossProjectValue, InProjectValue} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model._ import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources +import ch.epfl.bluebrain.nexus.delta.sdk.{ConfigFixtures, ResolverResource} import ch.epfl.bluebrain.nexus.delta.sourcing.EntityDependencyStore import ch.epfl.bluebrain.nexus.delta.sourcing.model.EntityDependency.DependsOn import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Authenticated, Group, User} @@ -620,6 +620,22 @@ class ResolversImplSpec extends CatsEffectSpec with DoobieScalaTestFixture with } } + + "validating priority" should { + "allow creating a resolver with the same priority as a deprecated resolver" in { + givenADeprecatedInProjectResolver(666) { resolver => + resolvers.create(nxv + genString(), resolver.value.project, InProjectValue(resolver.value.priority)) + } + } + } + + def givenADeprecatedInProjectResolver(priority: Int)(test: ResolverResource => IO[ResolverResource]) = { + val id = nxv + genString() + val resolverValue = InProjectValue(Priority.unsafe(priority)) + resolvers.create(id, projectRef, resolverValue).accepted + val deprecatedResolver = resolvers.deprecate(id, projectRef, 1).accepted + test(deprecatedResolver).accepted + } } }