Skip to content

Commit

Permalink
Allow user to provide content type for S3 file registration
Browse files Browse the repository at this point in the history
  • Loading branch information
shinyhappydan committed Apr 24, 2024
1 parent 54fe6bc commit d8f9525
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package ch.epfl.bluebrain.nexus.delta.plugins.storage.files
import akka.actor.typed.ActorSystem
import akka.actor.{ActorSystem => ClassicActorSystem}
import akka.http.scaladsl.model.ContentTypes.`application/octet-stream`
import akka.http.scaladsl.model.{BodyPartEntity, HttpEntity, Uri}
import akka.http.scaladsl.model.{BodyPartEntity, ContentType, HttpEntity, Uri}
import cats.effect.{Clock, IO}
import cats.syntax.all._
import ch.epfl.bluebrain.nexus.delta.kernel.kamon.KamonMetricComponent
Expand Down Expand Up @@ -223,14 +223,18 @@ final class Files(
storageId: Option[IdSegment],
metadata: Option[FileCustomMetadata],
path: Uri.Path,
tag: Option[UserTag]
tag: Option[UserTag],
mediaType: Option[ContentType]
)(implicit caller: Caller): IO[FileResource] = {
for {
(iri, pc) <- id.expandIri(fetchContext.onCreate)
(storageRef, storage) <- fetchAndValidateActiveStorage(storageId, id.project, pc)
s3Metadata <- fileOperations.register(storage, path)
filename <- IO.fromOption(path.lastSegment)(InvalidFilePath)
attr = FileAttributes.from(FileDescription(filename, s3Metadata.contentType.some, metadata), s3Metadata.metadata)
attr = FileAttributes.from(
FileDescription(filename, Some(mediaType.getOrElse(s3Metadata.contentType)), metadata),
s3Metadata.metadata
)
res <- eval(
CreateFile(
iri,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ final class FilesRoutes(
storage,
registerRequest.metadata,
registerRequest.path,
tag
tag,
registerRequest.mediaType
)
.index(mode)
.attemptNarrow[FileRejection]
Expand Down Expand Up @@ -355,7 +356,7 @@ object FilesRoutes {
metadata: Option[FileCustomMetadata]
)

final case class RegisterFileRequest(path: Path, metadata: Option[FileCustomMetadata])
final case class RegisterFileRequest(path: Path, mediaType: Option[ContentType], metadata: Option[FileCustomMetadata])

object LinkFileRequest {
@nowarn("cat=unused")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,19 @@
package ch.epfl.bluebrain.nexus.testkit.scalatest

import ch.epfl.bluebrain.nexus.testkit.scalatest.JsonMatchers.field
import io.circe.Json
import org.scalatest.matchers.{HavePropertyMatchResult, HavePropertyMatcher}
import org.scalatest.matchers.HavePropertyMatcher

object FileMatchers {

def keywords(expected: (String, String)*): HavePropertyMatcher[Json, Map[String, String]] = keywords(expected.toMap)

def keywords(expected: Map[String, String]): HavePropertyMatcher[Json, Map[String, String]] = HavePropertyMatcher {
json =>
val actual = json.hcursor.downField("_keywords").as[Map[String, String]].toOption
HavePropertyMatchResult(
actual.contains(expected),
"keywords",
expected,
actual.orNull
)
}
def keywords(expected: Map[String, String]): HavePropertyMatcher[Json, Map[String, String]] =
field("_keywords", expected)

def description(expected: String): HavePropertyMatcher[Json, String] = HavePropertyMatcher { json =>
val actual = json.hcursor.downField("description").as[String].toOption
HavePropertyMatchResult(
actual.contains(expected),
"description",
expected,
actual.orNull
)
}
def description(expected: String): HavePropertyMatcher[Json, String] = field("description", expected)

def name(expected: String): HavePropertyMatcher[Json, String] = HavePropertyMatcher { json =>
val actual = json.hcursor.downField("name").as[String].toOption
HavePropertyMatchResult(
actual.contains(expected),
"name",
expected,
actual.orNull
)
}
def name(expected: String): HavePropertyMatcher[Json, String] = field("name", expected)

def mediaType(expected: String): HavePropertyMatcher[Json, String] = field("_mediaType", expected)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ch.epfl.bluebrain.nexus.testkit.scalatest

import io.circe.{Decoder, Json}
import org.scalatest.matchers.{HavePropertyMatchResult, HavePropertyMatcher}

import scala.reflect.ClassTag

object JsonMatchers {
def field[A: Decoder: ClassTag](key: String, expectedValue: A)(implicit
ev: Null <:< A
): HavePropertyMatcher[Json, A] = HavePropertyMatcher { json =>
val actual = json.hcursor.downField(key).as[A].toOption
HavePropertyMatchResult(
actual.contains(expectedValue),
key,
expectedValue,
actual.orNull
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package ch.epfl.bluebrain.nexus.tests.kg.files
import akka.http.scaladsl.model.StatusCodes
import cats.effect.IO
import cats.implicits.toTraverseOps
import ch.epfl.bluebrain.nexus.testkit.scalatest.FileMatchers.{mediaType => mediaTypeField}
import ch.epfl.bluebrain.nexus.tests.Identity.storages.Coyote
import ch.epfl.bluebrain.nexus.tests.Optics.{error, filterMetadataKeys}
import ch.epfl.bluebrain.nexus.tests.config.S3Config
import ch.epfl.bluebrain.nexus.tests.iam.types.Permission
import io.circe.Json
import io.circe.syntax.EncoderOps
import io.circe.syntax.{EncoderOps, KeyOps}
import io.laserdisc.pure.s3.tagless.Interpreter
import org.apache.commons.codec.binary.Hex
import org.scalatest.Assertion
Expand Down Expand Up @@ -196,10 +197,26 @@ class S3StorageSpec extends StorageSpec {
}
fullId = s"$attachmentPrefix$id"
location = s"$s3BucketEndpoint/$path"
expected = registrationResponse(fullId, logoSha256HexDigest, location)
assertion <- deltaClient.get[Json](s"/files/$projectRef/$id", Coyote) { (json, response) =>
response.status shouldEqual StatusCodes.OK
filterMetadataKeys(json) shouldEqual expected
filterMetadataKeys(json) shouldEqual registrationResponse(fullId, logoSha256HexDigest, location)
}
} yield assertion
}

"succeed when a content type is supplied" in {
val id = genId()
val path = s"$id/nexus-logo.png"
val payload = Json.obj("path" := path, "mediaType" := "image/dan")

for {
_ <- uploadLogoFile(path)
_ <- deltaClient.put[Json](s"/files/$projectRef/register/$id?storage=nxv:$storageId", payload, Coyote) {
(_, response) => response.status shouldEqual StatusCodes.Created
}
assertion <- deltaClient.get[Json](s"/files/$projectRef/$id", Coyote) { (json, response) =>
response.status shouldEqual StatusCodes.OK
json should have(mediaTypeField("image/dan"))
}
} yield assertion
}
Expand Down

0 comments on commit d8f9525

Please sign in to comment.