From 19604575ce85a3a369e7a0af99ba0d1c19286db3 Mon Sep 17 00:00:00 2001 From: Simon Dumas Date: Fri, 21 Feb 2025 15:20:47 +0100 Subject: [PATCH] Do not index schemas anymore --- .../nexus/delta/routes/ResolversRoutes.scala | 1 + .../nexus/delta/routes/SchemasRoutes.scala | 80 +++++++++---------- .../nexus/delta/wiring/ResolversModule.scala | 12 ++- .../nexus/delta/wiring/SchemasModule.scala | 18 +---- .../delta/routes/SchemasRoutesSpec.scala | 17 +++- .../delta/sdk/resolvers/MultiResolution.scala | 32 +++++++- .../sdk/resolvers/ResolverResolution.scala | 22 ----- .../delta/sdk/resources/model/Resource.scala | 7 +- .../nexus/delta/sdk/schemas/Schemas.scala | 17 ++-- .../nexus/delta/sdk/schemas/SchemasImpl.scala | 10 +++ .../delta/sdk/schemas/model/Schema.scala | 32 +------- .../delta/sdk/schemas/model/SchemaEvent.scala | 32 +------- .../model/SchemaSerializationSuite.scala | 34 ++------ .../paradox/docs/delta/api/schemas-api.md | 10 +-- .../kg/aggregations/org-aggregation.json | 8 +- .../kg/aggregations/project-aggregation.json | 8 +- .../kg/aggregations/root-aggregation.json | 8 +- .../kg/aggregations/schemas-aggregation.json | 22 ----- .../nexus/tests/kg/AggregationsSpec.scala | 37 ++------- 19 files changed, 150 insertions(+), 257 deletions(-) delete mode 100644 tests/src/test/resources/kg/aggregations/schemas-aggregation.json diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResolversRoutes.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResolversRoutes.scala index c54a3f604f..6d029ba339 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResolversRoutes.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/ResolversRoutes.scala @@ -89,6 +89,7 @@ final class ResolversRoutes( val authorizeRead = authorizeFor(project, Read) val authorizeWrite = authorizeFor(project, Write) concat( + // List resolvers pathEndOrSingleSlash { (get & authorizeRead) { implicit val searchJsonLdEncoder: JsonLdEncoder[SearchResults[ResolverResource]] = diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutes.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutes.scala index a236939434..164165e84c 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutes.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutes.scala @@ -20,11 +20,13 @@ import ch.epfl.bluebrain.nexus.delta.sdk.identities.Identities import ch.epfl.bluebrain.nexus.delta.sdk.implicits._ import ch.epfl.bluebrain.nexus.delta.sdk.marshalling.{OriginalSource, RdfMarshalling} import ch.epfl.bluebrain.nexus.delta.sdk.model.routes.Tag +import ch.epfl.bluebrain.nexus.delta.sdk.model.search.SearchResults +import ch.epfl.bluebrain.nexus.delta.sdk.model.search.SearchResults.searchResultsJsonLdEncoder import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, ResourceF} import ch.epfl.bluebrain.nexus.delta.sdk.permissions.Permissions.schemas.{read => Read, write => Write} import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaRejection import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaRejection.SchemaNotFound -import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.{Schema, SchemaRejection} import io.circe.Json /** @@ -38,15 +40,12 @@ import io.circe.Json * the schemas module * @param schemeDirectives * directives related to orgs and projects - * @param indexAction - * the indexing action on write operations */ final class SchemasRoutes( identities: Identities, aclCheck: AclCheck, schemas: Schemas, - schemeDirectives: DeltaSchemeDirectives, - indexAction: IndexingAction.Execute[Schema] + schemeDirectives: DeltaSchemeDirectives )(implicit baseUri: BaseUri, cr: RemoteContextResolution, @@ -72,13 +71,12 @@ final class SchemasRoutes( private def emitMetadataOrReject(io: IO[SchemaResource]): Route = emit(io.map(_.void).attemptNarrow[SchemaRejection].rejectOn[SchemaNotFound]) - private def emitSource(io: IO[SchemaResource], annotate: Boolean): Route = { + private def emitSource(io: IO[SchemaResource], annotate: Boolean): Route = emit( io.map { resource => OriginalSource(resource, resource.value.source, annotate) } .attemptNarrow[SchemaRejection] .rejectOn[SchemaNotFound] ) - } private def emitTags(io: IO[SchemaResource]): Route = emit(io.map(_.value.tags).attemptNarrow[SchemaRejection].rejectOn[SchemaNotFound]) @@ -87,13 +85,22 @@ final class SchemasRoutes( (baseUriPrefix(baseUri.prefix) & replaceUri("schemas", shacl)) { pathPrefix("schemas") { extractCaller { implicit caller => - (projectRef & indexingMode) { (ref, mode) => - def index(schema: SchemaResource): IO[Unit] = indexAction(schema.value.project, schema, mode) + projectRef { project => + val authorizeRead = authorizeFor(project, Read) + val authorizeWrite = authorizeFor(project, Write) concat( + // List schemas + pathEndOrSingleSlash { + (get & authorizeRead) { + implicit val searchJsonLdEncoder: JsonLdEncoder[SearchResults[ResourceF[Unit]]] = + searchResultsJsonLdEncoder(ContextValue.empty) + emit(schemas.list(project).map(_.map(_.void)).widen[SearchResults[ResourceF[Unit]]]) + } + }, // Create a schema without id segment (pathEndOrSingleSlash & post & noParameter("rev") & entity(as[Json])) { source => - authorizeFor(ref, Write).apply { - emitMetadata(Created, schemas.create(ref, source).flatTap(index)) + authorizeWrite { + emitMetadata(Created, schemas.create(project, source)) } }, idSegment { id => @@ -102,75 +109,69 @@ final class SchemasRoutes( concat( // Create or update a schema put { - authorizeFor(ref, Write).apply { + authorizeWrite { (parameter("rev".as[Int].?) & entity(as[Json])) { case (None, source) => // Create a schema with id segment - emitMetadata(Created, schemas.create(id, ref, source).flatTap(index)) + emitMetadata(Created, schemas.create(id, project, source)) case (Some(rev), source) => // Update a schema - emitMetadata(schemas.update(id, ref, rev, source).flatTap(index)) + emitMetadata(schemas.update(id, project, rev, source)) } } }, // Deprecate a schema (delete & parameter("rev".as[Int])) { rev => - authorizeFor(ref, Write).apply { - emitMetadataOrReject(schemas.deprecate(id, ref, rev).flatTap(index)) + authorizeWrite { + emitMetadataOrReject(schemas.deprecate(id, project, rev)) } }, // Fetch a schema (get & idSegmentRef(id)) { id => emitOrFusionRedirect( - ref, + project, id, - authorizeFor(ref, Read).apply { - emitFetch(schemas.fetch(id, ref)) + authorizeRead { + emitFetch(schemas.fetch(id, project)) } ) } ) }, (pathPrefix("undeprecate") & put & pathEndOrSingleSlash & parameter("rev".as[Int])) { rev => - authorizeFor(ref, Write).apply { - emitMetadataOrReject(schemas.undeprecate(id, ref, rev).flatTap(index)) + authorizeWrite { + emitMetadataOrReject(schemas.undeprecate(id, project, rev)) } }, (pathPrefix("refresh") & put & pathEndOrSingleSlash) { - authorizeFor(ref, Write).apply { - emitMetadata(schemas.refresh(id, ref).flatTap(index)) + authorizeWrite { + emitMetadata(schemas.refresh(id, project)) } }, // Fetch a schema original source (pathPrefix("source") & get & pathEndOrSingleSlash & idSegmentRef(id) & annotateSource) { (id, annotate) => - authorizeFor(ref, Read).apply { - emitSource(schemas.fetch(id, ref), annotate) + authorizeRead { + emitSource(schemas.fetch(id, project), annotate) } }, pathPrefix("tags") { concat( // Fetch a schema tags - (get & idSegmentRef(id) & pathEndOrSingleSlash & authorizeFor(ref, Read)) { id => - emitTags(schemas.fetch(id, ref)) + (get & idSegmentRef(id) & pathEndOrSingleSlash & authorizeRead) { id => + emitTags(schemas.fetch(id, project)) }, // Tag a schema (post & parameter("rev".as[Int]) & pathEndOrSingleSlash) { rev => - authorizeFor(ref, Write).apply { + authorizeWrite { entity(as[Tag]) { case Tag(tagRev, tag) => - emitMetadata( - Created, - schemas.tag(id, ref, tag, tagRev, rev).flatTap(index) - ) + emitMetadata(Created, schemas.tag(id, project, tag, tagRev, rev)) } } }, // Delete a tag - (tagLabel & delete & parameter("rev".as[Int]) & pathEndOrSingleSlash & authorizeFor( - ref, - Write - )) { (tag, rev) => - emitMetadataOrReject(schemas.deleteTag(id, ref, tag, rev).flatTap(index)) + (tagLabel & delete & parameter("rev".as[Int]) & pathEndOrSingleSlash & authorizeWrite) { + (tag, rev) => emitMetadataOrReject(schemas.deleteTag(id, project, tag, rev)) } ) } @@ -193,13 +194,12 @@ object SchemasRoutes { identities: Identities, aclCheck: AclCheck, schemas: Schemas, - schemeDirectives: DeltaSchemeDirectives, - index: IndexingAction.Execute[Schema] + schemeDirectives: DeltaSchemeDirectives )(implicit baseUri: BaseUri, cr: RemoteContextResolution, ordering: JsonKeyOrdering, fusionConfig: FusionConfig - ): Route = new SchemasRoutes(identities, aclCheck, schemas, schemeDirectives, index).routes + ): Route = new SchemasRoutes(identities, aclCheck, schemas, schemeDirectives).routes } diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResolversModule.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResolversModule.scala index 4bb78fa520..59c9899138 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResolversModule.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/ResolversModule.scala @@ -19,6 +19,8 @@ import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ApiMappings import ch.epfl.bluebrain.nexus.delta.sdk.resolvers._ import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverEvent +import ch.epfl.bluebrain.nexus.delta.sdk.resources.FetchResource +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.FetchSchema import ch.epfl.bluebrain.nexus.delta.sdk.sse.SseEncoder import ch.epfl.bluebrain.nexus.delta.sourcing.Transactors import izumi.distage.model.definition.{Id, ModuleDef} @@ -51,14 +53,18 @@ object ResolversModule extends ModuleDef { make[MultiResolution].from { ( - aclCheck: AclCheck, fetchContext: FetchContext, + aclCheck: AclCheck, resolvers: Resolvers, - shifts: ResourceShifts + fetchResource: FetchResource, + fetchSchema: FetchSchema ) => MultiResolution( fetchContext, - ResolverResolution(aclCheck, resolvers, shifts, excludeDeprecated = false) + aclCheck, + resolvers, + fetchResource, + fetchSchema ) } diff --git a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/SchemasModule.scala b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/SchemasModule.scala index a2d18cb415..06c6ec8f44 100644 --- a/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/SchemasModule.scala +++ b/delta/app/src/main/scala/ch/epfl/bluebrain/nexus/delta/wiring/SchemasModule.scala @@ -9,14 +9,12 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteCon import ch.epfl.bluebrain.nexus.delta.rdf.shacl.ValidateShacl import ch.epfl.bluebrain.nexus.delta.rdf.utils.JsonKeyOrdering import ch.epfl.bluebrain.nexus.delta.routes.{SchemaJobRoutes, SchemasRoutes} -import ch.epfl.bluebrain.nexus.delta.sdk.IndexingAction.AggregateIndexingAction import ch.epfl.bluebrain.nexus.delta.sdk._ import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck import ch.epfl.bluebrain.nexus.delta.sdk.directives.DeltaSchemeDirectives import ch.epfl.bluebrain.nexus.delta.sdk.fusion.FusionConfig import ch.epfl.bluebrain.nexus.delta.sdk.identities.Identities import ch.epfl.bluebrain.nexus.delta.sdk.model._ -import ch.epfl.bluebrain.nexus.delta.sdk.model.metrics.ScopedEventMetricEncoder import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ApiMappings import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.{ResolverContextResolution, Resolvers} @@ -24,7 +22,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.resources.{FetchResource, Resources, Va import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas.{SchemaDefinition, SchemaLog} import ch.epfl.bluebrain.nexus.delta.sdk.schemas._ import ch.epfl.bluebrain.nexus.delta.sdk.schemas.job.{SchemaValidationCoordinator, SchemaValidationStream} -import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.{Schema, SchemaEvent} +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaEvent import ch.epfl.bluebrain.nexus.delta.sdk.sse.SseEncoder import ch.epfl.bluebrain.nexus.delta.sourcing.projections.{ProjectionErrors, Projections} import ch.epfl.bluebrain.nexus.delta.sourcing.stream.Supervisor @@ -104,14 +102,12 @@ object SchemasModule extends ModuleDef { aclCheck: AclCheck, schemas: Schemas, schemeDirectives: DeltaSchemeDirectives, - indexingAction: AggregateIndexingAction, - shift: Schema.Shift, baseUri: BaseUri, cr: RemoteContextResolution @Id("aggregate"), ordering: JsonKeyOrdering, fusionConfig: FusionConfig ) => - new SchemasRoutes(identities, aclCheck, schemas, schemeDirectives, indexingAction(_, _, _)(shift))( + new SchemasRoutes(identities, aclCheck, schemas, schemeDirectives)( baseUri, cr, ordering, @@ -147,12 +143,8 @@ object SchemasModule extends ModuleDef { many[SseEncoder[_]].add { base: BaseUri => SchemaEvent.sseEncoder(base) } - many[ScopedEventMetricEncoder[_]].add { SchemaEvent.schemaEventMetricEncoder } - many[ApiMappings].add(Schemas.mappings) - many[ResourceToSchemaMappings].add(Schemas.resourcesToSchemas) - many[MetadataContextValue].addEffect(MetadataContextValue.fromFile("contexts/schemas-metadata.json")) many[RemoteContextResolution].addEffect( @@ -172,10 +164,4 @@ object SchemasModule extends ModuleDef { many[PriorityRoute].add { (route: SchemaJobRoutes) => PriorityRoute(pluginsMaxPriority + 8, route.routes, requiresStrictEntity = true) } - - make[Schema.Shift].from { (schemas: Schemas, base: BaseUri) => - Schema.shift(schemas)(base) - } - - many[ResourceShift[_, _, _]].ref[Schema.Shift] } diff --git a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutesSpec.scala b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutesSpec.scala index 53cf3bf53b..2e951e38ce 100644 --- a/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutesSpec.scala +++ b/delta/app/src/test/scala/ch/epfl/bluebrain/nexus/delta/routes/SchemasRoutesSpec.scala @@ -10,7 +10,6 @@ import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.{contexts, nxv} import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.JsonLdContext.keywords import ch.epfl.bluebrain.nexus.delta.rdf.shacl.ValidateShacl -import ch.epfl.bluebrain.nexus.delta.sdk.IndexingAction import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclSimpleCheck import ch.epfl.bluebrain.nexus.delta.sdk.acls.model.AclAddress import ch.epfl.bluebrain.nexus.delta.sdk.directives.DeltaSchemeDirectives @@ -85,8 +84,7 @@ class SchemasRoutesSpec extends BaseRouteSpec with IOFromMap with CatsIOValues { identities, aclCheck, SchemasImpl(schemaLog, fetchContext, schemaImports, resolverContextResolution), - groupDirectives, - IndexingAction.noop + groupDirectives ) ) @@ -118,6 +116,19 @@ class SchemasRoutesSpec extends BaseRouteSpec with IOFromMap with CatsIOValues { } } + "fail to list schemas" in { + Get("/v1/schemas/myorg/myproject") ~> routes ~> check { + status shouldEqual StatusCodes.Forbidden + } + } + + "list schemas in the project with the appropriate permission" in { + Get("/v1/schemas/myorg/myproject") ~> asReader ~> routes ~> check { + status shouldEqual StatusCodes.OK + response.asJson.asObject.value("_total").value shouldEqual Json.fromLong(2L) + } + } + "reject the creation of a schema which already exists" in { Put("/v1/schemas/myorg/myproject/myid", payload.toEntity) ~> asWriter ~> routes ~> check { status shouldEqual StatusCodes.Conflict diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/MultiResolution.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/MultiResolution.scala index f48328c4ee..aac588510c 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/MultiResolution.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/MultiResolution.scala @@ -1,18 +1,24 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resolvers import cats.effect.IO +import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.{ExpandIri, JsonLdContent} +import ch.epfl.bluebrain.nexus.delta.sdk.model.Fetch.Fetch import ch.epfl.bluebrain.nexus.delta.sdk.model.{IdSegment, IdSegmentRef} import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ProjectContext import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResolverRejection.{InvalidResolution, InvalidResolvedResourceId, InvalidResolverResolution} import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.ResourceResolutionReport.ResolverReport import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.model.{MultiResolutionResult, ResourceResolutionReport} -import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef +import ch.epfl.bluebrain.nexus.delta.sdk.resources.FetchResource +import ch.epfl.bluebrain.nexus.delta.sdk.resources.model.Resource +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.FetchSchema +import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema +import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef} /** - * Allow to attempt resolutions for the different resource types available + * Allow to attempt resolutions for data resources and schemas * @param fetchProject * how to fetch a project * @param resourceResolution @@ -81,6 +87,28 @@ final class MultiResolution( object MultiResolution { + def apply( + fetchContext: FetchContext, + aclCheck: AclCheck, + resolvers: Resolvers, + fetchResource: FetchResource, + fetchSchema: FetchSchema + ): MultiResolution = { + def combinedFetch(resourceRef: ResourceRef, project: ProjectRef): Fetch[JsonLdContent[_, _]] = + fetchResource.fetch(resourceRef, project).flatMap { + case Some(resource) => IO.some(Resource.toJsonLdContent(resource)) + case None => fetchSchema.option(resourceRef, project).map(_.map(Schema.toJsonLdContent)) + } + + val combinedResolution = ResolverResolution( + aclCheck, + resolvers, + combinedFetch, + excludeDeprecated = false + ) + apply(fetchContext, combinedResolution) + } + /** * Create a multi resolution instance * @param fetchContext diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolution.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolution.scala index 1a6218aab7..630b094bef 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolution.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/resolvers/ResolverResolution.scala @@ -3,7 +3,6 @@ package ch.epfl.bluebrain.nexus.delta.sdk.resolvers import cats.effect.IO import cats.implicits._ import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri -import ch.epfl.bluebrain.nexus.delta.sdk.ResourceShifts import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclCheck import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdContent @@ -272,27 +271,6 @@ object ResolverResolution { ) } - /** - * Resolution based on resolvers and reference exchanges - * - * @param aclCheck - * how to check acls - * @param resolvers - * a resolvers instance - * @param shifts - * how to fetch the resource - * @param excludeDeprecated - * to exclude deprecated resources from the resolution - */ - def apply( - aclCheck: AclCheck, - resolvers: Resolvers, - shifts: ResourceShifts, - excludeDeprecated: Boolean - ): ResolverResolution[JsonLdContent[_, _]] = { - apply(aclCheck, resolvers, shifts.fetch(_, _), excludeDeprecated) - } - def apply( aclCheck: AclCheck, resolvers: Resolvers, 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 e62c02745d..a14bbd345b 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 @@ -9,7 +9,7 @@ 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.sdk.jsonld.JsonLdContent -import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, IdSegmentRef, Tags} +import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, IdSegmentRef, ResourceF, 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.syntax._ @@ -76,6 +76,9 @@ object Resource { implicit val fileMetadataJsonLdEncoder: JsonLdEncoder[Metadata] = JsonLdEncoder.computeFromCirce(ContextValue(contexts.metadata)) + def toJsonLdContent(value: ResourceF[Resource]): JsonLdContent[Resource, Metadata] = + JsonLdContent(value, value.value.source, Some(value.value.metadata)) + type Shift = ResourceShift[ResourceState, Resource, Metadata] def shift(resources: Resources)(implicit baseUri: BaseUri): Shift = @@ -83,6 +86,6 @@ object Resource { Resources.entityType, (ref, project) => resources.fetch(IdSegmentRef(ref), project, None), state => state.toResource, - value => JsonLdContent(value, value.value.source, Some(value.value.metadata)) + toJsonLdContent ) } diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/Schemas.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/Schemas.scala index b59c385bdd..ed4c969852 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/Schemas.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/Schemas.scala @@ -11,6 +11,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.instances._ import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.ExpandIri import ch.epfl.bluebrain.nexus.delta.sdk.model._ +import ch.epfl.bluebrain.nexus.delta.sdk.model.search.SearchResults.UnscoredSearchResults import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ApiMappings import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaCommand._ import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaEvent._ @@ -195,6 +196,15 @@ trait Schemas { projectRef: ProjectRef ): IO[SchemaResource] = fetch(IdSegmentRef(resourceRef), projectRef) + /** + * Lists all resolvers. + * + * @param project + * the project the resolvers belong to + * @return + * the list of resolvers in that project + */ + def list(project: ProjectRef): IO[UnscoredSearchResults[SchemaResource]] } object Schemas { @@ -213,13 +223,6 @@ object Schemas { */ val mappings: ApiMappings = ApiMappings(ArrowAssoc("schema") -> schemas.shacl) - /** - * The schema resource to schema mapping - */ - val resourcesToSchemas: ResourceToSchemaMappings = ResourceToSchemaMappings( - ArrowAssoc(Label.unsafe("schemas")) -> schemas.shacl - ) - private[delta] def next(state: Option[SchemaState], event: SchemaEvent): Option[SchemaState] = { // format: off diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImpl.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImpl.scala index bca04b8632..6d069b40a9 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImpl.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/SchemasImpl.scala @@ -11,6 +11,8 @@ import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller import ch.epfl.bluebrain.nexus.delta.sdk.implicits._ import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdSourceProcessor.JsonLdSourceResolvingParser import ch.epfl.bluebrain.nexus.delta.sdk.model._ +import ch.epfl.bluebrain.nexus.delta.sdk.model.search.SearchResults +import ch.epfl.bluebrain.nexus.delta.sdk.model.search.SearchResults.UnscoredSearchResults import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext import ch.epfl.bluebrain.nexus.delta.sdk.resolvers.ResolverContextResolution import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas.{expandIri, kamonComponent, SchemaLog} @@ -152,6 +154,14 @@ final class SchemasImpl private ( private def resolveImports(id: Iri, projectRef: ProjectRef, expanded: ExpandedJsonLd)(implicit caller: Caller) = schemaImports.resolve(id, projectRef, expanded.addType(nxv.Schema)) + + def list(project: ProjectRef): IO[UnscoredSearchResults[SchemaResource]] = + log + .currentStates(Scope.Project(project), _.toResource) + .compile + .toList + .map { results => SearchResults(results.size.toLong, results) } + .span("listSchemas") } object SchemasImpl { diff --git a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/Schema.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/Schema.scala index 2140d59829..29d0c18826 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/Schema.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/Schema.scala @@ -12,16 +12,11 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.api.{JsonLdApi, JsonLdOptions, T import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.context.{ContextValue, RemoteContextResolution} 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.sdk.jsonld.JsonLdContent -import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, IdSegmentRef, Tags} -import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas -import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.Schema.Metadata +import ch.epfl.bluebrain.nexus.delta.sdk.model.{ResourceF, Tags} import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef -import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag -import io.circe.syntax.EncoderOps -import io.circe.{Encoder, Json} +import io.circe.Json /** * A schema representation @@ -72,13 +67,12 @@ final case class Schema( } .map { triples => Graph.empty(id).add(triples) } } - - def metadata: Metadata = Metadata(tags.tags) } object Schema { - final case class Metadata(tags: List[UserTag]) + def toJsonLdContent(schema: ResourceF[Schema]): JsonLdContent[Schema, Nothing] = + JsonLdContent(schema, schema.value.source, None) implicit val schemaJsonLdEncoder: JsonLdEncoder[Schema] = new JsonLdEncoder[Schema] { @@ -96,22 +90,4 @@ object Schema { override def context(value: Schema): ContextValue = value.source.topContextValueOrEmpty.merge(ContextValue(contexts.shacl)) } - - implicit private val fileMetadataEncoder: Encoder[Metadata] = { m => - Json.obj("_tags" -> m.tags.asJson) - } - - implicit val fileMetadataJsonLdEncoder: JsonLdEncoder[Metadata] = - JsonLdEncoder.computeFromCirce(ContextValue(contexts.metadata)) - - type Shift = ResourceShift[SchemaState, Schema, Metadata] - - def shift(schemas: Schemas)(implicit baseUri: BaseUri): Shift = - ResourceShift.withMetadata[SchemaState, Schema, Metadata]( - Schemas.entityType, - (ref, project) => schemas.fetch(IdSegmentRef(ref), project), - 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/schemas/model/SchemaEvent.scala b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/SchemaEvent.scala index 97080d001e..1f54ac2ea2 100644 --- a/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/SchemaEvent.scala +++ b/delta/sdk/src/main/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/SchemaEvent.scala @@ -9,10 +9,8 @@ import ch.epfl.bluebrain.nexus.delta.rdf.jsonld.{CompactedJsonLd, ExpandedJsonLd import ch.epfl.bluebrain.nexus.delta.sdk.instances._ import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.IriEncoder import ch.epfl.bluebrain.nexus.delta.sdk.model.BaseUri -import ch.epfl.bluebrain.nexus.delta.sdk.model.metrics.EventMetric._ -import ch.epfl.bluebrain.nexus.delta.sdk.model.metrics.ScopedEventMetricEncoder import ch.epfl.bluebrain.nexus.delta.sdk.schemas.Schemas -import ch.epfl.bluebrain.nexus.delta.sdk.sse.{resourcesSelector, SseEncoder} +import ch.epfl.bluebrain.nexus.delta.sdk.sse.SseEncoder import ch.epfl.bluebrain.nexus.delta.sourcing.Serializer import ch.epfl.bluebrain.nexus.delta.sourcing.event.Event.ScopedEvent import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject @@ -21,7 +19,7 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, Label, ProjectR import io.circe.generic.extras.Configuration import io.circe.generic.extras.semiauto.{deriveConfiguredCodec, deriveConfiguredEncoder} import io.circe.syntax._ -import io.circe.{Codec, Decoder, Encoder, Json, JsonObject} +import io.circe.{Codec, Decoder, Encoder, Json} import java.time.Instant @@ -243,37 +241,13 @@ object SchemaEvent { Serializer() } - val schemaEventMetricEncoder: ScopedEventMetricEncoder[SchemaEvent] = - new ScopedEventMetricEncoder[SchemaEvent] { - override def databaseDecoder: Decoder[SchemaEvent] = serializer.codec - - override def entityType: EntityType = Schemas.entityType - - override def eventToMetric: SchemaEvent => ProjectScopedMetric = event => - ProjectScopedMetric.from( - event, - event match { - case _: SchemaCreated => Created - case _: SchemaUpdated => Updated - case _: SchemaRefreshed => Refreshed - case _: SchemaTagAdded => Tagged - case _: SchemaTagDeleted => TagDeleted - case _: SchemaDeprecated => Deprecated - case _: SchemaUndeprecated => Undeprecated - }, - event.id, - Set(nxv.Schema), - JsonObject.empty - ) - } - def sseEncoder(implicit base: BaseUri): SseEncoder[SchemaEvent] = new SseEncoder[SchemaEvent] { override val databaseDecoder: Decoder[SchemaEvent] = serializer.codec override def entityType: EntityType = Schemas.entityType - override val selectors: Set[Label] = Set(Label.unsafe("schemas"), resourcesSelector) + override val selectors: Set[Label] = Set(Label.unsafe("schemas")) override val sseEncoder: Encoder.AsObject[SchemaEvent] = { val context = ContextValue(contexts.metadata, contexts.shacl) diff --git a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/SchemaSerializationSuite.scala b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/SchemaSerializationSuite.scala index 3996b2c256..0a773750c8 100644 --- a/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/SchemaSerializationSuite.scala +++ b/delta/sdk/src/test/scala/ch/epfl/bluebrain/nexus/delta/sdk/schemas/model/SchemaSerializationSuite.scala @@ -5,12 +5,10 @@ import ch.epfl.bluebrain.nexus.delta.rdf.syntax.jsonOpsSyntax import ch.epfl.bluebrain.nexus.delta.sdk.SerializationSuite import ch.epfl.bluebrain.nexus.delta.sdk.generators.SchemaGen import ch.epfl.bluebrain.nexus.delta.sdk.model.Tags -import ch.epfl.bluebrain.nexus.delta.sdk.model.metrics.EventMetric._ import ch.epfl.bluebrain.nexus.delta.sdk.schemas.model.SchemaEvent._ import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Subject, User} import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Label, ProjectRef} -import io.circe.JsonObject import java.time.Instant @@ -101,16 +99,16 @@ class SchemaSerializationSuite extends SerializationSuite { ) private val schemasMapping = List( - (created, jsonContentOf("schemas/schema-created.json"), Created), - (updated, jsonContentOf("schemas/schema-updated.json"), Updated), - (refreshed, jsonContentOf("schemas/schema-refreshed.json"), Refreshed), - (tagged, jsonContentOf("schemas/schema-tagged.json"), Tagged), - (tagDeleted, jsonContentOf("schemas/schema-tag-deleted.json"), TagDeleted), - (deprecated, jsonContentOf("schemas/schema-deprecated.json"), Deprecated), - (undeprecated, jsonContentOf("schemas/schema-undeprecated.json"), Undeprecated) + (created, jsonContentOf("schemas/schema-created.json")), + (updated, jsonContentOf("schemas/schema-updated.json")), + (refreshed, jsonContentOf("schemas/schema-refreshed.json")), + (tagged, jsonContentOf("schemas/schema-tagged.json")), + (tagDeleted, jsonContentOf("schemas/schema-tag-deleted.json")), + (deprecated, jsonContentOf("schemas/schema-deprecated.json")), + (undeprecated, jsonContentOf("schemas/schema-undeprecated.json")) ) - schemasMapping.foreach { case (event, json, action) => + schemasMapping.foreach { case (event, json) => test(s"Correctly serialize ${event.getClass.getSimpleName}") { assertOutput(SchemaEvent.serializer, event, json) } @@ -118,22 +116,6 @@ class SchemaSerializationSuite extends SerializationSuite { test(s"Correctly deserialize ${event.getClass.getSimpleName}") { assertEquals(SchemaEvent.serializer.codec.decodeJson(json), Right(event)) } - - test(s"Correctly encode ${event.getClass.getSimpleName} to metric") { - SchemaEvent.schemaEventMetricEncoder.toMetric.decodeJson(json).assertRight { - ProjectScopedMetric( - instant, - subject, - event.rev, - Set(action), - ProjectRef(org, proj), - org, - event.id, - Set(nxv.Schema), - JsonObject.empty - ) - } - } } private val state = SchemaState( diff --git a/docs/src/main/paradox/docs/delta/api/schemas-api.md b/docs/src/main/paradox/docs/delta/api/schemas-api.md index 6b5a8c18bb..24a3e81083 100644 --- a/docs/src/main/paradox/docs/delta/api/schemas-api.md +++ b/docs/src/main/paradox/docs/delta/api/schemas-api.md @@ -27,19 +27,11 @@ Please visit @ref:[Authentication & authorization](authentication.md) section to @@@ note { .warning } -From Delta v1.5, remote contexts and `owl:imports` are only resolved during creates and updates. +Remote contexts and `owl:imports` are only resolved during creates and updates. That means that when those get updated, the schemas importing them must be also updated to take the changes into account. @@@ -## Indexing - -All the API calls modifying a schema (creation, update, tagging, deprecation) can specify whether the schema should be indexed -synchronously or in the background. This behaviour is controlled using `indexing` query param, which can be one of two values: - -- `async` - (default value) the schema will be indexed asynchronously -- `sync` - the schema will be indexed synchronously and the API call won't return until the indexing is finished - ## Create using POST ``` diff --git a/tests/src/test/resources/kg/aggregations/org-aggregation.json b/tests/src/test/resources/kg/aggregations/org-aggregation.json index 79d5e1e4a8..64aff8bddd 100644 --- a/tests/src/test/resources/kg/aggregations/org-aggregation.json +++ b/tests/src/test/resources/kg/aggregations/org-aggregation.json @@ -4,7 +4,7 @@ "projects" : { "buckets" : [ { - "doc_count" : 5, + "doc_count" : 4, "key" : "{{org1}}/{{proj11}}" }, { @@ -32,15 +32,11 @@ { "doc_count" : 2, "key" : "https://bluebrain.github.io/nexus/vocabulary/SparqlView" - }, - { - "doc_count" : 1, - "key" : "https://bluebrain.github.io/nexus/vocabulary/Schema" } ], "doc_count_error_upper_bound" : 0, "sum_other_doc_count" : 0 } }, - "total" : 8 + "total" : 7 } \ No newline at end of file diff --git a/tests/src/test/resources/kg/aggregations/project-aggregation.json b/tests/src/test/resources/kg/aggregations/project-aggregation.json index 9804f562cd..e77a82ed15 100644 --- a/tests/src/test/resources/kg/aggregations/project-aggregation.json +++ b/tests/src/test/resources/kg/aggregations/project-aggregation.json @@ -3,7 +3,7 @@ "aggregations" : { "projects" : { "buckets" : { - "doc_count" : 5, + "doc_count" : 4, "key" : "{{org}}/{{project}}" }, "doc_count_error_upper_bound" : 0, @@ -23,10 +23,6 @@ "doc_count" : 1, "key" : "https://bluebrain.github.io/nexus/vocabulary/CompositeView" }, - { - "doc_count" : 1, - "key" : "https://bluebrain.github.io/nexus/vocabulary/Schema" - }, { "doc_count" : 1, "key" : "https://bluebrain.github.io/nexus/vocabulary/SparqlView" @@ -36,5 +32,5 @@ "sum_other_doc_count" : 0 } }, - "total" : 5 + "total" : 4 } \ No newline at end of file diff --git a/tests/src/test/resources/kg/aggregations/root-aggregation.json b/tests/src/test/resources/kg/aggregations/root-aggregation.json index bbcd0f63dc..c2847c9f60 100644 --- a/tests/src/test/resources/kg/aggregations/root-aggregation.json +++ b/tests/src/test/resources/kg/aggregations/root-aggregation.json @@ -4,7 +4,7 @@ "projects" : { "buckets" : [ { - "doc_count" : 5, + "doc_count" : 4, "key" : "{{org1}}/{{proj11}}" }, { @@ -36,15 +36,11 @@ { "doc_count" : 3, "key" : "https://bluebrain.github.io/nexus/vocabulary/SparqlView" - }, - { - "doc_count" : 1, - "key" : "https://bluebrain.github.io/nexus/vocabulary/Schema" } ], "doc_count_error_upper_bound" : 0, "sum_other_doc_count" : 0 } }, - "total" : 11 + "total" : 10 } \ No newline at end of file diff --git a/tests/src/test/resources/kg/aggregations/schemas-aggregation.json b/tests/src/test/resources/kg/aggregations/schemas-aggregation.json deleted file mode 100644 index 52f511a4d6..0000000000 --- a/tests/src/test/resources/kg/aggregations/schemas-aggregation.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "@context" : "https://bluebrain.github.io/nexus/contexts/aggregations.json", - "aggregations" : { - "projects" : { - "buckets" : { - "doc_count" : 1, - "key" : "{{org}}/{{project}}" - }, - "doc_count_error_upper_bound" : 0, - "sum_other_doc_count" : 0 - }, - "types" : { - "buckets" : { - "doc_count" : 1, - "key" : "https://bluebrain.github.io/nexus/vocabulary/Schema" - }, - "doc_count_error_upper_bound" : 0, - "sum_other_doc_count" : 0 - } - }, - "total" : 1 -} \ No newline at end of file diff --git a/tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/kg/AggregationsSpec.scala b/tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/kg/AggregationsSpec.scala index 433c7eecdd..ab2dc1adea 100644 --- a/tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/kg/AggregationsSpec.scala +++ b/tests/src/test/scala/ch/epfl/bluebrain/nexus/tests/kg/AggregationsSpec.scala @@ -38,16 +38,12 @@ final class AggregationsSpec extends BaseIntegrationSpec { } yield () val resourcePayload = SimpleResource.sourcePayload(5).accepted - val schemaPayload = SchemaPayload.loadSimple().accepted val postResources = for { // Creation - _ <- deltaClient.put[Json](s"/resources/$ref11/_/resource11", resourcePayload, Charlie)(expectCreated) - _ <- deltaClient.put[Json](s"/schemas/$ref11/test-schema", schemaPayload, Charlie)(expectCreated) - _ <- deltaClient.put[Json](s"/resources/$ref11/test-schema/resource11_with_schema", resourcePayload, Charlie)( - expectCreated - ) - _ <- deltaClient.put[Json](s"/resources/$ref12/_/resource12", resourcePayload, Charlie)(expectCreated) - _ <- deltaClient.put[Json](s"/resources/$ref21/_/resource21", resourcePayload, Charlie)(expectCreated) + _ <- deltaClient.put[Json](s"/resources/$ref11/_/r11_1", resourcePayload, Charlie)(expectCreated) + _ <- deltaClient.put[Json](s"/resources/$ref11/_/r11_2", resourcePayload, Charlie)(expectCreated) + _ <- deltaClient.put[Json](s"/resources/$ref12/_/r12_1", resourcePayload, Charlie)(expectCreated) + _ <- deltaClient.put[Json](s"/resources/$ref21/_/r21_1", resourcePayload, Charlie)(expectCreated) } yield () (setup >> postResources).accepted @@ -56,10 +52,7 @@ final class AggregationsSpec extends BaseIntegrationSpec { "Aggregating resources within a project" should { "get an error if the user has no access" in { - - deltaClient.get[Json](s"/resources/$ref11?aggregations=true", Rose) { (_, response) => - response.status shouldEqual StatusCodes.Forbidden - } + deltaClient.get[Json](s"/resources/$ref11?aggregations=true", Rose) { expectForbidden } } "aggregate correctly for a user that has project permissions" in eventually { @@ -85,26 +78,12 @@ final class AggregationsSpec extends BaseIntegrationSpec { json should equalIgnoreArrayOrder(expected) } } - - "aggregate schemas" in { - val expected = jsonContentOf( - "kg/aggregations/schemas-aggregation.json", - "org" -> org1, - "project" -> proj11 - ) - deltaClient.get[Json](s"/schemas/$ref11?aggregations=true", Charlie) { (json, response) => - response.status shouldEqual StatusCodes.OK - json should equalIgnoreArrayOrder(expected) - } - } } "Aggregating resources within an org" should { "get an error if the user has no access on the org" in { - deltaClient.get[Json](s"/resources/$org2?aggregations=true", Rose) { (_, response) => - response.status shouldEqual StatusCodes.Forbidden - } + deltaClient.get[Json](s"/resources/$org2?aggregations=true", Rose) { expectForbidden } } "aggregate correctly for a user that has access" in eventually { @@ -125,9 +104,7 @@ final class AggregationsSpec extends BaseIntegrationSpec { "Aggregating resources within all accessible projects in the system" should { "get an error for anonymous" in { - deltaClient.get[Json](s"/resources?aggregations=true", Anonymous) { (_, response) => - response.status shouldEqual StatusCodes.Forbidden - } + deltaClient.get[Json](s"/resources?aggregations=true", Anonymous) { expectForbidden } } "aggregate correctly for a user that has permissions on at least one project" in eventually {