Skip to content

Commit

Permalink
Remove the ability to tag storages (#4675)
Browse files Browse the repository at this point in the history
* Remove the ability to tag storages

* Reinstate tests

* Remove tags from state, reject lookup by tag

* Keep tag event processing, update docs
  • Loading branch information
dantb authored Jan 22, 2024
1 parent 9d5be7f commit fab16c1
Show file tree
Hide file tree
Showing 25 changed files with 63 additions and 383 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ch.epfl.bluebrain.nexus.delta.kernel.utils.UUIDF
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.Storages._
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.StoragesConfig.StorageTypeConfig
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model.StorageCommand._
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model.StorageEvent.{StorageCreated, StorageDeprecated, StorageTagAdded, StorageUndeprecated, StorageUpdated}
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model.StorageEvent._
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model.StorageRejection._
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model.StorageValue.DiskStorageValue
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model._
Expand All @@ -26,10 +26,8 @@ import ch.epfl.bluebrain.nexus.delta.sdk.permissions.model.Permission
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
import ch.epfl.bluebrain.nexus.delta.sourcing.ScopedEntityDefinition.Tagger
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.Tag.UserTag
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{EntityType, ProjectRef}
import ch.epfl.bluebrain.nexus.delta.sourcing.stream.Elem
import fs2.Stream
Expand Down Expand Up @@ -183,34 +181,6 @@ final class Storages private (
} yield res
}.span("updateStorage")

/**
* Add a tag to an existing storage
*
* @param id
* the storage identifier to expand as the id of the storage
* @param projectRef
* the project where the storage belongs
* @param tag
* the tag name
* @param tagRev
* the tag revision
* @param rev
* the current revision of the storage
*/
def tag(
id: IdSegment,
projectRef: ProjectRef,
tag: UserTag,
tagRev: Int,
rev: Int
)(implicit subject: Subject): IO[StorageResource] = {
for {
pc <- fetchContext.onModify(projectRef)
iri <- expandIri(id, pc)
res <- eval(TagStorage(iri, projectRef, tagRev, tag, rev, subject))
} yield res
}.span("tagStorage")

/**
* Deprecate an existing storage
*
Expand Down Expand Up @@ -262,10 +232,8 @@ final class Storages private (
notFound = StorageNotFound(iri, project)
state <- id match {
case Latest(_) => log.stateOr(project, iri, notFound)
case Revision(_, rev) =>
log.stateOr(project, iri, rev, notFound, RevisionNotFound)
case Tag(_, tag) =>
log.stateOr(project, iri, tag, notFound, TagNotFound(tag))
case Revision(_, rev) => log.stateOr(project, iri, rev, notFound, RevisionNotFound)
case t: Tag => IO.raiseError(FetchByTagNotSupported(t))
}
} yield state.toResource
}.span("fetchStorage")
Expand Down Expand Up @@ -354,7 +322,6 @@ object Storages {
e.project,
e.value,
e.source,
Tags.empty,
e.rev,
deprecated = false,
e.instant,
Expand All @@ -369,7 +336,7 @@ object Storages {
}

def tagAdded(e: StorageTagAdded): Option[StorageState] = state.map { s =>
s.copy(rev = e.rev, tags = s.tags + (e.tag -> e.targetRev), updatedAt = e.instant, updatedBy = e.subject)
s.copy(rev = e.rev, updatedAt = e.instant, updatedBy = e.subject)
}

def deprecated(e: StorageDeprecated): Option[StorageState] = state.map { s =>
Expand Down Expand Up @@ -464,16 +431,6 @@ object Storages {
} yield StorageUpdated(c.id, c.project, value, c.source, s.rev + 1, instant, c.subject)
}

def tag(c: TagStorage) = state match {
case None => IO.raiseError(StorageNotFound(c.id, c.project))
case Some(s) if s.rev != c.rev => IO.raiseError(IncorrectRev(c.rev, s.rev))
case Some(s) if c.targetRev <= 0 || c.targetRev > s.rev => IO.raiseError(RevisionNotFound(c.targetRev, s.rev))
case Some(s) =>
clock.realTimeInstant.map(
StorageTagAdded(c.id, c.project, s.value.tpe, c.targetRev, c.tag, s.rev + 1, _, c.subject)
)
}

def deprecate(c: DeprecateStorage) = state match {
case None => IO.raiseError(StorageNotFound(c.id, c.project))
case Some(s) if s.rev != c.rev => IO.raiseError(IncorrectRev(c.rev, s.rev))
Expand All @@ -493,7 +450,6 @@ object Storages {
cmd match {
case c: CreateStorage => create(c)
case c: UpdateStorage => update(c)
case c: TagStorage => tag(c)
case c: DeprecateStorage => deprecate(c)
case c: UndeprecateStorage => undeprecate(c)
}
Expand All @@ -505,21 +461,11 @@ object Storages {
fetchPermissions: IO[Set[Permission]],
clock: Clock[IO]
): ScopedEntityDefinition[Iri, StorageState, StorageCommand, StorageEvent, StorageRejection] =
ScopedEntityDefinition(
ScopedEntityDefinition.untagged(
entityType,
StateMachine(None, evaluate(access, fetchPermissions, config, clock)(_, _), next),
StorageEvent.serializer,
StorageState.serializer,
Tagger[StorageEvent](
{
case r: StorageTagAdded => Some(r.tag -> r.targetRev)
case _ => None
},
{ _ =>
None
}
),
_ => None,
onUniqueViolation = (id: Iri, c: StorageCommand) =>
c match {
case c: CreateStorage => ResourceAlreadyExists(id, c.project)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ 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.sdk.jsonld.JsonLdContent
import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, IdSegmentRef, Tags}
import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, IdSegmentRef}
import ch.epfl.bluebrain.nexus.delta.sdk.{OrderingFields, ResourceShift}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef
import io.circe.syntax._
Expand All @@ -35,12 +35,6 @@ sealed trait Storage extends Product with Serializable {
*/
def project: ProjectRef

