-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement custom media type detection (#4312)
* Implement custom content type detection --------- Co-authored-by: Simon Dumas <[email protected]>
- Loading branch information
Showing
53 changed files
with
994 additions
and
509 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
...el/src/main/scala/ch/epfl/bluebrain/nexus/delta/kernel/http/MediaTypeDetectorConfig.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package ch.epfl.bluebrain.nexus.delta.kernel.http | ||
|
||
import akka.http.scaladsl.model.MediaType | ||
import cats.syntax.all._ | ||
import pureconfig.ConfigReader | ||
import pureconfig.configurable.genericMapReader | ||
import pureconfig.error.CannotConvert | ||
|
||
/** | ||
* Allows to define custom media types for the given extensions | ||
*/ | ||
final case class MediaTypeDetectorConfig(extensions: Map[String, MediaType]) { | ||
def find(extension: String): Option[MediaType] = extensions.get(extension) | ||
|
||
} | ||
|
||
object MediaTypeDetectorConfig { | ||
|
||
val Empty = new MediaTypeDetectorConfig(Map.empty) | ||
|
||
def apply(values: (String, MediaType)*) = new MediaTypeDetectorConfig(values.toMap) | ||
|
||
implicit final val mediaTypeDetectorConfigReader: ConfigReader[MediaTypeDetectorConfig] = { | ||
implicit val mediaTypeConfigReader: ConfigReader[MediaType] = | ||
ConfigReader.fromString(str => | ||
MediaType | ||
.parse(str) | ||
.leftMap(_ => CannotConvert(str, classOf[MediaType].getSimpleName, s"'$str' is not a valid content type.")) | ||
) | ||
implicit val mapReader: ConfigReader[Map[String, MediaType]] = genericMapReader(Right(_)) | ||
|
||
ConfigReader.fromCursor { cursor => | ||
for { | ||
obj <- cursor.asObjectCursor | ||
extensionsKey <- obj.atKey("extensions") | ||
extensions <- ConfigReader[Map[String, MediaType]].from(extensionsKey) | ||
} yield MediaTypeDetectorConfig(extensions) | ||
} | ||
} | ||
|
||
} |
15 changes: 15 additions & 0 deletions
15
delta/kernel/src/main/scala/ch/epfl/bluebrain/nexus/delta/kernel/utils/FileUtils.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package ch.epfl.bluebrain.nexus.delta.kernel.utils | ||
|
||
object FileUtils { | ||
|
||
/** | ||
* Extracts the extension from the given filename | ||
*/ | ||
def extension(filename: String): Option[String] = { | ||
val lastDotIndex = filename.lastIndexOf('.') | ||
Option.when(lastDotIndex >= 0) { | ||
filename.substring(lastDotIndex + 1) | ||
} | ||
} | ||
|
||
} |
54 changes: 54 additions & 0 deletions
54
...c/test/scala/ch/epfl/bluebrain/nexus/delta/kernel/http/MediaTypeDetectorConfigSuite.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package ch.epfl.bluebrain.nexus.delta.kernel.http | ||
|
||
import akka.http.scaladsl.model.ContentTypes | ||
import munit.FunSuite | ||
import pureconfig.ConfigSource | ||
|
||
class MediaTypeDetectorConfigSuite extends FunSuite { | ||
|
||
private def parseConfig(value: String) = | ||
ConfigSource.string(value).at("media-type-detector").load[MediaTypeDetectorConfig] | ||
|
||
test("Parse successfully the config with no defined extension") { | ||
val config = parseConfig( | ||
""" | ||
|media-type-detector { | ||
| extensions { | ||
| } | ||
|} | ||
|""".stripMargin | ||
) | ||
|
||
val expected = MediaTypeDetectorConfig.Empty | ||
assertEquals(config, Right(expected)) | ||
} | ||
|
||
test("Parse successfully the config") { | ||
val config = parseConfig( | ||
""" | ||
|media-type-detector { | ||
| extensions { | ||
| json = application/json | ||
| } | ||
|} | ||
|""".stripMargin | ||
) | ||
|
||
val expected = MediaTypeDetectorConfig("json" -> ContentTypes.`application/json`.mediaType) | ||
assertEquals(config, Right(expected)) | ||
} | ||
|
||
test("Fail to parse the config with an invalid content type") { | ||
val config = parseConfig( | ||
""" | ||
|media-type-detector { | ||
| extensions { | ||
| json = xxx | ||
| } | ||
|} | ||
|""".stripMargin | ||
) | ||
|
||
assert(config.isLeft, "Parsing must fail with an invalid content type") | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
delta/kernel/src/test/scala/ch/epfl/bluebrain/nexus/delta/kernel/utils/FileUtilsSuite.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package ch.epfl.bluebrain.nexus.delta.kernel.utils | ||
|
||
import munit.FunSuite | ||
|
||
class FileUtilsSuite extends FunSuite { | ||
|
||
test("Detect json extension") { | ||
val obtained = FileUtils.extension("my-file.json") | ||
val expected = Some("json") | ||
assertEquals(obtained, expected) | ||
} | ||
|
||
test("Detect zip extension") { | ||
val obtained = FileUtils.extension("my-file.json.zip") | ||
val expected = Some("zip") | ||
assertEquals(obtained, expected) | ||
} | ||
|
||
test("Detect no extension") { | ||
val obtained = FileUtils.extension("my-file") | ||
val expected = None | ||
assertEquals(obtained, expected) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.