From 7ef5876944b54fefcdd07cdf2aa307991c1e778e Mon Sep 17 00:00:00 2001 From: Daniel Bell Date: Wed, 18 Oct 2023 14:08:59 +0100 Subject: [PATCH] Migrate resources to Cats Effect (#4370) * Migrate ResourceRoutes to Cats Effect * Migrate ResourcesTrialRoutes to Cats Effect * Migrate ResourcesTrialRoutes to Cats Effect * Migrate Resources to Cats Effect * Migrate ResourceGenerationResult to Cats Effect * Migrate ValidateResource to Cats Effect * Use .rethrow * Migrate ResourceShift to Cats Effect * Migrate Expanded and CompactedJsonLd to Cats Effect * use ioJsonContentOf to fix test --- .../nexus/delta/routes/ResourcesRoutes.scala | 63 ++++++++++------ .../delta/routes/ResourcesTrialRoutes.scala | 32 ++++---- .../nexus/delta/wiring/MultiFetchModule.scala | 3 +- .../nexus/delta/wiring/ResourcesModule.scala | 8 +- .../delta/wiring/ResourcesTrialModule.scala | 8 +- .../delta/routes/ResourcesRoutesSpec.scala | 3 +- .../routes/ResourcesTrialRoutesSpec.scala | 24 +++--- .../migration/MigrateEffectSyntax.scala | 2 + .../blazegraph/model/BlazegraphView.scala | 3 +- .../client/BlazegraphClientSpec.scala | 4 +- .../compositeviews/CompositeSink.scala | 4 +- .../compositeviews/model/CompositeView.scala | 3 +- .../ElasticSearchPluginModule.scala | 2 +- .../model/ElasticSearchView.scala | 3 +- .../graph/analytics/JsonLdDocumentSpec.scala | 2 + .../search/SearchSparqlQuerySpec.scala | 2 +- .../plugins/storage/files/model/File.scala | 3 +- .../storage/storages/model/Storage.scala | 3 +- .../nexus/delta/rdf/ExplainResult.scala | 4 +- .../nexus/delta/rdf/graph/Graph.scala | 11 ++- .../delta/rdf/jsonld/CompactedJsonLd.scala | 17 ++--- .../delta/rdf/jsonld/ExpandedJsonLd.scala | 11 ++- .../rdf/jsonld/encoder/JsonLdEncoder.scala | 4 +- .../delta/rdf/shacl/ShaclEngineSpec.scala | 2 + .../rdf/shacl/ValidationReportSpec.scala | 2 + .../nexus/delta/sdk/IndexingAction.scala | 2 +- .../nexus/delta/sdk/ResourceShift.scala | 27 +++---- .../nexus/delta/sdk/ResourceShifts.scala | 27 ++++--- .../sdk/jsonld/JsonLdSourceProcessor.scala | 10 ++- .../delta/sdk/projects/model/Project.scala | 3 +- .../sdk/resolvers/ResourceResolution.scala | 3 +- .../nexus/delta/sdk/resources/Resources.scala | 62 ++++++++-------- .../delta/sdk/resources/ResourcesImpl.scala | 74 +++++++++---------- .../delta/sdk/resources/ResourcesTrial.scala | 43 +++++------ .../sdk/resources/ValidateResource.scala | 44 +++++++---- .../delta/sdk/resources/model/Resource.scala | 4 +- .../model/ResourceGenerationResult.scala | 22 +++--- .../sdk/stream/GraphResourceStream.scala | 5 +- .../delta/sdk/generators/ResourceGen.scala | 9 +-- .../delta/sdk/generators/SchemaGen.scala | 10 +-- .../resolvers/model/ResolverValueSpec.scala | 5 +- .../sdk/resources/ResourcesImplSpec.scala | 8 +- .../delta/sdk/resources/ResourcesSpec.scala | 11 +-- .../sdk/resources/ResourcesTrialSuite.scala | 26 ++++--- .../resources/ValidateResourceFixture.scala | 10 +-- .../sdk/schemas/SchemaImportsSuite.scala | 3 +- .../delta/sourcing/EphemeralDefinition.scala | 4 +- .../nexus/testkit/ce/CatsEffectSuite.scala | 1 + 48 files changed, 349 insertions(+), 287 deletions(-) diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutes.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutes.scala index f8bd30a8d1..69d21e1798 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutes.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesRoutes.scala @@ -3,6 +3,7 @@ package ch.epfl.bluebrain.nexus.delta.routes import akka.http.scaladsl.model.StatusCodes.{Created, OK} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ +import cats.effect.IO import cats.syntax.all._ import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.schemas @@ -13,7 +14,7 @@ import ch.epfl.bluebrain.nexus.delta.routes.ResourcesRoutes.asSourceWithMetadata import ch.epfl.bluebrain.nexus.delta.sdk._ import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck import ch.epfl.bluebrain.nexus.delta.sdk.circe.CirceUnmarshalling -import ch.epfl.bluebrain.nexus.delta.sdk.directives.DeltaDirectives._ +import ch.epfl.bluebrain.nexus.delta.sdk.ce.DeltaDirectives._ import ch.epfl.bluebrain.nexus.delta.sdk.directives.{AuthDirectives, DeltaSchemeDirectives} import ch.epfl.bluebrain.nexus.delta.sdk.fusion.FusionConfig import ch.epfl.bluebrain.nexus.delta.sdk.identities.Identities @@ -28,8 +29,6 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{Resource, ResourceReje import ch.epfl.bluebrain.nexus.delta.sdk.resources.{NexusSource, Resources} import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef import io.circe.{Json, Printer} -import monix.bio.IO -import monix.execution.Scheduler /** * The resource routes @@ -53,7 +52,6 @@ final class ResourcesRoutes( index: IndexingAction.Execute[Resource] )(implicit baseUri: BaseUri, - s: Scheduler, cr: RemoteContextResolution, ordering: JsonKeyOrdering, fusionConfig: FusionConfig, @@ -86,8 +84,9 @@ final class ResourcesRoutes( Created, resources .create(ref, resourceSchema, source.value, tag) - .tapEval(indexUIO(ref, _, mode)) + .flatTap(indexUIO(ref, _, mode)) .map(_.void) + .attemptNarrow[ResourceRejection] ) } }, @@ -102,8 +101,9 @@ final class ResourcesRoutes( Created, resources .create(ref, schema, source.value, tag) - .tapEval(indexUIO(ref, _, mode)) + .flatTap(indexUIO(ref, _, mode)) .map(_.void) + .attemptNarrow[ResourceRejection] .rejectWhen(wrongJsonOrNotFound) ) } @@ -123,8 +123,9 @@ final class ResourcesRoutes( Created, resources .create(id, ref, schema, source.value, tag) - .tapEval(indexUIO(ref, _, mode)) + .flatTap(indexUIO(ref, _, mode)) .map(_.void) + .attemptNarrow[ResourceRejection] .rejectWhen(wrongJsonOrNotFound) ) case (Some(rev), source, _) => @@ -132,8 +133,9 @@ final class ResourcesRoutes( emit( resources .update(id, ref, schemaOpt, rev, source.value) - .tapEval(indexUIO(ref, _, mode)) + .flatTap(indexUIO(ref, _, mode)) .map(_.void) + .attemptNarrow[ResourceRejection] .rejectWhen(wrongJsonOrNotFound) ) } @@ -145,8 +147,9 @@ final class ResourcesRoutes( emit( resources .deprecate(id, ref, schemaOpt, rev) - .tapEval(indexUIO(ref, _, mode)) + .flatTap(indexUIO(ref, _, mode)) .map(_.void) + .attemptNarrow[ResourceRejection] .rejectWhen(wrongJsonOrNotFound) ) } @@ -160,7 +163,7 @@ final class ResourcesRoutes( emit( resources .fetch(id, ref, schemaOpt) - .leftWiden[ResourceRejection] + .attemptNarrow[ResourceRejection] .rejectWhen(wrongJsonOrNotFound) ) } @@ -174,8 +177,9 @@ final class ResourcesRoutes( OK, resources .refresh(id, ref, schemaOpt) - .tapEval(indexUIO(ref, _, mode)) + .flatTap(indexUIO(ref, _, mode)) .map(_.void) + .attemptNarrow[ResourceRejection] .rejectWhen(wrongJsonOrNotFound) ) } @@ -190,11 +194,16 @@ final class ResourcesRoutes( resources .fetch(id, ref, schemaOpt) .flatMap(asSourceWithMetadata) + .attemptNarrow[ResourceRejection] ) } else { - val sourceIO = resources.fetch(id, ref, schemaOpt).map(_.value.source) - val value = sourceIO.leftWiden[ResourceRejection] - emit(value.rejectWhen(wrongJsonOrNotFound)) + emit( + resources + .fetch(id, ref, schemaOpt) + .map(_.value.source) + .attemptNarrow[ResourceRejection] + .rejectWhen(wrongJsonOrNotFound) + ) } } } @@ -202,8 +211,12 @@ final class ResourcesRoutes( // Get remote contexts pathPrefix("remote-contexts") { (get & idSegmentRef(id) & pathEndOrSingleSlash & authorizeFor(ref, Read)) { id => - val remoteContextsIO = resources.fetchState(id, ref, schemaOpt).map(_.remoteContexts) - emit(remoteContextsIO.leftWiden[ResourceRejection]) + emit( + resources + .fetchState(id, ref, schemaOpt) + .map(_.remoteContexts) + .attemptNarrow[ResourceRejection] + ) } }, // Tag a resource @@ -211,8 +224,13 @@ final class ResourcesRoutes( concat( // Fetch a resource tags (get & idSegmentRef(id) & pathEndOrSingleSlash & authorizeFor(ref, Read)) { id => - val tagsIO = resources.fetch(id, ref, schemaOpt).map(_.value.tags) - emit(tagsIO.leftWiden[ResourceRejection].rejectWhen(wrongJsonOrNotFound)) + emit( + resources + .fetch(id, ref, schemaOpt) + .map(_.value.tags) + .attemptNarrow[ResourceRejection] + .rejectWhen(wrongJsonOrNotFound) + ) }, // Tag a resource (post & parameter("rev".as[Int]) & pathEndOrSingleSlash) { rev => @@ -222,8 +240,9 @@ final class ResourcesRoutes( Created, resources .tag(id, ref, schemaOpt, tag, tagRev, rev) - .tapEval(indexUIO(ref, _, mode)) + .flatTap(indexUIO(ref, _, mode)) .map(_.void) + .attemptNarrow[ResourceRejection] .rejectWhen(wrongJsonOrNotFound) ) } @@ -237,8 +256,9 @@ final class ResourcesRoutes( emit( resources .deleteTag(id, ref, schemaOpt, tag, rev) - .tapEval(indexUIO(ref, _, mode)) + .flatTap(indexUIO(ref, _, mode)) .map(_.void) + .attemptNarrow[ResourceRejection] .rejectOn[ResourceNotFound] ) } @@ -274,7 +294,6 @@ object ResourcesRoutes { index: IndexingAction.Execute[Resource] )(implicit baseUri: BaseUri, - s: Scheduler, cr: RemoteContextResolution, ordering: JsonKeyOrdering, fusionConfig: FusionConfig, @@ -283,7 +302,7 @@ object ResourcesRoutes { def asSourceWithMetadata( resource: ResourceF[Resource] - )(implicit baseUri: BaseUri, cr: RemoteContextResolution): IO[ResourceRejection, Json] = + )(implicit baseUri: BaseUri, cr: RemoteContextResolution): IO[Json] = AnnotatedSource(resource, resource.value.source).mapError(e => InvalidJsonLdFormat(Some(resource.id), e)) } diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesTrialRoutes.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesTrialRoutes.scala index bfd6e47f16..14602280b2 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesTrialRoutes.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesTrialRoutes.scala @@ -2,8 +2,8 @@ package ch.epfl.bluebrain.nexus.delta.routes import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Route +import cats.effect.IO import cats.syntax.all._ -import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.schemas import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContextResolution import ch.epfl.bluebrain.nexus.delta.rdf.utils.JsonKeyOrdering @@ -11,8 +11,8 @@ import ch.epfl.bluebrain.nexus.delta.routes.ResourcesTrialRoutes.SchemaInput._ import ch.epfl.bluebrain.nexus.delta.routes.ResourcesTrialRoutes.{GenerateSchema, GenerationInput} import ch.epfl.bluebrain.nexus.delta.sdk.SchemaResource import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck +import ch.epfl.bluebrain.nexus.delta.sdk.ce.DeltaDirectives._ import ch.epfl.bluebrain.nexus.delta.sdk.circe.CirceUnmarshalling -import ch.epfl.bluebrain.nexus.delta.sdk.directives.DeltaDirectives._ import ch.epfl.bluebrain.nexus.delta.sdk.directives.{AuthDirectives, DeltaSchemeDirectives} import ch.epfl.bluebrain.nexus.delta.sdk.identities.Identities import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller @@ -29,8 +29,6 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef import io.circe.generic.extras.Configuration import io.circe.generic.extras.semiauto.deriveConfiguredDecoder import io.circe.{Decoder, Json} -import monix.bio.IO -import monix.execution.Scheduler import scala.annotation.nowarn @@ -45,7 +43,6 @@ final class ResourcesTrialRoutes( schemeDirectives: DeltaSchemeDirectives )(implicit baseUri: BaseUri, - s: Scheduler, cr: RemoteContextResolution, ordering: JsonKeyOrdering, decodingOption: DecodingOption @@ -67,7 +64,9 @@ final class ResourcesTrialRoutes( authorizeFor(project, Write).apply { val schemaOpt = underscoreToOption(schema) emit( - resourcesTrial.validate(id, project, schemaOpt).leftWiden[ResourceRejection] + resourcesTrial + .validate(id, project, schemaOpt) + .attemptNarrow[ResourceRejection] ) } } @@ -92,19 +91,27 @@ final class ResourcesTrialRoutes( private def generate(project: ProjectRef, input: GenerationInput)(implicit caller: Caller) = input.schema match { case ExistingSchema(schemaId) => - emit(resourcesTrial.generate(project, schemaId, input.resource).flatMap(_.asJson)) + emit( + resourcesTrial + .generate(project, schemaId, input.resource) + .flatMap(_.asJson) + ) case NewSchema(schemaSource) => emit( - generateSchema(project, schemaSource, caller).flatMap { schema => - resourcesTrial.generate(project, schema, input.resource).flatMap(_.asJson) - } + generateSchema(project, schemaSource, caller) + .flatMap { schema => + resourcesTrial + .generate(project, schema, input.resource) + .flatMap(_.asJson) + } + .attemptNarrow[SchemaRejection] ) } } object ResourcesTrialRoutes { - type GenerateSchema = (ProjectRef, Json, Caller) => IO[SchemaRejection, SchemaResource] + type GenerateSchema = (ProjectRef, Json, Caller) => IO[SchemaResource] sealed private[routes] trait SchemaInput extends Product @@ -146,7 +153,6 @@ object ResourcesTrialRoutes { schemeDirectives: DeltaSchemeDirectives )(implicit baseUri: BaseUri, - s: Scheduler, cr: RemoteContextResolution, ordering: JsonKeyOrdering, decodingOption: DecodingOption @@ -154,7 +160,7 @@ object ResourcesTrialRoutes { new ResourcesTrialRoutes( identities, aclCheck, - (project, source, caller) => schemas.createDryRun(project, source)(caller).toBIO[SchemaRejection], + (project, source, caller) => schemas.createDryRun(project, source)(caller), resourcesTrial, schemeDirectives ) diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/MultiFetchModule.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/MultiFetchModule.scala index dae425ae9a..daf47c1314 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/MultiFetchModule.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/MultiFetchModule.scala @@ -10,6 +10,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.model.BaseUri import ch.epfl.bluebrain.nexus.delta.sdk.multifetch.MultiFetch import ch.epfl.bluebrain.nexus.delta.sdk.multifetch.model.MultiFetchRequest import ch.epfl.bluebrain.nexus.delta.sdk.{PriorityRoute, ResourceShifts} +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import distage.ModuleDef import izumi.distage.model.definition.Id import monix.execution.Scheduler @@ -23,7 +24,7 @@ object MultiFetchModule extends ModuleDef { ) => MultiFetch( aclCheck, - (input: MultiFetchRequest.Input) => shifts.fetch(input.id, input.project) + (input: MultiFetchRequest.Input) => shifts.fetch(input.id, input.project).toUIO ) } 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 4f158debde..99463814d9 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 @@ -1,6 +1,6 @@ package ch.epfl.bluebrain.nexus.delta.wiring -import cats.effect.Clock +import cats.effect.{Clock, IO} import ch.epfl.bluebrain.nexus.delta.Main.pluginsMinPriority import ch.epfl.bluebrain.nexus.delta.config.AppConfig import ch.epfl.bluebrain.nexus.delta.kernel.utils.UUIDF @@ -29,8 +29,6 @@ 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 izumi.distage.model.definition.{Id, ModuleDef} -import monix.bio.UIO -import monix.execution.Scheduler /** * Resources wiring @@ -51,7 +49,7 @@ object ResourcesModule extends ModuleDef { resolverContextResolution: ResolverContextResolution, api: JsonLdApi, xas: Transactors, - clock: Clock[UIO], + clock: Clock[IO], uuidF: UUIDF ) => ResourcesImpl( @@ -85,7 +83,6 @@ object ResourcesModule extends ModuleDef { indexingAction: AggregateIndexingAction, shift: Resource.Shift, baseUri: BaseUri, - s: Scheduler, cr: RemoteContextResolution @Id("aggregate"), ordering: JsonKeyOrdering, fusionConfig: FusionConfig, @@ -99,7 +96,6 @@ object ResourcesModule extends ModuleDef { indexingAction(_, _, _)(shift) )( baseUri, - s, cr, ordering, fusionConfig, diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResourcesTrialModule.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResourcesTrialModule.scala index 7135e8d258..dd97b03a92 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResourcesTrialModule.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResourcesTrialModule.scala @@ -1,6 +1,6 @@ package ch.epfl.bluebrain.nexus.delta.wiring -import cats.effect.Clock +import cats.effect.{Clock, IO} import ch.epfl.bluebrain.nexus.delta.Main.pluginsMinPriority import ch.epfl.bluebrain.nexus.delta.kernel.utils.UUIDF import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.JsonLdApi @@ -20,8 +20,6 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resources.{Resources, ResourcesConfig, import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas import distage.ModuleDef import izumi.distage.model.definition.Id -import monix.bio.UIO -import monix.execution.Scheduler /** * Resources trial wiring @@ -35,7 +33,7 @@ object ResourcesTrialModule extends ModuleDef { fetchContext: FetchContext[ContextRejection], contextResolution: ResolverContextResolution, api: JsonLdApi, - clock: Clock[UIO], + clock: Clock[IO], uuidF: UUIDF ) => ResourcesTrial( @@ -54,7 +52,6 @@ object ResourcesTrialModule extends ModuleDef { resourcesTrial: ResourcesTrial, schemeDirectives: DeltaSchemeDirectives, baseUri: BaseUri, - s: Scheduler, cr: RemoteContextResolution @Id("aggregate"), ordering: JsonKeyOrdering, config: ResourcesConfig @@ -67,7 +64,6 @@ object ResourcesTrialModule extends ModuleDef { schemeDirectives )( baseUri, - s, cr, ordering, config.decodingOption 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 c40ba4cc62..6ef5ffaf1c 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 @@ -32,11 +32,12 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Anonymous, Authent import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} import ch.epfl.bluebrain.nexus.testkit.bio.IOFromMap +import ch.epfl.bluebrain.nexus.testkit.ce.{CatsIOValues, IOFixedClock} import io.circe.{Json, Printer} import java.util.UUID -class ResourcesRoutesSpec extends BaseRouteSpec with IOFromMap { +class ResourcesRoutesSpec extends BaseRouteSpec with IOFromMap with IOFixedClock with CatsIOValues { private val uuid = UUID.randomUUID() implicit private val uuidF: UUIDF = UUIDF.fixed(uuid) diff --git a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesTrialRoutesSpec.scala b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesTrialRoutesSpec.scala index cd9c914429..c4e1b50f91 100644 --- a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesTrialRoutesSpec.scala +++ b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/ResourcesTrialRoutesSpec.scala @@ -3,6 +3,7 @@ package ch.epfl.bluebrain.nexus.delta.routes import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers.OAuth2BearerToken import akka.http.scaladsl.server.Route +import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.kernel.utils.UrlUtils import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{contexts, nxv, schemas} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords @@ -21,14 +22,13 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resources.NexusSource.DecodingOption import ch.epfl.bluebrain.nexus.delta.sdk.resources.ValidationResult._ import ch.epfl.bluebrain.nexus.delta.sdk.resources._ import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{ProjectContextRejection, ReservedResourceId, ResourceNotFound} -import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{ResourceGenerationResult, ResourceRejection, ResourceState} +import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{ResourceGenerationResult, ResourceState} 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.utils.BaseRouteSpec import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Anonymous, Authenticated, Group} -import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef.Revision -import monix.bio.{IO, UIO} +import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} import java.time.Instant @@ -75,17 +75,17 @@ class ResourcesTrialRoutesSpec extends BaseRouteSpec with ResourceInstanceFixtur private val resourcesTrial = new ResourcesTrial { override def generate(project: ProjectRef, schema: IdSegment, source: NexusSource)(implicit caller: Caller - ): UIO[ResourceGenerationResult] = + ): IO[ResourceGenerationResult] = generate(source, None) override def generate(project: ProjectRef, schema: ResourceF[Schema], source: NexusSource)(implicit caller: Caller - ): UIO[ResourceGenerationResult] = + ): IO[ResourceGenerationResult] = generate(source, Some(schema)) // Successfully generates a resource if `validSource` is passed, fails otherwise - private def generate(source: NexusSource, schemaOpt: Option[ResourceF[Schema]]): UIO[ResourceGenerationResult] = - UIO.pure { + private def generate(source: NexusSource, schemaOpt: Option[ResourceF[Schema]]): IO[ResourceGenerationResult] = + IO.pure { source match { case NexusSource(`validSource`) => ResourceGenerationResult(schemaOpt, Right(resourceF)) case _ => ResourceGenerationResult(schemaOpt, Left(expectedError)) @@ -94,21 +94,21 @@ class ResourcesTrialRoutesSpec extends BaseRouteSpec with ResourceInstanceFixtur override def validate(id: IdSegmentRef, project: ProjectRef, schemaOpt: Option[IdSegment])(implicit caller: Caller - ): IO[ResourceRejection, ValidationResult] = + ): IO[ValidationResult] = (id.value, schemaOpt) match { // Returns a validated result for myId when no schema is provided case (StringSegment("myId") | IriSegment(`myId`), None) => - UIO.pure(Validated(projectRef, ResourceRef.Revision(schemaId, defaultSchemaRevision), defaultReport)) + IO.pure(Validated(projectRef, ResourceRef.Revision(schemaId, defaultSchemaRevision), defaultReport)) // Returns no validation result for myId for `schemas.resources` case (StringSegment("myId") | IriSegment(`myId`), Some(IriSegment(schemas.resources))) => - UIO.pure(NoValidation(projectRef)) + IO.pure(NoValidation(projectRef)) case (IriSegment(iri), None) => IO.raiseError(ResourceNotFound(iri, project)) - case _ => IO.terminate(new IllegalStateException("Should not happen !")) + case _ => IO.raiseError(new IllegalStateException("Should not happen !")) } } private val generateSchema: GenerateSchema = { - case (_, `schemaSource`, _) => UIO.pure(SchemaGen.resourceFor(schema1)) + case (_, `schemaSource`, _) => IO.pure(SchemaGen.resourceFor(schema1)) case _ => IO.raiseError(SchemaShaclEngineRejection(nxv + "invalid", "Invalid schema")) } diff --git a/delta/kernel/src/main/scala/ch/epfl/bluebrain/nexus/delta/kernel/effect/migration/MigrateEffectSyntax.scala b/delta/kernel/src/main/scala/ch/epfl/bluebrain/nexus/delta/kernel/effect/migration/MigrateEffectSyntax.scala index c1c4d6d25a..f9c2b1fdba 100644 --- a/delta/kernel/src/main/scala/ch/epfl/bluebrain/nexus/delta/kernel/effect/migration/MigrateEffectSyntax.scala +++ b/delta/kernel/src/main/scala/ch/epfl/bluebrain/nexus/delta/kernel/effect/migration/MigrateEffectSyntax.scala @@ -41,4 +41,6 @@ final class CatsIOToBioOps[A](private val io: IO[A]) extends AnyVal { } def toUIO: UIO[A] = BIO.from(io).hideErrors + + def toTask: Task[A] = Task.from(io) } diff --git a/delta/plugins/blazegraph/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/model/BlazegraphView.scala b/delta/plugins/blazegraph/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/model/BlazegraphView.scala index 0c4be12686..7f82f3ce15 100644 --- a/delta/plugins/blazegraph/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/model/BlazegraphView.scala +++ b/delta/plugins/blazegraph/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/model/BlazegraphView.scala @@ -17,6 +17,7 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import io.circe.generic.extras.Configuration import io.circe.generic.extras.semiauto.deriveConfiguredEncoder +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import io.circe.syntax._ import io.circe.{Encoder, Json, JsonObject} @@ -196,7 +197,7 @@ object BlazegraphView { def shift(views: BlazegraphViews)(implicit baseUri: BaseUri): Shift = ResourceShift.withMetadata[BlazegraphViewState, BlazegraphView, Metadata]( BlazegraphViews.entityType, - (ref, project) => views.fetch(IdSegmentRef(ref), project), + (ref, project) => views.fetch(IdSegmentRef(ref), project).toCatsIO, state => state.toResource, value => JsonLdContent(value, value.value.source, Some(value.value.metadata)) ) diff --git a/delta/plugins/blazegraph/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/client/BlazegraphClientSpec.scala b/delta/plugins/blazegraph/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/client/BlazegraphClientSpec.scala index 14fd67bf78..5f64269aa0 100644 --- a/delta/plugins/blazegraph/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/client/BlazegraphClientSpec.scala +++ b/delta/plugins/blazegraph/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/blazegraph/client/BlazegraphClientSpec.scala @@ -22,6 +22,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.model.ComponentDescription.ServiceDescr import ch.epfl.bluebrain.nexus.delta.sdk.model.Name import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ import ch.epfl.bluebrain.nexus.testkit.blazegraph.BlazegraphDocker +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues import ch.epfl.bluebrain.nexus.testkit.{EitherValuable, IOValues, TestHelpers, TestMatchers} import io.circe.Json import monix.execution.Scheduler @@ -46,7 +47,8 @@ class BlazegraphClientSpec(docker: BlazegraphDocker) with Eventually with Inspectors with TestMatchers - with IOValues { + with IOValues + with CatsIOValues { implicit private val sc: Scheduler = Scheduler.global implicit private val httpCfg: HttpClientConfig = httpClientConfig diff --git a/delta/plugins/composite-views/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/CompositeSink.scala b/delta/plugins/composite-views/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/CompositeSink.scala index 911ec085e0..027242b678 100644 --- a/delta/plugins/composite-views/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/CompositeSink.scala +++ b/delta/plugins/composite-views/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/CompositeSink.scala @@ -13,6 +13,7 @@ import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.model.CompositeViewP import ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch.client.ElasticSearchClient.Refresh import ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch.client.{ElasticSearchClient, IndexLabel} import ch.epfl.bluebrain.nexus.delta.plugins.elasticsearch.indexing.{ElasticSearchSink, GraphResourceToDocument} +import ch.epfl.bluebrain.nexus.delta.rdf.RdfError import ch.epfl.bluebrain.nexus.delta.rdf.graph.Graph import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdJavaApi} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteContextResolution} @@ -24,6 +25,7 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.state.GraphResource import ch.epfl.bluebrain.nexus.delta.sourcing.stream.Elem import ch.epfl.bluebrain.nexus.delta.sourcing.stream.Elem.{DroppedElem, FailedElem, SuccessElem} import ch.epfl.bluebrain.nexus.delta.sourcing.stream.Operation.Sink +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import fs2.Chunk import monix.bio.Task import shapeless.Typeable @@ -124,7 +126,7 @@ final class Batch[SinkFormat]( fullGraph .replaceRootNode(iri"${gr.id}/alias") .toCompactedJsonLd(ContextValue.empty) - .flatMap(_.toGraph) + .flatMap(_.toGraph.toBIO[RdfError]) .map(g => gr.copy(graph = g.replaceRootNode(gr.id))) } diff --git a/delta/plugins/composite-views/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/model/CompositeView.scala b/delta/plugins/composite-views/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/model/CompositeView.scala index ccfabdea33..18d68166dd 100644 --- a/delta/plugins/composite-views/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/model/CompositeView.scala +++ b/delta/plugins/composite-views/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/model/CompositeView.scala @@ -27,6 +27,7 @@ import java.time.Instant import java.util.UUID import scala.annotation.nowarn import scala.concurrent.duration.FiniteDuration +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ /** * Representation of a composite view. @@ -170,7 +171,7 @@ object CompositeView { def shift(views: CompositeViews)(implicit baseUri: BaseUri): Shift = ResourceShift.withMetadata[CompositeViewState, CompositeView, Metadata]( CompositeViews.entityType, - (ref, project) => views.fetch(IdSegmentRef(ref), project), + (ref, project) => views.fetch(IdSegmentRef(ref), project).toCatsIO, state => state.toResource, value => JsonLdContent(value, value.value.source, Some(value.value.metadata)) ) diff --git a/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/ElasticSearchPluginModule.scala b/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/ElasticSearchPluginModule.scala index 3904adacc6..7ae1b1487d 100644 --- a/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/ElasticSearchPluginModule.scala +++ b/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/ElasticSearchPluginModule.scala @@ -270,7 +270,7 @@ class ElasticSearchPluginModule(priority: Int) extends ModuleDef { } make[IdResolution].from { (defaultViewsQuery: DefaultViewsQuery.Elasticsearch, shifts: ResourceShifts) => - new IdResolution(defaultViewsQuery, (resourceRef, projectRef) => shifts.fetch(resourceRef, projectRef)) + new IdResolution(defaultViewsQuery, (resourceRef, projectRef) => shifts.fetch(resourceRef, projectRef).toUIO) } make[IdResolutionRoutes].from { diff --git a/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/model/ElasticSearchView.scala b/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/model/ElasticSearchView.scala index fde5b2d5c2..a6b146f46c 100644 --- a/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/model/ElasticSearchView.scala +++ b/delta/plugins/elasticsearch/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/elasticsearch/model/ElasticSearchView.scala @@ -26,6 +26,7 @@ import io.circe.parser.parse import io.circe.syntax._ import io.circe.{Encoder, Json, JsonObject} import monix.bio.IO +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import java.util.UUID import scala.annotation.nowarn @@ -276,7 +277,7 @@ object ElasticSearchView { ): Shift = ResourceShift.withMetadata[ElasticSearchViewState, ElasticSearchView, Metadata]( ElasticSearchViews.entityType, - (ref, project) => views.fetch(IdSegmentRef(ref), project), + (ref, project) => views.fetch(IdSegmentRef(ref), project).toCatsIO, state => state.toResource(defaultMapping, defaultSettings), value => JsonLdContent(value, value.value.source, Some(value.value.metadata)) ) diff --git a/delta/plugins/graph-analytics/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/graph/analytics/JsonLdDocumentSpec.scala b/delta/plugins/graph-analytics/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/graph/analytics/JsonLdDocumentSpec.scala index 400fec5d4f..945a4a2dae 100644 --- a/delta/plugins/graph-analytics/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/graph/analytics/JsonLdDocumentSpec.scala +++ b/delta/plugins/graph-analytics/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/graph/analytics/JsonLdDocumentSpec.scala @@ -6,6 +6,7 @@ import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.ExpandedJsonLd import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdJavaApi} import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues import ch.epfl.bluebrain.nexus.testkit.{CirceEq, IOValues, TestHelpers} import io.circe.syntax.EncoderOps import monix.bio.UIO @@ -18,6 +19,7 @@ class JsonLdDocumentSpec with Matchers with TestHelpers with IOValues + with CatsIOValues with OptionValues with ContextFixtures with CirceEq { diff --git a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSparqlQuerySpec.scala b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSparqlQuerySpec.scala index 6b0252771e..5bf6ae2eb3 100644 --- a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSparqlQuerySpec.scala +++ b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSparqlQuerySpec.scala @@ -60,7 +60,7 @@ class SearchSparqlQuerySpec private def toNTriples(json: Json): NTriples = { for { - expanded <- toCatsIO(ExpandedJsonLd(json)) + expanded <- ExpandedJsonLd(json) graph <- IO.fromEither(expanded.toGraph) ntriples <- IO.fromEither(graph.toNTriples) } yield ntriples diff --git a/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/files/model/File.scala b/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/files/model/File.scala index a9d21f2fc4..758baec542 100644 --- a/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/files/model/File.scala +++ b/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/files/model/File.scala @@ -16,6 +16,7 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import io.circe.syntax._ +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import io.circe.{Encoder, Json} /** @@ -74,7 +75,7 @@ object File { def shift(files: Files)(implicit baseUri: BaseUri, config: StorageTypeConfig): Shift = ResourceShift.withMetadata[FileState, File, Metadata]( Files.entityType, - (ref, project) => files.fetch(IdSegmentRef(ref), project), + (ref, project) => files.fetch(IdSegmentRef(ref), project).toCatsIO, state => state.toResource, value => JsonLdContent(value, value.value.asJson, Some(value.value.metadata)) ) diff --git a/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/storages/model/Storage.scala b/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/storages/model/Storage.scala index 35a256462b..f249a3344c 100644 --- a/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/storages/model/Storage.scala +++ b/delta/plugins/storage/src/main/scala/ch/epfl/bluebrain/nexus/delta/plugins/storage/storages/model/Storage.scala @@ -14,6 +14,7 @@ import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.ContextValue import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.encoder.JsonLdEncoder +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdContent import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, IdSegmentRef, Tags} import ch.epfl.bluebrain.nexus.delta.sdk.{OrderingFields, ResourceShift} @@ -173,7 +174,7 @@ object Storage { def shift(storages: Storages)(implicit baseUri: BaseUri): Shift = ResourceShift.withMetadata[StorageState, Storage, Metadata]( Storages.entityType, - (ref, project) => storages.fetch(IdSegmentRef(ref), project), + (ref, project) => storages.fetch(IdSegmentRef(ref), project).toCatsIO, state => state.toResource, value => JsonLdContent(value, value.value.source, Some(value.value.metadata)) ) diff --git a/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/ExplainResult.scala b/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/ExplainResult.scala index ccf50adeaa..d09359d44b 100644 --- a/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/ExplainResult.scala +++ b/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/ExplainResult.scala @@ -1,8 +1,8 @@ package ch.epfl.bluebrain.nexus.delta.rdf +import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContext -import monix.bio.IO /** * Gives additional information besides the result of the Json-Ld operation @@ -18,6 +18,6 @@ final case class ExplainResult[A](remoteContexts: Map[Iri, RemoteContext], value def map[B](f: A => B): ExplainResult[B] = copy(value = f(value)) - def evalMap[E, B](f: A => IO[E, B]): IO[E, ExplainResult[B]] = + def evalMap[B](f: A => IO[B]): IO[ExplainResult[B]] = f(value).map { b => copy(value = b) } } diff --git a/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/graph/Graph.scala b/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/graph/Graph.scala index 1c5832744f..dbde998da9 100644 --- a/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/graph/Graph.scala +++ b/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/graph/Graph.scala @@ -230,10 +230,13 @@ final case class Graph private (rootNode: IriOrBNode, value: DatasetGraph) { sel opts: JsonLdOptions ): IO[RdfError, CompactedJsonLd] = { - def computeCompacted(id: IriOrBNode, input: Json) = { + def computeCompacted(id: IriOrBNode, input: Json): IO[RdfError, CompactedJsonLd] = { if (triples.isEmpty) UIO.delay(CompactedJsonLd.unsafe(id, contextValue, JsonObject.empty)) - else if (value.listGraphNodes().asScala.nonEmpty) CompactedJsonLd(id, contextValue, input) - else CompactedJsonLd.frame(id, contextValue, input) + else if (value.listGraphNodes().asScala.nonEmpty) { + CompactedJsonLd(id, contextValue, input).toBIO[RdfError] + } else { + CompactedJsonLd.frame(id, contextValue, input).toBIO[RdfError] + } } if (rootNode.isBNode) @@ -257,7 +260,7 @@ final case class Graph private (rootNode: IriOrBNode, value: DatasetGraph) { sel resolution: RemoteContextResolution, opts: JsonLdOptions ): IO[RdfError, ExpandedJsonLd] = - toCompactedJsonLd(ContextValue.empty).flatMap(_.toExpanded) + toCompactedJsonLd(ContextValue.empty).flatMap(_.toExpanded.toBIO[RdfError]) /** * Merges the current graph with the passed ''that'' while keeping the current ''rootNode'' diff --git a/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/CompactedJsonLd.scala b/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/CompactedJsonLd.scala index 980a948c32..adeefebe5b 100644 --- a/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/CompactedJsonLd.scala +++ b/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/CompactedJsonLd.scala @@ -1,16 +1,15 @@ package ch.epfl.bluebrain.nexus.delta.rdf.jsonld +import cats.effect.IO +import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.{BNode, Iri} import ch.epfl.bluebrain.nexus.delta.rdf.graph.Graph import ch.epfl.bluebrain.nexus.delta.rdf.implicits._ -import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdOptions} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context._ -import ch.epfl.bluebrain.nexus.delta.rdf.{IriOrBNode, RdfError} import io.circe.syntax._ -import io.circe.{Decoder, DecodingFailure, Encoder, Json, JsonObject} -import monix.bio.IO +import io.circe._ /** * Json-LD Compacted Document. CompactedJsonLd specific implementation is entity centric, having always only one root @@ -29,7 +28,7 @@ final case class CompactedJsonLd private (rootId: IriOrBNode, ctx: ContextValue, opts: JsonLdOptions, api: JsonLdApi, resolution: RemoteContextResolution - ): IO[RdfError, ExpandedJsonLd] = + ): IO[ExpandedJsonLd] = ExpandedJsonLd(json).map(_.replaceId(rootId)) /** @@ -39,7 +38,7 @@ final case class CompactedJsonLd private (rootId: IriOrBNode, ctx: ContextValue, opts: JsonLdOptions, api: JsonLdApi, resolution: RemoteContextResolution - ): IO[RdfError, Graph] = + ): IO[Graph] = toExpanded.flatMap(expanded => IO.fromEither(expanded.toGraph)) /** @@ -95,13 +94,12 @@ object CompactedJsonLd { rootId: IriOrBNode, contextValue: ContextValue, input: Json - )(implicit api: JsonLdApi, rcr: RemoteContextResolution, opts: JsonLdOptions): IO[RdfError, CompactedJsonLd] = + )(implicit api: JsonLdApi, rcr: RemoteContextResolution, opts: JsonLdOptions): IO[CompactedJsonLd] = api .compact(input, contextValue) .map { compacted => CompactedJsonLd(rootId, contextValue, compacted.remove(keywords.context)) } - .toBIO[RdfError] /** * Creates a [[CompactedJsonLd]] document framed on the passed ''rootId''. @@ -117,7 +115,7 @@ object CompactedJsonLd { rootId: IriOrBNode, contextValue: ContextValue, input: Json - )(implicit api: JsonLdApi, rcr: RemoteContextResolution, opts: JsonLdOptions): IO[RdfError, CompactedJsonLd] = + )(implicit api: JsonLdApi, rcr: RemoteContextResolution, opts: JsonLdOptions): IO[CompactedJsonLd] = rootId.asIri.map(iri => contextValue.contextObj deepMerge JsonObject(keywords.id -> iri.asJson)) match { case Some(frame) => api @@ -125,7 +123,6 @@ object CompactedJsonLd { .map { compacted => CompactedJsonLd(rootId, contextValue, compacted.remove(keywords.context)) } - .toBIO[RdfError] case _ => apply(rootId, contextValue, input) } diff --git a/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/ExpandedJsonLd.scala b/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/ExpandedJsonLd.scala index 89e90181aa..a06b7f9677 100644 --- a/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/ExpandedJsonLd.scala +++ b/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/ExpandedJsonLd.scala @@ -1,5 +1,6 @@ package ch.epfl.bluebrain.nexus.delta.rdf.jsonld +import cats.effect.IO import cats.implicits._ import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.{BNode, Iri} import ch.epfl.bluebrain.nexus.delta.rdf.RdfError.{InvalidIri, UnexpectedJsonLd} @@ -15,7 +16,6 @@ import ch.epfl.bluebrain.nexus.delta.rdf.{ExplainResult, IriOrBNode, RdfError} import ch.epfl.bluebrain.nexus.delta.rdf.syntax._ import io.circe.syntax._ import io.circe.{Decoder, Encoder, Json, JsonObject} -import monix.bio.{IO, UIO} import java.util.UUID @@ -42,7 +42,7 @@ final case class ExpandedJsonLd private (rootId: IriOrBNode, obj: JsonObject) ex opts: JsonLdOptions, api: JsonLdApi, resolution: RemoteContextResolution - ): IO[RdfError, CompactedJsonLd] = + ): IO[CompactedJsonLd] = CompactedJsonLd(rootId, contextValue, json) /** @@ -185,14 +185,14 @@ object ExpandedJsonLd { api: JsonLdApi, resolution: RemoteContextResolution, opts: JsonLdOptions - ): IO[RdfError, ExpandedJsonLd] = + ): IO[ExpandedJsonLd] = explain(input).map(_.value) def explain(input: Json)(implicit api: JsonLdApi, resolution: RemoteContextResolution, opts: JsonLdOptions - ): IO[RdfError, ExplainResult[ExpandedJsonLd]] = + ): IO[ExplainResult[ExpandedJsonLd]] = api .explainExpand(input) .flatMap { @@ -244,9 +244,8 @@ object ExpandedJsonLd { api .expand(Json.obj(keywords.id -> graphId.asJson, keywords.graph -> expandedSeq.asJson)) .map(_ -> true) - .toBIO[RdfError] else - UIO.pure((expandedSeq, false)) + IO.pure((expandedSeq, false)) result <- IO.fromEither(expanded(expandedSeqFinal)) } yield (result, isGraph) diff --git a/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/encoder/JsonLdEncoder.scala b/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/encoder/JsonLdEncoder.scala index f310ec0487..cbcc6b7253 100644 --- a/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/encoder/JsonLdEncoder.scala +++ b/delta/rdf/src/main/scala/ch/epfl/bluebrain/nexus/delta/rdf/jsonld/encoder/JsonLdEncoder.scala @@ -10,6 +10,7 @@ import ch.epfl.bluebrain.nexus.delta.rdf.{IriOrBNode, RdfError} import io.circe.{Encoder, Json} import io.circe.syntax._ import monix.bio.IO +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ trait JsonLdEncoder[A] { @@ -138,7 +139,7 @@ object JsonLdEncoder { )(implicit opts: JsonLdOptions, api: JsonLdApi, rcr: RemoteContextResolution): IO[RdfError, CompactedJsonLd] = for { (expanded, context) <- expandAndExtractContext(value) - compacted <- expanded.toCompacted(context) + compacted <- expanded.toCompacted(context).toBIO[RdfError] } yield compacted override def expand( @@ -152,6 +153,7 @@ object JsonLdEncoder { val json = value.asJson val context = contextFromJson(json) ExpandedJsonLd(json.replaceContext(context.contextObj)) + .toBIO[RdfError] .map { case expanded if fId(value).isBNode && expanded.rootId.isIri => expanded case expanded => expanded.replaceId(fId(value)) diff --git a/delta/rdf/src/test/scala/ch/epfl/bluebrain/nexus/delta/rdf/shacl/ShaclEngineSpec.scala b/delta/rdf/src/test/scala/ch/epfl/bluebrain/nexus/delta/rdf/shacl/ShaclEngineSpec.scala index 3fcabe9cbf..b834ca4402 100644 --- a/delta/rdf/src/test/scala/ch/epfl/bluebrain/nexus/delta/rdf/shacl/ShaclEngineSpec.scala +++ b/delta/rdf/src/test/scala/ch/epfl/bluebrain/nexus/delta/rdf/shacl/ShaclEngineSpec.scala @@ -7,6 +7,7 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdJavaApi} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContextResolution import ch.epfl.bluebrain.nexus.delta.rdf.syntax._ +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues import ch.epfl.bluebrain.nexus.testkit.{EitherValuable, IOValues, TestHelpers} import org.scalatest.Inspectors import org.scalatest.matchers.should.Matchers @@ -17,6 +18,7 @@ class ShaclEngineSpec with Matchers with TestHelpers with IOValues + with CatsIOValues with EitherValuable with Inspectors { diff --git a/delta/rdf/src/test/scala/ch/epfl/bluebrain/nexus/delta/rdf/shacl/ValidationReportSpec.scala b/delta/rdf/src/test/scala/ch/epfl/bluebrain/nexus/delta/rdf/shacl/ValidationReportSpec.scala index 096e9c54fa..9b5b459be6 100644 --- a/delta/rdf/src/test/scala/ch/epfl/bluebrain/nexus/delta/rdf/shacl/ValidationReportSpec.scala +++ b/delta/rdf/src/test/scala/ch/epfl/bluebrain/nexus/delta/rdf/shacl/ValidationReportSpec.scala @@ -6,6 +6,7 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.ExpandedJsonLd 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.rdf.syntax._ +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues import ch.epfl.bluebrain.nexus.testkit.{EitherValuable, IOValues, TestHelpers} import io.circe.Json import io.circe.syntax._ @@ -21,6 +22,7 @@ class ValidationReportSpec with TestHelpers with EitherValuable with OptionValues + with CatsIOValues with IOValues { implicit val api: JsonLdApi = JsonLdJavaApi.strict diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/IndexingAction.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/IndexingAction.scala index 57364febe9..4ab0bf685f 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/IndexingAction.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/IndexingAction.scala @@ -98,7 +98,7 @@ object IndexingAction { for { _ <- logger.debug(s"Synchronous indexing of resource '$project/${res.id}' has been requested.") // We create the GraphResource wrapped in an `Elem` - elem <- toCatsIO(shift.toGraphResourceElem(project, res)) + elem <- shift.toGraphResourceElem(project, res) errorsPerAction <- internal.traverse(_.apply(project, elem)) errors = errorsPerAction.toList.flatMap(_.map(_.throwable)) _ <- IO.raiseWhen(errors.nonEmpty)(IndexingFailed(res.void, errors)) diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/ResourceShift.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/ResourceShift.scala index 875bf2f102..47f36b9125 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/ResourceShift.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/ResourceShift.scala @@ -1,5 +1,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk +import cats.effect.IO import cats.syntax.all._ import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdJavaApi} @@ -9,6 +10,7 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.encoder.JsonLdEncoder import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdContent import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, ResourceF} import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.sourcing.Serializer import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, ProjectRef, ResourceRef} import ch.epfl.bluebrain.nexus.delta.sourcing.offset.Offset @@ -17,7 +19,6 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.state.State.ScopedState import ch.epfl.bluebrain.nexus.delta.sourcing.stream.Elem import ch.epfl.bluebrain.nexus.delta.sourcing.stream.Elem.{FailedElem, SuccessElem} import io.circe.Json -import monix.bio.{IO, Task, UIO} /** * Defines common operations to retrieve the different resources in a common format for tasks like indexing or @@ -44,7 +45,7 @@ import monix.bio.{IO, Task, UIO} */ abstract class ResourceShift[State <: ScopedState, A, M]( val entityType: EntityType, - fetchResource: (ResourceRef, ProjectRef) => UIO[Option[ResourceF[A]]], + fetchResource: (ResourceRef, ProjectRef) => IO[Option[ResourceF[A]]], valueEncoder: JsonLdEncoder[A], metadataEncoder: Option[JsonLdEncoder[M]] )(implicit serializer: Serializer[_, State], baseUri: BaseUri) { @@ -61,7 +62,7 @@ abstract class ResourceShift[State <: ScopedState, A, M]( * @return * the resource with its original source, its metadata and its encoder */ - def fetch(reference: ResourceRef, project: ProjectRef): UIO[Option[JsonLdContent[A, M]]] = + def fetch(reference: ResourceRef, project: ProjectRef): IO[Option[JsonLdContent[A, M]]] = fetchResource(reference, project).map(_.map(resourceToContent)) protected def resourceToContent(value: ResourceF[A]): JsonLdContent[A, M] @@ -71,30 +72,30 @@ abstract class ResourceShift[State <: ScopedState, A, M]( */ def toGraphResource(json: Json)(implicit cr: RemoteContextResolution - ): Task[GraphResource] = + ): IO[GraphResource] = for { - state <- Task.fromEither(serializer.codec.decodeJson(json)) + state <- IO.fromEither(serializer.codec.decodeJson(json)) resource = toResourceF(state) graph <- toGraphResource(state.project, resource) } yield graph def toGraphResourceElem(project: ProjectRef, resource: ResourceF[A])(implicit cr: RemoteContextResolution - ): UIO[Elem[GraphResource]] = toGraphResource(project, resource).redeem( + ): IO[Elem[GraphResource]] = toGraphResource(project, resource).redeem( err => FailedElem(entityType, resource.id, Some(project), resource.updatedAt, Offset.Start, err, resource.rev), graph => SuccessElem(entityType, resource.id, Some(project), resource.updatedAt, Offset.Start, graph, resource.rev) ) private def toGraphResource(project: ProjectRef, resource: ResourceF[A])(implicit cr: RemoteContextResolution - ): Task[GraphResource] = { + ): IO[GraphResource] = { val content = resourceToContent(resource) val metadata = content.metadata val id = resource.resolvedId for { - graph <- valueJsonLdEncoder.graph(resource.value) + graph <- valueJsonLdEncoder.graph(resource.value).toCatsIO rootGraph = graph.replaceRootNode(id) - resourceMetaGraph <- resourceFJsonLdEncoder.graph(resource.void) + resourceMetaGraph <- resourceFJsonLdEncoder.graph(resource.void).toCatsIO metaGraph <- encodeMetadata(id, metadata) rootMetaGraph = metaGraph.fold(resourceMetaGraph)(_ ++ resourceMetaGraph) typesGraph = rootMetaGraph.rootTypesGraph @@ -115,8 +116,8 @@ abstract class ResourceShift[State <: ScopedState, A, M]( private def encodeMetadata(id: Iri, metadata: Option[M])(implicit cr: RemoteContextResolution) = (metadata, metadataEncoder) match { - case (Some(m), Some(e)) => e.graph(m).map { g => Some(g.replaceRootNode(id)) } - case (_, _) => UIO.none + case (Some(m), Some(e)) => e.graph(m).toCatsIO.map { g => Some(g.replaceRootNode(id)) } + case (_, _) => IO.none } } @@ -142,7 +143,7 @@ object ResourceShift { */ def withMetadata[State <: ScopedState, A, M]( entityType: EntityType, - fetchResource: (ResourceRef, ProjectRef) => IO[_, ResourceF[A]], + fetchResource: (ResourceRef, ProjectRef) => IO[ResourceF[A]], stateToResource: State => ResourceF[A], asContent: ResourceF[A] => JsonLdContent[A, M] )(implicit @@ -180,7 +181,7 @@ object ResourceShift { */ def apply[State <: ScopedState, B]( entityType: EntityType, - fetchResource: (ResourceRef, ProjectRef) => IO[_, ResourceF[B]], + fetchResource: (ResourceRef, ProjectRef) => IO[ResourceF[B]], stateToResource: State => ResourceF[B], asContent: ResourceF[B] => JsonLdContent[B, Nothing] )(implicit diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/ResourceShifts.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/ResourceShifts.scala index 95762a3a61..b7cf43143b 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/ResourceShifts.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/ResourceShifts.scala @@ -1,15 +1,16 @@ package ch.epfl.bluebrain.nexus.delta.sdk +import cats.effect.IO import cats.syntax.all._ +import ch.epfl.bluebrain.nexus.delta.kernel.Logger import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContextResolution import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdContent import ch.epfl.bluebrain.nexus.delta.sourcing.implicits._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, ProjectRef, ResourceRef} import ch.epfl.bluebrain.nexus.delta.sourcing.state.GraphResource import ch.epfl.bluebrain.nexus.delta.sourcing.{EntityCheck, Transactors} -import com.typesafe.scalalogging.Logger import io.circe.Json -import monix.bio.{IO, Task, UIO} +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ /** * Aggregates the different [[ResourceShift]] to perform operations on resources independently of their types @@ -19,18 +20,18 @@ trait ResourceShifts { /** * Fetch a resource as a [[JsonLdContent]] */ - def fetch(reference: ResourceRef, project: ProjectRef): UIO[Option[JsonLdContent[_, _]]] + def fetch(reference: ResourceRef, project: ProjectRef): IO[Option[JsonLdContent[_, _]]] /** * Return a function to decode a json to a [[GraphResource]] according to its [[EntityType]] */ - def decodeGraphResource: (EntityType, Json) => Task[GraphResource] + def decodeGraphResource: (EntityType, Json) => IO[GraphResource] } object ResourceShifts { - private val logger: Logger = Logger[ResourceShifts] + private val logger = Logger.cats[ResourceShifts] private case class NoShiftAvailable(entityType: EntityType) extends Exception(s"No shift is available for entity type $entityType") @@ -40,29 +41,27 @@ object ResourceShifts { ): ResourceShifts = new ResourceShifts { private val shiftsMap = shifts.map { encoder => encoder.entityType -> encoder }.toMap - private def findShift(entityType: EntityType): UIO[ResourceShift[_, _, _]] = IO - .fromOption( - shiftsMap.get(entityType), + private def findShift(entityType: EntityType): IO[ResourceShift[_, _, _]] = IO + .fromOption(shiftsMap.get(entityType))( NoShiftAvailable(entityType) ) - .hideErrors - override def fetch(reference: ResourceRef, project: ProjectRef): UIO[Option[JsonLdContent[_, _]]] = + override def fetch(reference: ResourceRef, project: ProjectRef): IO[Option[JsonLdContent[_, _]]] = for { - entityType <- EntityCheck.findType(reference.iri, project, xas) + entityType <- EntityCheck.findType(reference.iri, project, xas).toCatsIO shift <- entityType.traverse(findShift) resource <- shift.flatTraverse(_.fetch(reference, project)) } yield resource - override def decodeGraphResource: (EntityType, Json) => Task[GraphResource] = { + override def decodeGraphResource: (EntityType, Json) => IO[GraphResource] = { (entityType: EntityType, json: Json) => { for { shift <- findShift(entityType) result <- shift.toGraphResource(json) } yield result - }.tapError { err => - UIO.delay(logger.error(s"Entity of type '$entityType' could not be decoded as a graph resource", err)) + }.onError { err => + logger.error(err)(s"Entity of type '$entityType' could not be decoded as a graph resource") } } } diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/jsonld/JsonLdSourceProcessor.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/jsonld/JsonLdSourceProcessor.scala index d7fbd366d2..ea661fad62 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/jsonld/JsonLdSourceProcessor.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/jsonld/JsonLdSourceProcessor.scala @@ -2,7 +2,7 @@ package ch.epfl.bluebrain.nexus.delta.sdk.jsonld import ch.epfl.bluebrain.nexus.delta.kernel.Mapper import ch.epfl.bluebrain.nexus.delta.kernel.utils.UUIDF -import ch.epfl.bluebrain.nexus.delta.rdf.ExplainResult +import ch.epfl.bluebrain.nexus.delta.rdf.{ExplainResult, RdfError} import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.{BNode, Iri} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdOptions} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.ContextValue.ContextObject @@ -17,6 +17,7 @@ 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.syntax._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import io.circe.syntax._ import io.circe.{Json, JsonObject} import monix.bio.{IO, UIO} @@ -38,10 +39,11 @@ sealed abstract class JsonLdSourceProcessor(implicit api: JsonLdApi) { implicit val opts: JsonLdOptions = JsonLdOptions(base = Some(context.base.iri)) ExpandedJsonLd .explain(source) + .toBIO[RdfError] .flatMap { case result if result.value.isEmpty && source.topContextValueOrEmpty.isEmpty => val ctx = defaultCtx(context) - ExpandedJsonLd.explain(source.addContext(ctx.contextObj)).map(ctx -> _) + ExpandedJsonLd.explain(source.addContext(ctx.contextObj)).map(ctx -> _).toBIO[RdfError] case result => UIO.pure(source.topContextValueOrEmpty -> result) } @@ -119,7 +121,7 @@ object JsonLdSourceProcessor { originalExpanded = result.value iri <- getOrGenerateId(originalExpanded.rootId.asIri, context) expanded = originalExpanded.replaceId(iri) - compacted <- expanded.toCompacted(ctx).mapError(err => InvalidJsonLdFormat(Some(iri), err)) + compacted <- expanded.toCompacted(ctx).toBIO[RdfError].mapError(err => InvalidJsonLdFormat(Some(iri), err)) } yield JsonLdResult(iri, compacted, expanded, result.remoteContexts) }.mapError(rejectionMapper.to) @@ -146,7 +148,7 @@ object JsonLdSourceProcessor { (ctx, result) <- expandSource(context, source.addContext(contextIri: _*)) originalExpanded = result.value expanded <- checkAndSetSameId(iri, originalExpanded) - compacted <- expanded.toCompacted(ctx).mapError(err => InvalidJsonLdFormat(Some(iri), err)) + compacted <- expanded.toCompacted(ctx).toBIO[RdfError].mapError(err => InvalidJsonLdFormat(Some(iri), err)) } yield JsonLdResult(iri, compacted, expanded, result.remoteContexts) }.mapError(rejectionMapper.to) diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/projects/model/Project.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/projects/model/Project.scala index 7b71a549a7..e98959c98c 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/projects/model/Project.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/projects/model/Project.scala @@ -10,6 +10,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.projects.Projects import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.Project.{Metadata, Source} import ch.epfl.bluebrain.nexus.delta.sdk.{OrderingFields, ResourceShift} import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Label, ProjectRef} +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import io.circe.Encoder import io.circe.generic.extras.Configuration import io.circe.generic.extras.semiauto.deriveConfiguredEncoder @@ -163,7 +164,7 @@ object Project { def shift(projects: Projects, defaultMappings: ApiMappings)(implicit baseUri: BaseUri): Shift = ResourceShift.withMetadata[ProjectState, Project, Metadata]( Projects.entityType, - (_, ref) => projects.fetch(ref), + (_, ref) => projects.fetch(ref).toCatsIO, state => state.toResource(defaultMappings), value => JsonLdContent(value, value.value.asJson, Some(value.value.metadata)) ) 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 24ddb3303c..8102826ab5 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 @@ -1,7 +1,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resolvers import cats.effect.IO -import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ 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.ResourceF @@ -67,7 +66,7 @@ object ResourceResolution { apply( aclCheck, resolvers, - (ref: ResourceRef, project: ProjectRef) => toCatsIO(resources.fetch(ref, project).redeem(_ => None, Some(_))), + (ref: ResourceRef, project: ProjectRef) => resources.fetch(ref, project).redeem(_ => None, Some(_)), Permissions.resources.read ) 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 812e8628bd..445213ecdc 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 @@ -1,8 +1,10 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resources -import cats.effect.Clock +import cats.effect.{Clock, IO} +import cats.implicits.catsSyntaxMonadError import ch.epfl.bluebrain.nexus.delta.kernel.Mapper -import ch.epfl.bluebrain.nexus.delta.kernel.utils.IOUtils +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ +import ch.epfl.bluebrain.nexus.delta.kernel.utils.IOInstant 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.rdf.jsonld.ExpandedJsonLd @@ -22,7 +24,6 @@ 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 io.circe.Json -import monix.bio.{IO, UIO} /** * Operations pertaining to managing resources. @@ -44,7 +45,7 @@ trait Resources { schema: IdSegment, source: Json, tag: Option[UserTag] - )(implicit caller: Caller): IO[ResourceRejection, DataResource] + )(implicit caller: Caller): IO[DataResource] /** * Creates a new resource with the expanded form of the passed id. @@ -64,7 +65,7 @@ trait Resources { schema: IdSegment, source: Json, tag: Option[UserTag] - )(implicit caller: Caller): IO[ResourceRejection, DataResource] + )(implicit caller: Caller): IO[DataResource] /** * Updates an existing resource. @@ -87,7 +88,7 @@ trait Resources { schemaOpt: Option[IdSegment], rev: Int, source: Json - )(implicit caller: Caller): IO[ResourceRejection, DataResource] + )(implicit caller: Caller): IO[DataResource] /** * Refreshes an existing resource. This is equivalent to posting an update with the latest source. Used for when the @@ -105,7 +106,7 @@ trait Resources { id: IdSegment, projectRef: ProjectRef, schemaOpt: Option[IdSegment] - )(implicit caller: Caller): IO[ResourceRejection, DataResource] + )(implicit caller: Caller): IO[DataResource] /** * Adds a tag to an existing resource. @@ -131,7 +132,7 @@ trait Resources { tag: UserTag, tagRev: Int, rev: Int - )(implicit caller: Subject): IO[ResourceRejection, DataResource] + )(implicit caller: Subject): IO[DataResource] /** * Delete a tag on an existing resource. @@ -154,7 +155,7 @@ trait Resources { schemaOpt: Option[IdSegment], tag: UserTag, rev: Int - )(implicit caller: Subject): IO[ResourceRejection, DataResource] + )(implicit caller: Subject): IO[DataResource] /** * Deprecates an existing resource. @@ -174,7 +175,7 @@ trait Resources { projectRef: ProjectRef, schemaOpt: Option[IdSegment], rev: Int - )(implicit caller: Subject): IO[ResourceRejection, DataResource] + )(implicit caller: Subject): IO[DataResource] /** * Fetches a resource state. @@ -191,7 +192,7 @@ trait Resources { id: IdSegmentRef, projectRef: ProjectRef, schemaOpt: Option[IdSegment] - ): IO[ResourceFetchRejection, ResourceState] + ): IO[ResourceState] /** * Fetches a resource. @@ -208,7 +209,7 @@ trait Resources { id: IdSegmentRef, projectRef: ProjectRef, schemaOpt: Option[IdSegment] - ): IO[ResourceFetchRejection, DataResource] + ): IO[DataResource] /** * Fetch the [[DataResource]] from the provided ''projectRef'' and ''resourceRef''. Return on the error channel if @@ -219,11 +220,13 @@ trait Resources { * @param projectRef * the project reference where the schema belongs */ - def fetch[R]( + def fetch[R <: Throwable]( resourceRef: ResourceRef, projectRef: ProjectRef - )(implicit rejectionMapper: Mapper[ResourceFetchRejection, R]): IO[R, DataResource] = - fetch(IdSegmentRef(resourceRef), projectRef, None).mapError(rejectionMapper.to) + )(implicit rejectionMapper: Mapper[ResourceFetchRejection, R]): IO[DataResource] = + fetch(IdSegmentRef(resourceRef), projectRef, None).adaptError { case e: ResourceFetchRejection => + rejectionMapper.to(e) + } } object Resources { @@ -243,9 +246,8 @@ object Resources { /** * Expands the segment to a [[ResourceRef]] */ - def expandResourceRef(segment: IdSegment, context: ProjectContext): IO[InvalidResourceId, ResourceRef] = - IO.fromOption( - segment.toIri(context.apiMappings, context.base).map(ResourceRef(_)), + def expandResourceRef(segment: IdSegment, context: ProjectContext): IO[ResourceRef] = + IO.fromOption(segment.toIri(context.apiMappings, context.base).map(ResourceRef(_)))( InvalidResourceId(segment.asString) ) @@ -255,7 +257,7 @@ object Resources { def expandResourceRef( segmentOpt: Option[IdSegment], context: ProjectContext - ): IO[InvalidResourceId, Option[ResourceRef]] = + ): IO[Option[ResourceRef]] = segmentOpt match { case None => IO.none case Some(schema) => expandResourceRef(schema, context).map(Some.apply) @@ -305,8 +307,8 @@ object Resources { private[delta] def evaluate( validateResource: ValidateResource )(state: Option[ResourceState], cmd: ResourceCommand)(implicit - clock: Clock[UIO] - ): IO[ResourceRejection, ResourceEvent] = { + clock: Clock[IO] + ): IO[ResourceEvent] = { def validate( id: Iri, @@ -314,7 +316,7 @@ object Resources { schemaRef: ResourceRef, projectRef: ProjectRef, caller: Caller - ): IO[ResourceRejection, (ResourceRef.Revision, ProjectRef)] = { + ): IO[(ResourceRef.Revision, ProjectRef)] = { validateResource .apply(id, expanded, schemaRef, projectRef, caller) .map(result => (result.schema, result.project)) @@ -327,7 +329,7 @@ object Resources { // format: off for { (schemaRev, schemaProject) <- validate(c.id, expanded, c.schema, c.project, c.caller) - t <- IOUtils.instant + t <- IOInstant.now } yield ResourceCreated(c.id, c.project, schemaRev, schemaProject, types, c.source, compacted, expanded, remoteContextRefs, 1, t, c.subject, c.tag) // format: on @@ -373,7 +375,7 @@ object Resources { s <- stateWhereResourceIsEditable(u) schemaRef = u.schemaOpt.getOrElse(ResourceRef.Latest(s.schema.iri)) (schemaRev, schemaProject) <- validate(u.id, expanded, schemaRef, s.project, u.caller) - time <- IOUtils.instant + time <- IOInstant.now } yield ResourceUpdated(u.id, u.project, schemaRev, schemaProject, types, u.source, compacted, expanded, remoteContextRefs, s.rev + 1, time, u.subject) // format: on } @@ -384,7 +386,7 @@ object Resources { for { s <- stateWhereResourceIsEditable(c) (schemaRev, schemaProject) <- validate(c.id, expanded, c.schemaOpt.getOrElse(s.schema), s.project, c.caller) - time <- IOUtils.instant + time <- IOInstant.now } yield ResourceRefreshed(c.id, c.project, schemaRev, schemaProject, types, compacted, expanded, remoteContextRefs, s.rev + 1, time, c.subject) // format: on } @@ -392,7 +394,7 @@ object Resources { def tag(c: TagResource) = { for { s <- stateWhereRevisionExists(c, c.targetRev) - time <- IOUtils.instant + time <- IOInstant.now } yield { ResourceTagAdded(c.id, c.project, s.types, c.targetRev, c.tag, s.rev + 1, time, c.subject) } @@ -401,14 +403,14 @@ object Resources { def deleteTag(c: DeleteResourceTag) = { for { s <- stateWhereTagExistsOnResource(c, c.tag) - time <- IOUtils.instant + time <- IOInstant.now } yield ResourceTagDeleted(c.id, c.project, s.types, c.tag, s.rev + 1, time, c.subject) } def deprecate(c: DeprecateResource) = { for { s <- stateWhereResourceIsEditable(c) - time <- IOUtils.instant + time <- IOInstant.now } yield ResourceDeprecated(c.id, c.project, s.types, s.rev + 1, time, c.subject) } @@ -428,11 +430,11 @@ object Resources { def definition( resourceValidator: ValidateResource )(implicit - clock: Clock[UIO] + clock: Clock[IO] ): ScopedEntityDefinition[Iri, ResourceState, ResourceCommand, ResourceEvent, ResourceRejection] = ScopedEntityDefinition( entityType, - StateMachine(None, evaluate(resourceValidator), next), + StateMachine(None, evaluate(resourceValidator)(_, _).toBIO[ResourceRejection], next), ResourceEvent.serializer, ResourceState.serializer, Tagger[ResourceEvent]( 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 c2ce4b66be..13634f544d 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,7 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resources -import cats.effect.Clock +import cats.effect.{Clock, IO} +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.kernel.kamon.KamonMetricComponent import ch.epfl.bluebrain.nexus.delta.kernel.utils.UUIDF import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri @@ -16,14 +17,13 @@ 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.ResourcesImpl.ResourcesLog import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceCommand._ -import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{ProjectContextRejection, ResourceFetchRejection, ResourceNotFound, RevisionNotFound, TagNotFound} +import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{ProjectContextRejection, ResourceNotFound, RevisionNotFound, TagNotFound} 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 import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import io.circe.Json -import monix.bio.{IO, UIO} final class ResourcesImpl private ( log: ResourcesLog, @@ -38,11 +38,11 @@ final class ResourcesImpl private ( schema: IdSegment, source: Json, tag: Option[UserTag] - )(implicit caller: Caller): IO[ResourceRejection, DataResource] = { + )(implicit caller: Caller): IO[DataResource] = { for { - projectContext <- fetchContext.onCreate(projectRef) + projectContext <- fetchContext.onCreate(projectRef).toCatsIO schemeRef <- expandResourceRef(schema, projectContext) - jsonld <- sourceParser(projectRef, projectContext, source) + jsonld <- sourceParser(projectRef, projectContext, source).toCatsIO res <- eval(CreateResource(jsonld.iri, projectRef, schemeRef, source, jsonld, caller, tag)) } yield res }.span("createResource") @@ -53,12 +53,12 @@ final class ResourcesImpl private ( schema: IdSegment, source: Json, tag: Option[UserTag] - )(implicit caller: Caller): IO[ResourceRejection, DataResource] = { + )(implicit caller: Caller): IO[DataResource] = { for { - projectContext <- fetchContext.onCreate(projectRef) - iri <- expandIri(id, projectContext) + projectContext <- fetchContext.onCreate(projectRef).toCatsIO + iri <- expandIri(id, projectContext).toCatsIO schemeRef <- expandResourceRef(schema, projectContext) - jsonld <- sourceParser(projectRef, projectContext, iri, source) + jsonld <- sourceParser(projectRef, projectContext, iri, source).toCatsIO res <- eval(CreateResource(iri, projectRef, schemeRef, source, jsonld, caller, tag)) } yield res }.span("createResource") @@ -69,12 +69,12 @@ final class ResourcesImpl private ( schemaOpt: Option[IdSegment], rev: Int, source: Json - )(implicit caller: Caller): IO[ResourceRejection, DataResource] = { + )(implicit caller: Caller): IO[DataResource] = { for { - projectContext <- fetchContext.onModify(projectRef) - iri <- expandIri(id, projectContext) + projectContext <- fetchContext.onModify(projectRef).toCatsIO + iri <- expandIri(id, projectContext).toCatsIO schemeRefOpt <- expandResourceRef(schemaOpt, projectContext) - jsonld <- sourceParser(projectRef, projectContext, iri, source) + jsonld <- sourceParser(projectRef, projectContext, iri, source).toCatsIO res <- eval(UpdateResource(iri, projectRef, schemeRefOpt, source, jsonld, rev, caller)) } yield res }.span("updateResource") @@ -83,13 +83,13 @@ final class ResourcesImpl private ( id: IdSegment, projectRef: ProjectRef, schemaOpt: Option[IdSegment] - )(implicit caller: Caller): IO[ResourceRejection, DataResource] = { + )(implicit caller: Caller): IO[DataResource] = { for { - projectContext <- fetchContext.onModify(projectRef) - iri <- expandIri(id, projectContext) + projectContext <- fetchContext.onModify(projectRef).toCatsIO + iri <- expandIri(id, projectContext).toCatsIO schemaRefOpt <- expandResourceRef(schemaOpt, projectContext) - resource <- log.stateOr(projectRef, iri, ResourceNotFound(iri, projectRef)) - jsonld <- sourceParser(projectRef, projectContext, iri, resource.source) + resource <- log.stateOr(projectRef, iri, ResourceNotFound(iri, projectRef)).toCatsIO + jsonld <- sourceParser(projectRef, projectContext, iri, resource.source).toCatsIO res <- eval(RefreshResource(iri, projectRef, schemaRefOpt, jsonld, resource.rev, caller)) } yield res }.span("refreshResource") @@ -101,10 +101,10 @@ final class ResourcesImpl private ( tag: UserTag, tagRev: Int, rev: Int - )(implicit caller: Subject): IO[ResourceRejection, DataResource] = + )(implicit caller: Subject): IO[DataResource] = (for { - projectContext <- fetchContext.onModify(projectRef) - iri <- expandIri(id, projectContext) + projectContext <- fetchContext.onModify(projectRef).toCatsIO + iri <- expandIri(id, projectContext).toCatsIO schemeRefOpt <- expandResourceRef(schemaOpt, projectContext) res <- eval(TagResource(iri, projectRef, schemeRefOpt, tagRev, tag, rev, caller)) } yield res).span("tagResource") @@ -115,10 +115,10 @@ final class ResourcesImpl private ( schemaOpt: Option[IdSegment], tag: UserTag, rev: Int - )(implicit caller: Subject): IO[ResourceRejection, DataResource] = + )(implicit caller: Subject): IO[DataResource] = (for { - projectContext <- fetchContext.onModify(projectRef) - iri <- expandIri(id, projectContext) + projectContext <- fetchContext.onModify(projectRef).toCatsIO + iri <- expandIri(id, projectContext).toCatsIO schemeRefOpt <- expandResourceRef(schemaOpt, projectContext) res <- eval(DeleteResourceTag(iri, projectRef, schemeRefOpt, tag, rev, caller)) } yield res).span("deleteResourceTag") @@ -128,10 +128,10 @@ final class ResourcesImpl private ( projectRef: ProjectRef, schemaOpt: Option[IdSegment], rev: Int - )(implicit caller: Subject): IO[ResourceRejection, DataResource] = + )(implicit caller: Subject): IO[DataResource] = (for { - projectContext <- fetchContext.onModify(projectRef) - iri <- expandIri(id, projectContext) + projectContext <- fetchContext.onModify(projectRef).toCatsIO + iri <- expandIri(id, projectContext).toCatsIO schemeRefOpt <- expandResourceRef(schemaOpt, projectContext) res <- eval(DeprecateResource(iri, projectRef, schemeRefOpt, rev, caller)) } yield res).span("deprecateResource") @@ -140,18 +140,18 @@ final class ResourcesImpl private ( id: IdSegmentRef, projectRef: ProjectRef, schemaOpt: Option[IdSegment] - ): IO[ResourceFetchRejection, ResourceState] = { + ): IO[ResourceState] = { for { - pc <- fetchContext.onRead(projectRef) - iri <- expandIri(id.value, pc) + pc <- fetchContext.onRead(projectRef).toCatsIO + iri <- expandIri(id.value, pc).toCatsIO schemaRefOpt <- expandResourceRef(schemaOpt, pc) notFound = ResourceNotFound(iri, projectRef) state <- id match { - case Latest(_) => log.stateOr(projectRef, iri, notFound) + case Latest(_) => log.stateOr(projectRef, iri, notFound).toCatsIO case Revision(_, rev) => - log.stateOr(projectRef, iri, rev, notFound, RevisionNotFound) + log.stateOr(projectRef, iri, rev, notFound, RevisionNotFound).toCatsIO case Tag(_, tag) => - log.stateOr(projectRef, iri, tag, notFound, TagNotFound(tag)) + log.stateOr(projectRef, iri, tag, notFound, TagNotFound(tag)).toCatsIO } _ <- IO.raiseWhen(schemaRefOpt.exists(_.iri != state.schema.iri))(notFound) } yield state @@ -161,9 +161,9 @@ final class ResourcesImpl private ( id: IdSegmentRef, projectRef: ProjectRef, schemaOpt: Option[IdSegment] - ): IO[ResourceFetchRejection, DataResource] = fetchState(id, projectRef, schemaOpt).map(_.toResource) + ): IO[DataResource] = fetchState(id, projectRef, schemaOpt).map(_.toResource) - private def eval(cmd: ResourceCommand): IO[ResourceRejection, DataResource] = + private def eval(cmd: ResourceCommand): IO[DataResource] = log.evaluate(cmd.project, cmd.id, cmd).map(_._2.toResource) } @@ -192,7 +192,7 @@ object ResourcesImpl { contextResolution: ResolverContextResolution, config: ResourcesConfig, xas: Transactors - )(implicit api: JsonLdApi, clock: Clock[UIO], uuidF: UUIDF = UUIDF.random): Resources = + )(implicit api: JsonLdApi, clock: Clock[IO], uuidF: UUIDF = UUIDF.random): Resources = new ResourcesImpl( ScopedEventLog(Resources.definition(validateResource), config.eventLog, xas), fetchContext, diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesTrial.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesTrial.scala index 987b77a0f1..5d886ff87d 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesTrial.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesTrial.scala @@ -1,7 +1,9 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resources -import cats.effect.Clock -import ch.epfl.bluebrain.nexus.delta.kernel.utils.{IOUtils, UUIDF} +import cats.effect.{Clock, IO} +import cats.implicits.catsSyntaxApplicativeError +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ +import ch.epfl.bluebrain.nexus.delta.kernel.utils.{IOInstant, UUIDF} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.JsonLdApi import ch.epfl.bluebrain.nexus.delta.sdk.DataResource import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller @@ -11,11 +13,10 @@ import ch.epfl.bluebrain.nexus.delta.sdk.model.{IdSegment, IdSegmentRef, Resourc 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.Resources.expandResourceRef -import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{ProjectContextRejection, ResourceFetchRejection} +import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.ProjectContextRejection import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{ResourceGenerationResult, ResourceRejection, ResourceState} import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef -import monix.bio.{IO, UIO} /** * Operations allowing to perform read-only operations on resources @@ -35,7 +36,7 @@ trait ResourcesTrial { */ def generate(project: ProjectRef, schema: IdSegment, source: NexusSource)(implicit caller: Caller - ): UIO[ResourceGenerationResult] + ): IO[ResourceGenerationResult] /** * Generates the resource and validate it against the provided schema @@ -51,7 +52,7 @@ trait ResourcesTrial { */ def generate(project: ProjectRef, schema: ResourceF[Schema], source: NexusSource)(implicit caller: Caller - ): UIO[ResourceGenerationResult] + ): IO[ResourceGenerationResult] /** * Validates an existing resource. @@ -66,51 +67,51 @@ trait ResourcesTrial { */ def validate(id: IdSegmentRef, project: ProjectRef, schemaOpt: Option[IdSegment])(implicit caller: Caller - ): IO[ResourceRejection, ValidationResult] + ): IO[ValidationResult] } object ResourcesTrial { def apply( - fetchResource: (IdSegmentRef, ProjectRef) => IO[ResourceFetchRejection, DataResource], + fetchResource: (IdSegmentRef, ProjectRef) => IO[DataResource], validateResource: ValidateResource, fetchContext: FetchContext[ProjectContextRejection], contextResolution: ResolverContextResolution - )(implicit api: JsonLdApi, clock: Clock[UIO], uuidF: UUIDF): ResourcesTrial = new ResourcesTrial { + )(implicit api: JsonLdApi, clock: Clock[IO], uuidF: UUIDF): ResourcesTrial = new ResourcesTrial { private val sourceParser = JsonLdSourceResolvingParser[ResourceRejection](contextResolution, uuidF) override def generate(project: ProjectRef, schema: IdSegment, source: NexusSource)(implicit caller: Caller - ): UIO[ResourceGenerationResult] = { + ): IO[ResourceGenerationResult] = { for { - projectContext <- fetchContext.onRead(project) + projectContext <- fetchContext.onRead(project).toCatsIO schemaRef <- Resources.expandResourceRef(schema, projectContext) - jsonld <- sourceParser(project, projectContext, source.value) + jsonld <- sourceParser(project, projectContext, source.value).toCatsIO validation <- validateResource(jsonld.iri, jsonld.expanded, schemaRef, project, caller) result <- toResourceF(project, jsonld, source, validation) } yield result - }.attempt.map { attempt => + }.attemptNarrow[ResourceRejection].map { attempt => ResourceGenerationResult(None, attempt) } override def generate(project: ProjectRef, schema: ResourceF[Schema], source: NexusSource)(implicit caller: Caller - ): UIO[ResourceGenerationResult] = { + ): IO[ResourceGenerationResult] = { for { - projectContext <- fetchContext.onRead(project) - jsonld <- sourceParser(project, projectContext, source.value) + projectContext <- fetchContext.onRead(project).toCatsIO + jsonld <- sourceParser(project, projectContext, source.value).toCatsIO validation <- validateResource(jsonld.iri, jsonld.expanded, schema) result <- toResourceF(project, jsonld, source, validation) } yield result - }.attempt.map { attempt => + }.attemptNarrow[ResourceRejection].map { attempt => ResourceGenerationResult(Some(schema), attempt) } def validate(id: IdSegmentRef, project: ProjectRef, schemaOpt: Option[IdSegment])(implicit caller: Caller - ): IO[ResourceRejection, ValidationResult] = { + ): IO[ValidationResult] = { for { - projectContext <- fetchContext.onRead(project) + projectContext <- fetchContext.onRead(project).toCatsIO schemaRefOpt <- expandResourceRef(schemaOpt, projectContext) resource <- fetchResource(id, project) report <- validateResource( @@ -128,8 +129,8 @@ object ResourcesTrial { jsonld: JsonLdResult, source: NexusSource, validation: ValidationResult - )(implicit caller: Caller): UIO[DataResource] = { - IOUtils.instant.map { now => + )(implicit caller: Caller): IO[DataResource] = { + IOInstant.now.map { now => ResourceState( id = jsonld.iri, project = project, diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResource.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResource.scala index 5de2089d00..bce99cee46 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResource.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResource.scala @@ -1,23 +1,22 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resources -import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri +import cats.effect.IO import cats.syntax.all._ import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ +import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{contexts, schemas} import ch.epfl.bluebrain.nexus.delta.rdf.graph.Graph import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.ExpandedJsonLd import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.JsonLdApi -import ch.epfl.bluebrain.nexus.delta.rdf.shacl.ShaclEngine +import ch.epfl.bluebrain.nexus.delta.rdf.shacl.{ShaclEngine, ValidationReport} import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.model.ResourceF import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverResolution.ResourceResolution -import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection +import ch.epfl.bluebrain.nexus.delta.sdk.resources.ValidationResult._ import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{InvalidJsonLdFormat, InvalidResource, InvalidSchemaRejection, ReservedResourceId, ResourceShaclEngineRejection, SchemaIsDeprecated} import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef.Latest import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} -import ch.epfl.bluebrain.nexus.delta.sdk.resources.ValidationResult._ -import monix.bio.IO /** * Allows to validate the resource: @@ -45,7 +44,7 @@ trait ValidateResource { schemaRef: ResourceRef, projectRef: ProjectRef, caller: Caller - ): IO[ResourceRejection, ValidationResult] + ): IO[ValidationResult] /** * Validate against a schema @@ -60,7 +59,7 @@ trait ValidateResource { resourceId: Iri, expanded: ExpandedJsonLd, schema: ResourceF[Schema] - ): IO[ResourceRejection, ValidationResult] + ): IO[ValidationResult] } object ValidateResource { @@ -73,7 +72,7 @@ object ValidateResource { schemaRef: ResourceRef, projectRef: ProjectRef, caller: Caller - ): IO[ResourceRejection, ValidationResult] = + ): IO[ValidationResult] = if (isUnconstrained(schemaRef)) assertNotReservedId(resourceId) >> toGraph(resourceId, expanded) >> @@ -88,7 +87,7 @@ object ValidateResource { resourceId: Iri, expanded: ExpandedJsonLd, schema: ResourceF[Schema] - ): IO[ResourceRejection, ValidationResult] = + ): IO[ValidationResult] = for { _ <- assertNotReservedId(resourceId) graph <- toGraph(resourceId, expanded) @@ -97,12 +96,27 @@ object ValidateResource { _ <- IO.raiseWhen(!report.isValid())(InvalidResource(resourceId, schemaRef, report, expanded)) } yield Validated(schema.value.project, ResourceRef.Revision(schema.id, schema.rev), report) - private def toGraph(id: Iri, expanded: ExpandedJsonLd): IO[ResourceRejection, Graph] = - IO.fromEither(expanded.toGraph).mapError(err => InvalidJsonLdFormat(Some(id), err)) + private def toGraph(id: Iri, expanded: ExpandedJsonLd): IO[Graph] = { + IO.fromEither( + expanded.toGraph + .leftMap[InvalidJsonLdFormat](err => InvalidJsonLdFormat(Some(id), err)) + ) + } - private def shaclValidate(resourceId: Iri, graph: Graph, schemaRef: ResourceRef, schema: ResourceF[Schema]) = { - ShaclEngine(graph ++ schema.value.ontologies, schema.value.shapes, reportDetails = true, validateShapes = false) - .mapError(ResourceShaclEngineRejection(resourceId, schemaRef, _)) + private def shaclValidate( + resourceId: Iri, + graph: Graph, + schemaRef: ResourceRef, + schema: ResourceF[Schema] + ): IO[ValidationReport] = { + ShaclEngine( + graph ++ schema.value.ontologies, + schema.value.shapes, + reportDetails = true, + validateShapes = false + ).toCatsIOEither + .map(_.leftMap(ResourceShaclEngineRejection(resourceId, schemaRef, _))) + .rethrow } private def assertNotDeprecated(schema: ResourceF[Schema]) = { @@ -130,6 +144,6 @@ object ValidateResource { IO.fromEither(invalidSchema) } .flatTap(schema => assertNotDeprecated(schema)) - }.toBIO[ResourceRejection] + } } } diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/model/Resource.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/model/Resource.scala index 9a69509744..6942b3b60e 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/model/Resource.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/model/Resource.scala @@ -8,10 +8,12 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteCon import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.encoder.JsonLdEncoder import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.{CompactedJsonLd, ExpandedJsonLd} import ch.epfl.bluebrain.nexus.delta.sdk.ResourceShift +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdContent import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, IdSegmentRef, Tags} import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.Resource.Metadata +import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.ResourceFetchRejection import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} @@ -82,7 +84,7 @@ object Resource { def shift(resources: Resources)(implicit baseUri: BaseUri): Shift = ResourceShift.withMetadata[ResourceState, Resource, Metadata]( Resources.entityType, - (ref, project) => resources.fetch(IdSegmentRef(ref), project, None), + (ref, project) => resources.fetch(IdSegmentRef(ref), project, None).toBIO[ResourceFetchRejection], state => state.toResource, value => JsonLdContent(value, value.value.source, Some(value.value.metadata)) ) diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/model/ResourceGenerationResult.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/model/ResourceGenerationResult.scala index 4fa1041b6c..a4923689f6 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/model/ResourceGenerationResult.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/model/ResourceGenerationResult.scala @@ -1,15 +1,15 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resources.model +import cats.effect.IO +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ 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.{DataResource, SchemaResource} +import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.encoder.JsonLdEncoder import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, ResourceF} -import io.circe.Json -import monix.bio.{IO, UIO} +import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceGenerationResult._ import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ -import ResourceGenerationResult._ -import ch.epfl.bluebrain.nexus.delta.rdf.RdfError -import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.encoder.JsonLdEncoder +import ch.epfl.bluebrain.nexus.delta.sdk.{DataResource, SchemaResource} +import io.circe.Json /** * Result of the generation of a resource @@ -23,7 +23,7 @@ final case class ResourceGenerationResult( attempt: Either[ResourceRejection, DataResource] ) { - def asJson(implicit base: BaseUri, rcr: RemoteContextResolution): UIO[Json] = { + def asJson(implicit base: BaseUri, rcr: RemoteContextResolution): IO[Json] = { for { schema <- schema.fold(emptySchema)(toJsonField("schema", _)) resourceOrError <- attempt.fold( @@ -31,24 +31,24 @@ final case class ResourceGenerationResult( toJsonField("result", _) ) } yield schema deepMerge resourceOrError - }.hideErrors + } private def toJsonField[A](fieldName: String, value: A)(implicit encoder: JsonLdEncoder[A], rcr: RemoteContextResolution ) = - value.toCompactedJsonLd.map { v => v.json }.map { s => Json.obj(fieldName -> s) } + value.toCompactedJsonLd.toCatsIO.map { v => v.json }.map { s => Json.obj(fieldName -> s) } private def toJsonField[A](fieldName: String, value: ResourceF[A])(implicit encoder: JsonLdEncoder[A], base: BaseUri, rcr: RemoteContextResolution ) = - value.toCompactedJsonLd.map { v => v.json }.map { s => Json.obj(fieldName -> s) } + value.toCompactedJsonLd.toCatsIO.map { v => v.json }.map { s => Json.obj(fieldName -> s) } } object ResourceGenerationResult { implicit private[model] val api: JsonLdApi = JsonLdJavaApi.lenient - val emptySchema: IO[RdfError, Json] = IO.pure(Json.obj()) + val emptySchema: IO[Json] = IO.pure(Json.obj()) } diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/stream/GraphResourceStream.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/stream/GraphResourceStream.scala index 9ed7e1d5dd..5ada289f74 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/stream/GraphResourceStream.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/stream/GraphResourceStream.scala @@ -8,6 +8,7 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.offset.Offset import ch.epfl.bluebrain.nexus.delta.sourcing.query.{RefreshStrategy, SelectFilter, StreamingQuery} import ch.epfl.bluebrain.nexus.delta.sourcing.state.GraphResource import ch.epfl.bluebrain.nexus.delta.sourcing.stream.RemainingElems +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import fs2.Stream import monix.bio.{Task, UIO} @@ -77,7 +78,7 @@ object GraphResourceStream { ): GraphResourceStream = new GraphResourceStream { override def continuous(project: ProjectRef, selectFilter: SelectFilter, start: Offset): ElemStream[GraphResource] = - StreamingQuery.elems(project, start, selectFilter, qc, xas, shifts.decodeGraphResource) + StreamingQuery.elems(project, start, selectFilter, qc, xas, shifts.decodeGraphResource(_, _).toTask) override def currents(project: ProjectRef, selectFilter: SelectFilter, start: Offset): ElemStream[GraphResource] = StreamingQuery.elems( @@ -86,7 +87,7 @@ object GraphResourceStream { selectFilter, qc.copy(refreshStrategy = RefreshStrategy.Stop), xas, - shifts.decodeGraphResource + shifts.decodeGraphResource(_, _).toTask ) override def remaining( diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResourceGen.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResourceGen.scala index 6dfa0e84d3..28e776632f 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResourceGen.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/ResourceGen.scala @@ -1,7 +1,7 @@ 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.RdfError import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.schemas import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.ExpandedJsonLd import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdJavaApi} @@ -16,13 +16,12 @@ import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Anonymous, Subject} import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef.Latest import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} -import ch.epfl.bluebrain.nexus.testkit.IOValues +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues import io.circe.Json -import monix.bio import java.time.Instant -object ResourceGen extends IOValues { +object ResourceGen extends CatsIOValues { // We put a lenient api for schemas otherwise the api checks data types before the actual schema validation process implicit val api: JsonLdApi = JsonLdJavaApi.strict @@ -75,7 +74,7 @@ object ResourceGen extends IOValues { source: Json, schema: ResourceRef = Latest(schemas.resources), tags: Tags = Tags.empty - )(implicit resolution: RemoteContextResolution): bio.IO[RdfError, Resource] = { + )(implicit resolution: RemoteContextResolution): IO[Resource] = { for { expanded <- ExpandedJsonLd(source).map(_.replaceId(id)) compacted <- expanded.toCompacted(source.topContextValueOrEmpty) diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/SchemaGen.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/SchemaGen.scala index e5ab577296..7d5fb707c4 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/SchemaGen.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/generators/SchemaGen.scala @@ -1,8 +1,8 @@ package ch.epfl.bluebrain.nexus.delta.sdk.generators import cats.data.NonEmptyList +import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri -import ch.epfl.bluebrain.nexus.delta.rdf.RdfError import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.ExpandedJsonLd import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdJavaApi} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContextResolution @@ -12,13 +12,13 @@ import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.{Schema, SchemaState} import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Anonymous, Subject} import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef -import ch.epfl.bluebrain.nexus.testkit.{EitherValuable, IOValues} +import ch.epfl.bluebrain.nexus.testkit.EitherValuable +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues import io.circe.Json -import monix.bio.IO import java.time.Instant -object SchemaGen extends IOValues with EitherValuable { +object SchemaGen extends CatsIOValues with EitherValuable { // We put a lenient api for schemas otherwise the api checks data types before the actual schema validation process implicit val api: JsonLdApi = JsonLdJavaApi.lenient @@ -58,7 +58,7 @@ object SchemaGen extends IOValues with EitherValuable { project: ProjectRef, source: Json, tags: Tags = Tags.empty - )(implicit resolution: RemoteContextResolution): IO[RdfError, Schema] = { + )(implicit resolution: RemoteContextResolution): IO[Schema] = { for { expanded <- ExpandedJsonLd(source).map(_.replaceId(id)) compacted <- expanded.toCompacted(source.topContextValueOrEmpty) diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/model/ResolverValueSpec.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/model/ResolverValueSpec.scala index 0ea1425da4..94b88db58b 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/model/ResolverValueSpec.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/model/ResolverValueSpec.scala @@ -9,7 +9,8 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverValue.{CrossPro import ch.epfl.bluebrain.nexus.delta.sdk.utils.Fixtures import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Authenticated, Group, User} import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Label, ProjectRef} -import ch.epfl.bluebrain.nexus.testkit.{EitherValuable, IOValues, TestHelpers} +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues +import ch.epfl.bluebrain.nexus.testkit.{EitherValuable, TestHelpers} import org.scalatest.Inspectors import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike @@ -18,7 +19,7 @@ class ResolverValueSpec extends AnyWordSpecLike with Matchers with EitherValuable - with IOValues + with CatsIOValues with Inspectors with TestHelpers with Fixtures { 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 11d235df66..b4d76a2ade 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 @@ -2,7 +2,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resources import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.kernel.utils.UUIDF -import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{contexts, nxv, schema, schemas} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdJavaApi} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords @@ -27,7 +26,8 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef.{Latest, Revisio import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Identity, Label, ProjectRef, ResourceRef} import ch.epfl.bluebrain.nexus.delta.sourcing.postgres.DoobieScalaTestFixture -import ch.epfl.bluebrain.nexus.testkit.{CirceLiteral, IOFixedClock, IOValues} +import ch.epfl.bluebrain.nexus.testkit.CirceLiteral +import ch.epfl.bluebrain.nexus.testkit.ce.{CatsIOValues, IOFixedClock} import org.scalatest.matchers.should.Matchers import org.scalatest.{CancelAfterFailure, Inspectors, OptionValues} @@ -36,7 +36,7 @@ import java.util.UUID class ResourcesImplSpec extends DoobieScalaTestFixture with Matchers - with IOValues + with CatsIOValues with IOFixedClock with CancelAfterFailure with CirceLiteral @@ -91,7 +91,7 @@ class ResourcesImplSpec private val resolverContextResolution: ResolverContextResolution = new ResolverContextResolution( res, - (r, p, _) => resources.fetch(r, p).bimap(_ => ResourceResolutionReport(), identity).attempt + (r, p, _) => resources.fetch(r, p).attempt.map(_.left.map(_ => ResourceResolutionReport())) ) private lazy val resources: Resources = ResourcesImpl( diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesSpec.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesSpec.scala index aa0e0fe388..951f936c5b 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesSpec.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesSpec.scala @@ -1,5 +1,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resources +import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{nxv, schemas} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.ContextValue import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.RemoteContext._ @@ -13,16 +14,16 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resources.Resources.{evaluate, next} import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceCommand._ import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceEvent._ import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{IncorrectRev, ResourceIsDeprecated, ResourceNotFound, RevisionNotFound} -import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{ResourceCommand, ResourceEvent, ResourceRejection, ResourceState} +import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{ResourceCommand, ResourceEvent, ResourceState} import ch.epfl.bluebrain.nexus.delta.sdk.utils.Fixtures import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.User import ch.epfl.bluebrain.nexus.delta.sourcing.model.Label import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef.{Latest, Revision} import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag -import ch.epfl.bluebrain.nexus.testkit._ +import ch.epfl.bluebrain.nexus.testkit.ce.{CatsIOValues, IOFixedClock} +import ch.epfl.bluebrain.nexus.testkit.{CirceLiteral, EitherValuable, TestHelpers} import io.circe.Json import io.circe.syntax.{EncoderOps, KeyOps} -import monix.bio.IO import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike import org.scalatest.{Inspectors, OptionValues} @@ -35,7 +36,7 @@ class ResourcesSpec with EitherValuable with Inspectors with IOFixedClock - with IOValues + with CatsIOValues with TestHelpers with CirceLiteral with OptionValues @@ -54,7 +55,7 @@ class ResourcesSpec val schema1 = nxv + "myschema" - val eval: (Option[ResourceState], ResourceCommand) => IO[ResourceRejection, ResourceEvent] = + val eval: (Option[ResourceState], ResourceCommand) => IO[ResourceEvent] = evaluate(alwaysValidate) "evaluating an incoming command" should { diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesTrialSuite.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesTrialSuite.scala index 67bbcf82ff..27e2d8e5a7 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesTrialSuite.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ResourcesTrialSuite.scala @@ -1,5 +1,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resources +import cats.effect.IO 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} @@ -16,14 +17,14 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.ResourceRejection.{Inva import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.{Resource, ResourceGenerationResult, ResourceRejection} import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef.Revision -import ch.epfl.bluebrain.nexus.testkit.bio.BioSuite -import ch.epfl.bluebrain.nexus.testkit.{IOFixedClock, TestHelpers} -import monix.bio.{IO, UIO} +import ch.epfl.bluebrain.nexus.testkit.TestHelpers +import ch.epfl.bluebrain.nexus.testkit.ce.CatsEffectSuite import munit.Location +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import java.util.UUID -class ResourcesTrialSuite extends BioSuite with ValidateResourceFixture with TestHelpers with IOFixedClock { +class ResourcesTrialSuite extends CatsEffectSuite with ValidateResourceFixture with TestHelpers { private val uuid = UUID.randomUUID() implicit private val uuidF: UUIDF = UUIDF.fixed(uuid) @@ -39,7 +40,7 @@ class ResourcesTrialSuite extends BioSuite with ValidateResourceFixture with Tes contexts.schemasMetadata -> ContextValue.fromFile("contexts/schemas-metadata.json") ) - private val fetchResourceFail = IO.terminate(new IllegalStateException("Should not be attempt to fetch a resource")) + private val fetchResourceFail = IO.raiseError(new IllegalStateException("Should not be attempt to fetch a resource")) private val resolverContextResolution: ResolverContextResolution = ResolverContextResolution(res) @@ -58,7 +59,7 @@ class ResourcesTrialSuite extends BioSuite with ValidateResourceFixture with Tes private val source = NexusSource(jsonContentOf("resources/resource.json", "id" -> id)) private val resourceSchema = nxv + "schema" - private def assertSuccessSync( + private def assertSuccess( result: ResourceGenerationResult )(schema: Option[SchemaResource], expected: Resource)(implicit loc: Location) = { assertEquals(result.schema, schema) @@ -66,7 +67,7 @@ class ResourcesTrialSuite extends BioSuite with ValidateResourceFixture with Tes } private def assertError( - io: UIO[ResourceGenerationResult] + io: IO[ResourceGenerationResult] )(schema: Option[SchemaResource], error: ResourceRejection)(implicit loc: Location) = io.map { generated => assertEquals(generated.schema, schema) @@ -85,7 +86,7 @@ class ResourcesTrialSuite extends BioSuite with ValidateResourceFixture with Tes ResourceGen.resourceAsync(id, projectRef, source.value, Revision(resourceSchema, defaultSchemaRevision)) result <- trial.generate(projectRef, resourceSchema, source) } yield { - assertSuccessSync(result)(None, expectedData) + assertSuccess(result)(None, expectedData) } } @@ -98,8 +99,9 @@ class ResourcesTrialSuite extends BioSuite with ValidateResourceFixture with Tes ) val anotherSchema = nxv + "anotherSchema" - val schemaSource = jsonContentOf("resources/schema.json").addContext(contexts.shacl, contexts.schemasMetadata) for { + schemaSource <- + ioJsonContentOf("resources/schema.json").map(_.addContext(contexts.shacl, contexts.schemasMetadata)).toCatsIO schema <- SchemaGen .schemaAsync(anotherSchema, project.ref, schemaSource.removeKeys(keywords.id)) .map(SchemaGen.resourceFor(_)) @@ -107,7 +109,7 @@ class ResourcesTrialSuite extends BioSuite with ValidateResourceFixture with Tes ResourceGen.resourceAsync(id, projectRef, source.value, Revision(anotherSchema, defaultSchemaRevision)) result <- trial.generate(projectRef, schema, source) } yield { - assertSuccessSync(result)(Some(schema), expectedData) + assertSuccess(result)(Some(schema), expectedData) } } @@ -173,9 +175,9 @@ class ResourcesTrialSuite extends BioSuite with ValidateResourceFixture with Tes fetchContext, resolverContextResolution ) - result <- trial.validate(id, projectRef, Some(anotherSchema)).flip + result <- trial.validate(id, projectRef, Some(anotherSchema)).attempt } yield { - assertEquals(result, expectedError) + assertEquals(result, Left(expectedError)) } } diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResourceFixture.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResourceFixture.scala index f44b7439d2..6967c29f83 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResourceFixture.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/resources/ValidateResourceFixture.scala @@ -1,5 +1,6 @@ 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.rdf.jsonld.ExpandedJsonLd import ch.epfl.bluebrain.nexus.delta.rdf.shacl.ValidationReport @@ -11,7 +12,6 @@ import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} import io.circe.Json import io.circe.syntax.KeyOps -import monix.bio.IO trait ValidateResourceFixture { @@ -25,7 +25,7 @@ trait ValidateResourceFixture { schemaRef: ResourceRef, projectRef: ProjectRef, caller: Caller - ): IO[ResourceRejection, ValidationResult] = + ): IO[ValidationResult] = IO.pure( Validated( projectRef, @@ -38,7 +38,7 @@ trait ValidateResourceFixture { resourceId: Iri, expanded: ExpandedJsonLd, schema: ResourceF[Schema] - ): IO[ResourceRejection, ValidationResult] = + ): IO[ValidationResult] = IO.pure( Validated( schema.value.project, @@ -55,13 +55,13 @@ trait ValidateResourceFixture { schemaRef: ResourceRef, projectRef: ProjectRef, caller: Caller - ): IO[ResourceRejection, ValidationResult] = IO.raiseError(expected) + ): IO[ValidationResult] = IO.raiseError(expected) override def apply( resourceId: Iri, expanded: ExpandedJsonLd, schema: ResourceF[Schema] - ): IO[ResourceRejection, ValidationResult] = IO.raiseError(expected) + ): IO[ValidationResult] = IO.raiseError(expected) } } diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemaImportsSuite.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemaImportsSuite.scala index 674f1d36d5..ee7fd3d01f 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemaImportsSuite.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemaImportsSuite.scala @@ -2,7 +2,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk.schemas import cats.data.NonEmptyList import cats.effect.IO -import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.ExpandedJsonLd import ch.epfl.bluebrain.nexus.delta.sdk.Resolve import ch.epfl.bluebrain.nexus.delta.sdk.generators.ResourceGen @@ -74,7 +73,7 @@ class SchemaImportsSuite extends CatsEffectSuite with TestHelpers with CirceLite case (_, _, _) => IO.pure(Left(errorReport)) } - private def toExpanded(json: Json) = toCatsIO(ExpandedJsonLd(json)) + private def toExpanded(json: Json) = ExpandedJsonLd(json) val imports = new SchemaImports(fetchSchema, fetchResource) diff --git a/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/EphemeralDefinition.scala b/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/EphemeralDefinition.scala index 48b80b0289..893bfa2035 100644 --- a/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/EphemeralDefinition.scala +++ b/delta/sourcing-psql/src/main/scala/ch/epfl/bluebrain/nexus/delta/sourcing/EphemeralDefinition.scala @@ -1,6 +1,7 @@ package ch.epfl.bluebrain.nexus.delta.sourcing import cats.effect.IO +import cats.implicits.catsSyntaxMonadErrorRethrow import ch.epfl.bluebrain.nexus.delta.sourcing.EvaluationError.EvaluationTimeout import ch.epfl.bluebrain.nexus.delta.sourcing.execution.EvaluationExecution import ch.epfl.bluebrain.nexus.delta.sourcing.model.EntityType @@ -25,6 +26,5 @@ final case class EphemeralDefinition[Id, S <: EphemeralState, Command, +R <: Rej execution.timer, execution.contextShift ) - .flatMap(IO.fromEither) - + .rethrow } diff --git a/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsEffectSuite.scala b/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsEffectSuite.scala index 70402a7bb5..6522803699 100644 --- a/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsEffectSuite.scala +++ b/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsEffectSuite.scala @@ -17,6 +17,7 @@ abstract class CatsEffectSuite with CatsRunContext with CatsEffectAssertions with CatsStreamAssertions + with CatsIOValues with CollectionAssertions with EitherAssertions { protected val ioTimeout: FiniteDuration = 45.seconds