/**
* @return
* the tag -> rev mapping
*/
def tags: Tags

/**
* @return
* the original json document provided at creation or update
Expand Down Expand Up @@ -77,7 +71,6 @@ object Storage {
id: Iri,
project: ProjectRef,
value: DiskStorageValue,
tags: Tags,
source: Json
) extends Storage {
override val default: Boolean = value.default
Expand All @@ -97,7 +90,6 @@ object Storage {
id: Iri,
project: ProjectRef,
value: S3StorageValue,
tags: Tags,
source: Json
) extends Storage {

Expand All @@ -122,7 +114,6 @@ object Storage {
id: Iri,
project: ProjectRef,
value: RemoteDiskStorageValue,
tags: Tags,
source: Json
) extends Storage {
override val default: Boolean = value.default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model
import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri
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

/**
Expand Down Expand Up @@ -87,25 +86,6 @@ object StorageCommand {
subject: Subject
) extends StorageCommand

/**
* Command to tag a storage
*
* @param id
* the storage identifier
* @param project
* the project the storage belongs to
* @param targetRev
* the revision that is being aliased with the provided ''tag''
* @param tag
* the tag of the alias for the provided ''tagRev''
* @param rev
* the last known revision of the storage
* @param subject
* the identity associated to this command
*/
final case class TagStorage(id: Iri, project: ProjectRef, targetRev: Int, tag: UserTag, rev: Int, subject: Subject)
extends StorageCommand

/**
* Command to deprecate a storage
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import ch.epfl.bluebrain.nexus.delta.rdf.{RdfError, Vocabulary}
import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdRejection
import ch.epfl.bluebrain.nexus.delta.sdk.jsonld.JsonLdRejection.UnexpectedId
import ch.epfl.bluebrain.nexus.delta.sdk.marshalling.HttpResponseFields
import ch.epfl.bluebrain.nexus.delta.sdk.model.IdSegmentRef
import ch.epfl.bluebrain.nexus.delta.sdk.permissions.model.Permission
import ch.epfl.bluebrain.nexus.delta.sdk.projects.FetchContext.ContextRejection
import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag
import io.circe.syntax._
import io.circe.{Encoder, JsonObject}

Expand Down Expand Up @@ -47,14 +47,10 @@ object StorageRejection {
final case class RevisionNotFound(provided: Int, current: Int)
extends StorageFetchRejection(s"Revision requested '$provided' not found, last known revision is '$current'.")

/**
* Rejection returned when a subject intends to retrieve a storage at a specific tag, but the provided tag does not
* exist.
*
* @param tag
* the provided tag
*/
final case class TagNotFound(tag: UserTag) extends StorageFetchRejection(s"Tag requested '$tag' not found.")
final case class FetchByTagNotSupported(tag: IdSegmentRef.Tag)
extends StorageFetchRejection(
s"Fetching storages by tag is no longer supported. Id ${tag.value.asString} and tag ${tag.tag.value}"
)

