Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove the ability to tag storages #4675

Merged
merged 5 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
dantb marked this conversation as resolved.
Show resolved Hide resolved
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 =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still need to compute a new state and update the rev, we may break some history otherwise.
For example, this should still work:

  • StorageCreated
  • StorageTagged
  • StorageUpdated
    We should still have a storage with revision 3

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 {
dantb marked this conversation as resolved.
Show resolved Hide resolved
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
dantb marked this conversation as resolved.
Show resolved Hide resolved
(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