Skip to content

Commit

Permalink
Allow creating/linking file with tag
Browse files Browse the repository at this point in the history
  • Loading branch information
dantb committed Oct 16, 2023
1 parent cb4b6b1 commit 764c121
Show file tree
Hide file tree
Showing 16 changed files with 298 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,12 @@ object UUIDF {
override def apply(): UIO[UUID] = uuidRef.get.hideErrors
}
} yield uuidF).hideErrors

/**
* Creates a [[UUIDF]] sourcing [[UUID]] values from a mutable reference.
*
* @param ref
* the pre-initialised mutable reference used to store the [[UUID]]
*/
final def fromRef(ref: Ref[Task, UUID]): UUIDF = () => ref.get.hideErrors
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,22 @@ final class Files(
* the project where the file will belong
* @param entity
* the http FormData entity
* @param tag
* the optional tag this file is being created with, attached to the current revision
*/
def create(
storageId: Option[IdSegment],
projectRef: ProjectRef,
entity: HttpEntity
entity: HttpEntity,
tag: Option[UserTag]
)(implicit caller: Caller): IO[FileRejection, FileResource] = {
for {
pc <- fetchContext.onCreate(projectRef)
iri <- generateId(pc)
_ <- test(CreateFile(iri, projectRef, testStorageRef, testStorageType, testAttributes, caller.subject))
_ <- test(CreateFile(iri, projectRef, testStorageRef, testStorageType, testAttributes, caller.subject, tag))
(storageRef, storage) <- fetchActiveStorage(storageId, projectRef, pc)
attributes <- extractFileAttributes(iri, entity, storage)
res <- eval(CreateFile(iri, projectRef, storageRef, storage.tpe, attributes, caller.subject))
res <- eval(CreateFile(iri, projectRef, storageRef, storage.tpe, attributes, caller.subject, tag))
} yield res
}.span("createFile")

Expand All @@ -112,20 +115,23 @@ final class Files(
* the project where the file will belong
* @param entity
* the http FormData entity
* @param tag
* the optional tag this file is being created with, attached to the current revision
*/
def create(
id: IdSegment,
storageId: Option[IdSegment],
projectRef: ProjectRef,
entity: HttpEntity
entity: HttpEntity,
tag: Option[UserTag]
)(implicit caller: Caller): IO[FileRejection, FileResource] = {
for {
pc <- fetchContext.onCreate(projectRef)
iri <- expandIri(id, pc)
_ <- test(CreateFile(iri, projectRef, testStorageRef, testStorageType, testAttributes, caller.subject))
_ <- test(CreateFile(iri, projectRef, testStorageRef, testStorageType, testAttributes, caller.subject, tag))
(storageRef, storage) <- fetchActiveStorage(storageId, projectRef, pc)
attributes <- extractFileAttributes(iri, entity, storage)
res <- eval(CreateFile(iri, projectRef, storageRef, storage.tpe, attributes, caller.subject))
res <- eval(CreateFile(iri, projectRef, storageRef, storage.tpe, attributes, caller.subject, tag))
} yield res
}.span("createFile")

Expand All @@ -142,18 +148,21 @@ final class Files(
* the optional media type to use
* @param path
* the path where the file is located inside the storage
* @param tag
* the optional tag this file link is being created with, attached to the current revision
*/
def createLink(
storageId: Option[IdSegment],
projectRef: ProjectRef,
filename: Option[String],
mediaType: Option[ContentType],
path: Uri.Path
path: Uri.Path,
tag: Option[UserTag]
)(implicit caller: Caller): IO[FileRejection, FileResource] = {
for {
pc <- fetchContext.onCreate(projectRef)
iri <- generateId(pc)
res <- createLink(iri, projectRef, pc, storageId, filename, mediaType, path)
res <- createLink(iri, projectRef, pc, storageId, filename, mediaType, path, tag)
} yield res
}.span("createLink")

Expand All @@ -172,19 +181,22 @@ final class Files(
* the optional media type to use
* @param path
* the path where the file is located inside the storage
* @param tag
* the optional tag this file link is being created with, attached to the current revision
*/
def createLink(
id: IdSegment,
storageId: Option[IdSegment],
projectRef: ProjectRef,
filename: Option[String],
mediaType: Option[ContentType],
path: Uri.Path
path: Uri.Path,
tag: Option[UserTag]
)(implicit caller: Caller): IO[FileRejection, FileResource] = {
for {
pc <- fetchContext.onCreate(projectRef)
iri <- expandIri(id, pc)
res <- createLink(iri, projectRef, pc, storageId, filename, mediaType, path)
res <- createLink(iri, projectRef, pc, storageId, filename, mediaType, path, tag)
} yield res
}.span("createLink")

Expand Down Expand Up @@ -388,17 +400,18 @@ final class Files(
storageId: Option[IdSegment],
filename: Option[String],
mediaType: Option[ContentType],
path: Uri.Path
path: Uri.Path,
tag: Option[UserTag]
)(implicit caller: Caller): IO[FileRejection, FileResource] =
for {
_ <- test(CreateFile(iri, ref, testStorageRef, testStorageType, testAttributes, caller.subject))
_ <- test(CreateFile(iri, ref, testStorageRef, testStorageType, testAttributes, caller.subject, tag))
(storageRef, storage) <- fetchActiveStorage(storageId, ref, pc)
resolvedFilename <- IO.fromOption(filename.orElse(path.lastSegment), InvalidFileLink(iri))
description <- FileDescription(resolvedFilename, mediaType)
attributes <- LinkFile(storage, remoteDiskStorageClient, config)
.apply(path, description)
.mapError(LinkRejection(iri, storage.id, _))
res <- eval(CreateFile(iri, ref, storageRef, storage.tpe, attributes, caller.subject))
res <- eval(CreateFile(iri, ref, storageRef, storage.tpe, attributes, caller.subject, tag))
} yield res

private def eval(cmd: FileCommand): IO[FileRejection, FileResource] =
Expand Down Expand Up @@ -567,7 +580,7 @@ object Files {
): Option[FileState] = {
// format: off
def created(e: FileCreated): Option[FileState] = Option.when(state.isEmpty) {
FileState(e.id, e.project, e.storage, e.storageType, e.attributes, Tags.empty, e.rev, deprecated = false, e.instant, e.subject, e.instant, e.subject)
FileState(e.id, e.project, e.storage, e.storageType, e.attributes, Tags(e.tag, e.rev), e.rev, deprecated = false, e.instant, e.subject, e.instant, e.subject)
}

def updated(e: FileUpdated): Option[FileState] = state.map { s =>
Expand Down Expand Up @@ -607,7 +620,9 @@ object Files {

def create(c: CreateFile) = state match {
case None =>
IOUtils.instant.map(FileCreated(c.id, c.project, c.storage, c.storageType, c.attributes, 1, _, c.subject))
IOUtils.instant.map(
FileCreated(c.id, c.project, c.storage, c.storageType, c.attributes, 1, _, c.subject, c.tag)
)
case Some(_) =>
IO.raiseError(ResourceAlreadyExists(c.id, c.project))
}
Expand Down Expand Up @@ -684,6 +699,7 @@ object Files {
FileState.serializer,
Tagger[FileEvent](
{
case f: FileCreated => f.tag.flatMap(t => Some(t -> f.rev))
case f: FileTagAdded => Some(f.tag -> f.targetRev)
case _ => None
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ object FileCommand {
storage: ResourceRef.Revision,
storageType: StorageType,
attributes: FileAttributes,
subject: Subject
subject: Subject,
tag: Option[UserTag]
) extends FileCommand {
override def rev: Int = 0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ object FileEvent {
* the instant this event was created
* @param subject
* the subject which created this event
* @param tag
* an optional tag attached at creation
*/
final case class FileCreated(
id: Iri,
Expand All @@ -90,7 +92,8 @@ object FileEvent {
attributes: FileAttributes,
rev: Int,
instant: Instant,
subject: Subject
subject: Subject,
tag: Option[UserTag]
) extends FileEvent

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import ch.epfl.bluebrain.nexus.delta.sdk.implicits._
import ch.epfl.bluebrain.nexus.delta.sdk.model.routes.Tag
import ch.epfl.bluebrain.nexus.delta.sdk.model.{BaseUri, IdSegment, IdSegmentRef, ResourceF}
import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef
import ch.epfl.bluebrain.nexus.delta.sourcing.model.Tag.UserTag
import io.circe.Decoder
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.semiauto.deriveConfiguredDecoder
Expand Down Expand Up @@ -80,19 +81,19 @@ final class FilesRoutes(
concat(
(post & pathEndOrSingleSlash & noParameter("rev") & parameter(
"storage".as[IdSegment].?
) & indexingMode) { (storage, mode) =>
) & indexingMode & tagParam) { (storage, mode, tag) =>
operationName(s"$prefixSegment/files/{org}/{project}") {
concat(
// Link a file without id segment
entity(as[LinkFile]) { case LinkFile(filename, mediaType, path) =>
emit(
Created,
files.createLink(storage, ref, filename, mediaType, path).tapEval(indexUIO(ref, _, mode))
files.createLink(storage, ref, filename, mediaType, path, tag).tapEval(indexUIO(ref, _, mode))
)
},
// Create a file without id segment
extractRequestEntity { entity =>
emit(Created, files.create(storage, ref, entity).tapEval(indexUIO(ref, _, mode)))
emit(Created, files.create(storage, ref, entity, tag).tapEval(indexUIO(ref, _, mode)))
}
)
}
Expand All @@ -103,24 +104,27 @@ final class FilesRoutes(
operationName(s"$prefixSegment/files/{org}/{project}/{id}") {
concat(
(put & pathEndOrSingleSlash) {
parameters("rev".as[Int].?, "storage".as[IdSegment].?) {
case (None, storage) =>
parameters("rev".as[Int].?, "storage".as[IdSegment].?, "tag".as[UserTag].?) {
case (None, storage, tag) =>
concat(
// Link a file with id segment
entity(as[LinkFile]) { case LinkFile(filename, mediaType, path) =>
emit(
Created,
files
.createLink(id, storage, ref, filename, mediaType, path)
.createLink(id, storage, ref, filename, mediaType, path, tag)
.tapEval(indexUIO(ref, _, mode))
)
},
// Create a file with id segment
extractRequestEntity { entity =>
emit(Created, files.create(id, storage, ref, entity).tapEval(indexUIO(ref, _, mode)))
emit(
Created,
files.create(id, storage, ref, entity, tag).tapEval(indexUIO(ref, _, mode))
)
}
)
case (Some(rev), storage) =>
case (Some(rev), storage, _) =>
concat(
// Update a Link
entity(as[LinkFile]) { case LinkFile(filename, mediaType, path) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"id" : "https://bluebrain.github.io/nexus/vocabulary/file",
"project" : "myorg/myproj",
"storage" : "https://bluebrain.github.io/nexus/vocabulary/disk-storage?rev=1",
"storageType" : "DiskStorage",
"tag" : "mytag",
"attributes" : {
"origin" : "Client",
"uuid" : "8049ba90-7cc6-4de5-93a1-802c04200dcc",
"location" : "http://localhost/file.txt",
"path" : "file.txt",
"filename" : "file.txt",
"mediaType" : "text/plain; charset=UTF-8",
"bytes" : 12,
"digest" : {
"@type" : "NotComputedDigest"
}
},
"rev" : 1,
"instant" : "1970-01-01T00:00:00Z",
"subject" : {
"subject" : "username",
"realm" : "myrealm",
"@type" : "User"
},
"@type" : "FileCreated"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"project" : "myorg/myproj",
"storage" : "https://bluebrain.github.io/nexus/vocabulary/disk-storage?rev=1",
"storageType" : "DiskStorage",
"tag" : null,
"attributes" : {
"origin" : "Client",
"uuid" : "8049ba90-7cc6-4de5-93a1-802c04200dcc",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"@context": [
"https://bluebrain.github.io/nexus/contexts/metadata.json",
"https://bluebrain.github.io/nexus/contexts/files.json"
],
"@type": "FileCreated",
"tag": "mytag",
"_attributes": {
"_bytes": 12,
"_digest": {
"_value": ""
},
"_filename": "file.txt",
"_mediaType": "text/plain; charset=UTF-8",
"_origin": "Client",
"_uuid": "8049ba90-7cc6-4de5-93a1-802c04200dcc"
},
"_fileId": "https://bluebrain.github.io/nexus/vocabulary/file",
"_instant": "1970-01-01T00:00:00Z",
"_project": "http://localhost/v1/projects/myorg/myproj",
"_resourceId": "https://bluebrain.github.io/nexus/vocabulary/file",
"_rev": 1,
"_storage": {
"@id": "https://bluebrain.github.io/nexus/vocabulary/disk-storage",
"@type": "DiskStorage",
"_resourceId": "https://bluebrain.github.io/nexus/vocabulary/disk-storage",
"_rev": 1
},
"_subject": "http://localhost/v1/realms/myrealm/users/username"
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"https://bluebrain.github.io/nexus/contexts/files.json"
],
"@type": "FileCreated",
"tag": null,
"_attributes": {
"_bytes": 12,
"_digest": {
Expand Down
Loading

0 comments on commit 764c121

Please sign in to comment.