/**
* Rejection returned when attempting to update/fetch a storage with an id that doesn't exist.
Expand Down Expand Up @@ -235,12 +231,12 @@ object StorageRejection {
implicit final val storageRejectionHttpResponseFields: HttpResponseFields[StorageRejection] =
HttpResponseFields {
case RevisionNotFound(_, _) => StatusCodes.NotFound
case TagNotFound(_) => StatusCodes.NotFound
case StorageNotFound(_, _) => StatusCodes.NotFound
case DefaultStorageNotFound(_) => StatusCodes.NotFound
case ResourceAlreadyExists(_, _) => StatusCodes.Conflict
case IncorrectRev(_, _) => StatusCodes.Conflict
case ProjectContextRejection(rej) => rej.status
case FetchByTagNotSupported(_) => StatusCodes.BadRequest
case StorageNotAccessible(_, _) => StatusCodes.BadRequest
case _ => StatusCodes.BadRequest
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model.Storage.{Dis
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.model.StorageValue.{DiskStorageValue, RemoteDiskStorageValue, S3StorageValue}
import ch.epfl.bluebrain.nexus.delta.plugins.storage.storages.{schemas, StorageResource}
import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri
import ch.epfl.bluebrain.nexus.delta.sdk.model.{ResourceF, ResourceUris, Tags}
import ch.epfl.bluebrain.nexus.delta.sdk.model.{ResourceF, ResourceUris}
import ch.epfl.bluebrain.nexus.delta.sourcing.Serializer
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.Subject
import ch.epfl.bluebrain.nexus.delta.sourcing.model.ResourceRef.Latest
Expand Down Expand Up @@ -48,7 +48,6 @@ final case class StorageState(
project: ProjectRef,
value: StorageValue,
source: Json,
tags: Tags,
rev: Int,
deprecated: Boolean,
createdAt: Instant,
Expand All @@ -63,9 +62,9 @@ final case class StorageState(

def storage: Storage =
value match {
case value: DiskStorageValue => DiskStorage(id, project, value, tags, source)
case value: S3StorageValue => S3Storage(id, project, value, tags, source)
case value: RemoteDiskStorageValue => RemoteDiskStorage(id, project, value, tags, source)
case value: DiskStorageValue => DiskStorage(id, project, value, source)
case value: S3StorageValue => S3Storage(id, project, value, source)
case value: RemoteDiskStorageValue => RemoteDiskStorage(id, project, value, source)
}

def toResource: StorageResource =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ 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.RdfMarshalling
import ch.epfl.bluebrain.nexus.delta.sdk.model.BaseUri
import ch.epfl.bluebrain.nexus.delta.sdk.model.routes.Tag
import io.circe.Json
import kamon.instrumentation.akka.http.TracingDirectives.operationName

Expand Down Expand Up @@ -163,37 +162,6 @@ final class StoragesRoutes(
}
}
},
(pathPrefix("tags") & pathEndOrSingleSlash) {
operationName(s"$prefixSegment/storages/{org}/{project}/{id}/tags") {
concat(
// Fetch a storage tags
(get & idSegmentRef(id) & authorizeFor(project, Read)) { id =>
emit(
storages
.fetch(id, project)
.map(_.value.tags)
.attemptNarrow[StorageRejection]
.rejectOn[StorageNotFound]
)
},
// Tag a storage
(post & parameter("rev".as[Int])) { rev =>
authorizeFor(project, Write).apply {
entity(as[Tag]) { case Tag(tagRev, tag) =>
emit(
Created,
storages
.tag(id, project, tag, tagRev, rev)
.flatTap(index(project, _, mode))
.mapValue(_.metadata)
.attemptNarrow[StorageRejection]
)
}
}
}
)
}
},
(pathPrefix("statistics") & get & pathEndOrSingleSlash) {
authorizeFor(project, Read).apply {
emit(storagesStatistics.get(id, project).attemptNarrow[StorageRejection])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@
"capacity": 1000,
"maxFileSize": 50
},
"tags": {
"mytag": 3
},
"createdAt": "1970-01-01T00:00:00Z",
"createdBy": {
"subject": "username",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@
"writePermission": "remote/write",
"maxFileSize": 52
},
"tags": {
"mytag": 3
},
"createdAt": "1970-01-01T00:00:00Z",
"createdBy": {
"subject": "username",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@
"writePermission": "s3/write",
"maxFileSize": 51
},
"tags": {
"mytag": 3
},
"createdAt": "1970-01-01T00:00:00Z",
"createdBy": {
"subject": "username",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.acls.model.AclAddress
import ch.epfl.bluebrain.nexus.delta.sdk.acls.{AclCheck, AclSimpleCheck}
import ch.epfl.bluebrain.nexus.delta.sdk.error.ServiceError.AuthorizationFailed
import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller
import ch.epfl.bluebrain.nexus.delta.sdk.model.{IdSegment, IdSegmentRef, Tags}
import ch.epfl.bluebrain.nexus.delta.sdk.model.{IdSegment, IdSegmentRef}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.User
import ch.epfl.bluebrain.nexus.delta.sourcing.model.{ProjectRef, ResourceRef}
import ch.epfl.bluebrain.nexus.testkit.Generators
Expand Down Expand Up @@ -239,16 +239,16 @@ class BatchCopySuite extends NexusSuite with StorageFixtures with Generators wit

private def genDiskStorageWithCapacity(capacity: Long) = {
val limitedDiskVal = diskVal.copy(capacity = Some(capacity))
DiskStorage(nxv + genString(), genProject().ref, limitedDiskVal, Tags.empty, json"""{"disk": "value"}""")
DiskStorage(nxv + genString(), genProject().ref, limitedDiskVal, json"""{"disk": "value"}""")
}

private def genDiskStorage() = genDiskStorageWithCapacity(1000L)

private def genRemoteStorage() =
RemoteDiskStorage(nxv + genString(), genProject().ref, remoteVal, Tags.empty, json"""{"disk": "value"}""")
RemoteDiskStorage(nxv + genString(), genProject().ref, remoteVal, json"""{"disk": "value"}""")

private def genS3Storage() =
S3Storage(nxv + genString(), genProject().ref, s3Val, Tags.empty, json"""{"disk": "value"}""")
S3Storage(nxv + genString(), genProject().ref, s3Val, json"""{"disk": "value"}""")
}

object BatchCopySuite {
Expand Down
Loading

0 comments on commit fab16c1

Please sign in to comment.