From f6986f9940eb8bfe80509eb3d6c9e61422e8851f Mon Sep 17 00:00:00 2001 From: Oliver <20188437+olivergrabinski@users.noreply.github.com> Date: Mon, 18 Mar 2024 12:09:55 +0100 Subject: [PATCH] Break circular dependencies between resources and schemas (#4799) --- .../nexus/delta/wiring/ResourcesModule.scala | 50 +++++---- .../nexus/delta/wiring/SchemasModule.scala | 37 +++--- .../bluebrain/nexus/delta/MainSuite.scala | 3 + .../delta/routes/ResourcesRoutesSpec.scala | 22 ++-- .../delta/routes/SchemasRoutesSpec.scala | 8 +- .../nexus/delta/sdk/model/Fetch.scala | 10 ++ .../resolvers/ResolverContextResolution.scala | 12 +- .../sdk/resolvers/ResolverResolution.scala | 8 +- .../sdk/resolvers/ResourceResolution.scala | 27 ++--- .../delta/sdk/resources/FetchResource.scala | 47 ++++++++ .../nexus/delta/sdk/resources/Resources.scala | 9 +- .../delta/sdk/resources/ResourcesImpl.scala | 33 ++---- .../nexus/delta/sdk/schemas/FetchSchema.scala | 44 ++++++++ .../delta/sdk/schemas/SchemaImports.scala | 15 +-- .../nexus/delta/sdk/schemas/Schemas.scala | 7 +- .../nexus/delta/sdk/schemas/SchemasImpl.scala | 24 ++-- .../generators/ResolverResolutionGen.scala | 3 +- .../generators/ResourceResolutionGen.scala | 5 +- .../sdk/resolvers/MultiResolutionSuite.scala | 2 +- .../ResolverContextResolutionSuite.scala | 6 +- .../resolvers/ResolverResolutionSuite.scala | 4 +- .../sdk/resources/ResourcesImplSpec.scala | 19 ++-- .../sdk/resources/ValidateResourceSuite.scala | 7 +- .../delta/sdk/schemas/SchemasImplSuite.scala | 8 +- .../nexus/delta/sourcing/ScopedEventLog.scala | 95 +--------------- .../sourcing/ScopedEventLogReadOnly.scala | 106 ++++++++++++++++++ .../delta/sourcing/state/ScopedStateGet.scala | 13 ++- 27 files changed, 384 insertions(+), 240 deletions(-) create mode 100644 delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/model/Fetch.scala create mode 100644 delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/FetchResource.scala create mode 100644 delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/FetchSchema.scala create mode 100644 delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/ScopedEventLogReadOnly.scala diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResourcesModule.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResourcesModule.scala index c8ad4f5630..ff9084e782 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResourcesModule.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResourcesModule.scala @@ -19,18 +19,23 @@ import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ApiMappings import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.ResourceResolution import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.{ResolverContextResolution, Resolvers, ResourceResolution} +import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources.{ResourceDefinition, ResourceLog} +import ch.epfl.bluebrain.nexus.delta.sdk.resources._ import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{Resource, ResourceEvent} -import ch.epfl.bluebrain.nexus.delta.sdk.resources.{DetectChange, Resources, ResourcesConfig, ResourcesImpl, ValidateResource} -import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.FetchSchema import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema import ch.epfl.bluebrain.nexus.delta.sdk.sse.SseEncoder -import ch.epfl.bluebrain.nexus.delta.sourcing.Transactors +import ch.epfl.bluebrain.nexus.delta.sourcing.{ScopedEventLog, Transactors} import izumi.distage.model.definition.{Id, ModuleDef} /** * Resources wiring */ object ResourcesModule extends ModuleDef { + make[ResourceResolution[Schema]].from { (aclCheck: AclCheck, resolvers: Resolvers, fetchSchema: FetchSchema) => + ResourceResolution.schemaResource(aclCheck, resolvers, fetchSchema, excludeDeprecated = false) + } + make[ValidateResource].from { (resourceResolution: ResourceResolution[Schema], rcr: RemoteContextResolution @Id("aggregate")) => ValidateResource(resourceResolution)(rcr) @@ -40,26 +45,30 @@ object ResourcesModule extends ModuleDef { make[DetectChange].from { (config: ResourcesConfig) => DetectChange(config.skipUpdateNoChange) } + make[ResourceDefinition].from { (validateResource: ValidateResource, detectChange: DetectChange, clock: Clock[IO]) => + Resources.definition(validateResource, detectChange, clock) + } + + make[ResourceLog].from { (scopedDefinition: ResourceDefinition, config: ResourcesConfig, xas: Transactors) => + ScopedEventLog(scopedDefinition, config.eventLog, xas) + } + + make[FetchResource].from { (scopedLog: ResourceLog) => + FetchResource(scopedLog) + } + make[Resources].from { ( - validate: ValidateResource, - detectChange: DetectChange, + resourceLog: ResourceLog, fetchContext: FetchContext, - config: ResourcesConfig, resolverContextResolution: ResolverContextResolution, api: JsonLdApi, - xas: Transactors, - clock: Clock[IO], uuidF: UUIDF ) => ResourcesImpl( - validate, - detectChange, + resourceLog, fetchContext, - resolverContextResolution, - config, - xas, - clock + resolverContextResolution )( api, uuidF @@ -67,12 +76,13 @@ object ResourcesModule extends ModuleDef { } make[ResolverContextResolution].from { - (aclCheck: AclCheck, resolvers: Resolvers, resources: Resources, rcr: RemoteContextResolution @Id("aggregate")) => - ResolverContextResolution(aclCheck, resolvers, resources, rcr) - } - - make[ResourceResolution[Schema]].from { (aclCheck: AclCheck, resolvers: Resolvers, schemas: Schemas) => - ResourceResolution.schemaResource(aclCheck, resolvers, schemas, excludeDeprecated = false) + ( + aclCheck: AclCheck, + resolvers: Resolvers, + rcr: RemoteContextResolution @Id("aggregate"), + fetchResource: FetchResource + ) => + ResolverContextResolution(aclCheck, resolvers, rcr, fetchResource) } make[ResourcesRoutes].from { diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/SchemasModule.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/SchemasModule.scala index 8cff18a434..e6ebc102e4 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/SchemasModule.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/SchemasModule.scala @@ -21,11 +21,12 @@ import ch.epfl.bluebrain.nexus.delta.sdk.model.metrics.ScopedEventMetricEncoder import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ApiMappings import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.{ResolverContextResolution, Resolvers} -import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources +import ch.epfl.bluebrain.nexus.delta.sdk.resources.FetchResource +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas.{SchemaDefinition, SchemaLog} +import ch.epfl.bluebrain.nexus.delta.sdk.schemas._ import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.{Schema, SchemaEvent} -import ch.epfl.bluebrain.nexus.delta.sdk.schemas.{SchemaImports, Schemas, SchemasImpl, ValidateSchema} import ch.epfl.bluebrain.nexus.delta.sdk.sse.SseEncoder -import ch.epfl.bluebrain.nexus.delta.sourcing.Transactors +import ch.epfl.bluebrain.nexus.delta.sourcing.{ScopedEventLog, Transactors} import izumi.distage.model.definition.{Id, ModuleDef} /** @@ -40,26 +41,32 @@ object SchemasModule extends ModuleDef { } + make[SchemaDefinition].from { (validateSchema: ValidateSchema, clock: Clock[IO]) => + Schemas.definition(validateSchema, clock) + } + + make[SchemaLog].from { (scopedDefinition: SchemaDefinition, config: AppConfig, xas: Transactors) => + ScopedEventLog(scopedDefinition, config.schemas.eventLog, xas) + } + + make[FetchSchema].from { (schemaLog: SchemaLog) => + FetchSchema(schemaLog) + } + make[Schemas].from { ( + schemaLog: SchemaLog, fetchContext: FetchContext, schemaImports: SchemaImports, api: JsonLdApi, - validate: ValidateSchema, resolverContextResolution: ResolverContextResolution, - config: AppConfig, - xas: Transactors, - clock: Clock[IO], uuidF: UUIDF ) => SchemasImpl( + schemaLog, fetchContext, schemaImports, - resolverContextResolution, - validate, - config.schemas, - xas, - clock + resolverContextResolution )(api, uuidF) } @@ -67,10 +74,10 @@ object SchemasModule extends ModuleDef { ( aclCheck: AclCheck, resolvers: Resolvers, - resources: Resources, - schemas: Schemas + fetchSchema: FetchSchema, + fetchResource: FetchResource ) => - SchemaImports(aclCheck, resolvers, schemas, resources) + SchemaImports(aclCheck, resolvers, fetchSchema, fetchResource) } make[SchemasRoutes].from { diff --git a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/MainSuite.scala b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/MainSuite.scala index c1b35b6513..7aadcbac6b 100644 --- a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/MainSuite.scala +++ b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/MainSuite.scala @@ -17,6 +17,7 @@ import munit.catseffect.IOFixture import munit.{AnyFixture, CatsEffectSuite} import java.nio.file.{Files, Paths} +import scala.concurrent.duration.Duration /** * Test class that allows to check that across core and plugins: @@ -26,6 +27,8 @@ import java.nio.file.{Files, Paths} */ class MainSuite extends NexusSuite with MainSuite.Fixture { + override val munitIOTimeout: Duration = Duration(60, "s") + private val pluginsParentPath = Paths.get("target/plugins").toAbsolutePath private val pluginLoaderConfig = PluginLoaderConfig(pluginsParentPath.toString) diff --git a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutesSpec.scala b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutesSpec.scala index 1a34cd9b27..a0cf8286f1 100644 --- a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutesSpec.scala +++ b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutesSpec.scala @@ -17,16 +17,17 @@ import ch.epfl.bluebrain.nexus.delta.sdk.generators.{ProjectGen, ResourceResolut import ch.epfl.bluebrain.nexus.delta.sdk.identities.IdentitiesDummy import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.implicits._ +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.FetchF import ch.epfl.bluebrain.nexus.delta.sdk.model.{IdSegmentRef, ResourceUris} import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.resources import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContextDummy import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ApiMappings import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverContextResolution -import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.FetchResource import ch.epfl.bluebrain.nexus.delta.sdk.resources.NexusSource.DecodingOption -import ch.epfl.bluebrain.nexus.delta.sdk.resources.{DetectChange, Resources, ResourcesConfig, ResourcesImpl, ValidateResource} +import ch.epfl.bluebrain.nexus.delta.sdk.resources._ import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema import ch.epfl.bluebrain.nexus.delta.sdk.utils.BaseRouteSpec +import ch.epfl.bluebrain.nexus.delta.sourcing.ScopedEventLog import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Anonymous, Authenticated, Group, Subject, User} import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} @@ -87,7 +88,7 @@ class ResourcesRoutesSpec extends BaseRouteSpec with CatsIOValues { private val aclCheck = AclSimpleCheck().accepted - private val fetchSchema: (ResourceRef, ProjectRef) => FetchResource[Schema] = { + private val fetchSchema: (ResourceRef, ProjectRef) => FetchF[Schema] = { case (ref, _) if ref.iri == schema2.id => IO.pure(Some(SchemaGen.resourceFor(schema2, deprecated = true))) case (ref, _) if ref.iri == schema1.id => IO.pure(Some(SchemaGen.resourceFor(schema1))) case (ref, _) if ref.iri == schema3.id => IO.pure(Some(SchemaGen.resourceFor(schema3))) @@ -101,14 +102,17 @@ class ResourcesRoutesSpec extends BaseRouteSpec with CatsIOValues { private val resolverContextResolution: ResolverContextResolution = ResolverContextResolution(rcr) private def routesWithDecodingOption(implicit decodingOption: DecodingOption): (Route, Resources) = { + val resourceDef = Resources.definition(validator, DetectChange(enabled = true), clock) + val scopedLog = ScopedEventLog( + resourceDef, + ResourcesConfig(eventLogConfig, decodingOption, skipUpdateNoChange = true).eventLog, + xas + ) + val resources = ResourcesImpl( - validator, - DetectChange(enabled = true), + scopedLog, fetchContext, - resolverContextResolution, - ResourcesConfig(eventLogConfig, decodingOption, skipUpdateNoChange = true), - xas, - clock + resolverContextResolution ) ( Route.seal( diff --git a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutesSpec.scala b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutesSpec.scala index 472d6c3c0e..1d803ba63b 100644 --- a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutesSpec.scala +++ b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutesSpec.scala @@ -23,8 +23,9 @@ import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.schemas import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContextDummy import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ApiMappings import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverContextResolution -import ch.epfl.bluebrain.nexus.delta.sdk.schemas.{SchemaImports, SchemasConfig, SchemasImpl, ValidateSchema} +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.{SchemaImports, Schemas, SchemasConfig, SchemasImpl, ValidateSchema} import ch.epfl.bluebrain.nexus.delta.sdk.utils.BaseRouteSpec +import ch.epfl.bluebrain.nexus.delta.sourcing.ScopedEventLog import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Anonymous, Authenticated, Group, Subject, User} import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef import ch.epfl.bluebrain.nexus.testkit.ce.IOFromMap @@ -78,12 +79,15 @@ class SchemasRoutesSpec extends BaseRouteSpec with IOFromMap with CatsIOValues { private val config = SchemasConfig(eventLogConfig) + private val schemaDef = Schemas.definition(ValidateSchema.apply, clock) + private lazy val schemaLog = ScopedEventLog(schemaDef, config.eventLog, xas) + private lazy val routes = Route.seal( SchemasRoutes( identities, aclCheck, - SchemasImpl(fetchContext, schemaImports, resolverContextResolution, ValidateSchema.apply, config, xas, clock), + SchemasImpl(schemaLog, fetchContext, schemaImports, resolverContextResolution), groupDirectives, IndexingAction.noop ) diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/model/Fetch.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/model/Fetch.scala new file mode 100644 index 0000000000..97affef962 --- /dev/null +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/model/Fetch.scala @@ -0,0 +1,10 @@ +package ch.epfl.bluebrain.nexus.delta.sdk.model + +import cats.effect.IO + +object Fetch { + + type Fetch[R] = IO[Option[R]] + type FetchF[R] = Fetch[ResourceF[R]] + +} diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverContextResolution.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverContextResolution.scala index c14a36c5a4..574edc2f8d 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverContextResolution.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverContextResolution.scala @@ -11,7 +11,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverContextResolution.{logger, ProjectRemoteContext} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.ResourceResolution import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResourceResolutionReport -import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources +import ch.epfl.bluebrain.nexus.delta.sdk.resources.FetchResource import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.Resource import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} @@ -111,18 +111,18 @@ object ResolverContextResolution { * how to check acls * @param resolvers * a resolvers instance - * @param resources - * a resource instance * @param rcr * a previously defined 'RemoteContextResolution' + * @param fetchResource + * how to fetch a resource */ def apply( aclCheck: AclCheck, resolvers: Resolvers, - resources: Resources, - rcr: RemoteContextResolution + rcr: RemoteContextResolution, + fetchResource: FetchResource ): ResolverContextResolution = - apply(rcr, ResourceResolution.dataResource(aclCheck, resolvers, resources, excludeDeprecated = false)) + apply(rcr, ResourceResolution.dataResource(aclCheck, resolvers, fetchResource, excludeDeprecated = false)) /** * A [[ResolverContextResolution]] that never resolves diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolution.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolution.scala index 1ca104be73..47a24db77a 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolution.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolution.scala @@ -2,18 +2,18 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resolvers import cats.effect.IO import cats.implicits._ - import ch.epfl.bluebrain.nexus.delta.kernel.search.Pagination import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdContent +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.Fetch import ch.epfl.bluebrain.nexus.delta.sdk.model.ResourceF import ch.epfl.bluebrain.nexus.delta.sdk.model.search.ResultEntry import ch.epfl.bluebrain.nexus.delta.sdk.model.search.SearchParams.ResolverSearchParams import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions import ch.epfl.bluebrain.nexus.delta.sdk.permissions.model.Permission -import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.{DeprecationCheck, Fetch, ResolverResolutionResult} +import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.{DeprecationCheck, ResolverResolutionResult} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.IdentityResolution.{ProvidedIdentities, UseCurrentCaller} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.Resolver.{CrossProjectResolver, InProjectResolver} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverResolutionRejection._ @@ -227,10 +227,6 @@ object ResolverResolution { */ type ResourceResolution[R] = ResolverResolution[ResourceF[R]] - type Fetch[R] = IO[Option[R]] - - type FetchResource[R] = IO[Option[ResourceF[R]]] - type ResolverResolutionResult[R] = (ResolverReport, Option[R]) private val resolverSearchParams = ResolverSearchParams(deprecated = Some(false), filter = _ => IO.pure(true)) diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResourceResolution.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResourceResolution.scala index a225041e0d..0f350c51b0 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResourceResolution.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResourceResolution.scala @@ -3,14 +3,15 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resolvers import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.FetchF import ch.epfl.bluebrain.nexus.delta.sdk.model.ResourceF import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions import ch.epfl.bluebrain.nexus.delta.sdk.permissions.model.Permission -import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.{DeprecationCheck, FetchResource, ResourceResolution} +import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.{DeprecationCheck, ResourceResolution} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.Resolver -import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources +import ch.epfl.bluebrain.nexus.delta.sdk.resources.FetchResource import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.Resource -import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.FetchSchema import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Identity, ProjectRef, ResourceRef} @@ -31,7 +32,7 @@ object ResourceResolution { checkAcls: (ProjectRef, Set[Identity]) => IO[Boolean], listResolvers: ProjectRef => IO[List[Resolver]], fetchResolver: (Iri, ProjectRef) => IO[Resolver], - fetch: (ResourceRef, ProjectRef) => FetchResource[R], + fetch: (ResourceRef, ProjectRef) => FetchF[R], excludeDeprecated: Boolean ): ResourceResolution[R] = new ResolverResolution( @@ -60,7 +61,7 @@ object ResourceResolution { def apply[R]( aclCheck: AclCheck, resolvers: Resolvers, - fetchResource: (ResourceRef, ProjectRef) => FetchResource[R], + fetchResource: (ResourceRef, ProjectRef) => FetchF[R], readPermission: Permission, excludeDeprecated: Boolean ): ResourceResolution[R] = @@ -73,21 +74,21 @@ object ResourceResolution { * how to check acls * @param resolvers * a resolvers instance - * @param resources - * a resources instance + * @param fetchResource + * how to fetch a resource * @param excludeDeprecated * to exclude deprecated resources from the resolution */ def dataResource( aclCheck: AclCheck, resolvers: Resolvers, - resources: Resources, + fetchResource: FetchResource, excludeDeprecated: Boolean ): ResourceResolution[Resource] = apply( aclCheck, resolvers, - (ref: ResourceRef, project: ProjectRef) => resources.fetch(ref, project).redeem(_ => None, Some(_)), + fetchResource.fetch _, Permissions.resources.read, excludeDeprecated ) @@ -99,21 +100,21 @@ object ResourceResolution { * how to check acls * @param resolvers * a resolvers instance - * @param schemas - * a schemas instance + * @param fetchSchema + * how to fetch a schema * @param excludeDeprecated * to exclude deprecated resources from the resolution */ def schemaResource( aclCheck: AclCheck, resolvers: Resolvers, - schemas: Schemas, + fetchSchema: FetchSchema, excludeDeprecated: Boolean ): ResourceResolution[Schema] = apply( aclCheck, resolvers, - (ref: ResourceRef, project: ProjectRef) => schemas.fetch(ref, project).redeem(_ => None, Some(_)), + fetchSchema.fetch _, Permissions.schemas.read, excludeDeprecated ) diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/FetchResource.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/FetchResource.scala new file mode 100644 index 0000000000..0460ca1d3e --- /dev/null +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/FetchResource.scala @@ -0,0 +1,47 @@ +package ch.epfl.bluebrain.nexus.delta.sdk.resources + +import cats.effect.IO +import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.FetchF +import ch.epfl.bluebrain.nexus.delta.sdk.model.IdSegmentRef +import ch.epfl.bluebrain.nexus.delta.sdk.model.IdSegmentRef.{Latest, Revision, Tag} +import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{ResourceNotFound, RevisionNotFound, TagNotFound} +import ch.epfl.bluebrain.nexus.delta.sdk.resources.model._ +import ch.epfl.bluebrain.nexus.delta.sourcing.ScopedEventLogReadOnly +import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} + +trait FetchResource { + + /** Fetch the referenced resource in the given project */ + def fetch(ref: ResourceRef, project: ProjectRef): FetchF[Resource] + + def stateOrNotFound(id: IdSegmentRef, iri: Iri, ref: ProjectRef): IO[ResourceState] + +} + +object FetchResource { + + def apply( + log: ScopedEventLogReadOnly[Iri, ResourceState, ResourceRejection] + ): FetchResource = { + + def notFound(iri: Iri, ref: ProjectRef) = ResourceNotFound(iri, ref) + + new FetchResource { + override def fetch(ref: ResourceRef, project: ProjectRef): FetchF[Resource] = { + stateOrNotFound(IdSegmentRef(ref), ref.iri, project).attempt + .map(_.toOption) + .map(_.map(_.toResource)) + } + + override def stateOrNotFound(id: IdSegmentRef, iri: Iri, ref: ProjectRef): IO[ResourceState] = + id match { + case Latest(_) => log.stateOr(ref, iri, notFound(iri, ref)) + case Revision(_, rev) => log.stateOr(ref, iri, rev, notFound(iri, ref), RevisionNotFound) + case Tag(_, tag) => log.stateOr(ref, iri, tag, notFound(iri, ref), TagNotFound(tag)) + } + } + + } + +} diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/Resources.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/Resources.scala index 7d3ea4e277..695a9ba519 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/Resources.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/Resources.scala @@ -19,7 +19,7 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.ScopedEntityDefinition.Tagger import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import ch.epfl.bluebrain.nexus.delta.sourcing.model._ -import ch.epfl.bluebrain.nexus.delta.sourcing.{ScopedEntityDefinition, StateMachine} +import ch.epfl.bluebrain.nexus.delta.sourcing.{ScopedEntityDefinition, ScopedEventLog, StateMachine} import io.circe.Json /** @@ -505,6 +505,11 @@ object Resources { } } + type ResourceDefinition = + ScopedEntityDefinition[Iri, ResourceState, ResourceCommand, ResourceEvent, ResourceRejection] + type ResourceLog = + ScopedEventLog[Iri, ResourceState, ResourceCommand, ResourceEvent, ResourceRejection] + /** * Entity definition for [[Resources]] */ @@ -512,7 +517,7 @@ object Resources { validateResource: ValidateResource, detectChange: DetectChange, clock: Clock[IO] - ): ScopedEntityDefinition[Iri, ResourceState, ResourceCommand, ResourceEvent, ResourceRejection] = + ): ResourceDefinition = ScopedEntityDefinition( entityType, StateMachine(None, evaluate(validateResource, detectChange, clock)(_, _), next), diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesImpl.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesImpl.scala index 563a320f6f..d743bea7ee 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesImpl.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesImpl.scala @@ -1,6 +1,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resources -import cats.effect.{Clock, IO} +import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.kernel.Logger import ch.epfl.bluebrain.nexus.delta.kernel.kamon.KamonMetricComponent import ch.epfl.bluebrain.nexus.delta.kernel.utils.UUIDF @@ -10,15 +10,14 @@ import ch.epfl.bluebrain.nexus.delta.sdk._ import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.implicits._ import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdSourceProcessor.JsonLdSourceResolvingParser -import ch.epfl.bluebrain.nexus.delta.sdk.model.IdSegmentRef.{Latest, Revision, Tag} import ch.epfl.bluebrain.nexus.delta.sdk.model._ import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ProjectContext import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverContextResolution -import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources.{entityType, expandIri, expandResourceRef} +import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources.{entityType, expandIri, expandResourceRef, ResourceLog} import ch.epfl.bluebrain.nexus.delta.sdk.resources.ResourcesImpl.{logger, ResourcesLog} import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceCommand._ -import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{NoChangeDetected, ResourceNotFound, RevisionNotFound, TagNotFound} +import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{NoChangeDetected, ResourceNotFound} import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{ResourceCommand, ResourceEvent, ResourceRejection, ResourceState} import ch.epfl.bluebrain.nexus.delta.sourcing._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject @@ -165,17 +164,11 @@ final class ResourcesImpl private ( for { (iri, pc) <- expandWithContext(fetchContext.onRead, projectRef, id.value) schemaRefOpt <- IO.fromEither(expandResourceRef(schemaOpt, pc)) - state <- stateOrNotFound(id, iri, projectRef) + state <- FetchResource(log).stateOrNotFound(id, iri, projectRef) _ <- IO.raiseWhen(schemaRefOpt.exists(_.iri != state.schema.iri))(notFound(iri, projectRef)) } yield state }.span("fetchResource") - private def stateOrNotFound(id: IdSegmentRef, iri: Iri, ref: ProjectRef): IO[ResourceState] = id match { - case Latest(_) => log.stateOr(ref, iri, notFound(iri, ref)) - case Revision(_, rev) => log.stateOr(ref, iri, rev, notFound(iri, ref), RevisionNotFound) - case Tag(_, tag) => log.stateOr(ref, iri, tag, notFound(iri, ref), TagNotFound(tag)) - } - private def notFound(iri: Iri, ref: ProjectRef) = ResourceNotFound(iri, ref) override def fetch( @@ -210,31 +203,23 @@ object ResourcesImpl { /** * Constructs a [[Resources]] instance. * - * @param validateResource - * how to validate resource + * @param scopedLog + * the scoped resource log * @param fetchContext * to fetch the project context * @param contextResolution * the context resolver - * @param config - * the resources config - * @param xas - * the database context */ final def apply( - validateResource: ValidateResource, - detectChange: DetectChange, + scopedLog: ResourceLog, fetchContext: FetchContext, - contextResolution: ResolverContextResolution, - config: ResourcesConfig, - xas: Transactors, - clock: Clock[IO] + contextResolution: ResolverContextResolution )(implicit api: JsonLdApi, uuidF: UUIDF = UUIDF.random ): Resources = new ResourcesImpl( - ScopedEventLog(Resources.definition(validateResource, detectChange, clock), config.eventLog, xas), + scopedLog, fetchContext, JsonLdSourceResolvingParser(contextResolution, uuidF) ) diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/FetchSchema.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/FetchSchema.scala new file mode 100644 index 0000000000..484549f69f --- /dev/null +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/FetchSchema.scala @@ -0,0 +1,44 @@ +package ch.epfl.bluebrain.nexus.delta.sdk.schemas + +import cats.effect.IO +import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.FetchF +import ch.epfl.bluebrain.nexus.delta.sdk.model.IdSegmentRef +import ch.epfl.bluebrain.nexus.delta.sdk.model.IdSegmentRef.{Latest, Revision, Tag} +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaRejection.{RevisionNotFound, SchemaNotFound, TagNotFound} +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.{Schema, SchemaRejection, SchemaState} +import ch.epfl.bluebrain.nexus.delta.sourcing.ScopedEventLogReadOnly +import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} + +trait FetchSchema { + + /** Fetch the referenced resource in the given project */ + def fetch(ref: ResourceRef, project: ProjectRef): FetchF[Schema] + + def stateOrNotFound(id: IdSegmentRef, iri: Iri, ref: ProjectRef): IO[SchemaState] + +} + +object FetchSchema { + + def apply(log: ScopedEventLogReadOnly[Iri, SchemaState, SchemaRejection]): FetchSchema = { + + def notFound(iri: Iri, ref: ProjectRef) = SchemaNotFound(iri, ref) + + new FetchSchema { + override def fetch(ref: ResourceRef, project: ProjectRef): FetchF[Schema] = + stateOrNotFound(IdSegmentRef(ref), ref.iri, project).attempt + .map(_.toOption) + .map(_.map(_.toResource)) + + override def stateOrNotFound(id: IdSegmentRef, iri: Iri, ref: ProjectRef): IO[SchemaState] = + id match { + case Latest(_) => log.stateOr(ref, iri, notFound(iri, ref)) + case Revision(_, rev) => log.stateOr(ref, iri, rev, notFound(iri, ref), RevisionNotFound) + case Tag(_, tag) => log.stateOr(ref, iri, tag, notFound(iri, ref), TagNotFound(tag)) + } + } + + } + +} diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemaImports.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemaImports.scala index ba887cd476..4a671208ba 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemaImports.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemaImports.scala @@ -3,7 +3,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk.schemas import cats.data.NonEmptyList import cats.effect.IO import cats.implicits._ - import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.owl import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.ExpandedJsonLd @@ -12,7 +11,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResourceResolutionReport import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.{Resolvers, ResourceResolution} -import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources +import ch.epfl.bluebrain.nexus.delta.sdk.resources.FetchResource import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.Resource import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaRejection.InvalidSchemaResolution @@ -98,19 +97,21 @@ object SchemaImports { final def apply( aclCheck: AclCheck, resolvers: Resolvers, - schemas: Schemas, - resources: Resources + fetchSchema: FetchSchema, + fetchResource: FetchResource ): SchemaImports = { - def resolveSchema(ref: ResourceRef, projectRef: ProjectRef, caller: Caller) = + def resolveSchema(ref: ResourceRef, projectRef: ProjectRef, caller: Caller) = ResourceResolution - .schemaResource(aclCheck, resolvers, schemas, excludeDeprecated = true) + .schemaResource(aclCheck, resolvers, fetchSchema, excludeDeprecated = true) .resolve(ref, projectRef)(caller) .map(_.map(_.value)) + def resolveResource(ref: ResourceRef, projectRef: ProjectRef, caller: Caller) = ResourceResolution - .dataResource(aclCheck, resolvers, resources, excludeDeprecated = true) + .dataResource(aclCheck, resolvers, fetchResource, excludeDeprecated = true) .resolve(ref, projectRef)(caller) .map(_.map(_.value)) + new SchemaImports(resolveSchema, resolveResource) } diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/Schemas.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/Schemas.scala index e4f4f8629b..242479d4a6 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/Schemas.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/Schemas.scala @@ -19,7 +19,7 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.ScopedEntityDefinition.Tagger import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import ch.epfl.bluebrain.nexus.delta.sourcing.model._ -import ch.epfl.bluebrain.nexus.delta.sourcing.{ScopedEntityDefinition, StateMachine} +import ch.epfl.bluebrain.nexus.delta.sourcing.{ScopedEntityDefinition, ScopedEventLog, StateMachine} import io.circe.Json /** @@ -375,13 +375,16 @@ object Schemas { } } + type SchemaDefinition = ScopedEntityDefinition[Iri, SchemaState, SchemaCommand, SchemaEvent, SchemaRejection] + type SchemaLog = ScopedEventLog[Iri, SchemaState, SchemaCommand, SchemaEvent, SchemaRejection] + /** * Entity definition for [[Schemas]] */ def definition( validate: ValidateSchema, clock: Clock[IO] - ): ScopedEntityDefinition[Iri, SchemaState, SchemaCommand, SchemaEvent, SchemaRejection] = + ): SchemaDefinition = ScopedEntityDefinition( entityType, StateMachine(None, evaluate(validate, clock), next), diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImpl.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImpl.scala index 70553aed35..cd886c800c 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImpl.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImpl.scala @@ -1,6 +1,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk.schemas -import cats.effect.{Clock, IO} +import cats.effect.IO import cats.syntax.all._ import ch.epfl.bluebrain.nexus.delta.kernel.kamon.KamonMetricComponent import ch.epfl.bluebrain.nexus.delta.kernel.utils.UUIDF @@ -12,14 +12,13 @@ import ch.epfl.bluebrain.nexus.delta.sdk._ import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.implicits._ import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdSourceProcessor.JsonLdSourceResolvingParser -import ch.epfl.bluebrain.nexus.delta.sdk.model.IdSegmentRef.{Latest, Revision, Tag} import ch.epfl.bluebrain.nexus.delta.sdk.model._ 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.schemas.Schemas.{entityType, expandIri} +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas.{entityType, expandIri, SchemaLog} import ch.epfl.bluebrain.nexus.delta.sdk.schemas.SchemasImpl.SchemasLog import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaCommand._ -import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaRejection.{RevisionNotFound, SchemaNotFound, TagNotFound} +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaRejection.SchemaNotFound import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.{SchemaCommand, SchemaEvent, SchemaRejection, SchemaState} import ch.epfl.bluebrain.nexus.delta.sourcing._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject @@ -145,13 +144,7 @@ final class SchemasImpl private ( for { pc <- fetchContext.onRead(projectRef) iri <- expandIri(id.value, pc) - state <- id match { - case Latest(_) => log.stateOr(projectRef, iri, SchemaNotFound(iri, projectRef)) - case Revision(_, rev) => - log.stateOr(projectRef, iri, rev, SchemaNotFound(iri, projectRef), RevisionNotFound) - case Tag(_, tag) => - log.stateOr(projectRef, iri, tag, SchemaNotFound(iri, projectRef), TagNotFound(tag)) - } + state <- FetchSchema(log).stateOrNotFound(id, iri, projectRef) } yield state.toResource }.span("fetchSchema") @@ -173,13 +166,10 @@ object SchemasImpl { * Constructs a [[Schemas]] instance. */ final def apply( + scopedLog: SchemaLog, fetchContext: FetchContext, schemaImports: SchemaImports, - contextResolution: ResolverContextResolution, - validate: ValidateSchema, - config: SchemasConfig, - xas: Transactors, - clock: Clock[IO] + contextResolution: ResolverContextResolution )(implicit api: JsonLdApi, uuidF: UUIDF @@ -191,7 +181,7 @@ object SchemasImpl { uuidF ) new SchemasImpl( - ScopedEventLog(Schemas.definition(validate, clock), config.eventLog, xas), + scopedLog, fetchContext, schemaImports, parser diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResolverResolutionGen.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResolverResolutionGen.scala index ecae57e709..423111e7d8 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResolverResolutionGen.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResolverResolutionGen.scala @@ -3,8 +3,9 @@ package ch.epfl.bluebrain.nexus.delta.sdk.generators import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.nxv +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.Fetch import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution -import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.{DeprecationCheck, Fetch} +import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.DeprecationCheck import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverRejection.ResolverNotFound import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Identity, ProjectRef, ResourceRef} diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResourceResolutionGen.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResourceResolutionGen.scala index 47b53aa1f4..4c68bf21c2 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResourceResolutionGen.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResourceResolutionGen.scala @@ -3,7 +3,8 @@ package ch.epfl.bluebrain.nexus.delta.sdk.generators import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.nxv -import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.{FetchResource, ResourceResolution} +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.FetchF +import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.ResourceResolution import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResourceResolution import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverRejection.ResolverNotFound import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Identity, ProjectRef, ResourceRef} @@ -19,7 +20,7 @@ object ResourceResolutionGen { */ def singleInProject[R]( projectRef: ProjectRef, - fetchResource: (ResourceRef, ProjectRef) => FetchResource[R] + fetchResource: (ResourceRef, ProjectRef) => FetchF[R] ): ResourceResolution[R] = { val resolver = ResolverGen.inProject(nxv + "in-project", projectRef) diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/MultiResolutionSuite.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/MultiResolutionSuite.scala index 69a29cf2e2..c43e22a84c 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/MultiResolutionSuite.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/MultiResolutionSuite.scala @@ -6,10 +6,10 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.encoder.JsonLdEncoder import ch.epfl.bluebrain.nexus.delta.sdk.generators.{ResolverResolutionGen, ResourceGen, SchemaGen} import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdContent +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.Fetch import ch.epfl.bluebrain.nexus.delta.sdk.model.{IdSegmentRef, ResourceF} import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContextDummy import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.{ApiMappings, ProjectContext} -import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.Fetch import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverRejection.{InvalidResolution, InvalidResolverResolution} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverResolutionRejection.ResourceNotFound import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResourceResolutionReport.ResolverReport diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverContextResolutionSuite.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverContextResolutionSuite.scala index b4684ddfe7..72d10701ee 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverContextResolutionSuite.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverContextResolutionSuite.scala @@ -4,17 +4,17 @@ import akka.http.scaladsl.model.Uri import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{contexts, nxv, schemas} -import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.{CompactedJsonLd, ExpandedJsonLd} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContext.StaticContext import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContextResolutionError.RemoteContextNotAccessible import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteContextResolution} +import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.{CompactedJsonLd, ExpandedJsonLd} import ch.epfl.bluebrain.nexus.delta.sdk.generators.ResourceResolutionGen import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.implicits._ +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.FetchF import ch.epfl.bluebrain.nexus.delta.sdk.model.{ResourceF, ResourceUris, Tags} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverContextResolution.ProjectRemoteContext -import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.FetchResource import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.Resource import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.User import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef.Latest @@ -62,7 +62,7 @@ class ResolverContextResolutionSuite extends NexusSuite { ) ) - def fetchResource: (ResourceRef, ProjectRef) => FetchResource[Resource] = { (r: ResourceRef, p: ProjectRef) => + def fetchResource: (ResourceRef, ProjectRef) => FetchF[Resource] = { (r: ResourceRef, p: ProjectRef) => (r, p) match { case (Latest(id), `project`) if resourceId == id => IO.pure(Some(resource)) case _ => IO.none diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolutionSuite.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolutionSuite.scala index 5c98a6a6e3..5fa1e0053e 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolutionSuite.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolutionSuite.scala @@ -7,8 +7,8 @@ import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{nxv, schemas} import ch.epfl.bluebrain.nexus.delta.sdk.generators.ResolverGen import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.FetchF import ch.epfl.bluebrain.nexus.delta.sdk.model.{ResourceF, ResourceUris} -import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.FetchResource import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolutionSuite.ResourceExample import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.IdentityResolution.{ProvidedIdentities, UseCurrentCaller} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.Resolver.CrossProjectResolver @@ -95,7 +95,7 @@ class ResolverResolutionSuite extends NexusSuite { def fetchResource( projectRef: ProjectRef - ): (ResourceRef, ProjectRef) => FetchResource[ResourceExample] = + ): (ResourceRef, ProjectRef) => FetchF[ResourceExample] = (_: ResourceRef, p: ProjectRef) => p match { case `projectRef` => IO.pure(Some(resource)) diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesImplSpec.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesImplSpec.scala index 96c26af3a9..ed93effc4f 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesImplSpec.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesImplSpec.scala @@ -8,13 +8,14 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteContextResolution} import ch.epfl.bluebrain.nexus.delta.sdk.generators.{ProjectGen, ResourceGen, ResourceResolutionGen, SchemaGen} import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.FetchF import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdRejection._ import ch.epfl.bluebrain.nexus.delta.sdk.model.{IdSegment, IdSegmentRef, Tags} import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContextDummy import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ApiMappings import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ProjectRejection.{ProjectIsDeprecated, ProjectNotFound} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverContextResolution -import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.{FetchResource, ResourceResolution} +import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.ResourceResolution import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResourceResolutionReport.ResolverReport import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.{ResolverResolutionRejection, ResourceResolutionReport} import ch.epfl.bluebrain.nexus.delta.sdk.resources.NexusSource.DecodingOption @@ -23,6 +24,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection._ import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ import ch.epfl.bluebrain.nexus.delta.sdk.{ConfigFixtures, DataResource} +import ch.epfl.bluebrain.nexus.delta.sourcing.ScopedEventLog import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef.{Latest, Revision} import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag @@ -69,13 +71,13 @@ class ResourcesImplSpec private val schema2 = SchemaGen.schema(schema.Person, project.ref, schemaSource.removeKeys(keywords.id)) private val schema3 = SchemaGen.schema(nxv + "myschema3", project.ref, schemaSource.removeKeys(keywords.id)) - private val fetchSchema: (ResourceRef, ProjectRef) => FetchResource[Schema] = { + private val fetchSchema: (ResourceRef, ProjectRef) => FetchF[Schema] = { case (ref, _) if ref.iri == schema2.id => IO.pure(Some(SchemaGen.resourceFor(schema2, deprecated = true))) case (ref, _) if ref.iri == schema1.id => IO.pure(Some(SchemaGen.resourceFor(schema1))) case (ref, _) if ref.iri == schema3.id => IO.pure(Some(SchemaGen.resourceFor(schema3))) case _ => IO.none } - private val resourceResolution: ResourceResolution[Schema] = + private val resourceResolution: ResourceResolution[Schema] = ResourceResolutionGen.singleInProject(projectRef, fetchSchema) private val fetchContext = FetchContextDummy( @@ -93,14 +95,13 @@ class ResourcesImplSpec (r, p, _) => resources.fetch(r, p).attempt.map(_.left.map(_ => ResourceResolutionReport())) ) + private val resourceDef = Resources.definition(ValidateResource(resourceResolution), detectChanges, clock) + private lazy val resourceLog = ScopedEventLog(resourceDef, eventLogConfig, xas) + private lazy val resources: Resources = ResourcesImpl( - ValidateResource(resourceResolution), - detectChanges, + resourceLog, fetchContext, - resolverContextResolution, - config, - xas, - clock + resolverContextResolution ) private val simpleSourcePaylod = (id: IdSegment) => json"""{ "@id": "$id", "some": "content" }""" diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResourceSuite.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResourceSuite.scala index 841b524570..6e33db8e10 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResourceSuite.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResourceSuite.scala @@ -9,7 +9,8 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.{CompactedJsonLd, ExpandedJsonLd import ch.epfl.bluebrain.nexus.delta.sdk.generators.{ResourceResolutionGen, SchemaGen} import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdAssembly -import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.{FetchResource, ResourceResolution} +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.FetchF +import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.ResourceResolution import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResourceResolutionReport.ResolverReport import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.{ResolverResolutionRejection, ResourceResolutionReport} import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection._ @@ -56,12 +57,12 @@ class ValidateResourceSuite extends NexusSuite { private val unconstrained = ResourceRef.Revision(schemas.resources, 1) - private val fetchSchema: (ResourceRef, ProjectRef) => FetchResource[Schema] = { + private val fetchSchema: (ResourceRef, ProjectRef) => FetchF[Schema] = { case (ref, p) if ref.iri == schemaId && p == project => schema.map(Some(_)) case (ref, p) if ref.iri == deprecatedSchemaId && p == project => deprecatedSchema.map(Some(_)) case _ => IO.none } - private val schemaResolution: ResourceResolution[Schema] = + private val schemaResolution: ResourceResolution[Schema] = ResourceResolutionGen.singleInProject(project, fetchSchema) private def sourceWithId(id: Iri) = diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImplSuite.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImplSuite.scala index 0e6403bd06..bd06c3827a 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImplSuite.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImplSuite.scala @@ -10,8 +10,8 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteCon import ch.epfl.bluebrain.nexus.delta.rdf.shacl.ShaclShapesGraph import ch.epfl.bluebrain.nexus.delta.sdk.ConfigFixtures import ch.epfl.bluebrain.nexus.delta.sdk.generators.{ProjectGen, SchemaGen} -import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdRejection.UnexpectedId import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller +import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdRejection.UnexpectedId import ch.epfl.bluebrain.nexus.delta.sdk.model.{IdSegmentRef, Tags} import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContextDummy import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ApiMappings @@ -20,6 +20,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverContextResolution import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaRejection._ import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ +import ch.epfl.bluebrain.nexus.delta.sourcing.ScopedEventLog import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Identity, Label, ProjectRef} @@ -75,8 +76,11 @@ class SchemasImplSuite extends NexusSuite with Doobie.Fixture with ConfigFixture private val fetchContext = FetchContextDummy(Map(project.ref -> project.context), Set(projectDeprecated.ref)) private val config = SchemasConfig(eventLogConfig) + private val schemaDef = Schemas.definition(ValidateSchema.apply, clock) + private lazy val schemaLog = ScopedEventLog(schemaDef, config.eventLog, xas) + private lazy val schemas: Schemas = - SchemasImpl(fetchContext, schemaImports, resolverContextResolution, ValidateSchema.apply, config, xas, clock) + SchemasImpl(schemaLog, fetchContext, schemaImports, resolverContextResolution) private def schemaSourceWithId(id: Iri) = { source deepMerge json"""{"@id": "$id"}""" diff --git a/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/ScopedEventLog.scala b/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/ScopedEventLog.scala index 096f98cb95..eda2440165 100644 --- a/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/ScopedEventLog.scala +++ b/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/ScopedEventLog.scala @@ -33,55 +33,8 @@ import scala.concurrent.duration.FiniteDuration * Unsuccessful commands result in rejections returned to the caller context without any events being generated or * state transitions applied. */ -trait ScopedEventLog[Id, S <: ScopedState, Command, E <: ScopedEvent, Rejection <: Throwable] { - - /** - * Get the latest state for the entity with the given __id__ in the given project - * - * @param ref - * the project the entity belongs in - * @param id - * the entity identifier - * @param notFound - * if no state is found, fails with this rejection - */ - def stateOr[R <: Rejection](ref: ProjectRef, id: Id, notFound: => R): IO[S] - - /** - * Get the state for the entity with the given __id__ at the given __tag__ in the given project - * @param ref - * the project the entity belongs in - * @param id - * the entity identifier - * @param tag - * the tag - * @param notFound - * if no state is found, fails with this rejection - * @param tagNotFound - * if no state is found with the provided tag, fails with this rejection - */ - def stateOr[R <: Rejection](ref: ProjectRef, id: Id, tag: Tag, notFound: => R, tagNotFound: => R): IO[S] - - /** - * Get the state for the entity with the given __id__ at the given __revision__ in the given project - * @param ref - * the project the entity belongs in - * @param id - * the entity identifier - * @param rev - * the revision - * @param notFound - * if no state is found, fails with this rejection - * @param invalidRevision - * if the revision of the resulting state does not match with the one provided - */ - def stateOr[R <: Rejection]( - ref: ProjectRef, - id: Id, - rev: Int, - notFound: => R, - invalidRevision: (Int, Int) => R - ): IO[S] +trait ScopedEventLog[Id, S <: ScopedState, Command, E <: ScopedEvent, Rejection <: Throwable] + extends ScopedEventLogReadOnly[Id, S, Rejection] { /** * Evaluates the argument __command__ in the context of entity identified by __id__. @@ -113,50 +66,6 @@ trait ScopedEventLog[Id, S <: ScopedState, Command, E <: ScopedEvent, Rejection */ def dryRun(ref: ProjectRef, id: Id, command: Command): IO[(E, S)] - /** - * Allow to stream all latest states within [[Elem.SuccessElem]] s without applying transformation - * @param scope - * to filter returned states - * @param offset - * offset to start from - */ - def currentStates(scope: Scope, offset: Offset): SuccessElemStream[S] - - /** - * Allow to stream all latest states from the beginning within [[Elem.SuccessElem]] s without applying transformation - * @param scope - * to filter returned states - */ - def currentStates(scope: Scope): SuccessElemStream[S] = currentStates(scope, Offset.Start) - - /** - * Allow to stream all current states from the provided offset - * @param scope - * to filter returned states - * @param offset - * offset to start from - * @param f - * the function to apply on each state - */ - def currentStates[T](scope: Scope, offset: Offset, f: S => T): Stream[IO, T] - - /** - * Allow to stream all current states from the beginning - * @param scope - * to filter returned states - * @param f - * the function to apply on each state - */ - def currentStates[T](scope: Scope, f: S => T): Stream[IO, T] = currentStates(scope, Offset.Start, f) - - /** - * Stream the state changes continuously from the provided offset. - * @param scope - * to filter returned states - * @param offset - * the start offset - */ - def states(scope: Scope, offset: Offset): SuccessElemStream[S] } object ScopedEventLog { diff --git a/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/ScopedEventLogReadOnly.scala b/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/ScopedEventLogReadOnly.scala new file mode 100644 index 0000000000..9c333428a3 --- /dev/null +++ b/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/ScopedEventLogReadOnly.scala @@ -0,0 +1,106 @@ +package ch.epfl.bluebrain.nexus.delta.sourcing + +import cats.effect.IO +import ch.epfl.bluebrain.nexus.delta.sourcing.model._ +import ch.epfl.bluebrain.nexus.delta.sourcing.offset.Offset +import ch.epfl.bluebrain.nexus.delta.sourcing.state.State.ScopedState +import fs2.Stream + +/** + * Read-only event log for project-scoped entities + */ +trait ScopedEventLogReadOnly[Id, S <: ScopedState, Rejection <: Throwable] { + + /** + * Get the latest state for the entity with the given __id__ in the given project + * + * @param ref + * the project the entity belongs in + * @param id + * the entity identifier + * @param notFound + * if no state is found, fails with this rejection + */ + def stateOr[R <: Rejection](ref: ProjectRef, id: Id, notFound: => R): IO[S] + + /** + * Get the state for the entity with the given __id__ at the given __tag__ in the given project + * @param ref + * the project the entity belongs in + * @param id + * the entity identifier + * @param tag + * the tag + * @param notFound + * if no state is found, fails with this rejection + * @param tagNotFound + * if no state is found with the provided tag, fails with this rejection + */ + def stateOr[R <: Rejection](ref: ProjectRef, id: Id, tag: Tag, notFound: => R, tagNotFound: => R): IO[S] + + /** + * Get the state for the entity with the given __id__ at the given __revision__ in the given project + * @param ref + * the project the entity belongs in + * @param id + * the entity identifier + * @param rev + * the revision + * @param notFound + * if no state is found, fails with this rejection + * @param invalidRevision + * if the revision of the resulting state does not match with the one provided + */ + def stateOr[R <: Rejection]( + ref: ProjectRef, + id: Id, + rev: Int, + notFound: => R, + invalidRevision: (Int, Int) => R + ): IO[S] + + /** + * Allow to stream all latest states within [[Elem.SuccessElem]] s without applying transformation + * @param scope + * to filter returned states + * @param offset + * offset to start from + */ + def currentStates(scope: Scope, offset: Offset): SuccessElemStream[S] + + /** + * Allow to stream all latest states from the beginning within [[Elem.SuccessElem]] s without applying transformation + * @param scope + * to filter returned states + */ + def currentStates(scope: Scope): SuccessElemStream[S] = currentStates(scope, Offset.Start) + + /** + * Allow to stream all current states from the provided offset + * @param scope + * to filter returned states + * @param offset + * offset to start from + * @param f + * the function to apply on each state + */ + def currentStates[T](scope: Scope, offset: Offset, f: S => T): Stream[IO, T] + + /** + * Allow to stream all current states from the beginning + * @param scope + * to filter returned states + * @param f + * the function to apply on each state + */ + def currentStates[T](scope: Scope, f: S => T): Stream[IO, T] = currentStates(scope, Offset.Start, f) + + /** + * Stream the state changes continuously from the provided offset. + * @param scope + * to filter returned states + * @param offset + * the start offset + */ + def states(scope: Scope, offset: Offset): SuccessElemStream[S] +} diff --git a/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/state/ScopedStateGet.scala b/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/state/ScopedStateGet.scala index b377cbf9ce..1c6d18e6a2 100644 --- a/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/state/ScopedStateGet.scala +++ b/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/state/ScopedStateGet.scala @@ -8,11 +8,22 @@ import doobie.{ConnectionIO, Get, Put} object ScopedStateGet { def apply[Id: Put, S: Get](tpe: EntityType, project: ProjectRef, id: Id, tag: Tag): ConnectionIO[Option[S]] = - sql"""SELECT value FROM scoped_states WHERE type = $tpe AND org = ${project.organization} AND project = ${project.project} AND id = $id AND tag = $tag""" + sql"""SELECT value FROM scoped_states WHERE type = $tpe AND org = ${project.organization} AND project = ${project.project} AND id = $id AND tag = $tag""" + .query[S] + .option + + def apply[Id: Put, S: Get](tpe: EntityType, project: ProjectRef, id: Id, rev: Int): ConnectionIO[Option[S]] = + sql"""SELECT value FROM scoped_states WHERE type = $tpe AND org = ${project.organization} AND project = ${project.project} AND id = $id AND rev = $rev""" .query[S] .option def latest[Id: Put, S: Get](tpe: EntityType, project: ProjectRef, id: Id): ConnectionIO[Option[S]] = apply(tpe, project, id, Latest) + def tag[Id: Put, S: Get](tpe: EntityType, project: ProjectRef, id: Id, tag: Tag): ConnectionIO[Option[S]] = + apply(tpe, project, id, tag) + + def rev[Id: Put, S: Get](tpe: EntityType, project: ProjectRef, id: Id, rev: Int): ConnectionIO[Option[S]] = + apply(tpe, project, id, rev) + }